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