From 2b019fb1bc278a7835800847fc0ccbea81a53708 Mon Sep 17 00:00:00 2001 From: Tokage Date: Tue, 12 Jan 2021 11:15:22 +0900 Subject: [PATCH] backport improvement skeleton --- scene/3d/skeleton.cpp | 553 ++++++++++++--------------- scene/3d/skeleton.h | 94 ++--- scene/animation/animation_player.cpp | 3 - 3 files changed, 291 insertions(+), 359 deletions(-) diff --git a/scene/3d/skeleton.cpp b/scene/3d/skeleton.cpp index 565f28b76105..aa35011a9ed2 100644 --- a/scene/3d/skeleton.cpp +++ b/scene/3d/skeleton.cpp @@ -31,14 +31,13 @@ #include "skeleton.h" #include "core/message_queue.h" - #include "core/project_settings.h" #include "scene/3d/physics_body.h" #include "scene/resources/surface_tool.h" void SkinReference::_skin_changed() { if (skeleton_node) { - skeleton_node->_make_skins_dirty(); + skeleton_node->dirty = true; } skeleton_version = 0; } @@ -69,40 +68,40 @@ SkinReference::~SkinReference() { VS::get_singleton()->free(skeleton); } -bool Skeleton::_set(const StringName &p_path, const Variant &p_value) { +/////////////////////////////////////// +bool Skeleton::_set(const StringName &p_path, const Variant &p_value) { String path = p_path; - if (!path.begins_with("bones/")) + if (!path.begins_with("bones/")) { return false; + } int which = path.get_slicec('/', 1).to_int(); String what = path.get_slicec('/', 2); if (which == bones.size() && what == "name") { - add_bone(p_value); return true; } ERR_FAIL_INDEX_V(which, bones.size(), false); - if (what == "parent") + if (what == "parent") { set_bone_parent(which, p_value); - else if (what == "rest") + } else if (what == "rest") { set_bone_rest(which, p_value); - else if (what == "enabled") + } else if (what == "enabled") { set_bone_enabled(which, p_value); - else if (what == "pose") + } else if (what == "pose") { set_bone_pose(which, p_value); - else if (what == "bound_children") { + } else if (what == "bound_children") { Array children = p_value; if (is_inside_tree()) { bones.write[which].nodes_bound.clear(); for (int i = 0; i < children.size(); i++) { - NodePath npath = children[i]; ERR_CONTINUE(npath.operator String() == ""); Node *node = get_node(npath); @@ -118,32 +117,31 @@ bool Skeleton::_set(const StringName &p_path, const Variant &p_value) { } bool Skeleton::_get(const StringName &p_path, Variant &r_ret) const { - String path = p_path; - if (!path.begins_with("bones/")) + if (!path.begins_with("bones/")) { return false; + } int which = path.get_slicec('/', 1).to_int(); String what = path.get_slicec('/', 2); ERR_FAIL_INDEX_V(which, bones.size(), false); - if (what == "name") + if (what == "name") { r_ret = get_bone_name(which); - else if (what == "parent") + } else if (what == "parent") { r_ret = get_bone_parent(which); - else if (what == "rest") + } else if (what == "rest") { r_ret = get_bone_rest(which); - else if (what == "enabled") + } else if (what == "enabled") { r_ret = is_bone_enabled(which); - else if (what == "pose") + } else if (what == "pose") { r_ret = get_bone_pose(which); - else if (what == "bound_children") { + } else if (what == "bound_children") { Array children; - for (const List::Element *E = bones[which].nodes_bound.front(); E; E = E->next()) { - + for (const List::Element *E = bones[which].nodes_bound.front(); E; E = E->next()) { Object *obj = ObjectDB::get_instance(E->get()); ERR_CONTINUE(!obj); Node *node = Object::cast_to(obj); @@ -153,29 +151,29 @@ bool Skeleton::_get(const StringName &p_path, Variant &r_ret) const { } r_ret = children; - } else + } else { return false; + } return true; } -void Skeleton::_get_property_list(List *p_list) const { +void Skeleton::_get_property_list(List *p_list) const { for (int i = 0; i < bones.size(); i++) { - String prep = "bones/" + itos(i) + "/"; - p_list->push_back(PropertyInfo(Variant::STRING, prep + "name")); - p_list->push_back(PropertyInfo(Variant::INT, prep + "parent", PROPERTY_HINT_RANGE, "-1," + itos(bones.size() - 1) + ",1")); - p_list->push_back(PropertyInfo(Variant::TRANSFORM, prep + "rest")); - p_list->push_back(PropertyInfo(Variant::BOOL, prep + "enabled")); - p_list->push_back(PropertyInfo(Variant::TRANSFORM, prep + "pose", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR)); - p_list->push_back(PropertyInfo(Variant::ARRAY, prep + "bound_children")); + p_list->push_back(PropertyInfo(Variant::STRING, prep + "name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); + p_list->push_back(PropertyInfo(Variant::INT, prep + "parent", PROPERTY_HINT_RANGE, "-1," + itos(bones.size() - 1) + ",1", PROPERTY_USAGE_NOEDITOR)); + p_list->push_back(PropertyInfo(Variant::TRANSFORM, prep + "rest", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); + p_list->push_back(PropertyInfo(Variant::BOOL, prep + "enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); + p_list->push_back(PropertyInfo(Variant::TRANSFORM, prep + "pose", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); + p_list->push_back(PropertyInfo(Variant::ARRAY, prep + "bound_children", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); } } void Skeleton::_update_process_order() { - - if (!process_order_dirty) + if (!process_order_dirty) { return; + } Bone *bonesptr = bones.ptrw(); int len = bones.size(); @@ -183,10 +181,9 @@ void Skeleton::_update_process_order() { process_order.resize(len); int *order = process_order.ptrw(); for (int i = 0; i < len; i++) { - if (bonesptr[i].parent >= len) { //validate this just in case - ERR_PRINTS("Bone " + itos(i) + " has invalid parent: " + itos(bonesptr[i].parent)); + ERR_PRINT("Bone " + itos(i) + " has invalid parent: " + itos(bonesptr[i].parent)); bonesptr[i].parent = -1; } order[i] = i; @@ -200,8 +197,9 @@ void Skeleton::_update_process_order() { bool swapped = false; for (int i = 0; i < len; i++) { int parent_idx = bonesptr[order[i]].parent; - if (parent_idx < 0) + if (parent_idx < 0) { continue; //do nothing because it has no parent + } //swap indices int parent_order = bonesptr[parent_idx].sort_index; if (parent_order > i) { @@ -213,8 +211,9 @@ void Skeleton::_update_process_order() { } } - if (!swapped) + if (!swapped) { break; + } pass_count++; } @@ -226,9 +225,7 @@ void Skeleton::_update_process_order() { for (int i = 0; i < len; i++) { Bone &b = bonesptr[order[i]]; b.children = Vector(); - if (b.parent < 0) { - bones_root = order[i]; - } else { + if (b.parent != -1) { bonesptr[b.parent].children.push_back(order[i]); } } @@ -236,178 +233,154 @@ void Skeleton::_update_process_order() { process_order_dirty = false; } -void Skeleton::_update_bone_transform(int p_bone) { - - Bone *bonesptr = bones.ptrw(); - Bone &b = bonesptr[p_bone]; - - if (b.global_pose_override_amount >= 0.999) { - b.pose_global = b.global_pose_override; - } else { - if (b.disable_rest) { - if (b.enabled) { - - Transform pose = b.pose; - if (b.custom_pose_enable) { - pose = b.custom_pose * pose; - } - if (b.parent >= 0) { - - b.pose_global = bonesptr[b.parent].pose_global * pose; - } else { - - b.pose_global = pose; - } - } else { - - if (b.parent >= 0) { - - b.pose_global = bonesptr[b.parent].pose_global; - } else { - - b.pose_global = Transform(); - } - } - - } else { - if (b.enabled) { - - Transform pose = b.pose; - if (b.custom_pose_enable) { - pose = b.custom_pose * pose; - } - if (b.parent >= 0) { - - b.pose_global = bonesptr[b.parent].pose_global * (b.rest * pose); - } else { - - b.pose_global = b.rest * pose; - } - } else { +bool Skeleton::update_bone_pose(Bone &p_bone, const Bone *p_bonesptr) const { + if (p_bone.parent >= 0) { + p_bone.dirty |= p_bonesptr[p_bone.parent].dirty; + } - if (b.parent >= 0) { + if (!p_bone.dirty) { + return false; + } - b.pose_global = bonesptr[b.parent].pose_global * b.rest; - } else { + Transform computed_pose; - b.pose_global = b.rest; - } - } - } + // we have a parent, we need its global pose + if (p_bone.parent >= 0) { + computed_pose = p_bonesptr[p_bone.parent].pose_global_final; + } - if (b.global_pose_override_amount >= CMP_EPSILON) { - b.pose_global = b.pose_global.interpolate_with(b.global_pose_override, b.global_pose_override_amount); - } + if (!p_bone.disable_rest) { + computed_pose *= p_bone.rest; } - if (b.global_pose_override_reset) { - b.global_pose_override_amount = 0.0; + // finalize the global pose + if (p_bone.enabled) { + computed_pose *= p_bone.pose; } - for (List::Element *E = b.nodes_bound.front(); E; E = E->next()) { + p_bone.pose_global_no_override = computed_pose; - Object *obj = ObjectDB::get_instance(E->get()); - ERR_CONTINUE(!obj); - Spatial *sp = Object::cast_to(obj); - ERR_CONTINUE(!sp); - sp->set_transform(b.pose_global); + //---------------------------- + // compute the final pose + //---------------------------- + + const real_t override_amount = p_bone.global_pose_override_amount; + + if (p_bone.global_pose_override_reset) { + p_bone.global_pose_override_amount = 0.0; } - int children_len = b.children.size(); - for (int i = 0; i < children_len; i++) { - _update_bone_transform(b.children[i]); + // if the override is near 1, we simply set the final pose to the override + if (override_amount >= (1.0 - CMP_EPSILON)) { + p_bone.pose_global_final = p_bone.global_pose_override; + return true; + } else if (override_amount >= CMP_EPSILON) { + p_bone.pose_global_final = computed_pose.interpolate_with(p_bone.global_pose_override, override_amount); + return true; } - dirty_idxes.erase(p_bone); - return; + p_bone.pose_global_final = computed_pose; + return true; } -void Skeleton::_update_skins() { - - VisualServer *vs = VisualServer::get_singleton(); - Bone *bonesptr = bones.ptrw(); - int len = bones.size(); - - //update skins - for (Set::Element *E = skin_bindings.front(); E; E = E->next()) { +void Skeleton::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_INTERNAL_PROCESS: + case NOTIFICATION_UPDATE_SKELETON: { + if (!process_order_dirty && !dirty) + break; - const Skin *skin = E->get()->skin.operator->(); - RID skeleton = E->get()->skeleton; - uint32_t bind_count = skin->get_bind_count(); + _update_process_order(); - if (E->get()->bind_count != bind_count) { - VS::get_singleton()->skeleton_allocate(skeleton, bind_count); - E->get()->bind_count = bind_count; - E->get()->skin_bone_indices.resize(bind_count); - E->get()->skin_bone_indices_ptrs = E->get()->skin_bone_indices.ptrw(); - } + VisualServer *vs = VisualServer::get_singleton(); + Bone *bonesptr = bones.ptrw(); + const int bones_len = bones.size(); - if (E->get()->skeleton_version != version) { + const int *order = process_order.ptr(); - for (uint32_t i = 0; i < bind_count; i++) { - StringName bind_name = skin->get_bind_name(i); + for (int i = 0; i < bones_len; i++) { + Bone &b = bonesptr[order[i]]; - if (bind_name != StringName()) { - //bind name used, use this - bool found = false; - for (int j = 0; j < len; j++) { - if (bonesptr[j].name == bind_name) { - E->get()->skin_bone_indices_ptrs[i] = j; - found = true; - break; - } - } + if (!update_bone_pose(b, bonesptr)) { + continue; + } - if (!found) { - ERR_PRINT("Skin bind #" + itos(i) + " contains named bind '" + String(bind_name) + "' but Skeleton has no bone by that name."); - E->get()->skin_bone_indices_ptrs[i] = 0; - } - } else if (skin->get_bind_bone(i) >= 0) { - int bind_index = skin->get_bind_bone(i); - if (bind_index >= len) { - ERR_PRINT("Skin bind #" + itos(i) + " contains bone index bind: " + itos(bind_index) + " , which is greater than the skeleton bone count: " + itos(len) + "."); - E->get()->skin_bone_indices_ptrs[i] = 0; - } else { - E->get()->skin_bone_indices_ptrs[i] = bind_index; - } - } else { - ERR_PRINT("Skin bind #" + itos(i) + " does not contain a name nor a bone index."); - E->get()->skin_bone_indices_ptrs[i] = 0; + for (List::Element *E = b.nodes_bound.front(); E; E = E->next()) { + Object *obj = ObjectDB::get_instance(E->get()); + ERR_CONTINUE(!obj); + Spatial *sp = Object::cast_to(obj); + ERR_CONTINUE(!sp); + sp->set_transform(b.pose_global_final); } } - E->get()->skeleton_version = version; - } - - for (uint32_t i = 0; i < bind_count; i++) { - uint32_t bone_index = E->get()->skin_bone_indices_ptrs[i]; - ERR_CONTINUE(bone_index >= (uint32_t)len); - vs->skeleton_bone_set_transform(skeleton, i, bonesptr[bone_index].pose_global * skin->get_bind_pose(i)); - } - } - skins_dirty = false; -} + //update skins + for (Set::Element *E = skin_bindings.front(); E; E = E->next()) { + const Skin *skin = E->get()->skin.operator->(); + RID skeleton = E->get()->skeleton; + uint32_t bind_count = skin->get_bind_count(); + + if (E->get()->bind_count != bind_count) { + vs->skeleton_allocate(skeleton, bind_count); + E->get()->bind_count = bind_count; + E->get()->skin_bone_indices.resize(bind_count); + E->get()->skin_bone_indices_ptrs = E->get()->skin_bone_indices.ptrw(); + } -void Skeleton::_notification(int p_what) { + bool full_update = false; + + if (E->get()->skeleton_version != version) { + full_update = true; + for (uint32_t i = 0; i < bind_count; i++) { + StringName bind_name = skin->get_bind_name(i); + + if (bind_name != StringName()) { + //bind name used, use this + bool found = false; + for (int j = 0; j < bones_len; j++) { + if (bonesptr[j].name == bind_name) { + E->get()->skin_bone_indices_ptrs[i] = j; + found = true; + break; + } + } + + if (!found) { + ERR_PRINT("Skin bind #" + itos(i) + " contains named bind '" + String(bind_name) + "' but Skeleton has no bone by that name."); + E->get()->skin_bone_indices_ptrs[i] = 0; + } + } else if (skin->get_bind_bone(i) >= 0) { + int bind_index = skin->get_bind_bone(i); + if (bind_index >= bones_len) { + ERR_PRINT("Skin bind #" + itos(i) + " contains bone index bind: " + itos(bind_index) + " , which is greater than the skeleton bone count: " + itos(bones_len) + "."); + E->get()->skin_bone_indices_ptrs[i] = 0; + } else { + E->get()->skin_bone_indices_ptrs[i] = bind_index; + } + } else { + ERR_PRINT("Skin bind #" + itos(i) + " does not contain a name nor a bone index."); + E->get()->skin_bone_indices_ptrs[i] = 0; + } + } - switch (p_what) { - case NOTIFICATION_UPDATE_SKELETON: - case NOTIFICATION_UPDATE_SKELETON_BONE_ONLY: { - _update_process_order(); - const int *order = process_order.ptr(); - int len = bones.size(); + E->get()->skeleton_version = version; + } - while (dirty_idxes.size() > 0) { - for (int i = 0; i < len; i++) { - int dirty_idx = dirty_idxes.find(order[i], 0); - if (dirty_idx != -1) { - _update_bone_transform(dirty_idxes[dirty_idx]); + for (uint32_t i = 0; i < bind_count; i++) { + uint32_t bone_index = E->get()->skin_bone_indices_ptrs[i]; + ERR_CONTINUE(bone_index >= (uint32_t)bones_len); + if (full_update || bonesptr[bone_index].dirty) { + vs->skeleton_bone_set_transform(skeleton, i, bonesptr[bone_index].pose_global_final * skin->get_bind_pose(i)); } } } - if (p_what == NOTIFICATION_UPDATE_SKELETON) { - _update_skins(); + dirty = false; + + // clear bone dirties + for (int i = 0; i < bones_len; ++i) { + bonesptr[i].dirty = false; } + emit_signal("skeleton_updated"); emit_signal("skeleton_updated"); } break; @@ -418,38 +391,50 @@ void Skeleton::clear_bones_global_pose_override() { for (int i = 0; i < bones.size(); i += 1) { bones.write[i].global_pose_override_amount = 0; } - _make_bone_dirty(); + dirty = true; } void Skeleton::set_bone_global_pose_override(int p_bone, const Transform &p_pose, float p_amount, bool p_persistent) { - ERR_FAIL_INDEX(p_bone, bones.size()); + bones.write[p_bone].dirty = true; bones.write[p_bone].global_pose_override_amount = p_amount; bones.write[p_bone].global_pose_override = p_pose; bones.write[p_bone].global_pose_override_reset = !p_persistent; - _make_bone_dirty(p_bone); + dirty = true; } -Transform Skeleton::get_bone_global_pose(int p_bone, bool update_skins) const { +Transform Skeleton::get_bone_global_pose(int p_bone, bool force_update) const { + ERR_FAIL_INDEX_V(p_bone, bones.size(), Transform()); + if (force_update) { + const_cast(this)->notification(NOTIFICATION_UPDATE_SKELETON); + } + return bones[p_bone].pose_global_final; +} +Transform Skeleton::get_bone_global_pose_without_override(int p_bone, bool force_update) const { ERR_FAIL_INDEX_V(p_bone, bones.size(), Transform()); - if (skins_dirty) { - if (update_skins) { - const_cast(this)->notification(NOTIFICATION_UPDATE_SKELETON); - } else { - const_cast(this)->notification(NOTIFICATION_UPDATE_SKELETON_BONE_ONLY); - } + if (force_update) { + const_cast(this)->notification(NOTIFICATION_UPDATE_SKELETON); } - return bones[p_bone].pose_global; + return bones[p_bone].pose_global_no_override; +} + +Transform Skeleton::get_bone_global_rest(int p_bone) const { + ERR_FAIL_INDEX_V(p_bone, bones.size(), Transform()); + Transform tform; + do { + tform *= bones[p_bone].rest.affine_inverse(); + p_bone = get_bone_parent(p_bone); + } while (p_bone >= 0); + + return tform.affine_inverse(); } // skeleton creation api void Skeleton::add_bone(const String &p_name) { - ERR_FAIL_COND(p_name == "" || p_name.find(":") != -1 || p_name.find("/") != -1); for (int i = 0; i < bones.size(); i++) { - ERR_FAIL_COND(bones[i].name == p_name); } @@ -458,56 +443,57 @@ void Skeleton::add_bone(const String &p_name) { bones.push_back(b); process_order_dirty = true; version++; - _make_skins_dirty(); + dirty = true; update_gizmo(); } -int Skeleton::find_bone(const String &p_name) const { +int Skeleton::find_bone(const String &p_name) const { for (int i = 0; i < bones.size(); i++) { - - if (bones[i].name == p_name) + if (bones[i].name == p_name) { return i; + } } return -1; } -String Skeleton::get_bone_name(int p_bone) const { +String Skeleton::get_bone_name(int p_bone) const { ERR_FAIL_INDEX_V(p_bone, bones.size(), ""); return bones[p_bone].name; } bool Skeleton::is_bone_parent_of(int p_bone, int p_parent_bone_id) const { - int parent_of_bone = get_bone_parent(p_bone); - if (-1 == parent_of_bone) + if (-1 == parent_of_bone) { return false; + } - if (parent_of_bone == p_parent_bone_id) + if (parent_of_bone == p_parent_bone_id) { return true; + } return is_bone_parent_of(parent_of_bone, p_parent_bone_id); } int Skeleton::get_bone_count() const { - return bones.size(); } void Skeleton::set_bone_parent(int p_bone, int p_parent) { - ERR_FAIL_INDEX(p_bone, bones.size()); ERR_FAIL_COND(p_parent != -1 && (p_parent < 0)); - bones.write[p_bone].parent = p_parent; + Bone &bone = bones.write[p_bone]; + bone.dirty = true; + bone.parent = p_parent; + process_order_dirty = true; - _make_bone_dirty(p_bone); + dirty = true; } void Skeleton::unparent_bone_and_rest(int p_bone) { - ERR_FAIL_INDEX(p_bone, bones.size()); _update_process_order(); @@ -518,33 +504,32 @@ void Skeleton::unparent_bone_and_rest(int p_bone) { parent = bones[parent].parent; } + bones.write[p_bone].dirty = true; bones.write[p_bone].parent = -1; process_order_dirty = true; - _make_bone_dirty(p_bone); + dirty = true; } void Skeleton::set_bone_disable_rest(int p_bone, bool p_disable) { - ERR_FAIL_INDEX(p_bone, bones.size()); + bones.write[p_bone].dirty = true; bones.write[p_bone].disable_rest = p_disable; + dirty = true; } bool Skeleton::is_bone_rest_disabled(int p_bone) const { - ERR_FAIL_INDEX_V(p_bone, bones.size(), false); return bones[p_bone].disable_rest; } int Skeleton::get_bone_parent(int p_bone) const { - ERR_FAIL_INDEX_V(p_bone, bones.size(), -1); return bones[p_bone].parent; } Array Skeleton::get_bone_children(int p_bone) { - Array a = Array(); ERR_FAIL_INDEX_V(p_bone, bones.size(), a); @@ -556,68 +541,60 @@ Array Skeleton::get_bone_children(int p_bone) { return a; } -int Skeleton::get_bones_root() { - - _update_process_order(); - return bones_root; -} - void Skeleton::set_bone_rest(int p_bone, const Transform &p_rest) { - ERR_FAIL_INDEX(p_bone, bones.size()); bones.write[p_bone].rest = p_rest; - _make_bone_dirty(p_bone); + bones.write[p_bone].dirty = true; + dirty = true; } -Transform Skeleton::get_bone_rest(int p_bone) const { +Transform Skeleton::get_bone_rest(int p_bone) const { ERR_FAIL_INDEX_V(p_bone, bones.size(), Transform()); return bones[p_bone].rest; } void Skeleton::set_bone_enabled(int p_bone, bool p_enabled) { - ERR_FAIL_INDEX(p_bone, bones.size()); + bones.write[p_bone].dirty = true; bones.write[p_bone].enabled = p_enabled; - _make_bone_dirty(p_bone); + dirty = true; } -bool Skeleton::is_bone_enabled(int p_bone) const { +bool Skeleton::is_bone_enabled(int p_bone) const { ERR_FAIL_INDEX_V(p_bone, bones.size(), false); return bones[p_bone].enabled; } void Skeleton::bind_child_node_to_bone(int p_bone, Node *p_node) { - ERR_FAIL_NULL(p_node); ERR_FAIL_INDEX(p_bone, bones.size()); - uint32_t id = p_node->get_instance_id(); - - for (const List::Element *E = bones[p_bone].nodes_bound.front(); E; E = E->next()) { + ObjectID id = p_node->get_instance_id(); - if (E->get() == id) + for (const List::Element *E = bones[p_bone].nodes_bound.front(); E; E = E->next()) { + if (E->get() == id) { return; // already here + } } bones.write[p_bone].nodes_bound.push_back(id); } -void Skeleton::unbind_child_node_from_bone(int p_bone, Node *p_node) { +void Skeleton::unbind_child_node_from_bone(int p_bone, Node *p_node) { ERR_FAIL_NULL(p_node); ERR_FAIL_INDEX(p_bone, bones.size()); - uint32_t id = p_node->get_instance_id(); + ObjectID id = p_node->get_instance_id(); bones.write[p_bone].nodes_bound.erase(id); } -void Skeleton::get_bound_child_nodes_to_bone(int p_bone, List *p_bound) const { +void Skeleton::get_bound_child_nodes_to_bone(int p_bone, List *p_bound) const { ERR_FAIL_INDEX(p_bone, bones.size()); - for (const List::Element *E = bones[p_bone].nodes_bound.front(); E; E = E->next()) { - + for (const List::Element *E = bones[p_bone].nodes_bound.front(); E; E = E->next()) { Object *obj = ObjectDB::get_instance(E->get()); ERR_CONTINUE(!obj); p_bound->push_back(Object::cast_to(obj)); @@ -625,66 +602,39 @@ void Skeleton::get_bound_child_nodes_to_bone(int p_bone, List *p_bound) } void Skeleton::clear_bones() { - bones.clear(); process_order_dirty = true; + dirty = true; version++; - _make_bone_dirty(); } // posing api void Skeleton::set_bone_pose(int p_bone, const Transform &p_pose) { - ERR_FAIL_INDEX(p_bone, bones.size()); + bones.write[p_bone].dirty = true; bones.write[p_bone].pose = p_pose; - if (is_inside_tree()) { - _make_bone_dirty(p_bone); - } + dirty = true; } -Transform Skeleton::get_bone_pose(int p_bone) const { +Transform Skeleton::get_bone_pose(int p_bone) const { ERR_FAIL_INDEX_V(p_bone, bones.size(), Transform()); return bones[p_bone].pose; } -void Skeleton::set_bone_custom_pose(int p_bone, const Transform &p_custom_pose) { - - ERR_FAIL_INDEX(p_bone, bones.size()); - //ERR_FAIL_COND( !is_inside_scene() ); - - bones.write[p_bone].custom_pose_enable = (p_custom_pose != Transform()); - bones.write[p_bone].custom_pose = p_custom_pose; - - _make_bone_dirty(p_bone); -} - -Transform Skeleton::get_bone_custom_pose(int p_bone) const { - - ERR_FAIL_INDEX_V(p_bone, bones.size(), Transform()); - return bones[p_bone].custom_pose; -} - -void Skeleton::_make_bone_dirty(int p_bone) { - - int dirty_idx = p_bone; - if (p_bone == -1) { - dirty_idx = get_bones_root(); - } - if (dirty_idxes.find(dirty_idx, 0) == -1) { - dirty_idxes.push_back(dirty_idx); +void Skeleton::reset_bone_poses() { + Skeleton::Bone *bones_w = bones.ptrw(); + const int size = bones.size(); + for (int i = 0; i < size; ++i) { + bones_w[i].dirty = true; + bones_w[i].pose = Transform(); + bones_w[i].global_pose_override = Transform(); + bones_w[i].global_pose_override_amount = 0.0; + bones_w[i].global_pose_override_reset = false; } - _make_skins_dirty(); -} - -void Skeleton::_make_skins_dirty() { - if (skins_dirty) - return; - - MessageQueue::get_singleton()->push_notification(this, NOTIFICATION_UPDATE_SKELETON); - skins_dirty = true; + dirty = true; } int Skeleton::get_process_order(int p_idx) { @@ -693,8 +643,12 @@ int Skeleton::get_process_order(int p_idx) { return process_order[p_idx]; } -void Skeleton::localize_rests() { +Vector Skeleton::get_bone_process_orders() { + _update_process_order(); + return process_order; +} +void Skeleton::localize_rests() { _update_process_order(); for (int i = bones.size() - 1; i >= 0; i--) { @@ -705,8 +659,6 @@ void Skeleton::localize_rests() { } } -#ifndef _3D_DISABLED - void Skeleton::bind_physical_bone_to_bone(int p_bone, PhysicalBone *p_physical_bone) { ERR_FAIL_INDEX(p_bone, bones.size()); ERR_FAIL_COND(bones[p_bone].physical_bone); @@ -854,14 +806,11 @@ void Skeleton::physical_bones_remove_collision_exception(RID p_exception) { _physical_bones_add_remove_collision_exception(false, this, p_exception); } -#endif // _3D_DISABLED - void Skeleton::_skin_changed() { - _make_skins_dirty(); + dirty = true; } Ref Skeleton::register_skin(const Ref &p_skin) { - for (Set::Element *E = skin_bindings.front(); E; E = E->next()) { if (E->get()->skin == p_skin) { return Ref(E->get()); @@ -915,12 +864,23 @@ Ref Skeleton::register_skin(const Ref &p_skin) { skin_bindings.insert(skin_ref.operator->()); skin->connect("changed", skin_ref.operator->(), "_skin_changed"); - _make_skins_dirty(); + + dirty = true; //skin needs to be updated, so update skeleton + return skin_ref; } -void Skeleton::_bind_methods() { +// helper functions +Transform Skeleton::bone_transform_to_world_transform(Transform p_bone_transform) { + return get_global_transform() * p_bone_transform; +} + +Transform Skeleton::world_transform_to_bone_transform(Transform p_world_transform) { + return get_global_transform().affine_inverse() * p_world_transform; +} +void Skeleton::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_bone_process_orders"), &Skeleton::get_bone_process_orders); ClassDB::bind_method(D_METHOD("add_bone", "name"), &Skeleton::add_bone); ClassDB::bind_method(D_METHOD("find_bone", "name"), &Skeleton::find_bone); ClassDB::bind_method(D_METHOD("get_bone_name", "bone_idx"), &Skeleton::get_bone_name); @@ -929,7 +889,6 @@ void Skeleton::_bind_methods() { ClassDB::bind_method(D_METHOD("set_bone_parent", "bone_idx", "parent_idx"), &Skeleton::set_bone_parent); ClassDB::bind_method(D_METHOD("get_bone_children", "bone_idx"), &Skeleton::get_bone_children); - ClassDB::bind_method(D_METHOD("get_bones_root"), &Skeleton::get_bones_root); ClassDB::bind_method(D_METHOD("get_bone_count"), &Skeleton::get_bone_count); @@ -956,35 +915,29 @@ void Skeleton::_bind_methods() { ClassDB::bind_method(D_METHOD("clear_bones_global_pose_override"), &Skeleton::clear_bones_global_pose_override); ClassDB::bind_method(D_METHOD("set_bone_global_pose_override", "bone_idx", "pose", "amount", "persistent"), &Skeleton::set_bone_global_pose_override, DEFVAL(false)); - ClassDB::bind_method(D_METHOD("get_bone_global_pose", "bone_idx", "update_skins"), &Skeleton::get_bone_global_pose, DEFVAL(true)); - - ClassDB::bind_method(D_METHOD("get_bone_custom_pose", "bone_idx"), &Skeleton::get_bone_custom_pose); - ClassDB::bind_method(D_METHOD("set_bone_custom_pose", "bone_idx", "custom_pose"), &Skeleton::set_bone_custom_pose); + ClassDB::bind_method(D_METHOD("get_bone_global_pose", "bone_idx", "force_update"), &Skeleton::get_bone_global_pose, DEFVAL(true)); + ClassDB::bind_method(D_METHOD("get_bone_global_pose_without_override", "bone_idx", "force_update"), &Skeleton::get_bone_global_pose_without_override, DEFVAL(true)); -#ifndef _3D_DISABLED + ClassDB::bind_method(D_METHOD("bone_transform_to_world_transform", "bone_transform"), &Skeleton::bone_transform_to_world_transform); + ClassDB::bind_method(D_METHOD("world_transform_to_bone_transform", "world_transform"), &Skeleton::world_transform_to_bone_transform); ClassDB::bind_method(D_METHOD("physical_bones_stop_simulation"), &Skeleton::physical_bones_stop_simulation); ClassDB::bind_method(D_METHOD("physical_bones_start_simulation", "bones"), &Skeleton::physical_bones_start_simulation_on, DEFVAL(Array())); ClassDB::bind_method(D_METHOD("physical_bones_add_collision_exception", "exception"), &Skeleton::physical_bones_add_collision_exception); ClassDB::bind_method(D_METHOD("physical_bones_remove_collision_exception", "exception"), &Skeleton::physical_bones_remove_collision_exception); -#endif // _3D_DISABLED - ADD_SIGNAL(MethodInfo("skeleton_updated")); - - BIND_CONSTANT(NOTIFICATION_UPDATE_SKELETON); +#ifdef TOOLS_ENABLED + ADD_SIGNAL(MethodInfo("pose_updated")); +#endif } -Skeleton::Skeleton() { - - skins_dirty = false; - version = 1; - process_order_dirty = true; - bones_root = 0; +Skeleton::Skeleton() : + process_order_dirty(1), dirty(true), version(1) { + set_process_internal(true); } Skeleton::~Skeleton() { - //some skins may remain bound for (Set::Element *E = skin_bindings.front(); E; E = E->next()) { E->get()->skeleton_node = nullptr; diff --git a/scene/3d/skeleton.h b/scene/3d/skeleton.h index 7ef50eec8943..031d57cf03c6 100644 --- a/scene/3d/skeleton.h +++ b/scene/3d/skeleton.h @@ -47,13 +47,13 @@ class SkinReference : public Reference { GDCLASS(SkinReference, Reference) friend class Skeleton; - Skeleton *skeleton_node = nullptr; + Skeleton *skeleton_node; RID skeleton; Ref skin; uint32_t bind_count = 0; uint64_t skeleton_version = 0; Vector skin_bone_indices; - uint32_t *skin_bone_indices_ptrs = nullptr; + uint32_t *skin_bone_indices_ptrs; void _skin_changed(); protected: @@ -67,82 +67,64 @@ class SkinReference : public Reference { }; class Skeleton : public Spatial { - GDCLASS(Skeleton, Spatial); private: friend class SkinReference; - Set skin_bindings; - - void _skin_changed(); - struct Bone { - String name; - bool enabled; - int parent; - Vector children; - int sort_index; //used for re-sorting process order + bool dirty = true; + + bool enabled = true; + int parent = -1; + int sort_index = -1; //used for re-sorting process order + Vector children = Vector(); - bool disable_rest; + bool disable_rest = false; + + // bones current rest bind Transform rest; + // the current pose Transform pose; - Transform pose_global; - bool custom_pose_enable; - Transform custom_pose; + // the current global pose (inheriting from parent) + // this is the "natural" global pose - without custom poses or override taken into account + Transform pose_global_no_override; - float global_pose_override_amount; - bool global_pose_override_reset; + real_t global_pose_override_amount = 0.0; + bool global_pose_override_reset = false; Transform global_pose_override; -#ifndef _3D_DISABLED + // the final pose, with custom pose applied or global_pose_override applied + Transform pose_global_final; + + List nodes_bound; + PhysicalBone *physical_bone; PhysicalBone *cache_parent_physical_bone; -#endif // _3D_DISABLED + }; - List nodes_bound; + Set skin_bindings; - Bone() { - parent = -1; - children = Vector(); - enabled = true; - disable_rest = false; - custom_pose_enable = false; - global_pose_override_amount = 0; - global_pose_override_reset = false; -#ifndef _3D_DISABLED - physical_bone = NULL; - cache_parent_physical_bone = NULL; -#endif // _3D_DISABLED - } - }; + void _skin_changed(); Vector bones; Vector process_order; - bool process_order_dirty; - - void _make_bone_dirty(int p_bone = -1); - void _make_skins_dirty(); - bool skins_dirty; - Vector dirty_idxes; - - int bones_root; + bool process_order_dirty; + bool dirty; uint64_t version; // bind helpers Array _get_bound_child_nodes_to_bone(int p_bone) const { - Array bound; List children; get_bound_child_nodes_to_bone(p_bone, &children); for (int i = 0; i < children.size(); i++) { - bound.push_back(children[i]); } return bound; @@ -150,19 +132,18 @@ class Skeleton : public Spatial { void _update_process_order(); + _FORCE_INLINE_ bool update_bone_pose(Bone &p_bone, const Bone *p_bonesptr) const; + protected: bool _get(const StringName &p_path, Variant &r_ret) const; bool _set(const StringName &p_path, const Variant &p_value); void _get_property_list(List *p_list) const; void _notification(int p_what); - void _update_bone_transform(int p_bone); - void _update_skins(); static void _bind_methods(); public: enum { - NOTIFICATION_UPDATE_SKELETON = 50, - NOTIFICATION_UPDATE_SKELETON_BONE_ONLY = 60 + NOTIFICATION_UPDATE_SKELETON = 50 }; // skeleton creation api @@ -176,7 +157,6 @@ class Skeleton : public Spatial { int get_bone_parent(int p_bone) const; Array get_bone_children(int p_bone); - int get_bones_root(); void unparent_bone_and_rest(int p_bone); @@ -187,7 +167,9 @@ class Skeleton : public Spatial { void set_bone_rest(int p_bone, const Transform &p_rest); Transform get_bone_rest(int p_bone) const; - Transform get_bone_global_pose(int p_bone, bool update_skins = false) const; + Transform get_bone_global_pose(int p_bone, bool force_update = false) const; + Transform get_bone_global_pose_without_override(int p_bone, bool force_update = false) const; + Transform get_bone_global_rest(int p_bone) const; void clear_bones_global_pose_override(); void set_bone_global_pose_override(int p_bone, const Transform &p_pose, float p_amount, bool p_persistent = false); @@ -206,16 +188,17 @@ class Skeleton : public Spatial { void set_bone_pose(int p_bone, const Transform &p_pose); Transform get_bone_pose(int p_bone) const; - void set_bone_custom_pose(int p_bone, const Transform &p_custom_pose); - Transform get_bone_custom_pose(int p_bone) const; + void reset_bone_poses(); void localize_rests(); // used for loaders and tools int get_process_order(int p_idx); + Vector get_bone_process_orders(); Ref register_skin(const Ref &p_skin); -#ifndef _3D_DISABLED - // Physical bone API + // Helper functions + Transform bone_transform_to_world_transform(Transform p_transform); + Transform world_transform_to_bone_transform(Transform p_transform); void bind_physical_bone_to_bone(int p_bone, PhysicalBone *p_physical_bone); void unbind_physical_bone_from_bone(int p_bone); @@ -233,7 +216,6 @@ class Skeleton : public Spatial { void physical_bones_start_simulation_on(const Array &p_bones); void physical_bones_add_collision_exception(RID p_exception); void physical_bones_remove_collision_exception(RID p_exception); -#endif // _3D_DISABLED public: Skeleton(); diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp index e0f46d48e3d2..ddac7fed8e31 100644 --- a/scene/animation/animation_player.cpp +++ b/scene/animation/animation_player.cpp @@ -43,9 +43,6 @@ void AnimatedValuesBackup::update_skeletons() { for (int i = 0; i < entries.size(); i++) { if (entries[i].bone_idx != -1) { - // 3D bone - Object::cast_to(entries[i].object)->notification(Skeleton::NOTIFICATION_UPDATE_SKELETON); - } else { Bone2D *bone = Object::cast_to(entries[i].object); if (bone && bone->skeleton) { // 2D bone