Browse Source

Merge pull request #91560 from ydeltastar/retarget-axis-fix

Make retarget keep global rest of unmapped bones if there are no mapped bones on the child
Rémi Verschelde 1 year ago
parent
commit
55b8724bd5
1 changed files with 48 additions and 7 deletions
  1. 48 7
      editor/import/3d/post_import_plugin_skeleton_rest_fixer.cpp

+ 48 - 7
editor/import/3d/post_import_plugin_skeleton_rest_fixer.cpp

@@ -41,8 +41,9 @@ void PostImportPluginSkeletonRestFixer::get_internal_import_options(InternalImpo
 	if (p_category == INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE) {
 	if (p_category == INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE) {
 		r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "retarget/rest_fixer/apply_node_transforms"), true));
 		r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "retarget/rest_fixer/apply_node_transforms"), true));
 		r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "retarget/rest_fixer/normalize_position_tracks"), true));
 		r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "retarget/rest_fixer/normalize_position_tracks"), true));
-		r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "retarget/rest_fixer/overwrite_axis"), true));
 		r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "retarget/rest_fixer/reset_all_bone_poses_after_import"), true));
 		r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "retarget/rest_fixer/reset_all_bone_poses_after_import"), true));
+		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));
+		r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "retarget/rest_fixer/keep_global_rest_on_leftovers"), true));
 		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));
 		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));
 		// TODO: PostImportPlugin need to be implemented such as validate_option(PropertyInfo &property, const Dictionary &p_options).
 		// TODO: PostImportPlugin need to be implemented such as validate_option(PropertyInfo &property, const Dictionary &p_options).
 		// get_internal_option_visibility() is not sufficient because it can only retrieve options implemented in the core and can only read option values.
 		// 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
 					return false;
 					return false;
 				}
 				}
 			}
 			}
+		} else if (p_option == "retarget/rest_fixer/keep_global_rest_on_leftovers") {
+			return bool(p_options["retarget/rest_fixer/overwrite_axis"]);
 		}
 		}
 	}
 	}
 	return true;
 	return true;
@@ -460,6 +463,48 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory
 				old_skeleton_global_rest.push_back(src_skeleton->get_bone_global_rest(i));
 				old_skeleton_global_rest.push_back(src_skeleton->get_bone_global_rest(i));
 			}
 			}
 
 
+			bool keep_global_rest_leftovers = bool(p_options["retarget/rest_fixer/keep_global_rest_on_leftovers"]);
+
+			// Scan hierarchy and populate a whitelist of unmapped bones without mapped descendants.
+			Vector<int> keep_bone_rest;
+			if (keep_global_rest_leftovers) {
+				Vector<int> bones_to_process = src_skeleton->get_parentless_bones();
+				while (bones_to_process.size() > 0) {
+					int src_idx = bones_to_process[0];
+					bones_to_process.erase(src_idx);
+					Vector<int> src_children = src_skeleton->get_bone_children(src_idx);
+					for (const int &src_child : src_children) {
+						bones_to_process.push_back(src_child);
+					}
+
+					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));
+					if (src_bone_name != StringName() && !profile->has_bone(src_bone_name)) {
+						// Scan descendants for mapped bones.
+						bool found_mapped = false;
+
+						Vector<int> decendants_to_process = src_skeleton->get_bone_children(src_idx);
+						while (decendants_to_process.size() > 0) {
+							int desc_idx = decendants_to_process[0];
+							decendants_to_process.erase(desc_idx);
+							Vector<int> desc_children = src_skeleton->get_bone_children(desc_idx);
+							for (const int &desc_child : desc_children) {
+								decendants_to_process.push_back(desc_child);
+							}
+
+							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));
+							if (desc_bone_name != StringName() && profile->has_bone(desc_bone_name)) {
+								found_mapped = true;
+								break;
+							}
+						}
+
+						if (!found_mapped) {
+							keep_bone_rest.push_back(src_idx); // No mapped descendants. Add to whitelist.
+						}
+					}
+				}
+			}
+
 			Vector<Basis> diffs;
 			Vector<Basis> diffs;
 			diffs.resize(src_skeleton->get_bone_count());
 			diffs.resize(src_skeleton->get_bone_count());
 			Basis *diffs_w = diffs.ptrw();
 			Basis *diffs_w = diffs.ptrw();
@@ -485,13 +530,9 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory
 					int prof_idx = profile->find_bone(src_bone_name);
 					int prof_idx = profile->find_bone(src_bone_name);
 					if (prof_idx >= 0) {
 					if (prof_idx >= 0) {
 						tgt_rot = src_pg.inverse() * prof_skeleton->get_bone_global_rest(prof_idx).basis; // Mapped bone uses reference pose.
 						tgt_rot = src_pg.inverse() * prof_skeleton->get_bone_global_rest(prof_idx).basis; // Mapped bone uses reference pose.
+					} else if (keep_global_rest_leftovers && keep_bone_rest.has(src_idx)) {
+						tgt_rot = src_pg.inverse() * old_skeleton_global_rest[src_idx].basis; // Non-Mapped bone without mapped children keeps global rest.
 					}
 					}
-					/*
-					// If there is rest-relative animation, this logic may be work fine, but currently not so...
-					} else {
-						// tgt_rot = src_pg.inverse() * old_skeleton_global_rest[src_idx].basis; // Non-Mapped bone keeps global rest.
-					}
-					*/
 				}
 				}
 
 
 				if (src_skeleton->get_bone_parent(src_idx) >= 0) {
 				if (src_skeleton->get_bone_parent(src_idx) >= 0) {