1 | /**************************************************************************/ |
2 | /* post_import_plugin_skeleton_renamer.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_renamer.h" |
32 | |
33 | #include "editor/import/scene_import_settings.h" |
34 | #include "scene/3d/importer_mesh_instance_3d.h" |
35 | #include "scene/3d/skeleton_3d.h" |
36 | #include "scene/animation/animation_player.h" |
37 | #include "scene/resources/bone_map.h" |
38 | |
39 | void PostImportPluginSkeletonRenamer::get_internal_import_options(InternalImportCategory p_category, List<ResourceImporter::ImportOption> *r_options) { |
40 | if (p_category == INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE) { |
41 | r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "retarget/bone_renamer/rename_bones" ), true)); |
42 | r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "retarget/bone_renamer/unique_node/make_unique" ), true)); |
43 | r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::STRING, "retarget/bone_renamer/unique_node/skeleton_name" ), "GeneralSkeleton" )); |
44 | } |
45 | } |
46 | |
47 | void PostImportPluginSkeletonRenamer::_internal_process(InternalImportCategory p_category, Node *p_base_scene, Node *p_node, Ref<Resource> p_resource, const Dictionary &p_options, HashMap<String, String> p_rename_map) { |
48 | // Prepare objects. |
49 | Object *map = p_options["retarget/bone_map" ].get_validated_object(); |
50 | if (!map || !bool(p_options["retarget/bone_renamer/rename_bones" ])) { |
51 | return; |
52 | } |
53 | Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(p_node); |
54 | |
55 | // Rename bones in Skeleton3D. |
56 | { |
57 | int len = skeleton->get_bone_count(); |
58 | for (int i = 0; i < len; i++) { |
59 | StringName bn = p_rename_map[skeleton->get_bone_name(i)]; |
60 | if (bn) { |
61 | skeleton->set_bone_name(i, bn); |
62 | } |
63 | } |
64 | } |
65 | |
66 | // Rename bones in Skin. |
67 | { |
68 | TypedArray<Node> nodes = p_base_scene->find_children("*" , "ImporterMeshInstance3D" ); |
69 | while (nodes.size()) { |
70 | ImporterMeshInstance3D *mi = Object::cast_to<ImporterMeshInstance3D>(nodes.pop_back()); |
71 | Ref<Skin> skin = mi->get_skin(); |
72 | if (skin.is_valid()) { |
73 | Node *node = mi->get_node(mi->get_skeleton_path()); |
74 | if (node) { |
75 | Skeleton3D *mesh_skeleton = Object::cast_to<Skeleton3D>(node); |
76 | if (mesh_skeleton && node == skeleton) { |
77 | int len = skin->get_bind_count(); |
78 | for (int i = 0; i < len; i++) { |
79 | StringName bn = p_rename_map[skin->get_bind_name(i)]; |
80 | if (bn) { |
81 | skin->set_bind_name(i, bn); |
82 | } |
83 | } |
84 | } |
85 | } |
86 | } |
87 | } |
88 | } |
89 | |
90 | // Rename bones in AnimationPlayer. |
91 | { |
92 | TypedArray<Node> nodes = p_base_scene->find_children("*" , "AnimationPlayer" ); |
93 | while (nodes.size()) { |
94 | AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(nodes.pop_back()); |
95 | List<StringName> anims; |
96 | ap->get_animation_list(&anims); |
97 | for (const StringName &name : anims) { |
98 | Ref<Animation> anim = ap->get_animation(name); |
99 | int len = anim->get_track_count(); |
100 | for (int i = 0; i < len; i++) { |
101 | 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)) { |
102 | continue; |
103 | } |
104 | String track_path = String(anim->track_get_path(i).get_concatenated_names()); |
105 | Node *node = (ap->get_node(ap->get_root()))->get_node(NodePath(track_path)); |
106 | if (node) { |
107 | Skeleton3D *track_skeleton = Object::cast_to<Skeleton3D>(node); |
108 | if (track_skeleton && track_skeleton == skeleton) { |
109 | StringName bn = p_rename_map[anim->track_get_path(i).get_subname(0)]; |
110 | if (bn) { |
111 | anim->track_set_path(i, track_path + ":" + bn); |
112 | } |
113 | } |
114 | } |
115 | } |
116 | } |
117 | } |
118 | } |
119 | |
120 | // Rename bones in all Nodes by calling method. |
121 | { |
122 | Vector<Variant> vargs; |
123 | vargs.push_back(p_base_scene); |
124 | vargs.push_back(skeleton); |
125 | Dictionary rename_map_dict; |
126 | for (HashMap<String, String>::Iterator E = p_rename_map.begin(); E; ++E) { |
127 | rename_map_dict[E->key] = E->value; |
128 | } |
129 | vargs.push_back(rename_map_dict); |
130 | const Variant **argptrs = (const Variant **)alloca(sizeof(const Variant **) * vargs.size()); |
131 | const Variant *args = vargs.ptr(); |
132 | uint32_t argcount = vargs.size(); |
133 | for (uint32_t i = 0; i < argcount; i++) { |
134 | argptrs[i] = &args[i]; |
135 | } |
136 | |
137 | TypedArray<Node> nodes = p_base_scene->find_children("*" ); |
138 | while (nodes.size()) { |
139 | Node *nd = Object::cast_to<Node>(nodes.pop_back()); |
140 | Callable::CallError ce; |
141 | nd->callp("_notify_skeleton_bones_renamed" , argptrs, argcount, ce); |
142 | } |
143 | } |
144 | } |
145 | |
146 | void PostImportPluginSkeletonRenamer::internal_process(InternalImportCategory p_category, Node *p_base_scene, Node *p_node, Ref<Resource> p_resource, const Dictionary &p_options) { |
147 | if (p_category == INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE) { |
148 | // Prepare objects. |
149 | Object *map = p_options["retarget/bone_map" ].get_validated_object(); |
150 | if (!map || !bool(p_options["retarget/bone_renamer/rename_bones" ])) { |
151 | return; |
152 | } |
153 | Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(p_node); |
154 | BoneMap *bone_map = Object::cast_to<BoneMap>(map); |
155 | int len = skeleton->get_bone_count(); |
156 | |
157 | // First, prepare main rename map. |
158 | HashMap<String, String> main_rename_map; |
159 | for (int i = 0; i < len; i++) { |
160 | String bone_name = skeleton->get_bone_name(i); |
161 | String target_name = bone_map->find_profile_bone_name(bone_name); |
162 | if (target_name.is_empty()) { |
163 | continue; |
164 | } |
165 | main_rename_map.insert(bone_name, target_name); |
166 | } |
167 | |
168 | // Preprocess of renaming bones to avoid to conflict with original bone name. |
169 | HashMap<String, String> pre_rename_map; // HashMap<skeleton bone name, target(profile) bone name> |
170 | { |
171 | Vector<String> solved_name_stack; |
172 | for (int i = 0; i < len; i++) { |
173 | String bone_name = skeleton->get_bone_name(i); |
174 | String target_name = bone_map->find_profile_bone_name(bone_name); |
175 | if (target_name.is_empty() || bone_name == target_name || skeleton->find_bone(target_name) == -1) { |
176 | continue; // No conflicting. |
177 | } |
178 | |
179 | // Solve conflicting. |
180 | Ref<SkeletonProfile> profile = bone_map->get_profile(); |
181 | String solved_name = target_name; |
182 | for (int j = 2; skeleton->find_bone(solved_name) >= 0 || profile->find_bone(solved_name) >= 0 || solved_name_stack.has(solved_name); j++) { |
183 | solved_name = target_name + itos(j); |
184 | } |
185 | solved_name_stack.push_back(solved_name); |
186 | pre_rename_map.insert(target_name, solved_name); |
187 | } |
188 | _internal_process(p_category, p_base_scene, p_node, p_resource, p_options, pre_rename_map); |
189 | } |
190 | |
191 | // Main process of renaming bones. |
192 | { |
193 | // Apply pre-renaming result to prepared main rename map. |
194 | Vector<String> remove_queue; |
195 | for (HashMap<String, String>::Iterator E = main_rename_map.begin(); E; ++E) { |
196 | if (pre_rename_map.has(E->key)) { |
197 | remove_queue.push_back(E->key); |
198 | } |
199 | } |
200 | for (int i = 0; i < remove_queue.size(); i++) { |
201 | main_rename_map.insert(pre_rename_map[remove_queue[i]], main_rename_map[remove_queue[i]]); |
202 | main_rename_map.erase(remove_queue[i]); |
203 | } |
204 | _internal_process(p_category, p_base_scene, p_node, p_resource, p_options, main_rename_map); |
205 | } |
206 | |
207 | // Make unique skeleton. |
208 | if (bool(p_options["retarget/bone_renamer/unique_node/make_unique" ])) { |
209 | String unique_name = String(p_options["retarget/bone_renamer/unique_node/skeleton_name" ]); |
210 | ERR_FAIL_COND_MSG(unique_name.is_empty(), "Skeleton unique name cannot be empty." ); |
211 | |
212 | TypedArray<Node> nodes = p_base_scene->find_children("*" , "AnimationPlayer" ); |
213 | while (nodes.size()) { |
214 | AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(nodes.pop_back()); |
215 | List<StringName> anims; |
216 | ap->get_animation_list(&anims); |
217 | for (const StringName &name : anims) { |
218 | Ref<Animation> anim = ap->get_animation(name); |
219 | int track_len = anim->get_track_count(); |
220 | for (int i = 0; i < track_len; i++) { |
221 | String track_path = String(anim->track_get_path(i).get_concatenated_names()); |
222 | Node *orig_node = (ap->get_node(ap->get_root()))->get_node(NodePath(track_path)); |
223 | Node *node = (ap->get_node(ap->get_root()))->get_node(NodePath(track_path)); |
224 | while (node) { |
225 | Skeleton3D *track_skeleton = Object::cast_to<Skeleton3D>(node); |
226 | if (track_skeleton && track_skeleton == skeleton) { |
227 | if (node == orig_node) { |
228 | if (anim->track_get_path(i).get_subname_count() > 0) { |
229 | anim->track_set_path(i, UNIQUE_NODE_PREFIX + unique_name + String(":" ) + anim->track_get_path(i).get_concatenated_subnames()); |
230 | } else { |
231 | anim->track_set_path(i, UNIQUE_NODE_PREFIX + unique_name); |
232 | } |
233 | } else { |
234 | if (anim->track_get_path(i).get_subname_count() > 0) { |
235 | anim->track_set_path(i, UNIQUE_NODE_PREFIX + unique_name + "/" + node->get_path_to(orig_node) + String(":" ) + anim->track_get_path(i).get_concatenated_subnames()); |
236 | } else { |
237 | anim->track_set_path(i, UNIQUE_NODE_PREFIX + unique_name + "/" + node->get_path_to(orig_node)); |
238 | } |
239 | } |
240 | break; |
241 | } |
242 | node = node->get_parent(); |
243 | } |
244 | } |
245 | } |
246 | } |
247 | skeleton->set_name(unique_name); |
248 | skeleton->set_unique_name_in_owner(true); |
249 | } |
250 | } |
251 | } |
252 | |
253 | PostImportPluginSkeletonRenamer::PostImportPluginSkeletonRenamer() { |
254 | } |
255 | |