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
39void 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
47void 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
146void 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
253PostImportPluginSkeletonRenamer::PostImportPluginSkeletonRenamer() {
254}
255