@@ -41,8 +41,9 @@ void PostImportPluginSkeletonRestFixer::get_internal_import_options(InternalImpo
4141 if (p_category == INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE) {
4242 r_options->push_back (ResourceImporter::ImportOption (PropertyInfo (Variant::BOOL, " retarget/rest_fixer/apply_node_transforms" ), true ));
4343 r_options->push_back (ResourceImporter::ImportOption (PropertyInfo (Variant::BOOL, " retarget/rest_fixer/normalize_position_tracks" ), true ));
44- r_options->push_back (ResourceImporter::ImportOption (PropertyInfo (Variant::BOOL, " retarget/rest_fixer/overwrite_axis" ), true ));
4544 r_options->push_back (ResourceImporter::ImportOption (PropertyInfo (Variant::BOOL, " retarget/rest_fixer/reset_all_bone_poses_after_import" ), true ));
45+ r_options->push_back (ResourceImporter::ImportOption (PropertyInfo (Variant::BOOL, " retarget/rest_fixer/overwrite_axis" , PROPERTY_HINT_NONE, " " , PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), true ));
46+ r_options->push_back (ResourceImporter::ImportOption (PropertyInfo (Variant::BOOL, " retarget/rest_fixer/keep_global_rest_on_leftovers" ), true ));
4647 r_options->push_back (ResourceImporter::ImportOption (PropertyInfo (Variant::BOOL, " retarget/rest_fixer/fix_silhouette/enable" , PROPERTY_HINT_NONE, " " , PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false ));
4748 // TODO: PostImportPlugin need to be implemented such as validate_option(PropertyInfo &property, const Dictionary &p_options).
4849 // get_internal_option_visibility() is not sufficient because it can only retrieve options implemented in the core and can only read option values.
@@ -61,6 +62,8 @@ Variant PostImportPluginSkeletonRestFixer::get_internal_option_visibility(Intern
6162 return false ;
6263 }
6364 }
65+ } else if (p_option == " retarget/rest_fixer/keep_global_rest_on_leftovers" ) {
66+ return bool (p_options[" retarget/rest_fixer/overwrite_axis" ]);
6467 }
6568 }
6669 return true ;
@@ -460,6 +463,48 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory
460463 old_skeleton_global_rest.push_back (src_skeleton->get_bone_global_rest (i));
461464 }
462465
466+ bool keep_global_rest_leftovers = bool (p_options[" retarget/rest_fixer/keep_global_rest_on_leftovers" ]);
467+
468+ // Scan hierarchy and populate a whitelist of unmapped bones without mapped descendants.
469+ Vector<int > keep_bone_rest;
470+ if (keep_global_rest_leftovers) {
471+ Vector<int > bones_to_process = src_skeleton->get_parentless_bones ();
472+ while (bones_to_process.size () > 0 ) {
473+ int src_idx = bones_to_process[0 ];
474+ bones_to_process.erase (src_idx);
475+ Vector<int > src_children = src_skeleton->get_bone_children (src_idx);
476+ for (const int &src_child : src_children) {
477+ bones_to_process.push_back (src_child);
478+ }
479+
480+ StringName src_bone_name = is_renamed ? StringName (src_skeleton->get_bone_name (src_idx)) : bone_map->find_profile_bone_name (src_skeleton->get_bone_name (src_idx));
481+ if (src_bone_name != StringName () && !profile->has_bone (src_bone_name)) {
482+ // Scan descendants for mapped bones.
483+ bool found_mapped = false ;
484+
485+ Vector<int > decendants_to_process = src_skeleton->get_bone_children (src_idx);
486+ while (decendants_to_process.size () > 0 ) {
487+ int desc_idx = decendants_to_process[0 ];
488+ decendants_to_process.erase (desc_idx);
489+ Vector<int > desc_children = src_skeleton->get_bone_children (desc_idx);
490+ for (const int &desc_child : desc_children) {
491+ decendants_to_process.push_back (desc_child);
492+ }
493+
494+ StringName desc_bone_name = is_renamed ? StringName (src_skeleton->get_bone_name (desc_idx)) : bone_map->find_profile_bone_name (src_skeleton->get_bone_name (desc_idx));
495+ if (desc_bone_name != StringName () && profile->has_bone (desc_bone_name)) {
496+ found_mapped = true ;
497+ break ;
498+ }
499+ }
500+
501+ if (!found_mapped) {
502+ keep_bone_rest.push_back (src_idx); // No mapped descendants. Add to whitelist.
503+ }
504+ }
505+ }
506+ }
507+
463508 Vector<Basis> diffs;
464509 diffs.resize (src_skeleton->get_bone_count ());
465510 Basis *diffs_w = diffs.ptrw ();
@@ -485,13 +530,9 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory
485530 int prof_idx = profile->find_bone (src_bone_name);
486531 if (prof_idx >= 0 ) {
487532 tgt_rot = src_pg.inverse () * prof_skeleton->get_bone_global_rest (prof_idx).basis ; // Mapped bone uses reference pose.
533+ } else if (keep_global_rest_leftovers && keep_bone_rest.has (src_idx)) {
534+ tgt_rot = src_pg.inverse () * old_skeleton_global_rest[src_idx].basis ; // Non-Mapped bone without mapped children keeps global rest.
488535 }
489- /*
490- // If there is rest-relative animation, this logic may be work fine, but currently not so...
491- } else {
492- // tgt_rot = src_pg.inverse() * old_skeleton_global_rest[src_idx].basis; // Non-Mapped bone keeps global rest.
493- }
494- */
495536 }
496537
497538 if (src_skeleton->get_bone_parent (src_idx) >= 0 ) {
0 commit comments