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 | |