1use animsmith_core::Document;
4use animsmith_core::scale::{ScaleCapabilityCoverage, ScaleCapabilityFacts};
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11#[non_exhaustive]
12pub enum FbxScaleDomainStatus {
13 Absent,
15 Normalized,
17 Baked,
19 Derived,
21 Rebuilt,
23 Unsupported,
25 Unverifiable,
27}
28
29#[derive(Debug, Clone, PartialEq, Eq)]
31#[non_exhaustive]
32pub struct FbxScaleDomainInventory {
33 pub rest_hierarchy: FbxScaleDomainStatus,
35 pub translation_animation: FbxScaleDomainStatus,
37 pub rotation_and_scale_animation: FbxScaleDomainStatus,
39 pub root_motion_and_velocity: FbxScaleDomainStatus,
41 pub base_mesh_geometry: FbxScaleDomainStatus,
43 pub morphs: FbxScaleDomainStatus,
45 pub skin_binds: FbxScaleDomainStatus,
47 pub cameras_and_lights: FbxScaleDomainStatus,
49 pub collision_and_custom_data: FbxScaleDomainStatus,
51 pub other_vertex_and_source_data: FbxScaleDomainStatus,
53 pub out_of_contract_node_transforms: FbxScaleDomainStatus,
55 pub animation_targeting_matrix_nodes: FbxScaleDomainStatus,
57 pub shared_raw_accessor_payloads: FbxScaleDomainStatus,
59 pub unreferenced_accessor_payloads: FbxScaleDomainStatus,
61 pub image_payload_aliases: FbxScaleDomainStatus,
63}
64
65impl FbxScaleDomainInventory {
66 pub fn named_rows(&self) -> [(&'static str, FbxScaleDomainStatus); 15] {
72 [
73 ("Rest hierarchy", self.rest_hierarchy),
74 ("Translation animation", self.translation_animation),
75 (
76 "Rotation and scale animation",
77 self.rotation_and_scale_animation,
78 ),
79 ("Root motion and velocity", self.root_motion_and_velocity),
80 ("Base mesh geometry", self.base_mesh_geometry),
81 ("Morphs", self.morphs),
82 ("Skin binds", self.skin_binds),
83 ("Cameras/lights", self.cameras_and_lights),
84 ("Collision/custom data", self.collision_and_custom_data),
85 (
86 "Other vertex/source data",
87 self.other_vertex_and_source_data,
88 ),
89 (
90 "Out-of-contract node transforms",
91 self.out_of_contract_node_transforms,
92 ),
93 (
94 "Animation targeting a matrix node",
95 self.animation_targeting_matrix_nodes,
96 ),
97 (
98 "Shared raw accessor payloads",
99 self.shared_raw_accessor_payloads,
100 ),
101 (
102 "Unreferenced accessor payloads",
103 self.unreferenced_accessor_payloads,
104 ),
105 ("Image payload aliases", self.image_payload_aliases),
106 ]
107 }
108}
109
110#[derive(Debug, Clone, Copy, PartialEq, Eq)]
112#[non_exhaustive]
113pub enum FbxCoordinateAxis {
114 PositiveX,
116 NegativeX,
118 PositiveY,
120 NegativeY,
122 PositiveZ,
124 NegativeZ,
126 Unknown,
128}
129
130impl From<ufbx::CoordinateAxis> for FbxCoordinateAxis {
131 fn from(value: ufbx::CoordinateAxis) -> Self {
132 match value {
133 ufbx::CoordinateAxis::PositiveX => Self::PositiveX,
134 ufbx::CoordinateAxis::NegativeX => Self::NegativeX,
135 ufbx::CoordinateAxis::PositiveY => Self::PositiveY,
136 ufbx::CoordinateAxis::NegativeY => Self::NegativeY,
137 ufbx::CoordinateAxis::PositiveZ => Self::PositiveZ,
138 ufbx::CoordinateAxis::NegativeZ => Self::NegativeZ,
139 ufbx::CoordinateAxis::Unknown => Self::Unknown,
140 }
141 }
142}
143
144#[derive(Debug, Clone, PartialEq)]
146#[non_exhaustive]
147pub struct FbxCoordinateNormalization {
148 pub original_up_axis: FbxCoordinateAxis,
150 pub original_unit_meters: f64,
152 pub target_right_handed_y_up: bool,
154 pub target_unit_meters: f64,
156 pub adjust_transforms: bool,
158}
159
160#[derive(Debug, Clone, Copy, PartialEq, Eq)]
162#[non_exhaustive]
163pub struct FbxSourceIdentity {
164 pub source_index: usize,
166 pub ufbx_typed_id: u32,
168 pub ufbx_element_id: u32,
173}
174
175#[derive(Debug, Clone, Copy, PartialEq, Eq)]
177#[non_exhaustive]
178pub enum FbxBindMatrixProvenance {
179 UfbxConvertedClusterMatrices,
182}
183
184#[derive(Debug, Clone, PartialEq)]
191#[non_exhaustive]
192pub struct FbxScaleCapabilityInventory {
193 pub domains: FbxScaleDomainInventory,
195 pub coordinate_normalization: FbxCoordinateNormalization,
197 pub animation_takes_baked: bool,
199 pub authored_curve_keys_preserved: bool,
201 pub animation_take_count: usize,
203 pub source_animation_curve_count: usize,
205 pub generated_geometry_helper_node_count: usize,
207 pub generated_scale_helper_node_count: usize,
209 pub inherit_modes_compensated: bool,
211 pub compensated_inherit_node_count: usize,
213 pub generated_normal_mesh_count: usize,
215 pub missing_normal_mesh_count: usize,
217 pub skin_deformer_count: usize,
219 pub skin_cluster_count: usize,
221 pub empty_skin_deformer_count: usize,
223 pub bind_matrix_provenance: FbxBindMatrixProvenance,
225 pub incomplete_bind_cluster_count: usize,
227 pub bone_convenience_bind_overwrite_count: usize,
230 pub identity_bind_defaults_invented: bool,
232 pub truncated_influence_vertex_count: usize,
234 pub discarded_influence_count: usize,
236 pub renormalized_influence_vertex_count: usize,
238 pub rejected_influence_count: usize,
240 pub missing_skin_influence_corner_count: usize,
242 pub non_triangle_face_count: usize,
244 pub triangulated_face_count: usize,
246 pub omitted_non_polygon_face_count: usize,
248 pub empty_mesh_definition_count: usize,
250 pub empty_source_meshes: Vec<FbxSourceIdentity>,
252 pub pre_weld_vertex_count: usize,
254 pub post_weld_vertex_count: usize,
256 pub multiple_skin_deformer_mesh_count: usize,
258 pub dual_quaternion_skin_count: usize,
260 pub blend_deformer_count: usize,
262 pub blend_channel_count: usize,
264 pub blend_shape_count: usize,
266 pub cache_deformer_count: usize,
268 pub unsupported_vertex_payload_mesh_count: usize,
270 pub camera_count: usize,
272 pub light_count: usize,
274 pub shared_mesh_definition_count: usize,
276 pub uninstanced_mesh_definition_count: usize,
278 pub uninstanced_source_meshes: Vec<FbxSourceIdentity>,
280 pub user_defined_property_count: usize,
282 pub unsupported_source_element_count: usize,
284 pub external_resource_count: usize,
286 pub source_nodes: Vec<FbxSourceIdentity>,
288 pub source_meshes: Vec<FbxSourceIdentity>,
290 pub source_skins: Vec<FbxSourceIdentity>,
292}
293
294#[derive(Debug, Clone)]
296pub struct FbxScaleSource {
297 pub(crate) document: Document,
298 pub(crate) inventory: FbxScaleCapabilityInventory,
299}
300
301impl FbxScaleSource {
302 pub fn document(&self) -> &Document {
304 &self.document
305 }
306
307 pub fn inventory(&self) -> &FbxScaleCapabilityInventory {
309 &self.inventory
310 }
311
312 pub fn into_document(self) -> Document {
314 self.document
315 }
316}
317
318pub fn capability_facts(inventory: &FbxScaleCapabilityInventory) -> ScaleCapabilityFacts {
325 let mut facts = ScaleCapabilityFacts::default();
326 facts.coverage = ScaleCapabilityCoverage::Complete;
327 let morph_source_present = inventory.blend_deformer_count > 0
328 || inventory.blend_channel_count > 0
329 || inventory.blend_shape_count > 0;
330 facts.morphs_present = morph_source_present;
331 facts.morph_weights_present = morph_source_present;
332 facts.cameras_present = inventory.camera_count > 0;
333 facts.lights_present = inventory.light_count > 0;
334 facts.instancing_present = inventory.shared_mesh_definition_count > 0;
335 facts.unregistered_extensions_present = inventory.unsupported_source_element_count > 0;
336 facts.extras_present = inventory.user_defined_property_count > 0;
337 facts.unknown_source_members_present = true;
341 facts.non_triangle_primitives_present = inventory.non_triangle_face_count > 0;
342 facts.unsupported_vertex_attributes_present = inventory.unsupported_vertex_payload_mesh_count
343 > 0
344 || inventory.uninstanced_mesh_definition_count > 0
345 || inventory.empty_mesh_definition_count > 0
346 || inventory.multiple_skin_deformer_mesh_count > 0
347 || inventory.dual_quaternion_skin_count > 0
348 || inventory.cache_deformer_count > 0
349 || inventory.missing_skin_influence_corner_count > 0
350 || inventory.rejected_influence_count > 0
351 || inventory.pre_weld_vertex_count != inventory.post_weld_vertex_count;
352 facts.secondary_skin_influences_present = inventory.truncated_influence_vertex_count > 0;
353 facts.inverse_bind_issues_present =
354 inventory.incomplete_bind_cluster_count > 0 || inventory.empty_skin_deformer_count > 0;
355 facts.unsafe_accessor_layout_present = true;
359 facts.external_resources_present = inventory.external_resource_count > 0;
360 facts
361}
362
363#[derive(Debug, Default)]
364pub(crate) struct AssetConversionFacts {
365 pub(crate) truncated_influence_vertex_count: usize,
366 pub(crate) discarded_influence_count: usize,
367 pub(crate) renormalized_influence_vertex_count: usize,
368 pub(crate) rejected_influence_count: usize,
369 pub(crate) missing_skin_influence_corner_count: usize,
370 pub(crate) pre_weld_vertex_count: usize,
371 pub(crate) post_weld_vertex_count: usize,
372}
373
374fn identity(index: usize, element: &ufbx::Element) -> FbxSourceIdentity {
375 FbxSourceIdentity {
376 source_index: index,
377 ufbx_typed_id: element.typed_id,
378 ufbx_element_id: element.element_id,
379 }
380}
381
382pub(crate) fn inventory(
383 scene: &ufbx::Scene,
384 conversion: &AssetConversionFacts,
385) -> FbxScaleCapabilityInventory {
386 let non_triangle_face_count = scene
387 .meshes
388 .iter()
389 .flat_map(|mesh| mesh.faces.iter())
390 .filter(|face| face.num_indices != 3)
391 .count();
392 let triangulated_face_count = scene
393 .meshes
394 .iter()
395 .flat_map(|mesh| mesh.faces.iter())
396 .filter(|face| face.num_indices > 3)
397 .count();
398 let omitted_non_polygon_face_count = scene
399 .meshes
400 .iter()
401 .flat_map(|mesh| mesh.faces.iter())
402 .filter(|face| face.num_indices < 3)
403 .count();
404 let empty_source_meshes = scene
405 .meshes
406 .iter()
407 .enumerate()
408 .filter(|(_, mesh)| mesh.faces.is_empty())
409 .map(|(index, mesh)| identity(index, &mesh.element))
410 .collect::<Vec<_>>();
411 let empty_mesh_definition_count = empty_source_meshes.len();
412 let generated_normal_mesh_count = scene
413 .meshes
414 .iter()
415 .filter(|mesh| mesh.generated_normals)
416 .count();
417 let missing_normal_mesh_count = scene
418 .meshes
419 .iter()
420 .filter(|mesh| !mesh.vertex_normal.exists)
421 .count();
422 let skin_cluster_count = scene
423 .skin_deformers
424 .iter()
425 .map(|skin| skin.clusters.len())
426 .sum();
427 let empty_skin_deformer_count = scene
428 .skin_deformers
429 .iter()
430 .filter(|skin| skin.clusters.is_empty())
431 .count();
432 let incomplete_bind_cluster_count = scene
433 .skin_clusters
434 .iter()
435 .filter(|cluster| super::project_cluster_bind(cluster).is_none())
436 .count();
437 let mut clusters_per_bone = std::collections::BTreeMap::<u32, usize>::new();
438 for cluster in &scene.skin_clusters {
439 if let (Some(node), Some(_)) = (&cluster.bone_node, super::project_cluster_bind(cluster)) {
440 *clusters_per_bone.entry(node.element.typed_id).or_default() += 1;
441 }
442 }
443 let bone_convenience_bind_overwrite_count = clusters_per_bone
444 .values()
445 .map(|count| count.saturating_sub(1))
446 .sum();
447 let multiple_skin_deformer_mesh_count = scene
448 .meshes
449 .iter()
450 .filter(|mesh| mesh.skin_deformers.len() > 1)
451 .count();
452 let dual_quaternion_skin_count = scene
453 .skin_deformers
454 .iter()
455 .filter(|skin| {
456 skin.num_dq_weights > 0 || !matches!(skin.skinning_method, ufbx::SkinningMethod::Linear)
457 })
458 .count();
459 let unsupported_vertex_payload_mesh_count = scene
460 .meshes
461 .iter()
462 .filter(|mesh| mesh_has_unsupported_source_payload(mesh))
463 .count();
464 let shared_mesh_definition_count = scene
465 .meshes
466 .iter()
467 .filter(|mesh| mesh.element.instances.len() > 1)
468 .count();
469 let uninstanced_source_meshes = scene
470 .meshes
471 .iter()
472 .enumerate()
473 .filter(|(_, mesh)| mesh.element.instances.is_empty())
474 .map(|(index, mesh)| identity(index, &mesh.element))
475 .collect::<Vec<_>>();
476 let uninstanced_mesh_definition_count = uninstanced_source_meshes.len();
477 let user_defined_property_count = scene
478 .elements
479 .iter()
480 .flat_map(|element| element.props.props.iter())
481 .filter(|prop| prop.flags.has_any(ufbx::PropFlags::USER_DEFINED))
482 .count();
483 let unsupported_source_element_count = unsupported_source_element_count(scene);
484 let external_resource_count = scene
485 .textures
486 .iter()
487 .filter(|texture| texture.content.is_empty() && texture.has_file)
488 .count()
489 + scene
490 .videos
491 .iter()
492 .filter(|video| {
493 video.content.is_empty()
494 && (!video.filename.is_empty()
495 || !video.relative_filename.is_empty()
496 || !video.absolute_filename.is_empty())
497 })
498 .count();
499 let compensated_inherit_node_count = scene
500 .nodes
501 .iter()
502 .filter(|node| {
503 node.original_inherit_mode != node.inherit_mode
504 || node.is_scale_helper
505 || node.is_scale_compensate_parent
506 })
507 .count();
508
509 let stackless_animation_present = scene.anim_stacks.is_empty()
510 && (!scene.anim_layers.is_empty()
511 || !scene.anim_values.is_empty()
512 || !scene.anim_curves.is_empty());
513 let animation = if !scene.anim_stacks.is_empty() {
514 FbxScaleDomainStatus::Baked
515 } else if stackless_animation_present {
516 FbxScaleDomainStatus::Unsupported
519 } else {
520 FbxScaleDomainStatus::Absent
521 };
522 let domains = FbxScaleDomainInventory {
523 rest_hierarchy: FbxScaleDomainStatus::Normalized,
524 translation_animation: animation,
525 rotation_and_scale_animation: animation,
526 root_motion_and_velocity: match animation {
527 FbxScaleDomainStatus::Baked => FbxScaleDomainStatus::Derived,
528 status => status,
529 },
530 base_mesh_geometry: if scene.meshes.is_empty() {
531 FbxScaleDomainStatus::Absent
532 } else if uninstanced_mesh_definition_count > 0
533 || omitted_non_polygon_face_count > 0
534 || empty_mesh_definition_count > 0
535 {
536 FbxScaleDomainStatus::Unsupported
537 } else {
538 FbxScaleDomainStatus::Rebuilt
539 },
540 morphs: if scene.blend_deformers.is_empty()
541 && scene.blend_channels.is_empty()
542 && scene.blend_shapes.is_empty()
543 {
544 FbxScaleDomainStatus::Absent
545 } else {
546 FbxScaleDomainStatus::Unsupported
547 },
548 skin_binds: if scene.skin_deformers.is_empty() {
549 FbxScaleDomainStatus::Absent
550 } else if incomplete_bind_cluster_count > 0 || empty_skin_deformer_count > 0 {
551 FbxScaleDomainStatus::Unsupported
552 } else {
553 FbxScaleDomainStatus::Derived
554 },
555 cameras_and_lights: if scene.cameras.is_empty() && scene.lights.is_empty() {
556 FbxScaleDomainStatus::Absent
557 } else {
558 FbxScaleDomainStatus::Unsupported
559 },
560 collision_and_custom_data: if unsupported_source_element_count == 0
561 && user_defined_property_count == 0
562 {
563 FbxScaleDomainStatus::Absent
564 } else {
565 FbxScaleDomainStatus::Unsupported
566 },
567 other_vertex_and_source_data: if unsupported_vertex_payload_mesh_count > 0
568 || uninstanced_mesh_definition_count > 0
569 || omitted_non_polygon_face_count > 0
570 || empty_mesh_definition_count > 0
571 || multiple_skin_deformer_mesh_count > 0
572 || dual_quaternion_skin_count > 0
573 || conversion.truncated_influence_vertex_count > 0
574 || conversion.missing_skin_influence_corner_count > 0
575 || conversion.rejected_influence_count > 0
576 || !scene.blend_deformers.is_empty()
577 || !scene.blend_channels.is_empty()
578 || !scene.blend_shapes.is_empty()
579 || !scene.cache_deformers.is_empty()
580 || !scene.cache_files.is_empty()
581 {
582 FbxScaleDomainStatus::Unsupported
583 } else if !scene.meshes.is_empty() {
584 FbxScaleDomainStatus::Rebuilt
585 } else {
586 FbxScaleDomainStatus::Absent
587 },
588 out_of_contract_node_transforms: FbxScaleDomainStatus::Normalized,
589 animation_targeting_matrix_nodes: animation,
590 shared_raw_accessor_payloads: FbxScaleDomainStatus::Unverifiable,
591 unreferenced_accessor_payloads: FbxScaleDomainStatus::Unverifiable,
592 image_payload_aliases: FbxScaleDomainStatus::Unverifiable,
593 };
594
595 FbxScaleCapabilityInventory {
596 domains,
597 coordinate_normalization: FbxCoordinateNormalization {
598 original_up_axis: scene.settings.original_axis_up.into(),
599 original_unit_meters: scene.settings.original_unit_meters,
600 target_right_handed_y_up: true,
601 target_unit_meters: 1.0,
602 adjust_transforms: matches!(
603 scene.metadata.space_conversion,
604 ufbx::SpaceConversion::AdjustTransforms
605 ),
606 },
607 animation_takes_baked: true,
608 authored_curve_keys_preserved: false,
609 animation_take_count: scene.anim_stacks.len(),
610 source_animation_curve_count: scene.anim_curves.len(),
611 generated_geometry_helper_node_count: scene
612 .nodes
613 .iter()
614 .filter(|node| node.is_geometry_transform_helper)
615 .count(),
616 generated_scale_helper_node_count: scene
617 .nodes
618 .iter()
619 .filter(|node| node.is_scale_helper)
620 .count(),
621 inherit_modes_compensated: matches!(
622 scene.metadata.inherit_mode_handling,
623 ufbx::InheritModeHandling::Compensate
624 ),
625 compensated_inherit_node_count,
626 generated_normal_mesh_count,
627 missing_normal_mesh_count,
628 skin_deformer_count: scene.skin_deformers.len(),
629 skin_cluster_count,
630 empty_skin_deformer_count,
631 bind_matrix_provenance: FbxBindMatrixProvenance::UfbxConvertedClusterMatrices,
632 incomplete_bind_cluster_count,
633 bone_convenience_bind_overwrite_count,
634 identity_bind_defaults_invented: false,
635 truncated_influence_vertex_count: conversion.truncated_influence_vertex_count,
636 discarded_influence_count: conversion.discarded_influence_count,
637 renormalized_influence_vertex_count: conversion.renormalized_influence_vertex_count,
638 rejected_influence_count: conversion.rejected_influence_count,
639 missing_skin_influence_corner_count: conversion.missing_skin_influence_corner_count,
640 non_triangle_face_count,
641 triangulated_face_count,
642 omitted_non_polygon_face_count,
643 empty_mesh_definition_count,
644 empty_source_meshes,
645 pre_weld_vertex_count: conversion.pre_weld_vertex_count,
646 post_weld_vertex_count: conversion.post_weld_vertex_count,
647 multiple_skin_deformer_mesh_count,
648 dual_quaternion_skin_count,
649 blend_deformer_count: scene.blend_deformers.len(),
650 blend_channel_count: scene.blend_channels.len(),
651 blend_shape_count: scene.blend_shapes.len(),
652 cache_deformer_count: scene.cache_deformers.len(),
653 unsupported_vertex_payload_mesh_count,
654 camera_count: scene.cameras.len(),
655 light_count: scene.lights.len(),
656 shared_mesh_definition_count,
657 uninstanced_mesh_definition_count,
658 uninstanced_source_meshes,
659 user_defined_property_count,
660 unsupported_source_element_count,
661 external_resource_count,
662 source_nodes: scene
663 .nodes
664 .iter()
665 .enumerate()
666 .map(|(index, node)| identity(index, &node.element))
667 .collect(),
668 source_meshes: scene
669 .meshes
670 .iter()
671 .enumerate()
672 .map(|(index, mesh)| identity(index, &mesh.element))
673 .collect(),
674 source_skins: scene
675 .skin_deformers
676 .iter()
677 .enumerate()
678 .map(|(index, skin)| identity(index, &skin.element))
679 .collect(),
680 }
681}
682
683fn mesh_has_unsupported_source_payload(mesh: &ufbx::Mesh) -> bool {
687 let ufbx::Mesh {
688 element: _,
689 num_vertices: _,
690 num_indices: _,
691 num_faces: _,
692 num_triangles: _,
693 num_edges: _,
694 max_face_triangles: _,
695 num_empty_faces: _,
696 num_point_faces: _,
697 num_line_faces: _,
698 faces: _,
699 face_smoothing,
701 face_material: _,
702 face_group,
703 face_hole,
704 edges,
705 edge_smoothing,
706 edge_crease,
707 edge_visibility,
708 vertex_indices: _,
709 vertices: _,
710 vertex_first_index: _,
711 vertex_position: _,
712 vertex_normal: _,
713 vertex_uv: _,
714 vertex_tangent,
715 vertex_bitangent,
716 vertex_color,
717 vertex_crease,
718 uv_sets,
719 color_sets,
720 materials: _,
721 face_groups,
722 material_parts: _,
724 face_group_parts: _,
725 material_part_usage_order: _,
726 skinned_is_local: _,
727 skinned_position: _,
728 skinned_normal: _,
729 skin_deformers: _,
731 blend_deformers: _,
732 cache_deformers: _,
733 all_deformers: _,
734 subdivision_preview_levels,
735 subdivision_render_levels,
736 subdivision_display_mode,
737 subdivision_boundary,
738 subdivision_uv_boundary,
739 reversed_winding: _,
741 generated_normals: _,
742 subdivision_evaluated,
743 subdivision_result,
744 from_tessellated_nurbs,
745 } = mesh;
746
747 !face_smoothing.is_empty()
748 || !face_group.is_empty()
749 || !face_hole.is_empty()
750 || !edges.is_empty()
751 || !edge_smoothing.is_empty()
752 || !edge_crease.is_empty()
753 || !edge_visibility.is_empty()
754 || vertex_tangent.exists
755 || vertex_bitangent.exists
756 || vertex_color.exists
757 || vertex_crease.exists
758 || uv_sets.len() > 1
759 || !color_sets.is_empty()
760 || !face_groups.is_empty()
761 || *subdivision_preview_levels > 0
762 || *subdivision_render_levels > 0
763 || !matches!(
764 subdivision_display_mode,
765 ufbx::SubdivisionDisplayMode::Disabled
766 )
767 || !matches!(subdivision_boundary, ufbx::SubdivisionBoundary::Default)
768 || !matches!(subdivision_uv_boundary, ufbx::SubdivisionBoundary::Default)
769 || *subdivision_evaluated
770 || subdivision_result.is_some()
771 || *from_tessellated_nurbs
772}
773
774fn unsupported_source_element_count(scene: &ufbx::Scene) -> usize {
778 let ufbx::Scene {
779 metadata: _,
780 settings: _,
781 root_node: _,
782 anim: _,
783 unknowns,
785 nodes: _,
786 meshes: _,
787 lights: _,
789 cameras: _,
790 bones: _,
792 empties: _,
793 line_curves,
794 nurbs_curves,
795 nurbs_surfaces,
796 nurbs_trim_surfaces,
797 nurbs_trim_boundaries,
798 procedural_geometries,
799 stereo_cameras,
800 camera_switchers,
801 markers,
802 lod_groups,
803 skin_deformers: _,
805 skin_clusters: _,
806 blend_deformers: _,
809 blend_channels,
810 blend_shapes,
811 cache_deformers: _,
812 cache_files,
813 materials: _,
816 textures: _,
817 videos: _,
818 shaders,
819 shader_bindings,
820 anim_stacks: _,
822 anim_layers: _,
823 anim_values: _,
824 anim_curves: _,
825 display_layers,
826 selection_sets,
827 selection_nodes,
828 characters,
829 constraints,
830 audio_layers,
831 audio_clips,
832 poses,
833 metadata_objects,
834 texture_files,
837 elements: _,
840 connections_src: _,
841 connections_dst: _,
842 elements_by_name: _,
843 dom_root: _,
844 } = scene;
845
846 unknowns.len()
847 + line_curves.len()
848 + nurbs_curves.len()
849 + nurbs_surfaces.len()
850 + nurbs_trim_surfaces.len()
851 + nurbs_trim_boundaries.len()
852 + procedural_geometries.len()
853 + stereo_cameras.len()
854 + camera_switchers.len()
855 + markers.len()
856 + lod_groups.len()
857 + blend_channels.len()
858 + blend_shapes.len()
859 + cache_files.len()
860 + shaders.len()
861 + shader_bindings.len()
862 + display_layers.len()
863 + selection_sets.len()
864 + selection_nodes.len()
865 + characters.len()
866 + constraints.len()
867 + audio_layers.len()
868 + audio_clips.len()
869 + poses.len()
870 + metadata_objects.len()
871 + texture_files.len()
872}