1 | /**************************************************************************/ |
2 | /* skeleton_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 "skeleton_3d.h" |
32 | |
33 | #include "core/object/message_queue.h" |
34 | #include "core/variant/type_info.h" |
35 | #include "scene/3d/physics_body_3d.h" |
36 | #include "scene/resources/surface_tool.h" |
37 | #include "scene/scene_string_names.h" |
38 | |
39 | void SkinReference::_skin_changed() { |
40 | if (skeleton_node) { |
41 | skeleton_node->_make_dirty(); |
42 | } |
43 | skeleton_version = 0; |
44 | } |
45 | |
46 | void SkinReference::_bind_methods() { |
47 | ClassDB::bind_method(D_METHOD("get_skeleton" ), &SkinReference::get_skeleton); |
48 | ClassDB::bind_method(D_METHOD("get_skin" ), &SkinReference::get_skin); |
49 | } |
50 | |
51 | RID SkinReference::get_skeleton() const { |
52 | return skeleton; |
53 | } |
54 | |
55 | Ref<Skin> SkinReference::get_skin() const { |
56 | return skin; |
57 | } |
58 | |
59 | SkinReference::~SkinReference() { |
60 | ERR_FAIL_NULL(RenderingServer::get_singleton()); |
61 | if (skeleton_node) { |
62 | skeleton_node->skin_bindings.erase(this); |
63 | } |
64 | RS::get_singleton()->free(skeleton); |
65 | } |
66 | |
67 | /////////////////////////////////////// |
68 | |
69 | bool Skeleton3D::_set(const StringName &p_path, const Variant &p_value) { |
70 | String path = p_path; |
71 | |
72 | if (!path.begins_with("bones/" )) { |
73 | return false; |
74 | } |
75 | |
76 | int which = path.get_slicec('/', 1).to_int(); |
77 | String what = path.get_slicec('/', 2); |
78 | |
79 | if (which == bones.size() && what == "name" ) { |
80 | add_bone(p_value); |
81 | return true; |
82 | } |
83 | |
84 | ERR_FAIL_INDEX_V(which, bones.size(), false); |
85 | |
86 | if (what == "parent" ) { |
87 | set_bone_parent(which, p_value); |
88 | } else if (what == "rest" ) { |
89 | set_bone_rest(which, p_value); |
90 | } else if (what == "enabled" ) { |
91 | set_bone_enabled(which, p_value); |
92 | } else if (what == "position" ) { |
93 | set_bone_pose_position(which, p_value); |
94 | } else if (what == "rotation" ) { |
95 | set_bone_pose_rotation(which, p_value); |
96 | } else if (what == "scale" ) { |
97 | set_bone_pose_scale(which, p_value); |
98 | } else { |
99 | return false; |
100 | } |
101 | |
102 | return true; |
103 | } |
104 | |
105 | bool Skeleton3D::_get(const StringName &p_path, Variant &r_ret) const { |
106 | String path = p_path; |
107 | |
108 | if (!path.begins_with("bones/" )) { |
109 | return false; |
110 | } |
111 | |
112 | int which = path.get_slicec('/', 1).to_int(); |
113 | String what = path.get_slicec('/', 2); |
114 | |
115 | ERR_FAIL_INDEX_V(which, bones.size(), false); |
116 | |
117 | if (what == "name" ) { |
118 | r_ret = get_bone_name(which); |
119 | } else if (what == "parent" ) { |
120 | r_ret = get_bone_parent(which); |
121 | } else if (what == "rest" ) { |
122 | r_ret = get_bone_rest(which); |
123 | } else if (what == "enabled" ) { |
124 | r_ret = is_bone_enabled(which); |
125 | } else if (what == "position" ) { |
126 | r_ret = get_bone_pose_position(which); |
127 | } else if (what == "rotation" ) { |
128 | r_ret = get_bone_pose_rotation(which); |
129 | } else if (what == "scale" ) { |
130 | r_ret = get_bone_pose_scale(which); |
131 | } else { |
132 | return false; |
133 | } |
134 | |
135 | return true; |
136 | } |
137 | |
138 | void Skeleton3D::_get_property_list(List<PropertyInfo> *p_list) const { |
139 | for (int i = 0; i < bones.size(); i++) { |
140 | const String prep = vformat("%s/%d/" , PNAME("bones" ), i); |
141 | p_list->push_back(PropertyInfo(Variant::STRING, prep + PNAME("name" ), PROPERTY_HINT_NONE, "" , PROPERTY_USAGE_NO_EDITOR)); |
142 | p_list->push_back(PropertyInfo(Variant::INT, prep + PNAME("parent" ), PROPERTY_HINT_RANGE, "-1," + itos(bones.size() - 1) + ",1" , PROPERTY_USAGE_NO_EDITOR)); |
143 | p_list->push_back(PropertyInfo(Variant::TRANSFORM3D, prep + PNAME("rest" ), PROPERTY_HINT_NONE, "" , PROPERTY_USAGE_NO_EDITOR)); |
144 | p_list->push_back(PropertyInfo(Variant::BOOL, prep + PNAME("enabled" ), PROPERTY_HINT_NONE, "" , PROPERTY_USAGE_NO_EDITOR)); |
145 | p_list->push_back(PropertyInfo(Variant::VECTOR3, prep + PNAME("position" ), PROPERTY_HINT_NONE, "" , PROPERTY_USAGE_NO_EDITOR)); |
146 | p_list->push_back(PropertyInfo(Variant::QUATERNION, prep + PNAME("rotation" ), PROPERTY_HINT_NONE, "" , PROPERTY_USAGE_NO_EDITOR)); |
147 | p_list->push_back(PropertyInfo(Variant::VECTOR3, prep + PNAME("scale" ), PROPERTY_HINT_NONE, "" , PROPERTY_USAGE_NO_EDITOR)); |
148 | } |
149 | |
150 | for (PropertyInfo &E : *p_list) { |
151 | _validate_property(E); |
152 | } |
153 | } |
154 | |
155 | void Skeleton3D::_validate_property(PropertyInfo &p_property) const { |
156 | PackedStringArray split = p_property.name.split("/" ); |
157 | if (split.size() == 3 && split[0] == "bones" ) { |
158 | if (split[2] == "rest" ) { |
159 | p_property.usage |= PROPERTY_USAGE_READ_ONLY; |
160 | } |
161 | if (is_show_rest_only()) { |
162 | if (split[2] == "enabled" ) { |
163 | p_property.usage |= PROPERTY_USAGE_READ_ONLY; |
164 | } |
165 | if (split[2] == "position" ) { |
166 | p_property.usage |= PROPERTY_USAGE_READ_ONLY; |
167 | } |
168 | if (split[2] == "rotation" ) { |
169 | p_property.usage |= PROPERTY_USAGE_READ_ONLY; |
170 | } |
171 | if (split[2] == "scale" ) { |
172 | p_property.usage |= PROPERTY_USAGE_READ_ONLY; |
173 | } |
174 | } else if (!is_bone_enabled(split[1].to_int())) { |
175 | if (split[2] == "position" ) { |
176 | p_property.usage |= PROPERTY_USAGE_READ_ONLY; |
177 | } |
178 | if (split[2] == "rotation" ) { |
179 | p_property.usage |= PROPERTY_USAGE_READ_ONLY; |
180 | } |
181 | if (split[2] == "scale" ) { |
182 | p_property.usage |= PROPERTY_USAGE_READ_ONLY; |
183 | } |
184 | } |
185 | } |
186 | } |
187 | |
188 | void Skeleton3D::_update_process_order() { |
189 | if (!process_order_dirty) { |
190 | return; |
191 | } |
192 | |
193 | Bone *bonesptr = bones.ptrw(); |
194 | int len = bones.size(); |
195 | |
196 | parentless_bones.clear(); |
197 | |
198 | for (int i = 0; i < len; i++) { |
199 | bonesptr[i].child_bones.clear(); |
200 | } |
201 | |
202 | for (int i = 0; i < len; i++) { |
203 | if (bonesptr[i].parent >= len) { |
204 | // Validate this just in case. |
205 | ERR_PRINT("Bone " + itos(i) + " has invalid parent: " + itos(bonesptr[i].parent)); |
206 | bonesptr[i].parent = -1; |
207 | } |
208 | |
209 | if (bonesptr[i].parent != -1) { |
210 | int parent_bone_idx = bonesptr[i].parent; |
211 | |
212 | // Check to see if this node is already added to the parent. |
213 | if (bonesptr[parent_bone_idx].child_bones.find(i) < 0) { |
214 | // Add the child node. |
215 | bonesptr[parent_bone_idx].child_bones.push_back(i); |
216 | } else { |
217 | ERR_PRINT("Skeleton3D parenthood graph is cyclic" ); |
218 | } |
219 | } else { |
220 | parentless_bones.push_back(i); |
221 | } |
222 | } |
223 | |
224 | process_order_dirty = false; |
225 | } |
226 | |
227 | void Skeleton3D::_notification(int p_what) { |
228 | switch (p_what) { |
229 | case NOTIFICATION_ENTER_TREE: { |
230 | if (dirty) { |
231 | notification(NOTIFICATION_UPDATE_SKELETON); |
232 | } |
233 | } break; |
234 | case NOTIFICATION_UPDATE_SKELETON: { |
235 | RenderingServer *rs = RenderingServer::get_singleton(); |
236 | Bone *bonesptr = bones.ptrw(); |
237 | |
238 | int len = bones.size(); |
239 | dirty = false; |
240 | |
241 | // Update bone transforms. |
242 | force_update_all_bone_transforms(); |
243 | |
244 | // Update skins. |
245 | for (SkinReference *E : skin_bindings) { |
246 | const Skin *skin = E->skin.operator->(); |
247 | RID skeleton = E->skeleton; |
248 | uint32_t bind_count = skin->get_bind_count(); |
249 | |
250 | if (E->bind_count != bind_count) { |
251 | RS::get_singleton()->skeleton_allocate_data(skeleton, bind_count); |
252 | E->bind_count = bind_count; |
253 | E->skin_bone_indices.resize(bind_count); |
254 | E->skin_bone_indices_ptrs = E->skin_bone_indices.ptrw(); |
255 | } |
256 | |
257 | if (E->skeleton_version != version) { |
258 | for (uint32_t i = 0; i < bind_count; i++) { |
259 | StringName bind_name = skin->get_bind_name(i); |
260 | |
261 | if (bind_name != StringName()) { |
262 | // Bind name used, use this. |
263 | bool found = false; |
264 | for (int j = 0; j < len; j++) { |
265 | if (bonesptr[j].name == bind_name) { |
266 | E->skin_bone_indices_ptrs[i] = j; |
267 | found = true; |
268 | break; |
269 | } |
270 | } |
271 | |
272 | if (!found) { |
273 | ERR_PRINT("Skin bind #" + itos(i) + " contains named bind '" + String(bind_name) + "' but Skeleton3D has no bone by that name." ); |
274 | E->skin_bone_indices_ptrs[i] = 0; |
275 | } |
276 | } else if (skin->get_bind_bone(i) >= 0) { |
277 | int bind_index = skin->get_bind_bone(i); |
278 | if (bind_index >= len) { |
279 | ERR_PRINT("Skin bind #" + itos(i) + " contains bone index bind: " + itos(bind_index) + " , which is greater than the skeleton bone count: " + itos(len) + "." ); |
280 | E->skin_bone_indices_ptrs[i] = 0; |
281 | } else { |
282 | E->skin_bone_indices_ptrs[i] = bind_index; |
283 | } |
284 | } else { |
285 | ERR_PRINT("Skin bind #" + itos(i) + " does not contain a name nor a bone index." ); |
286 | E->skin_bone_indices_ptrs[i] = 0; |
287 | } |
288 | } |
289 | |
290 | E->skeleton_version = version; |
291 | } |
292 | |
293 | for (uint32_t i = 0; i < bind_count; i++) { |
294 | uint32_t bone_index = E->skin_bone_indices_ptrs[i]; |
295 | ERR_CONTINUE(bone_index >= (uint32_t)len); |
296 | rs->skeleton_bone_set_transform(skeleton, i, bonesptr[bone_index].pose_global * skin->get_bind_pose(i)); |
297 | } |
298 | } |
299 | emit_signal(SceneStringNames::get_singleton()->pose_updated); |
300 | } break; |
301 | |
302 | #ifndef _3D_DISABLED |
303 | case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { |
304 | // This is active only if the skeleton animates the physical bones |
305 | // and the state of the bone is not active. |
306 | if (animate_physical_bones) { |
307 | for (int i = 0; i < bones.size(); i += 1) { |
308 | if (bones[i].physical_bone) { |
309 | if (bones[i].physical_bone->is_simulating_physics() == false) { |
310 | bones[i].physical_bone->reset_to_rest_position(); |
311 | } |
312 | } |
313 | } |
314 | } |
315 | } break; |
316 | case NOTIFICATION_READY: { |
317 | if (Engine::get_singleton()->is_editor_hint()) { |
318 | set_physics_process_internal(true); |
319 | } |
320 | } break; |
321 | #endif // _3D_DISABLED |
322 | } |
323 | } |
324 | |
325 | void Skeleton3D::clear_bones_global_pose_override() { |
326 | for (int i = 0; i < bones.size(); i += 1) { |
327 | bones.write[i].global_pose_override_amount = 0; |
328 | bones.write[i].global_pose_override_reset = true; |
329 | } |
330 | _make_dirty(); |
331 | } |
332 | |
333 | void Skeleton3D::set_bone_global_pose_override(int p_bone, const Transform3D &p_pose, real_t p_amount, bool p_persistent) { |
334 | const int bone_size = bones.size(); |
335 | ERR_FAIL_INDEX(p_bone, bone_size); |
336 | bones.write[p_bone].global_pose_override_amount = p_amount; |
337 | bones.write[p_bone].global_pose_override = p_pose; |
338 | bones.write[p_bone].global_pose_override_reset = !p_persistent; |
339 | _make_dirty(); |
340 | } |
341 | |
342 | Transform3D Skeleton3D::get_bone_global_pose_override(int p_bone) const { |
343 | const int bone_size = bones.size(); |
344 | ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D()); |
345 | return bones[p_bone].global_pose_override; |
346 | } |
347 | |
348 | Transform3D Skeleton3D::get_bone_global_pose(int p_bone) const { |
349 | const int bone_size = bones.size(); |
350 | ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D()); |
351 | if (dirty) { |
352 | const_cast<Skeleton3D *>(this)->notification(NOTIFICATION_UPDATE_SKELETON); |
353 | } |
354 | return bones[p_bone].pose_global; |
355 | } |
356 | |
357 | Transform3D Skeleton3D::get_bone_global_pose_no_override(int p_bone) const { |
358 | const int bone_size = bones.size(); |
359 | ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D()); |
360 | if (dirty) { |
361 | const_cast<Skeleton3D *>(this)->notification(NOTIFICATION_UPDATE_SKELETON); |
362 | } |
363 | return bones[p_bone].pose_global_no_override; |
364 | } |
365 | |
366 | void Skeleton3D::set_motion_scale(float p_motion_scale) { |
367 | if (p_motion_scale <= 0) { |
368 | motion_scale = 1; |
369 | ERR_FAIL_MSG("Motion scale must be larger than 0." ); |
370 | } |
371 | motion_scale = p_motion_scale; |
372 | } |
373 | |
374 | float Skeleton3D::get_motion_scale() const { |
375 | ERR_FAIL_COND_V(motion_scale <= 0, 1); |
376 | return motion_scale; |
377 | } |
378 | |
379 | // Skeleton creation api |
380 | |
381 | uint64_t Skeleton3D::get_version() const { |
382 | return version; |
383 | } |
384 | |
385 | void Skeleton3D::add_bone(const String &p_name) { |
386 | ERR_FAIL_COND(p_name.is_empty() || p_name.contains(":" ) || p_name.contains("/" ) || name_to_bone_index.has(p_name)); |
387 | |
388 | Bone b; |
389 | b.name = p_name; |
390 | bones.push_back(b); |
391 | name_to_bone_index.insert(p_name, bones.size() - 1); |
392 | process_order_dirty = true; |
393 | version++; |
394 | rest_dirty = true; |
395 | _make_dirty(); |
396 | update_gizmos(); |
397 | } |
398 | |
399 | int Skeleton3D::find_bone(const String &p_name) const { |
400 | const int *bone_index_ptr = name_to_bone_index.getptr(p_name); |
401 | return bone_index_ptr != nullptr ? *bone_index_ptr : -1; |
402 | } |
403 | |
404 | String Skeleton3D::get_bone_name(int p_bone) const { |
405 | const int bone_size = bones.size(); |
406 | ERR_FAIL_INDEX_V(p_bone, bone_size, "" ); |
407 | return bones[p_bone].name; |
408 | } |
409 | |
410 | void Skeleton3D::set_bone_name(int p_bone, const String &p_name) { |
411 | const int bone_size = bones.size(); |
412 | ERR_FAIL_INDEX(p_bone, bone_size); |
413 | |
414 | const int *bone_index_ptr = name_to_bone_index.getptr(p_name); |
415 | if (bone_index_ptr != nullptr) { |
416 | ERR_FAIL_COND_MSG(*bone_index_ptr != p_bone, "Skeleton3D: '" + get_name() + "', bone name: '" + p_name + "' already exists." ); |
417 | return; // No need to rename, the bone already has the given name. |
418 | } |
419 | |
420 | name_to_bone_index.erase(bones[p_bone].name); |
421 | bones.write[p_bone].name = p_name; |
422 | name_to_bone_index.insert(p_name, p_bone); |
423 | |
424 | version++; |
425 | } |
426 | |
427 | bool Skeleton3D::is_bone_parent_of(int p_bone, int p_parent_bone_id) const { |
428 | int parent_of_bone = get_bone_parent(p_bone); |
429 | |
430 | if (-1 == parent_of_bone) { |
431 | return false; |
432 | } |
433 | |
434 | if (parent_of_bone == p_parent_bone_id) { |
435 | return true; |
436 | } |
437 | |
438 | return is_bone_parent_of(parent_of_bone, p_parent_bone_id); |
439 | } |
440 | |
441 | int Skeleton3D::get_bone_count() const { |
442 | return bones.size(); |
443 | } |
444 | |
445 | void Skeleton3D::set_bone_parent(int p_bone, int p_parent) { |
446 | const int bone_size = bones.size(); |
447 | ERR_FAIL_INDEX(p_bone, bone_size); |
448 | ERR_FAIL_COND(p_parent != -1 && (p_parent < 0)); |
449 | ERR_FAIL_COND(p_bone == p_parent); |
450 | |
451 | bones.write[p_bone].parent = p_parent; |
452 | process_order_dirty = true; |
453 | rest_dirty = true; |
454 | _make_dirty(); |
455 | } |
456 | |
457 | void Skeleton3D::unparent_bone_and_rest(int p_bone) { |
458 | const int bone_size = bones.size(); |
459 | ERR_FAIL_INDEX(p_bone, bone_size); |
460 | |
461 | _update_process_order(); |
462 | |
463 | int parent = bones[p_bone].parent; |
464 | while (parent >= 0) { |
465 | bones.write[p_bone].rest = bones[parent].rest * bones[p_bone].rest; |
466 | parent = bones[parent].parent; |
467 | } |
468 | |
469 | bones.write[p_bone].parent = -1; |
470 | process_order_dirty = true; |
471 | |
472 | rest_dirty = true; |
473 | _make_dirty(); |
474 | } |
475 | |
476 | int Skeleton3D::get_bone_parent(int p_bone) const { |
477 | const int bone_size = bones.size(); |
478 | ERR_FAIL_INDEX_V(p_bone, bone_size, -1); |
479 | if (process_order_dirty) { |
480 | const_cast<Skeleton3D *>(this)->_update_process_order(); |
481 | } |
482 | return bones[p_bone].parent; |
483 | } |
484 | |
485 | Vector<int> Skeleton3D::get_bone_children(int p_bone) const { |
486 | const int bone_size = bones.size(); |
487 | ERR_FAIL_INDEX_V(p_bone, bone_size, Vector<int>()); |
488 | if (process_order_dirty) { |
489 | const_cast<Skeleton3D *>(this)->_update_process_order(); |
490 | } |
491 | return bones[p_bone].child_bones; |
492 | } |
493 | |
494 | Vector<int> Skeleton3D::get_parentless_bones() const { |
495 | if (process_order_dirty) { |
496 | const_cast<Skeleton3D *>(this)->_update_process_order(); |
497 | } |
498 | return parentless_bones; |
499 | } |
500 | |
501 | void Skeleton3D::set_bone_rest(int p_bone, const Transform3D &p_rest) { |
502 | const int bone_size = bones.size(); |
503 | ERR_FAIL_INDEX(p_bone, bone_size); |
504 | |
505 | bones.write[p_bone].rest = p_rest; |
506 | rest_dirty = true; |
507 | _make_dirty(); |
508 | } |
509 | Transform3D Skeleton3D::get_bone_rest(int p_bone) const { |
510 | const int bone_size = bones.size(); |
511 | ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D()); |
512 | |
513 | return bones[p_bone].rest; |
514 | } |
515 | Transform3D Skeleton3D::get_bone_global_rest(int p_bone) const { |
516 | const int bone_size = bones.size(); |
517 | ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D()); |
518 | if (rest_dirty) { |
519 | const_cast<Skeleton3D *>(this)->notification(NOTIFICATION_UPDATE_SKELETON); |
520 | } |
521 | return bones[p_bone].global_rest; |
522 | } |
523 | |
524 | void Skeleton3D::set_bone_enabled(int p_bone, bool p_enabled) { |
525 | const int bone_size = bones.size(); |
526 | ERR_FAIL_INDEX(p_bone, bone_size); |
527 | |
528 | bones.write[p_bone].enabled = p_enabled; |
529 | emit_signal(SceneStringNames::get_singleton()->bone_enabled_changed, p_bone); |
530 | _make_dirty(); |
531 | } |
532 | |
533 | bool Skeleton3D::is_bone_enabled(int p_bone) const { |
534 | const int bone_size = bones.size(); |
535 | ERR_FAIL_INDEX_V(p_bone, bone_size, false); |
536 | return bones[p_bone].enabled; |
537 | } |
538 | |
539 | void Skeleton3D::set_show_rest_only(bool p_enabled) { |
540 | show_rest_only = p_enabled; |
541 | emit_signal(SceneStringNames::get_singleton()->show_rest_only_changed); |
542 | _make_dirty(); |
543 | } |
544 | |
545 | bool Skeleton3D::is_show_rest_only() const { |
546 | return show_rest_only; |
547 | } |
548 | |
549 | void Skeleton3D::clear_bones() { |
550 | bones.clear(); |
551 | name_to_bone_index.clear(); |
552 | process_order_dirty = true; |
553 | version++; |
554 | _make_dirty(); |
555 | } |
556 | |
557 | // Posing api |
558 | |
559 | void Skeleton3D::set_bone_pose_position(int p_bone, const Vector3 &p_position) { |
560 | const int bone_size = bones.size(); |
561 | ERR_FAIL_INDEX(p_bone, bone_size); |
562 | |
563 | bones.write[p_bone].pose_position = p_position; |
564 | bones.write[p_bone].pose_cache_dirty = true; |
565 | if (is_inside_tree()) { |
566 | _make_dirty(); |
567 | } |
568 | } |
569 | void Skeleton3D::set_bone_pose_rotation(int p_bone, const Quaternion &p_rotation) { |
570 | const int bone_size = bones.size(); |
571 | ERR_FAIL_INDEX(p_bone, bone_size); |
572 | |
573 | bones.write[p_bone].pose_rotation = p_rotation; |
574 | bones.write[p_bone].pose_cache_dirty = true; |
575 | if (is_inside_tree()) { |
576 | _make_dirty(); |
577 | } |
578 | } |
579 | void Skeleton3D::set_bone_pose_scale(int p_bone, const Vector3 &p_scale) { |
580 | const int bone_size = bones.size(); |
581 | ERR_FAIL_INDEX(p_bone, bone_size); |
582 | |
583 | bones.write[p_bone].pose_scale = p_scale; |
584 | bones.write[p_bone].pose_cache_dirty = true; |
585 | if (is_inside_tree()) { |
586 | _make_dirty(); |
587 | } |
588 | } |
589 | |
590 | Vector3 Skeleton3D::get_bone_pose_position(int p_bone) const { |
591 | const int bone_size = bones.size(); |
592 | ERR_FAIL_INDEX_V(p_bone, bone_size, Vector3()); |
593 | return bones[p_bone].pose_position; |
594 | } |
595 | |
596 | Quaternion Skeleton3D::get_bone_pose_rotation(int p_bone) const { |
597 | const int bone_size = bones.size(); |
598 | ERR_FAIL_INDEX_V(p_bone, bone_size, Quaternion()); |
599 | return bones[p_bone].pose_rotation; |
600 | } |
601 | |
602 | Vector3 Skeleton3D::get_bone_pose_scale(int p_bone) const { |
603 | const int bone_size = bones.size(); |
604 | ERR_FAIL_INDEX_V(p_bone, bone_size, Vector3()); |
605 | return bones[p_bone].pose_scale; |
606 | } |
607 | |
608 | void Skeleton3D::reset_bone_pose(int p_bone) { |
609 | const int bone_size = bones.size(); |
610 | ERR_FAIL_INDEX(p_bone, bone_size); |
611 | set_bone_pose_position(p_bone, bones[p_bone].rest.origin); |
612 | set_bone_pose_rotation(p_bone, bones[p_bone].rest.basis.get_rotation_quaternion()); |
613 | set_bone_pose_scale(p_bone, bones[p_bone].rest.basis.get_scale()); |
614 | } |
615 | |
616 | void Skeleton3D::reset_bone_poses() { |
617 | for (int i = 0; i < bones.size(); i++) { |
618 | reset_bone_pose(i); |
619 | } |
620 | } |
621 | |
622 | Transform3D Skeleton3D::get_bone_pose(int p_bone) const { |
623 | const int bone_size = bones.size(); |
624 | ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D()); |
625 | ((Skeleton3D *)this)->bones.write[p_bone].update_pose_cache(); |
626 | return bones[p_bone].pose_cache; |
627 | } |
628 | |
629 | void Skeleton3D::_make_dirty() { |
630 | if (dirty) { |
631 | return; |
632 | } |
633 | |
634 | if (is_inside_tree()) { |
635 | notify_deferred_thread_group(NOTIFICATION_UPDATE_SKELETON); |
636 | } |
637 | dirty = true; |
638 | } |
639 | |
640 | void Skeleton3D::localize_rests() { |
641 | Vector<int> bones_to_process = get_parentless_bones(); |
642 | while (bones_to_process.size() > 0) { |
643 | int current_bone_idx = bones_to_process[0]; |
644 | bones_to_process.erase(current_bone_idx); |
645 | |
646 | if (bones[current_bone_idx].parent >= 0) { |
647 | set_bone_rest(current_bone_idx, bones[bones[current_bone_idx].parent].rest.affine_inverse() * bones[current_bone_idx].rest); |
648 | } |
649 | |
650 | // Add the bone's children to the list of bones to be processed. |
651 | int child_bone_size = bones[current_bone_idx].child_bones.size(); |
652 | for (int i = 0; i < child_bone_size; i++) { |
653 | bones_to_process.push_back(bones[current_bone_idx].child_bones[i]); |
654 | } |
655 | } |
656 | } |
657 | |
658 | void Skeleton3D::set_animate_physical_bones(bool p_enabled) { |
659 | animate_physical_bones = p_enabled; |
660 | |
661 | if (Engine::get_singleton()->is_editor_hint() == false) { |
662 | bool sim = false; |
663 | for (int i = 0; i < bones.size(); i += 1) { |
664 | if (bones[i].physical_bone) { |
665 | bones[i].physical_bone->reset_physics_simulation_state(); |
666 | if (bones[i].physical_bone->is_simulating_physics()) { |
667 | sim = true; |
668 | } |
669 | } |
670 | } |
671 | set_physics_process_internal(sim == false && p_enabled); |
672 | } |
673 | } |
674 | |
675 | bool Skeleton3D::get_animate_physical_bones() const { |
676 | return animate_physical_bones; |
677 | } |
678 | |
679 | void Skeleton3D::bind_physical_bone_to_bone(int p_bone, PhysicalBone3D *p_physical_bone) { |
680 | const int bone_size = bones.size(); |
681 | ERR_FAIL_INDEX(p_bone, bone_size); |
682 | ERR_FAIL_COND(bones[p_bone].physical_bone); |
683 | ERR_FAIL_NULL(p_physical_bone); |
684 | bones.write[p_bone].physical_bone = p_physical_bone; |
685 | |
686 | _rebuild_physical_bones_cache(); |
687 | } |
688 | |
689 | void Skeleton3D::unbind_physical_bone_from_bone(int p_bone) { |
690 | const int bone_size = bones.size(); |
691 | ERR_FAIL_INDEX(p_bone, bone_size); |
692 | bones.write[p_bone].physical_bone = nullptr; |
693 | |
694 | _rebuild_physical_bones_cache(); |
695 | } |
696 | |
697 | PhysicalBone3D *Skeleton3D::get_physical_bone(int p_bone) { |
698 | const int bone_size = bones.size(); |
699 | ERR_FAIL_INDEX_V(p_bone, bone_size, nullptr); |
700 | |
701 | return bones[p_bone].physical_bone; |
702 | } |
703 | |
704 | PhysicalBone3D *Skeleton3D::get_physical_bone_parent(int p_bone) { |
705 | const int bone_size = bones.size(); |
706 | ERR_FAIL_INDEX_V(p_bone, bone_size, nullptr); |
707 | |
708 | if (bones[p_bone].cache_parent_physical_bone) { |
709 | return bones[p_bone].cache_parent_physical_bone; |
710 | } |
711 | |
712 | return _get_physical_bone_parent(p_bone); |
713 | } |
714 | |
715 | PhysicalBone3D *Skeleton3D::_get_physical_bone_parent(int p_bone) { |
716 | const int bone_size = bones.size(); |
717 | ERR_FAIL_INDEX_V(p_bone, bone_size, nullptr); |
718 | |
719 | const int parent_bone = bones[p_bone].parent; |
720 | if (0 > parent_bone) { |
721 | return nullptr; |
722 | } |
723 | |
724 | PhysicalBone3D *pb = bones[parent_bone].physical_bone; |
725 | if (pb) { |
726 | return pb; |
727 | } else { |
728 | return get_physical_bone_parent(parent_bone); |
729 | } |
730 | } |
731 | |
732 | void Skeleton3D::_rebuild_physical_bones_cache() { |
733 | const int b_size = bones.size(); |
734 | for (int i = 0; i < b_size; ++i) { |
735 | PhysicalBone3D *parent_pb = _get_physical_bone_parent(i); |
736 | if (parent_pb != bones[i].cache_parent_physical_bone) { |
737 | bones.write[i].cache_parent_physical_bone = parent_pb; |
738 | if (bones[i].physical_bone) { |
739 | bones[i].physical_bone->_on_bone_parent_changed(); |
740 | } |
741 | } |
742 | } |
743 | } |
744 | |
745 | void _pb_stop_simulation(Node *p_node) { |
746 | for (int i = p_node->get_child_count() - 1; 0 <= i; --i) { |
747 | _pb_stop_simulation(p_node->get_child(i)); |
748 | } |
749 | |
750 | PhysicalBone3D *pb = Object::cast_to<PhysicalBone3D>(p_node); |
751 | if (pb) { |
752 | pb->set_simulate_physics(false); |
753 | } |
754 | } |
755 | |
756 | void Skeleton3D::physical_bones_stop_simulation() { |
757 | _pb_stop_simulation(this); |
758 | if (Engine::get_singleton()->is_editor_hint() == false && animate_physical_bones) { |
759 | set_physics_process_internal(true); |
760 | } |
761 | } |
762 | |
763 | void _pb_start_simulation(const Skeleton3D *p_skeleton, Node *p_node, const Vector<int> &p_sim_bones) { |
764 | for (int i = p_node->get_child_count() - 1; 0 <= i; --i) { |
765 | _pb_start_simulation(p_skeleton, p_node->get_child(i), p_sim_bones); |
766 | } |
767 | |
768 | PhysicalBone3D *pb = Object::cast_to<PhysicalBone3D>(p_node); |
769 | if (pb) { |
770 | if (p_sim_bones.is_empty()) { // If no bones is specified, activate ragdoll on full body. |
771 | pb->set_simulate_physics(true); |
772 | } else { |
773 | for (int i = p_sim_bones.size() - 1; 0 <= i; --i) { |
774 | if (p_sim_bones[i] == pb->get_bone_id() || p_skeleton->is_bone_parent_of(pb->get_bone_id(), p_sim_bones[i])) { |
775 | pb->set_simulate_physics(true); |
776 | break; |
777 | } |
778 | } |
779 | } |
780 | } |
781 | } |
782 | |
783 | void Skeleton3D::physical_bones_start_simulation_on(const TypedArray<StringName> &p_bones) { |
784 | set_physics_process_internal(false); |
785 | |
786 | Vector<int> sim_bones; |
787 | if (p_bones.size() > 0) { |
788 | sim_bones.resize(p_bones.size()); |
789 | int c = 0; |
790 | for (int i = sim_bones.size() - 1; 0 <= i; --i) { |
791 | int bone_id = find_bone(p_bones[i]); |
792 | if (bone_id != -1) { |
793 | sim_bones.write[c++] = bone_id; |
794 | } |
795 | } |
796 | sim_bones.resize(c); |
797 | } |
798 | |
799 | _pb_start_simulation(this, this, sim_bones); |
800 | } |
801 | |
802 | void _physical_bones_add_remove_collision_exception(bool p_add, Node *p_node, RID p_exception) { |
803 | for (int i = p_node->get_child_count() - 1; 0 <= i; --i) { |
804 | _physical_bones_add_remove_collision_exception(p_add, p_node->get_child(i), p_exception); |
805 | } |
806 | |
807 | CollisionObject3D *co = Object::cast_to<CollisionObject3D>(p_node); |
808 | if (co) { |
809 | if (p_add) { |
810 | PhysicsServer3D::get_singleton()->body_add_collision_exception(co->get_rid(), p_exception); |
811 | } else { |
812 | PhysicsServer3D::get_singleton()->body_remove_collision_exception(co->get_rid(), p_exception); |
813 | } |
814 | } |
815 | } |
816 | |
817 | void Skeleton3D::physical_bones_add_collision_exception(RID p_exception) { |
818 | _physical_bones_add_remove_collision_exception(true, this, p_exception); |
819 | } |
820 | |
821 | void Skeleton3D::physical_bones_remove_collision_exception(RID p_exception) { |
822 | _physical_bones_add_remove_collision_exception(false, this, p_exception); |
823 | } |
824 | |
825 | void Skeleton3D::_skin_changed() { |
826 | _make_dirty(); |
827 | } |
828 | |
829 | Ref<Skin> Skeleton3D::create_skin_from_rest_transforms() { |
830 | Ref<Skin> skin; |
831 | |
832 | skin.instantiate(); |
833 | skin->set_bind_count(bones.size()); |
834 | |
835 | // Pose changed, rebuild cache of inverses. |
836 | const Bone *bonesptr = bones.ptr(); |
837 | int len = bones.size(); |
838 | |
839 | // Calculate global rests and invert them. |
840 | LocalVector<int> bones_to_process; |
841 | bones_to_process = get_parentless_bones(); |
842 | while (bones_to_process.size() > 0) { |
843 | int current_bone_idx = bones_to_process[0]; |
844 | const Bone &b = bonesptr[current_bone_idx]; |
845 | bones_to_process.erase(current_bone_idx); |
846 | LocalVector<int> child_bones_vector; |
847 | child_bones_vector = get_bone_children(current_bone_idx); |
848 | int child_bones_size = child_bones_vector.size(); |
849 | if (b.parent < 0) { |
850 | skin->set_bind_pose(current_bone_idx, b.rest); |
851 | } |
852 | for (int i = 0; i < child_bones_size; i++) { |
853 | int child_bone_idx = child_bones_vector[i]; |
854 | const Bone &cb = bonesptr[child_bone_idx]; |
855 | skin->set_bind_pose(child_bone_idx, skin->get_bind_pose(current_bone_idx) * cb.rest); |
856 | // Add the bone's children to the list of bones to be processed. |
857 | bones_to_process.push_back(child_bones_vector[i]); |
858 | } |
859 | } |
860 | |
861 | for (int i = 0; i < len; i++) { |
862 | // The inverse is what is actually required. |
863 | skin->set_bind_bone(i, i); |
864 | skin->set_bind_pose(i, skin->get_bind_pose(i).affine_inverse()); |
865 | } |
866 | |
867 | return skin; |
868 | } |
869 | |
870 | Ref<SkinReference> Skeleton3D::register_skin(const Ref<Skin> &p_skin) { |
871 | ERR_FAIL_COND_V(p_skin.is_null(), Ref<SkinReference>()); |
872 | |
873 | for (const SkinReference *E : skin_bindings) { |
874 | if (E->skin == p_skin) { |
875 | return Ref<SkinReference>(E); |
876 | } |
877 | } |
878 | |
879 | Ref<SkinReference> skin_ref; |
880 | skin_ref.instantiate(); |
881 | |
882 | skin_ref->skeleton_node = this; |
883 | skin_ref->bind_count = 0; |
884 | skin_ref->skeleton = RenderingServer::get_singleton()->skeleton_create(); |
885 | skin_ref->skeleton_node = this; |
886 | skin_ref->skin = p_skin; |
887 | |
888 | skin_bindings.insert(skin_ref.operator->()); |
889 | |
890 | skin_ref->skin->connect_changed(callable_mp(skin_ref.operator->(), &SkinReference::_skin_changed)); |
891 | |
892 | _make_dirty(); // Skin needs to be updated, so update skeleton. |
893 | |
894 | return skin_ref; |
895 | } |
896 | |
897 | void Skeleton3D::force_update_all_dirty_bones() { |
898 | if (dirty) { |
899 | const_cast<Skeleton3D *>(this)->notification(NOTIFICATION_UPDATE_SKELETON); |
900 | } |
901 | } |
902 | |
903 | void Skeleton3D::force_update_all_bone_transforms() { |
904 | _update_process_order(); |
905 | |
906 | for (int i = 0; i < parentless_bones.size(); i++) { |
907 | force_update_bone_children_transforms(parentless_bones[i]); |
908 | } |
909 | rest_dirty = false; |
910 | } |
911 | |
912 | void Skeleton3D::force_update_bone_children_transforms(int p_bone_idx) { |
913 | const int bone_size = bones.size(); |
914 | ERR_FAIL_INDEX(p_bone_idx, bone_size); |
915 | |
916 | Bone *bonesptr = bones.ptrw(); |
917 | List<int> bones_to_process = List<int>(); |
918 | bones_to_process.push_back(p_bone_idx); |
919 | |
920 | while (bones_to_process.size() > 0) { |
921 | int current_bone_idx = bones_to_process[0]; |
922 | bones_to_process.erase(current_bone_idx); |
923 | |
924 | Bone &b = bonesptr[current_bone_idx]; |
925 | bool bone_enabled = b.enabled && !show_rest_only; |
926 | |
927 | if (bone_enabled) { |
928 | b.update_pose_cache(); |
929 | Transform3D pose = b.pose_cache; |
930 | |
931 | if (b.parent >= 0) { |
932 | b.pose_global = bonesptr[b.parent].pose_global * pose; |
933 | b.pose_global_no_override = bonesptr[b.parent].pose_global_no_override * pose; |
934 | } else { |
935 | b.pose_global = pose; |
936 | b.pose_global_no_override = pose; |
937 | } |
938 | } else { |
939 | if (b.parent >= 0) { |
940 | b.pose_global = bonesptr[b.parent].pose_global * b.rest; |
941 | b.pose_global_no_override = bonesptr[b.parent].pose_global_no_override * b.rest; |
942 | } else { |
943 | b.pose_global = b.rest; |
944 | b.pose_global_no_override = b.rest; |
945 | } |
946 | } |
947 | if (rest_dirty) { |
948 | b.global_rest = b.parent >= 0 ? bonesptr[b.parent].global_rest * b.rest : b.rest; |
949 | } |
950 | |
951 | if (b.global_pose_override_amount >= CMP_EPSILON) { |
952 | b.pose_global = b.pose_global.interpolate_with(b.global_pose_override, b.global_pose_override_amount); |
953 | } |
954 | |
955 | if (b.global_pose_override_reset) { |
956 | b.global_pose_override_amount = 0.0; |
957 | } |
958 | |
959 | // Add the bone's children to the list of bones to be processed. |
960 | int child_bone_size = b.child_bones.size(); |
961 | for (int i = 0; i < child_bone_size; i++) { |
962 | bones_to_process.push_back(b.child_bones[i]); |
963 | } |
964 | |
965 | emit_signal(SceneStringNames::get_singleton()->bone_pose_changed, current_bone_idx); |
966 | } |
967 | } |
968 | |
969 | void Skeleton3D::_bind_methods() { |
970 | ClassDB::bind_method(D_METHOD("add_bone" , "name" ), &Skeleton3D::add_bone); |
971 | ClassDB::bind_method(D_METHOD("find_bone" , "name" ), &Skeleton3D::find_bone); |
972 | ClassDB::bind_method(D_METHOD("get_bone_name" , "bone_idx" ), &Skeleton3D::get_bone_name); |
973 | ClassDB::bind_method(D_METHOD("set_bone_name" , "bone_idx" , "name" ), &Skeleton3D::set_bone_name); |
974 | |
975 | ClassDB::bind_method(D_METHOD("get_bone_parent" , "bone_idx" ), &Skeleton3D::get_bone_parent); |
976 | ClassDB::bind_method(D_METHOD("set_bone_parent" , "bone_idx" , "parent_idx" ), &Skeleton3D::set_bone_parent); |
977 | |
978 | ClassDB::bind_method(D_METHOD("get_bone_count" ), &Skeleton3D::get_bone_count); |
979 | ClassDB::bind_method(D_METHOD("get_version" ), &Skeleton3D::get_version); |
980 | |
981 | ClassDB::bind_method(D_METHOD("unparent_bone_and_rest" , "bone_idx" ), &Skeleton3D::unparent_bone_and_rest); |
982 | |
983 | ClassDB::bind_method(D_METHOD("get_bone_children" , "bone_idx" ), &Skeleton3D::get_bone_children); |
984 | |
985 | ClassDB::bind_method(D_METHOD("get_parentless_bones" ), &Skeleton3D::get_parentless_bones); |
986 | |
987 | ClassDB::bind_method(D_METHOD("get_bone_rest" , "bone_idx" ), &Skeleton3D::get_bone_rest); |
988 | ClassDB::bind_method(D_METHOD("set_bone_rest" , "bone_idx" , "rest" ), &Skeleton3D::set_bone_rest); |
989 | ClassDB::bind_method(D_METHOD("get_bone_global_rest" , "bone_idx" ), &Skeleton3D::get_bone_global_rest); |
990 | |
991 | ClassDB::bind_method(D_METHOD("create_skin_from_rest_transforms" ), &Skeleton3D::create_skin_from_rest_transforms); |
992 | ClassDB::bind_method(D_METHOD("register_skin" , "skin" ), &Skeleton3D::register_skin); |
993 | |
994 | ClassDB::bind_method(D_METHOD("localize_rests" ), &Skeleton3D::localize_rests); |
995 | |
996 | ClassDB::bind_method(D_METHOD("clear_bones" ), &Skeleton3D::clear_bones); |
997 | |
998 | ClassDB::bind_method(D_METHOD("get_bone_pose" , "bone_idx" ), &Skeleton3D::get_bone_pose); |
999 | ClassDB::bind_method(D_METHOD("set_bone_pose_position" , "bone_idx" , "position" ), &Skeleton3D::set_bone_pose_position); |
1000 | ClassDB::bind_method(D_METHOD("set_bone_pose_rotation" , "bone_idx" , "rotation" ), &Skeleton3D::set_bone_pose_rotation); |
1001 | ClassDB::bind_method(D_METHOD("set_bone_pose_scale" , "bone_idx" , "scale" ), &Skeleton3D::set_bone_pose_scale); |
1002 | |
1003 | ClassDB::bind_method(D_METHOD("get_bone_pose_position" , "bone_idx" ), &Skeleton3D::get_bone_pose_position); |
1004 | ClassDB::bind_method(D_METHOD("get_bone_pose_rotation" , "bone_idx" ), &Skeleton3D::get_bone_pose_rotation); |
1005 | ClassDB::bind_method(D_METHOD("get_bone_pose_scale" , "bone_idx" ), &Skeleton3D::get_bone_pose_scale); |
1006 | |
1007 | ClassDB::bind_method(D_METHOD("reset_bone_pose" , "bone_idx" ), &Skeleton3D::reset_bone_pose); |
1008 | ClassDB::bind_method(D_METHOD("reset_bone_poses" ), &Skeleton3D::reset_bone_poses); |
1009 | |
1010 | ClassDB::bind_method(D_METHOD("is_bone_enabled" , "bone_idx" ), &Skeleton3D::is_bone_enabled); |
1011 | ClassDB::bind_method(D_METHOD("set_bone_enabled" , "bone_idx" , "enabled" ), &Skeleton3D::set_bone_enabled, DEFVAL(true)); |
1012 | |
1013 | ClassDB::bind_method(D_METHOD("clear_bones_global_pose_override" ), &Skeleton3D::clear_bones_global_pose_override); |
1014 | ClassDB::bind_method(D_METHOD("set_bone_global_pose_override" , "bone_idx" , "pose" , "amount" , "persistent" ), &Skeleton3D::set_bone_global_pose_override, DEFVAL(false)); |
1015 | ClassDB::bind_method(D_METHOD("get_bone_global_pose_override" , "bone_idx" ), &Skeleton3D::get_bone_global_pose_override); |
1016 | ClassDB::bind_method(D_METHOD("get_bone_global_pose" , "bone_idx" ), &Skeleton3D::get_bone_global_pose); |
1017 | ClassDB::bind_method(D_METHOD("get_bone_global_pose_no_override" , "bone_idx" ), &Skeleton3D::get_bone_global_pose_no_override); |
1018 | |
1019 | ClassDB::bind_method(D_METHOD("force_update_all_bone_transforms" ), &Skeleton3D::force_update_all_bone_transforms); |
1020 | ClassDB::bind_method(D_METHOD("force_update_bone_child_transform" , "bone_idx" ), &Skeleton3D::force_update_bone_children_transforms); |
1021 | |
1022 | ClassDB::bind_method(D_METHOD("set_motion_scale" , "motion_scale" ), &Skeleton3D::set_motion_scale); |
1023 | ClassDB::bind_method(D_METHOD("get_motion_scale" ), &Skeleton3D::get_motion_scale); |
1024 | |
1025 | ClassDB::bind_method(D_METHOD("set_show_rest_only" , "enabled" ), &Skeleton3D::set_show_rest_only); |
1026 | ClassDB::bind_method(D_METHOD("is_show_rest_only" ), &Skeleton3D::is_show_rest_only); |
1027 | |
1028 | ClassDB::bind_method(D_METHOD("set_animate_physical_bones" , "enabled" ), &Skeleton3D::set_animate_physical_bones); |
1029 | ClassDB::bind_method(D_METHOD("get_animate_physical_bones" ), &Skeleton3D::get_animate_physical_bones); |
1030 | |
1031 | ClassDB::bind_method(D_METHOD("physical_bones_stop_simulation" ), &Skeleton3D::physical_bones_stop_simulation); |
1032 | ClassDB::bind_method(D_METHOD("physical_bones_start_simulation" , "bones" ), &Skeleton3D::physical_bones_start_simulation_on, DEFVAL(Array())); |
1033 | ClassDB::bind_method(D_METHOD("physical_bones_add_collision_exception" , "exception" ), &Skeleton3D::physical_bones_add_collision_exception); |
1034 | ClassDB::bind_method(D_METHOD("physical_bones_remove_collision_exception" , "exception" ), &Skeleton3D::physical_bones_remove_collision_exception); |
1035 | |
1036 | ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "motion_scale" , PROPERTY_HINT_RANGE, "0.001,10,0.001,or_greater" ), "set_motion_scale" , "get_motion_scale" ); |
1037 | ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_rest_only" ), "set_show_rest_only" , "is_show_rest_only" ); |
1038 | #ifndef _3D_DISABLED |
1039 | ADD_PROPERTY(PropertyInfo(Variant::BOOL, "animate_physical_bones" ), "set_animate_physical_bones" , "get_animate_physical_bones" ); |
1040 | #endif // _3D_DISABLED |
1041 | |
1042 | ADD_SIGNAL(MethodInfo("pose_updated" )); |
1043 | ADD_SIGNAL(MethodInfo("bone_pose_changed" , PropertyInfo(Variant::INT, "bone_idx" ))); |
1044 | ADD_SIGNAL(MethodInfo("bone_enabled_changed" , PropertyInfo(Variant::INT, "bone_idx" ))); |
1045 | ADD_SIGNAL(MethodInfo("show_rest_only_changed" )); |
1046 | |
1047 | BIND_CONSTANT(NOTIFICATION_UPDATE_SKELETON); |
1048 | } |
1049 | |
1050 | Skeleton3D::Skeleton3D() { |
1051 | } |
1052 | |
1053 | Skeleton3D::~Skeleton3D() { |
1054 | // Some skins may remain bound. |
1055 | for (SkinReference *E : skin_bindings) { |
1056 | E->skeleton_node = nullptr; |
1057 | } |
1058 | } |
1059 | |