| 1 | /**************************************************************************/ |
| 2 | /* post_import_plugin_skeleton_rest_fixer.cpp */ |
| 3 | /**************************************************************************/ |
| 4 | /* This file is part of: */ |
| 5 | /* GODOT ENGINE */ |
| 6 | /* https://godotengine.org */ |
| 7 | /**************************************************************************/ |
| 8 | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ |
| 9 | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ |
| 10 | /* */ |
| 11 | /* Permission is hereby granted, free of charge, to any person obtaining */ |
| 12 | /* a copy of this software and associated documentation files (the */ |
| 13 | /* "Software"), to deal in the Software without restriction, including */ |
| 14 | /* without limitation the rights to use, copy, modify, merge, publish, */ |
| 15 | /* distribute, sublicense, and/or sell copies of the Software, and to */ |
| 16 | /* permit persons to whom the Software is furnished to do so, subject to */ |
| 17 | /* the following conditions: */ |
| 18 | /* */ |
| 19 | /* The above copyright notice and this permission notice shall be */ |
| 20 | /* included in all copies or substantial portions of the Software. */ |
| 21 | /* */ |
| 22 | /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ |
| 23 | /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ |
| 24 | /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ |
| 25 | /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ |
| 26 | /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ |
| 27 | /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ |
| 28 | /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ |
| 29 | /**************************************************************************/ |
| 30 | |
| 31 | #include "post_import_plugin_skeleton_rest_fixer.h" |
| 32 | |
| 33 | #include "editor/import/scene_import_settings.h" |
| 34 | #include "scene/3d/bone_attachment_3d.h" |
| 35 | #include "scene/3d/importer_mesh_instance_3d.h" |
| 36 | #include "scene/3d/skeleton_3d.h" |
| 37 | #include "scene/animation/animation_player.h" |
| 38 | #include "scene/resources/bone_map.h" |
| 39 | |
| 40 | void PostImportPluginSkeletonRestFixer::get_internal_import_options(InternalImportCategory p_category, List<ResourceImporter::ImportOption> *r_options) { |
| 41 | if (p_category == INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE) { |
| 42 | r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "retarget/rest_fixer/apply_node_transforms" ), true)); |
| 43 | 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)); |
| 45 | r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "retarget/rest_fixer/fix_silhouette/enable" ), false)); |
| 46 | // TODO: PostImportPlugin need to be implemented such as validate_option(PropertyInfo &property, const Dictionary &p_options). |
| 47 | // get_internal_option_visibility() is not sufficient because it can only retrieve options implemented in the core and can only read option values. |
| 48 | // r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::ARRAY, "retarget/rest_fixer/filter", PROPERTY_HINT_ARRAY_TYPE, vformat("%s/%s:%s", Variant::STRING_NAME, PROPERTY_HINT_ENUM, "Hips,Spine,Chest")), Array())); |
| 49 | r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::ARRAY, "retarget/rest_fixer/fix_silhouette/filter" , PROPERTY_HINT_ARRAY_TYPE, "StringName" ), Array())); |
| 50 | r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::FLOAT, "retarget/rest_fixer/fix_silhouette/threshold" ), 15)); |
| 51 | r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::FLOAT, "retarget/rest_fixer/fix_silhouette/base_height_adjustment" , PROPERTY_HINT_RANGE, "-1,1,0.01" ), 0.0)); |
| 52 | } |
| 53 | } |
| 54 | |
| 55 | void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory p_category, Node *p_base_scene, Node *p_node, Ref<Resource> p_resource, const Dictionary &p_options) { |
| 56 | if (p_category == INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE) { |
| 57 | // Prepare objects. |
| 58 | Object *map = p_options["retarget/bone_map" ].get_validated_object(); |
| 59 | if (!map) { |
| 60 | return; |
| 61 | } |
| 62 | BoneMap *bone_map = Object::cast_to<BoneMap>(map); |
| 63 | Ref<SkeletonProfile> profile = bone_map->get_profile(); |
| 64 | if (!profile.is_valid()) { |
| 65 | return; |
| 66 | } |
| 67 | Skeleton3D *src_skeleton = Object::cast_to<Skeleton3D>(p_node); |
| 68 | if (!src_skeleton) { |
| 69 | return; |
| 70 | } |
| 71 | |
| 72 | bool is_renamed = bool(p_options["retarget/bone_renamer/rename_bones" ]); |
| 73 | Array filter = p_options["retarget/rest_fixer/fix_silhouette/filter" ]; |
| 74 | bool is_rest_changed = false; |
| 75 | |
| 76 | // Build profile skeleton. |
| 77 | Skeleton3D *prof_skeleton = memnew(Skeleton3D); |
| 78 | { |
| 79 | int prof_bone_len = profile->get_bone_size(); |
| 80 | // Add single bones. |
| 81 | for (int i = 0; i < prof_bone_len; i++) { |
| 82 | prof_skeleton->add_bone(profile->get_bone_name(i)); |
| 83 | prof_skeleton->set_bone_rest(i, profile->get_reference_pose(i)); |
| 84 | } |
| 85 | // Set parents. |
| 86 | for (int i = 0; i < prof_bone_len; i++) { |
| 87 | int parent = profile->find_bone(profile->get_bone_parent(i)); |
| 88 | if (parent >= 0) { |
| 89 | prof_skeleton->set_bone_parent(i, parent); |
| 90 | } |
| 91 | } |
| 92 | } |
| 93 | |
| 94 | // Get global transform. |
| 95 | Transform3D global_transform; |
| 96 | if (bool(p_options["retarget/rest_fixer/apply_node_transforms" ])) { |
| 97 | Node *pr = src_skeleton; |
| 98 | while (pr) { |
| 99 | Node3D *pr3d = Object::cast_to<Node3D>(pr); |
| 100 | if (pr3d) { |
| 101 | global_transform = pr3d->get_transform() * global_transform; |
| 102 | pr3d->set_transform(Transform3D()); |
| 103 | } |
| 104 | pr = pr->get_parent(); |
| 105 | } |
| 106 | global_transform.origin = Vector3(); // Translation by a Node is not a bone animation, so the retargeted model should be at the origin. |
| 107 | } |
| 108 | |
| 109 | // Apply node transforms. |
| 110 | if (bool(p_options["retarget/rest_fixer/apply_node_transforms" ])) { |
| 111 | Vector3 scl = global_transform.basis.get_scale_local(); |
| 112 | |
| 113 | Vector<int> bones_to_process = src_skeleton->get_parentless_bones(); |
| 114 | for (int i = 0; i < bones_to_process.size(); i++) { |
| 115 | src_skeleton->set_bone_rest(bones_to_process[i], global_transform.orthonormalized() * src_skeleton->get_bone_rest(bones_to_process[i])); |
| 116 | } |
| 117 | |
| 118 | while (bones_to_process.size() > 0) { |
| 119 | int src_idx = bones_to_process[0]; |
| 120 | bones_to_process.erase(src_idx); |
| 121 | Vector<int> src_children = src_skeleton->get_bone_children(src_idx); |
| 122 | for (int i = 0; i < src_children.size(); i++) { |
| 123 | bones_to_process.push_back(src_children[i]); |
| 124 | } |
| 125 | src_skeleton->set_bone_rest(src_idx, Transform3D(src_skeleton->get_bone_rest(src_idx).basis, src_skeleton->get_bone_rest(src_idx).origin * scl)); |
| 126 | } |
| 127 | |
| 128 | // Fix animation. |
| 129 | bones_to_process = src_skeleton->get_parentless_bones(); |
| 130 | { |
| 131 | TypedArray<Node> nodes = p_base_scene->find_children("*" , "AnimationPlayer" ); |
| 132 | while (nodes.size()) { |
| 133 | AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(nodes.pop_back()); |
| 134 | List<StringName> anims; |
| 135 | ap->get_animation_list(&anims); |
| 136 | for (const StringName &name : anims) { |
| 137 | Ref<Animation> anim = ap->get_animation(name); |
| 138 | int track_len = anim->get_track_count(); |
| 139 | for (int i = 0; i < track_len; i++) { |
| 140 | if (anim->track_get_path(i).get_subname_count() != 1 || !(anim->track_get_type(i) == Animation::TYPE_POSITION_3D || anim->track_get_type(i) == Animation::TYPE_ROTATION_3D || anim->track_get_type(i) == Animation::TYPE_SCALE_3D)) { |
| 141 | continue; |
| 142 | } |
| 143 | |
| 144 | if (anim->track_is_compressed(i)) { |
| 145 | continue; // Shouldn't occur in internal_process(). |
| 146 | } |
| 147 | |
| 148 | String track_path = String(anim->track_get_path(i).get_concatenated_names()); |
| 149 | Node *node = (ap->get_node(ap->get_root()))->get_node(NodePath(track_path)); |
| 150 | ERR_CONTINUE(!node); |
| 151 | |
| 152 | Skeleton3D *track_skeleton = Object::cast_to<Skeleton3D>(node); |
| 153 | if (!track_skeleton || track_skeleton != src_skeleton) { |
| 154 | continue; |
| 155 | } |
| 156 | |
| 157 | StringName bn = anim->track_get_path(i).get_subname(0); |
| 158 | if (!bn) { |
| 159 | continue; |
| 160 | } |
| 161 | |
| 162 | int bone_idx = src_skeleton->find_bone(bn); |
| 163 | int key_len = anim->track_get_key_count(i); |
| 164 | if (anim->track_get_type(i) == Animation::TYPE_POSITION_3D) { |
| 165 | if (bones_to_process.has(bone_idx)) { |
| 166 | for (int j = 0; j < key_len; j++) { |
| 167 | Vector3 ps = static_cast<Vector3>(anim->track_get_key_value(i, j)); |
| 168 | anim->track_set_key_value(i, j, global_transform.basis.xform(ps) + global_transform.origin); |
| 169 | } |
| 170 | } else { |
| 171 | for (int j = 0; j < key_len; j++) { |
| 172 | Vector3 ps = static_cast<Vector3>(anim->track_get_key_value(i, j)); |
| 173 | anim->track_set_key_value(i, j, ps * scl); |
| 174 | } |
| 175 | } |
| 176 | } else if (bones_to_process.has(bone_idx)) { |
| 177 | if (anim->track_get_type(i) == Animation::TYPE_ROTATION_3D) { |
| 178 | for (int j = 0; j < key_len; j++) { |
| 179 | Quaternion qt = static_cast<Quaternion>(anim->track_get_key_value(i, j)); |
| 180 | anim->track_set_key_value(i, j, global_transform.basis.get_rotation_quaternion() * qt); |
| 181 | } |
| 182 | } else { |
| 183 | for (int j = 0; j < key_len; j++) { |
| 184 | Basis sc = Basis().scaled(static_cast<Vector3>(anim->track_get_key_value(i, j))); |
| 185 | anim->track_set_key_value(i, j, (global_transform.basis * sc).get_scale()); |
| 186 | } |
| 187 | } |
| 188 | } |
| 189 | } |
| 190 | } |
| 191 | } |
| 192 | } |
| 193 | |
| 194 | is_rest_changed = true; |
| 195 | } |
| 196 | |
| 197 | // Complement Rotation track for compatibility between different rests. |
| 198 | { |
| 199 | TypedArray<Node> nodes = p_base_scene->find_children("*" , "AnimationPlayer" ); |
| 200 | while (nodes.size()) { |
| 201 | AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(nodes.pop_back()); |
| 202 | List<StringName> anims; |
| 203 | ap->get_animation_list(&anims); |
| 204 | for (const StringName &name : anims) { |
| 205 | Ref<Animation> anim = ap->get_animation(name); |
| 206 | int track_len = anim->get_track_count(); |
| 207 | |
| 208 | // Detect does the animation have skeleton's TRS track. |
| 209 | String track_path; |
| 210 | bool found_skeleton = false; |
| 211 | for (int i = 0; i < track_len; i++) { |
| 212 | if (anim->track_get_path(i).get_subname_count() != 1 || !(anim->track_get_type(i) == Animation::TYPE_POSITION_3D || anim->track_get_type(i) == Animation::TYPE_ROTATION_3D || anim->track_get_type(i) == Animation::TYPE_SCALE_3D)) { |
| 213 | continue; |
| 214 | } |
| 215 | track_path = String(anim->track_get_path(i).get_concatenated_names()); |
| 216 | Node *node = (ap->get_node(ap->get_root()))->get_node(NodePath(track_path)); |
| 217 | if (node) { |
| 218 | Skeleton3D *track_skeleton = Object::cast_to<Skeleton3D>(node); |
| 219 | if (track_skeleton && track_skeleton == src_skeleton) { |
| 220 | found_skeleton = true; |
| 221 | break; |
| 222 | } |
| 223 | } |
| 224 | } |
| 225 | |
| 226 | if (!found_skeleton) { |
| 227 | continue; |
| 228 | } |
| 229 | |
| 230 | // Search and insert rot track if it doesn't exist. |
| 231 | for (int prof_idx = 0; prof_idx < prof_skeleton->get_bone_count(); prof_idx++) { |
| 232 | String bone_name = is_renamed ? prof_skeleton->get_bone_name(prof_idx) : String(bone_map->get_skeleton_bone_name(prof_skeleton->get_bone_name(prof_idx))); |
| 233 | if (bone_name.is_empty()) { |
| 234 | continue; |
| 235 | } |
| 236 | int src_idx = src_skeleton->find_bone(bone_name); |
| 237 | if (src_idx == -1) { |
| 238 | continue; |
| 239 | } |
| 240 | String insert_path = track_path + ":" + bone_name; |
| 241 | int rot_track = anim->find_track(insert_path, Animation::TYPE_ROTATION_3D); |
| 242 | if (rot_track == -1) { |
| 243 | int track = anim->add_track(Animation::TYPE_ROTATION_3D); |
| 244 | anim->track_set_path(track, insert_path); |
| 245 | anim->rotation_track_insert_key(track, 0, src_skeleton->get_bone_rest(src_idx).basis.get_rotation_quaternion()); |
| 246 | } |
| 247 | } |
| 248 | } |
| 249 | } |
| 250 | } |
| 251 | |
| 252 | // Fix silhouette. |
| 253 | Vector<Transform3D> silhouette_diff; // Transform values to be ignored when overwrite axis. |
| 254 | silhouette_diff.resize(src_skeleton->get_bone_count()); |
| 255 | Transform3D *silhouette_diff_w = silhouette_diff.ptrw(); |
| 256 | LocalVector<Transform3D> pre_silhouette_skeleton_global_rest; |
| 257 | for (int i = 0; i < src_skeleton->get_bone_count(); i++) { |
| 258 | pre_silhouette_skeleton_global_rest.push_back(src_skeleton->get_bone_global_rest(i)); |
| 259 | } |
| 260 | if (bool(p_options["retarget/rest_fixer/fix_silhouette/enable" ])) { |
| 261 | Vector<int> bones_to_process = prof_skeleton->get_parentless_bones(); |
| 262 | while (bones_to_process.size() > 0) { |
| 263 | int prof_idx = bones_to_process[0]; |
| 264 | bones_to_process.erase(prof_idx); |
| 265 | Vector<int> prof_children = prof_skeleton->get_bone_children(prof_idx); |
| 266 | for (int i = 0; i < prof_children.size(); i++) { |
| 267 | bones_to_process.push_back(prof_children[i]); |
| 268 | } |
| 269 | |
| 270 | // Calc virtual/looking direction with origins. |
| 271 | bool is_filtered = false; |
| 272 | for (int i = 0; i < filter.size(); i++) { |
| 273 | if (String(filter[i]) == prof_skeleton->get_bone_name(prof_idx)) { |
| 274 | is_filtered = true; |
| 275 | break; |
| 276 | } |
| 277 | } |
| 278 | if (is_filtered) { |
| 279 | continue; |
| 280 | } |
| 281 | |
| 282 | int src_idx = src_skeleton->find_bone(is_renamed ? prof_skeleton->get_bone_name(prof_idx) : String(bone_map->get_skeleton_bone_name(prof_skeleton->get_bone_name(prof_idx)))); |
| 283 | if (src_idx < 0 || profile->get_tail_direction(prof_idx) == SkeletonProfile::TAIL_DIRECTION_END) { |
| 284 | continue; |
| 285 | } |
| 286 | Vector3 prof_tail; |
| 287 | Vector3 src_tail; |
| 288 | if (profile->get_tail_direction(prof_idx) == SkeletonProfile::TAIL_DIRECTION_AVERAGE_CHILDREN) { |
| 289 | PackedInt32Array prof_bone_children = prof_skeleton->get_bone_children(prof_idx); |
| 290 | int children_size = prof_bone_children.size(); |
| 291 | if (children_size == 0) { |
| 292 | continue; |
| 293 | } |
| 294 | bool exist_all_children = true; |
| 295 | for (int i = 0; i < children_size; i++) { |
| 296 | int prof_child_idx = prof_bone_children[i]; |
| 297 | int src_child_idx = src_skeleton->find_bone(is_renamed ? prof_skeleton->get_bone_name(prof_child_idx) : String(bone_map->get_skeleton_bone_name(prof_skeleton->get_bone_name(prof_child_idx)))); |
| 298 | if (src_child_idx < 0) { |
| 299 | exist_all_children = false; |
| 300 | break; |
| 301 | } |
| 302 | prof_tail = prof_tail + prof_skeleton->get_bone_global_rest(prof_child_idx).origin; |
| 303 | src_tail = src_tail + src_skeleton->get_bone_global_rest(src_child_idx).origin; |
| 304 | } |
| 305 | if (!exist_all_children) { |
| 306 | continue; |
| 307 | } |
| 308 | prof_tail = prof_tail / children_size; |
| 309 | src_tail = src_tail / children_size; |
| 310 | } |
| 311 | if (profile->get_tail_direction(prof_idx) == SkeletonProfile::TAIL_DIRECTION_SPECIFIC_CHILD) { |
| 312 | int prof_tail_idx = prof_skeleton->find_bone(profile->get_bone_tail(prof_idx)); |
| 313 | if (prof_tail_idx < 0) { |
| 314 | continue; |
| 315 | } |
| 316 | int src_tail_idx = src_skeleton->find_bone(is_renamed ? prof_skeleton->get_bone_name(prof_tail_idx) : String(bone_map->get_skeleton_bone_name(prof_skeleton->get_bone_name(prof_tail_idx)))); |
| 317 | if (src_tail_idx < 0) { |
| 318 | continue; |
| 319 | } |
| 320 | prof_tail = prof_skeleton->get_bone_global_rest(prof_tail_idx).origin; |
| 321 | src_tail = src_skeleton->get_bone_global_rest(src_tail_idx).origin; |
| 322 | } |
| 323 | |
| 324 | Vector3 prof_head = prof_skeleton->get_bone_global_rest(prof_idx).origin; |
| 325 | Vector3 src_head = src_skeleton->get_bone_global_rest(src_idx).origin; |
| 326 | |
| 327 | Vector3 prof_dir = prof_tail - prof_head; |
| 328 | Vector3 src_dir = src_tail - src_head; |
| 329 | |
| 330 | // Rotate rest. |
| 331 | if (Math::abs(Math::rad_to_deg(src_dir.angle_to(prof_dir))) > float(p_options["retarget/rest_fixer/fix_silhouette/threshold" ])) { |
| 332 | // Get rotation difference. |
| 333 | Vector3 up_vec; // Need to rotate other than roll axis. |
| 334 | switch (Vector3(abs(src_dir.x), abs(src_dir.y), abs(src_dir.z)).min_axis_index()) { |
| 335 | case Vector3::AXIS_X: { |
| 336 | up_vec = Vector3(1, 0, 0); |
| 337 | } break; |
| 338 | case Vector3::AXIS_Y: { |
| 339 | up_vec = Vector3(0, 1, 0); |
| 340 | } break; |
| 341 | case Vector3::AXIS_Z: { |
| 342 | up_vec = Vector3(0, 0, 1); |
| 343 | } break; |
| 344 | } |
| 345 | Basis src_b; |
| 346 | src_b = src_b.looking_at(src_dir, up_vec); |
| 347 | Basis prof_b; |
| 348 | prof_b = src_b.looking_at(prof_dir, up_vec); |
| 349 | if (prof_b.is_equal_approx(Basis())) { |
| 350 | continue; // May not need to rotate. |
| 351 | } |
| 352 | Basis diff_b = prof_b * src_b.inverse(); |
| 353 | |
| 354 | // Apply rotation difference as global transform to skeleton. |
| 355 | Basis src_pg; |
| 356 | int src_parent = src_skeleton->get_bone_parent(src_idx); |
| 357 | if (src_parent >= 0) { |
| 358 | src_pg = src_skeleton->get_bone_global_rest(src_parent).basis; |
| 359 | } |
| 360 | Transform3D fixed_rest = Transform3D(src_pg.inverse() * diff_b * src_pg * src_skeleton->get_bone_rest(src_idx).basis, src_skeleton->get_bone_rest(src_idx).origin); |
| 361 | src_skeleton->set_bone_rest(src_idx, fixed_rest); |
| 362 | } |
| 363 | } |
| 364 | |
| 365 | // Adjust scale base bone height. |
| 366 | float base_adjustment = float(p_options["retarget/rest_fixer/fix_silhouette/base_height_adjustment" ]); |
| 367 | if (!Math::is_zero_approx(base_adjustment)) { |
| 368 | StringName scale_base_bone_name = profile->get_scale_base_bone(); |
| 369 | int src_bone_idx = src_skeleton->find_bone(scale_base_bone_name); |
| 370 | Transform3D src_rest = src_skeleton->get_bone_rest(src_bone_idx); |
| 371 | src_skeleton->set_bone_rest(src_bone_idx, Transform3D(src_rest.basis, Vector3(src_rest.origin.x, src_rest.origin.y + base_adjustment, src_rest.origin.z))); |
| 372 | |
| 373 | TypedArray<Node> nodes = p_base_scene->find_children("*" , "AnimationPlayer" ); |
| 374 | while (nodes.size()) { |
| 375 | AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(nodes.pop_back()); |
| 376 | List<StringName> anims; |
| 377 | ap->get_animation_list(&anims); |
| 378 | for (const StringName &name : anims) { |
| 379 | Ref<Animation> anim = ap->get_animation(name); |
| 380 | int track_len = anim->get_track_count(); |
| 381 | for (int i = 0; i < track_len; i++) { |
| 382 | if (anim->track_get_path(i).get_subname_count() != 1 || anim->track_get_type(i) != Animation::TYPE_POSITION_3D) { |
| 383 | continue; |
| 384 | } |
| 385 | |
| 386 | if (anim->track_is_compressed(i)) { |
| 387 | continue; // Shouldn't occur in internal_process(). |
| 388 | } |
| 389 | |
| 390 | String track_path = String(anim->track_get_path(i).get_concatenated_names()); |
| 391 | Node *node = (ap->get_node(ap->get_root()))->get_node(NodePath(track_path)); |
| 392 | ERR_CONTINUE(!node); |
| 393 | |
| 394 | Skeleton3D *track_skeleton = Object::cast_to<Skeleton3D>(node); |
| 395 | if (!track_skeleton || track_skeleton != src_skeleton) { |
| 396 | continue; |
| 397 | } |
| 398 | |
| 399 | StringName bn = anim->track_get_path(i).get_concatenated_subnames(); |
| 400 | if (bn != scale_base_bone_name) { |
| 401 | continue; |
| 402 | } |
| 403 | |
| 404 | int key_len = anim->track_get_key_count(i); |
| 405 | for (int j = 0; j < key_len; j++) { |
| 406 | Vector3 pos = static_cast<Vector3>(anim->track_get_key_value(i, j)); |
| 407 | pos.y += base_adjustment; |
| 408 | anim->track_set_key_value(i, j, pos); |
| 409 | } |
| 410 | } |
| 411 | } |
| 412 | } |
| 413 | } |
| 414 | |
| 415 | // For skin modification in overwrite rest. |
| 416 | for (int i = 0; i < src_skeleton->get_bone_count(); i++) { |
| 417 | silhouette_diff_w[i] = pre_silhouette_skeleton_global_rest[i] * src_skeleton->get_bone_global_rest(i).affine_inverse(); |
| 418 | } |
| 419 | |
| 420 | is_rest_changed = true; |
| 421 | } |
| 422 | |
| 423 | // Set motion scale to Skeleton if normalize position tracks. |
| 424 | if (bool(p_options["retarget/rest_fixer/normalize_position_tracks" ])) { |
| 425 | int src_bone_idx = src_skeleton->find_bone(profile->get_scale_base_bone()); |
| 426 | if (src_bone_idx >= 0) { |
| 427 | real_t motion_scale = abs(src_skeleton->get_bone_global_rest(src_bone_idx).origin.y); |
| 428 | if (motion_scale > 0) { |
| 429 | src_skeleton->set_motion_scale(motion_scale); |
| 430 | } |
| 431 | } |
| 432 | |
| 433 | TypedArray<Node> nodes = p_base_scene->find_children("*" , "AnimationPlayer" ); |
| 434 | while (nodes.size()) { |
| 435 | AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(nodes.pop_back()); |
| 436 | List<StringName> anims; |
| 437 | ap->get_animation_list(&anims); |
| 438 | for (const StringName &name : anims) { |
| 439 | Ref<Animation> anim = ap->get_animation(name); |
| 440 | int track_len = anim->get_track_count(); |
| 441 | for (int i = 0; i < track_len; i++) { |
| 442 | if (anim->track_get_path(i).get_subname_count() != 1 || anim->track_get_type(i) != Animation::TYPE_POSITION_3D) { |
| 443 | continue; |
| 444 | } |
| 445 | |
| 446 | if (anim->track_is_compressed(i)) { |
| 447 | continue; // Shouldn't occur in internal_process(). |
| 448 | } |
| 449 | |
| 450 | String track_path = String(anim->track_get_path(i).get_concatenated_names()); |
| 451 | Node *node = (ap->get_node(ap->get_root()))->get_node(NodePath(track_path)); |
| 452 | ERR_CONTINUE(!node); |
| 453 | |
| 454 | Skeleton3D *track_skeleton = Object::cast_to<Skeleton3D>(node); |
| 455 | if (!track_skeleton || track_skeleton != src_skeleton) { |
| 456 | continue; |
| 457 | } |
| 458 | |
| 459 | real_t mlt = 1 / src_skeleton->get_motion_scale(); |
| 460 | int key_len = anim->track_get_key_count(i); |
| 461 | for (int j = 0; j < key_len; j++) { |
| 462 | Vector3 pos = static_cast<Vector3>(anim->track_get_key_value(i, j)); |
| 463 | anim->track_set_key_value(i, j, pos * mlt); |
| 464 | } |
| 465 | } |
| 466 | } |
| 467 | } |
| 468 | } |
| 469 | |
| 470 | // Overwrite axis. |
| 471 | if (bool(p_options["retarget/rest_fixer/overwrite_axis" ])) { |
| 472 | LocalVector<Transform3D> old_skeleton_rest; |
| 473 | LocalVector<Transform3D> old_skeleton_global_rest; |
| 474 | for (int i = 0; i < src_skeleton->get_bone_count(); i++) { |
| 475 | old_skeleton_rest.push_back(src_skeleton->get_bone_rest(i)); |
| 476 | old_skeleton_global_rest.push_back(src_skeleton->get_bone_global_rest(i)); |
| 477 | } |
| 478 | |
| 479 | Vector<Basis> diffs; |
| 480 | diffs.resize(src_skeleton->get_bone_count()); |
| 481 | Basis *diffs_w = diffs.ptrw(); |
| 482 | |
| 483 | Vector<int> bones_to_process = src_skeleton->get_parentless_bones(); |
| 484 | while (bones_to_process.size() > 0) { |
| 485 | int src_idx = bones_to_process[0]; |
| 486 | bones_to_process.erase(src_idx); |
| 487 | Vector<int> src_children = src_skeleton->get_bone_children(src_idx); |
| 488 | for (int i = 0; i < src_children.size(); i++) { |
| 489 | bones_to_process.push_back(src_children[i]); |
| 490 | } |
| 491 | |
| 492 | Basis tgt_rot; |
| 493 | 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)); |
| 494 | if (src_bone_name != StringName()) { |
| 495 | Basis src_pg; |
| 496 | int src_parent_idx = src_skeleton->get_bone_parent(src_idx); |
| 497 | if (src_parent_idx >= 0) { |
| 498 | src_pg = src_skeleton->get_bone_global_rest(src_parent_idx).basis; |
| 499 | } |
| 500 | |
| 501 | int prof_idx = profile->find_bone(src_bone_name); |
| 502 | if (prof_idx >= 0) { |
| 503 | tgt_rot = src_pg.inverse() * prof_skeleton->get_bone_global_rest(prof_idx).basis; // Mapped bone uses reference pose. |
| 504 | } |
| 505 | /* |
| 506 | // If there is rest-relative animation, this logic may be work fine, but currently not so... |
| 507 | } else { |
| 508 | // tgt_rot = src_pg.inverse() * old_skeleton_global_rest[src_idx].basis; // Non-Mapped bone keeps global rest. |
| 509 | } |
| 510 | */ |
| 511 | } |
| 512 | |
| 513 | if (src_skeleton->get_bone_parent(src_idx) >= 0) { |
| 514 | diffs_w[src_idx] = tgt_rot.inverse() * diffs[src_skeleton->get_bone_parent(src_idx)] * src_skeleton->get_bone_rest(src_idx).basis; |
| 515 | } else { |
| 516 | diffs_w[src_idx] = tgt_rot.inverse() * src_skeleton->get_bone_rest(src_idx).basis; |
| 517 | } |
| 518 | |
| 519 | Basis diff; |
| 520 | if (src_skeleton->get_bone_parent(src_idx) >= 0) { |
| 521 | diff = diffs[src_skeleton->get_bone_parent(src_idx)]; |
| 522 | } |
| 523 | src_skeleton->set_bone_rest(src_idx, Transform3D(tgt_rot, diff.xform(src_skeleton->get_bone_rest(src_idx).origin))); |
| 524 | } |
| 525 | |
| 526 | // Fix animation. |
| 527 | { |
| 528 | TypedArray<Node> nodes = p_base_scene->find_children("*" , "AnimationPlayer" ); |
| 529 | while (nodes.size()) { |
| 530 | AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(nodes.pop_back()); |
| 531 | ERR_CONTINUE(!ap); |
| 532 | List<StringName> anims; |
| 533 | ap->get_animation_list(&anims); |
| 534 | for (const StringName &name : anims) { |
| 535 | Ref<Animation> anim = ap->get_animation(name); |
| 536 | int track_len = anim->get_track_count(); |
| 537 | for (int i = 0; i < track_len; i++) { |
| 538 | if (anim->track_get_path(i).get_subname_count() != 1 || !(anim->track_get_type(i) == Animation::TYPE_POSITION_3D || anim->track_get_type(i) == Animation::TYPE_ROTATION_3D || anim->track_get_type(i) == Animation::TYPE_SCALE_3D)) { |
| 539 | continue; |
| 540 | } |
| 541 | |
| 542 | if (anim->track_is_compressed(i)) { |
| 543 | continue; // Shouldn't occur in internal_process(). |
| 544 | } |
| 545 | |
| 546 | String track_path = String(anim->track_get_path(i).get_concatenated_names()); |
| 547 | Node *node = (ap->get_node(ap->get_root()))->get_node(NodePath(track_path)); |
| 548 | ERR_CONTINUE(!node); |
| 549 | |
| 550 | Skeleton3D *track_skeleton = Object::cast_to<Skeleton3D>(node); |
| 551 | if (!track_skeleton || track_skeleton != src_skeleton) { |
| 552 | continue; |
| 553 | } |
| 554 | |
| 555 | StringName bn = anim->track_get_path(i).get_subname(0); |
| 556 | if (!bn) { |
| 557 | continue; |
| 558 | } |
| 559 | |
| 560 | int bone_idx = src_skeleton->find_bone(bn); |
| 561 | |
| 562 | Transform3D old_rest = old_skeleton_rest[bone_idx]; |
| 563 | Transform3D new_rest = src_skeleton->get_bone_rest(bone_idx); |
| 564 | Transform3D old_pg; |
| 565 | Transform3D new_pg; |
| 566 | int parent_idx = src_skeleton->get_bone_parent(bone_idx); |
| 567 | if (parent_idx >= 0) { |
| 568 | old_pg = old_skeleton_global_rest[parent_idx]; |
| 569 | new_pg = src_skeleton->get_bone_global_rest(parent_idx); |
| 570 | } |
| 571 | |
| 572 | int key_len = anim->track_get_key_count(i); |
| 573 | if (anim->track_get_type(i) == Animation::TYPE_ROTATION_3D) { |
| 574 | Quaternion old_rest_q = old_rest.basis.get_rotation_quaternion(); |
| 575 | Quaternion new_rest_q = new_rest.basis.get_rotation_quaternion(); |
| 576 | Quaternion old_pg_q = old_pg.basis.get_rotation_quaternion(); |
| 577 | Quaternion new_pg_q = new_pg.basis.get_rotation_quaternion(); |
| 578 | for (int j = 0; j < key_len; j++) { |
| 579 | Quaternion qt = static_cast<Quaternion>(anim->track_get_key_value(i, j)); |
| 580 | anim->track_set_key_value(i, j, new_pg_q.inverse() * old_pg_q * qt * old_rest_q.inverse() * old_pg_q.inverse() * new_pg_q * new_rest_q); |
| 581 | } |
| 582 | } else if (anim->track_get_type(i) == Animation::TYPE_SCALE_3D) { |
| 583 | Basis old_rest_b = old_rest.basis; |
| 584 | Basis new_rest_b = new_rest.basis; |
| 585 | Basis old_pg_b = old_pg.basis; |
| 586 | Basis new_pg_b = new_pg.basis; |
| 587 | for (int j = 0; j < key_len; j++) { |
| 588 | Basis sc = Basis().scaled(static_cast<Vector3>(anim->track_get_key_value(i, j))); |
| 589 | anim->track_set_key_value(i, j, (new_pg_b.inverse() * old_pg_b * sc * old_rest_b.inverse() * old_pg_b.inverse() * new_pg_b * new_rest_b).get_scale()); |
| 590 | } |
| 591 | } else { |
| 592 | Vector3 old_rest_o = old_rest.origin; |
| 593 | Vector3 new_rest_o = new_rest.origin; |
| 594 | Quaternion old_pg_q = old_pg.basis.get_rotation_quaternion(); |
| 595 | Quaternion new_pg_q = new_pg.basis.get_rotation_quaternion(); |
| 596 | for (int j = 0; j < key_len; j++) { |
| 597 | Vector3 ps = static_cast<Vector3>(anim->track_get_key_value(i, j)); |
| 598 | anim->track_set_key_value(i, j, new_pg_q.xform_inv(old_pg_q.xform(ps - old_rest_o)) + new_rest_o); |
| 599 | } |
| 600 | } |
| 601 | } |
| 602 | } |
| 603 | } |
| 604 | } |
| 605 | |
| 606 | is_rest_changed = true; |
| 607 | } |
| 608 | |
| 609 | if (is_rest_changed) { |
| 610 | // Fix skin. |
| 611 | { |
| 612 | HashSet<Ref<Skin>> mutated_skins; |
| 613 | TypedArray<Node> nodes = p_base_scene->find_children("*" , "ImporterMeshInstance3D" ); |
| 614 | while (nodes.size()) { |
| 615 | ImporterMeshInstance3D *mi = Object::cast_to<ImporterMeshInstance3D>(nodes.pop_back()); |
| 616 | ERR_CONTINUE(!mi); |
| 617 | |
| 618 | Ref<Skin> skin = mi->get_skin(); |
| 619 | if (skin.is_null()) { |
| 620 | continue; |
| 621 | } |
| 622 | if (mutated_skins.has(skin)) { |
| 623 | continue; |
| 624 | } |
| 625 | mutated_skins.insert(skin); |
| 626 | |
| 627 | Node *node = mi->get_node(mi->get_skeleton_path()); |
| 628 | ERR_CONTINUE(!node); |
| 629 | |
| 630 | Skeleton3D *mesh_skeleton = Object::cast_to<Skeleton3D>(node); |
| 631 | if (!mesh_skeleton || mesh_skeleton != src_skeleton) { |
| 632 | continue; |
| 633 | } |
| 634 | |
| 635 | int skin_len = skin->get_bind_count(); |
| 636 | for (int i = 0; i < skin_len; i++) { |
| 637 | StringName bn = skin->get_bind_name(i); |
| 638 | int bone_idx = src_skeleton->find_bone(bn); |
| 639 | if (bone_idx >= 0) { |
| 640 | Transform3D adjust_transform = src_skeleton->get_bone_global_rest(bone_idx).affine_inverse() * silhouette_diff[bone_idx].affine_inverse() * pre_silhouette_skeleton_global_rest[bone_idx]; |
| 641 | adjust_transform.scale(global_transform.basis.get_scale_local()); |
| 642 | skin->set_bind_pose(i, adjust_transform * skin->get_bind_pose(i)); |
| 643 | } |
| 644 | } |
| 645 | } |
| 646 | nodes = src_skeleton->get_children(); |
| 647 | while (nodes.size()) { |
| 648 | BoneAttachment3D *attachment = Object::cast_to<BoneAttachment3D>(nodes.pop_back()); |
| 649 | if (attachment == nullptr) { |
| 650 | continue; |
| 651 | } |
| 652 | int bone_idx = attachment->get_bone_idx(); |
| 653 | if (bone_idx == -1) { |
| 654 | bone_idx = src_skeleton->find_bone(attachment->get_bone_name()); |
| 655 | } |
| 656 | ERR_CONTINUE(bone_idx < 0 || bone_idx >= src_skeleton->get_bone_count()); |
| 657 | Transform3D adjust_transform = src_skeleton->get_bone_global_rest(bone_idx).affine_inverse() * silhouette_diff[bone_idx].affine_inverse() * pre_silhouette_skeleton_global_rest[bone_idx]; |
| 658 | adjust_transform.scale(global_transform.basis.get_scale_local()); |
| 659 | |
| 660 | TypedArray<Node> child_nodes = attachment->get_children(); |
| 661 | while (child_nodes.size()) { |
| 662 | Node3D *child = Object::cast_to<Node3D>(child_nodes.pop_back()); |
| 663 | if (child == nullptr) { |
| 664 | continue; |
| 665 | } |
| 666 | child->set_transform(adjust_transform * child->get_transform()); |
| 667 | } |
| 668 | } |
| 669 | } |
| 670 | |
| 671 | // Init skeleton pose to new rest. |
| 672 | for (int i = 0; i < src_skeleton->get_bone_count(); i++) { |
| 673 | Transform3D fixed_rest = src_skeleton->get_bone_rest(i); |
| 674 | src_skeleton->set_bone_pose_position(i, fixed_rest.origin); |
| 675 | src_skeleton->set_bone_pose_rotation(i, fixed_rest.basis.get_rotation_quaternion()); |
| 676 | src_skeleton->set_bone_pose_scale(i, fixed_rest.basis.get_scale()); |
| 677 | } |
| 678 | } |
| 679 | |
| 680 | memdelete(prof_skeleton); |
| 681 | } |
| 682 | } |
| 683 | |
| 684 | PostImportPluginSkeletonRestFixer::PostImportPluginSkeletonRestFixer() { |
| 685 | } |
| 686 | |