1 | /**************************************************************************/ |
2 | /* bone_attachment_3d.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 "bone_attachment_3d.h" |
32 | |
33 | void BoneAttachment3D::_validate_property(PropertyInfo &p_property) const { |
34 | if (p_property.name == "bone_name" ) { |
35 | // Because it is a constant function, we cannot use the _get_skeleton_3d function. |
36 | const Skeleton3D *parent = nullptr; |
37 | if (use_external_skeleton) { |
38 | if (external_skeleton_node_cache.is_valid()) { |
39 | parent = Object::cast_to<Skeleton3D>(ObjectDB::get_instance(external_skeleton_node_cache)); |
40 | } |
41 | } else { |
42 | parent = Object::cast_to<Skeleton3D>(get_parent()); |
43 | } |
44 | |
45 | if (parent) { |
46 | String names; |
47 | for (int i = 0; i < parent->get_bone_count(); i++) { |
48 | if (i > 0) { |
49 | names += "," ; |
50 | } |
51 | names += parent->get_bone_name(i); |
52 | } |
53 | |
54 | p_property.hint = PROPERTY_HINT_ENUM; |
55 | p_property.hint_string = names; |
56 | } else { |
57 | p_property.hint = PROPERTY_HINT_NONE; |
58 | p_property.hint_string = "" ; |
59 | } |
60 | } |
61 | } |
62 | |
63 | bool BoneAttachment3D::_set(const StringName &p_path, const Variant &p_value) { |
64 | if (p_path == SNAME("use_external_skeleton" )) { |
65 | set_use_external_skeleton(p_value); |
66 | } else if (p_path == SNAME("external_skeleton" )) { |
67 | set_external_skeleton(p_value); |
68 | } |
69 | |
70 | return true; |
71 | } |
72 | |
73 | bool BoneAttachment3D::_get(const StringName &p_path, Variant &r_ret) const { |
74 | if (p_path == SNAME("use_external_skeleton" )) { |
75 | r_ret = get_use_external_skeleton(); |
76 | } else if (p_path == SNAME("external_skeleton" )) { |
77 | r_ret = get_external_skeleton(); |
78 | } |
79 | |
80 | return true; |
81 | } |
82 | |
83 | void BoneAttachment3D::_get_property_list(List<PropertyInfo> *p_list) const { |
84 | p_list->push_back(PropertyInfo(Variant::BOOL, "use_external_skeleton" , PROPERTY_HINT_NONE, "" )); |
85 | if (use_external_skeleton) { |
86 | p_list->push_back(PropertyInfo(Variant::NODE_PATH, "external_skeleton" , PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Skeleton3D" )); |
87 | } |
88 | } |
89 | |
90 | PackedStringArray BoneAttachment3D::get_configuration_warnings() const { |
91 | PackedStringArray warnings = Node3D::get_configuration_warnings(); |
92 | |
93 | if (use_external_skeleton) { |
94 | if (external_skeleton_node_cache.is_null()) { |
95 | warnings.push_back(RTR("External Skeleton3D node not set! Please set a path to an external Skeleton3D node." )); |
96 | } |
97 | } else { |
98 | Skeleton3D *parent = Object::cast_to<Skeleton3D>(get_parent()); |
99 | if (!parent) { |
100 | warnings.push_back(RTR("Parent node is not a Skeleton3D node! Please use an external Skeleton3D if you intend to use the BoneAttachment3D without it being a child of a Skeleton3D node." )); |
101 | } |
102 | } |
103 | |
104 | if (bone_idx == -1) { |
105 | warnings.push_back(RTR("BoneAttachment3D node is not bound to any bones! Please select a bone to attach this node." )); |
106 | } |
107 | |
108 | return warnings; |
109 | } |
110 | |
111 | void BoneAttachment3D::_update_external_skeleton_cache() { |
112 | external_skeleton_node_cache = ObjectID(); |
113 | if (has_node(external_skeleton_node)) { |
114 | Node *node = get_node(external_skeleton_node); |
115 | ERR_FAIL_NULL_MSG(node, "Cannot update external skeleton cache: Node cannot be found!" ); |
116 | |
117 | // Make sure it's a skeleton3D |
118 | Skeleton3D *sk = Object::cast_to<Skeleton3D>(node); |
119 | ERR_FAIL_NULL_MSG(sk, "Cannot update external skeleton cache: Skeleton3D Nodepath does not point to a Skeleton3D node!" ); |
120 | |
121 | external_skeleton_node_cache = node->get_instance_id(); |
122 | } else { |
123 | if (external_skeleton_node.is_empty()) { |
124 | BoneAttachment3D *parent_attachment = Object::cast_to<BoneAttachment3D>(get_parent()); |
125 | if (parent_attachment) { |
126 | parent_attachment->_update_external_skeleton_cache(); |
127 | if (parent_attachment->has_node(parent_attachment->external_skeleton_node)) { |
128 | Node *node = parent_attachment->get_node(parent_attachment->external_skeleton_node); |
129 | ERR_FAIL_NULL_MSG(node, "Cannot update external skeleton cache: Parent's Skeleton3D node cannot be found!" ); |
130 | |
131 | // Make sure it's a skeleton3D |
132 | Skeleton3D *sk = Object::cast_to<Skeleton3D>(node); |
133 | ERR_FAIL_NULL_MSG(sk, "Cannot update external skeleton cache: Parent Skeleton3D Nodepath does not point to a Skeleton3D node!" ); |
134 | |
135 | external_skeleton_node_cache = node->get_instance_id(); |
136 | external_skeleton_node = get_path_to(node); |
137 | } |
138 | } |
139 | } |
140 | } |
141 | } |
142 | |
143 | void BoneAttachment3D::_check_bind() { |
144 | Skeleton3D *sk = _get_skeleton3d(); |
145 | |
146 | if (sk && !bound) { |
147 | if (bone_idx <= -1) { |
148 | bone_idx = sk->find_bone(bone_name); |
149 | } |
150 | if (bone_idx != -1) { |
151 | sk->call_deferred(SNAME("connect" ), "bone_pose_changed" , callable_mp(this, &BoneAttachment3D::on_bone_pose_update)); |
152 | bound = true; |
153 | call_deferred(SNAME("on_bone_pose_update" ), bone_idx); |
154 | } |
155 | } |
156 | } |
157 | |
158 | Skeleton3D *BoneAttachment3D::_get_skeleton3d() { |
159 | if (use_external_skeleton) { |
160 | if (external_skeleton_node_cache.is_valid()) { |
161 | return Object::cast_to<Skeleton3D>(ObjectDB::get_instance(external_skeleton_node_cache)); |
162 | } else { |
163 | _update_external_skeleton_cache(); |
164 | if (external_skeleton_node_cache.is_valid()) { |
165 | return Object::cast_to<Skeleton3D>(ObjectDB::get_instance(external_skeleton_node_cache)); |
166 | } |
167 | } |
168 | } else { |
169 | return Object::cast_to<Skeleton3D>(get_parent()); |
170 | } |
171 | return nullptr; |
172 | } |
173 | |
174 | void BoneAttachment3D::_check_unbind() { |
175 | if (bound) { |
176 | Skeleton3D *sk = _get_skeleton3d(); |
177 | |
178 | if (sk) { |
179 | sk->disconnect(SNAME("bone_pose_changed" ), callable_mp(this, &BoneAttachment3D::on_bone_pose_update)); |
180 | } |
181 | bound = false; |
182 | } |
183 | } |
184 | |
185 | void BoneAttachment3D::_transform_changed() { |
186 | if (!is_inside_tree()) { |
187 | return; |
188 | } |
189 | |
190 | if (override_pose) { |
191 | Skeleton3D *sk = _get_skeleton3d(); |
192 | |
193 | ERR_FAIL_NULL_MSG(sk, "Cannot override pose: Skeleton not found!" ); |
194 | ERR_FAIL_INDEX_MSG(bone_idx, sk->get_bone_count(), "Cannot override pose: Bone index is out of range!" ); |
195 | |
196 | Transform3D our_trans = get_transform(); |
197 | if (use_external_skeleton) { |
198 | our_trans = sk->get_global_transform().affine_inverse() * get_global_transform(); |
199 | } |
200 | |
201 | sk->set_bone_global_pose_override(bone_idx, our_trans, 1.0, true); |
202 | } |
203 | } |
204 | |
205 | void BoneAttachment3D::set_bone_name(const String &p_name) { |
206 | bone_name = p_name; |
207 | Skeleton3D *sk = _get_skeleton3d(); |
208 | if (sk) { |
209 | set_bone_idx(sk->find_bone(bone_name)); |
210 | } |
211 | } |
212 | |
213 | String BoneAttachment3D::get_bone_name() const { |
214 | return bone_name; |
215 | } |
216 | |
217 | void BoneAttachment3D::set_bone_idx(const int &p_idx) { |
218 | if (is_inside_tree()) { |
219 | _check_unbind(); |
220 | } |
221 | |
222 | bone_idx = p_idx; |
223 | |
224 | Skeleton3D *sk = _get_skeleton3d(); |
225 | if (sk) { |
226 | if (bone_idx <= -1 || bone_idx >= sk->get_bone_count()) { |
227 | WARN_PRINT("Bone index out of range! Cannot connect BoneAttachment to node!" ); |
228 | bone_idx = -1; |
229 | } else { |
230 | bone_name = sk->get_bone_name(bone_idx); |
231 | } |
232 | } |
233 | |
234 | if (is_inside_tree()) { |
235 | _check_bind(); |
236 | } |
237 | |
238 | notify_property_list_changed(); |
239 | } |
240 | |
241 | int BoneAttachment3D::get_bone_idx() const { |
242 | return bone_idx; |
243 | } |
244 | |
245 | void BoneAttachment3D::set_override_pose(bool p_override) { |
246 | override_pose = p_override; |
247 | set_notify_local_transform(override_pose); |
248 | set_process_internal(override_pose); |
249 | |
250 | if (!override_pose) { |
251 | Skeleton3D *sk = _get_skeleton3d(); |
252 | if (sk) { |
253 | sk->set_bone_global_pose_override(bone_idx, Transform3D(), 0.0, false); |
254 | } |
255 | _transform_changed(); |
256 | } |
257 | notify_property_list_changed(); |
258 | } |
259 | |
260 | bool BoneAttachment3D::get_override_pose() const { |
261 | return override_pose; |
262 | } |
263 | |
264 | void BoneAttachment3D::set_use_external_skeleton(bool p_use_external) { |
265 | use_external_skeleton = p_use_external; |
266 | |
267 | if (use_external_skeleton) { |
268 | _check_unbind(); |
269 | _update_external_skeleton_cache(); |
270 | _check_bind(); |
271 | _transform_changed(); |
272 | } |
273 | |
274 | notify_property_list_changed(); |
275 | } |
276 | |
277 | bool BoneAttachment3D::get_use_external_skeleton() const { |
278 | return use_external_skeleton; |
279 | } |
280 | |
281 | void BoneAttachment3D::set_external_skeleton(NodePath p_path) { |
282 | external_skeleton_node = p_path; |
283 | _update_external_skeleton_cache(); |
284 | notify_property_list_changed(); |
285 | } |
286 | |
287 | NodePath BoneAttachment3D::get_external_skeleton() const { |
288 | return external_skeleton_node; |
289 | } |
290 | |
291 | void BoneAttachment3D::_notification(int p_what) { |
292 | switch (p_what) { |
293 | case NOTIFICATION_ENTER_TREE: { |
294 | if (use_external_skeleton) { |
295 | _update_external_skeleton_cache(); |
296 | } |
297 | _check_bind(); |
298 | } break; |
299 | |
300 | case NOTIFICATION_EXIT_TREE: { |
301 | _check_unbind(); |
302 | } break; |
303 | |
304 | case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: { |
305 | _transform_changed(); |
306 | } break; |
307 | |
308 | case NOTIFICATION_INTERNAL_PROCESS: { |
309 | if (_override_dirty) { |
310 | _override_dirty = false; |
311 | } |
312 | } break; |
313 | } |
314 | } |
315 | |
316 | void BoneAttachment3D::on_bone_pose_update(int p_bone_index) { |
317 | if (bone_idx == p_bone_index) { |
318 | Skeleton3D *sk = _get_skeleton3d(); |
319 | if (sk) { |
320 | if (!override_pose) { |
321 | if (use_external_skeleton) { |
322 | set_global_transform(sk->get_global_transform() * sk->get_bone_global_pose(bone_idx)); |
323 | } else { |
324 | set_transform(sk->get_bone_global_pose(bone_idx)); |
325 | } |
326 | } else { |
327 | if (!_override_dirty) { |
328 | _transform_changed(); |
329 | _override_dirty = true; |
330 | } |
331 | } |
332 | } |
333 | } |
334 | } |
335 | #ifdef TOOLS_ENABLED |
336 | void BoneAttachment3D::_notify_skeleton_bones_renamed(Node *p_base_scene, Skeleton3D *p_skeleton, Dictionary p_rename_map) { |
337 | const Skeleton3D *parent = nullptr; |
338 | if (use_external_skeleton) { |
339 | if (external_skeleton_node_cache.is_valid()) { |
340 | parent = Object::cast_to<Skeleton3D>(ObjectDB::get_instance(external_skeleton_node_cache)); |
341 | } |
342 | } else { |
343 | parent = Object::cast_to<Skeleton3D>(get_parent()); |
344 | } |
345 | if (parent && parent == p_skeleton) { |
346 | StringName bn = p_rename_map[bone_name]; |
347 | if (bn) { |
348 | set_bone_name(bn); |
349 | } |
350 | } |
351 | } |
352 | #endif // TOOLS_ENABLED |
353 | |
354 | BoneAttachment3D::BoneAttachment3D() { |
355 | } |
356 | |
357 | void BoneAttachment3D::_bind_methods() { |
358 | ClassDB::bind_method(D_METHOD("set_bone_name" , "bone_name" ), &BoneAttachment3D::set_bone_name); |
359 | ClassDB::bind_method(D_METHOD("get_bone_name" ), &BoneAttachment3D::get_bone_name); |
360 | |
361 | ClassDB::bind_method(D_METHOD("set_bone_idx" , "bone_idx" ), &BoneAttachment3D::set_bone_idx); |
362 | ClassDB::bind_method(D_METHOD("get_bone_idx" ), &BoneAttachment3D::get_bone_idx); |
363 | |
364 | ClassDB::bind_method(D_METHOD("on_bone_pose_update" , "bone_index" ), &BoneAttachment3D::on_bone_pose_update); |
365 | |
366 | ClassDB::bind_method(D_METHOD("set_override_pose" , "override_pose" ), &BoneAttachment3D::set_override_pose); |
367 | ClassDB::bind_method(D_METHOD("get_override_pose" ), &BoneAttachment3D::get_override_pose); |
368 | |
369 | ClassDB::bind_method(D_METHOD("set_use_external_skeleton" , "use_external_skeleton" ), &BoneAttachment3D::set_use_external_skeleton); |
370 | ClassDB::bind_method(D_METHOD("get_use_external_skeleton" ), &BoneAttachment3D::get_use_external_skeleton); |
371 | ClassDB::bind_method(D_METHOD("set_external_skeleton" , "external_skeleton" ), &BoneAttachment3D::set_external_skeleton); |
372 | ClassDB::bind_method(D_METHOD("get_external_skeleton" ), &BoneAttachment3D::get_external_skeleton); |
373 | #ifdef TOOLS_ENABLED |
374 | ClassDB::bind_method(D_METHOD("_notify_skeleton_bones_renamed" ), &BoneAttachment3D::_notify_skeleton_bones_renamed); |
375 | #endif // TOOLS_ENABLED |
376 | |
377 | ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "bone_name" ), "set_bone_name" , "get_bone_name" ); |
378 | ADD_PROPERTY(PropertyInfo(Variant::INT, "bone_idx" ), "set_bone_idx" , "get_bone_idx" ); |
379 | ADD_PROPERTY(PropertyInfo(Variant::BOOL, "override_pose" ), "set_override_pose" , "get_override_pose" ); |
380 | } |
381 | |