diff options
Diffstat (limited to 'scene')
-rw-r--r-- | scene/2d/mesh_instance_2d.cpp | 13 | ||||
-rw-r--r-- | scene/2d/skeleton_2d.cpp | 44 | ||||
-rw-r--r-- | scene/2d/skeleton_2d.h | 2 | ||||
-rw-r--r-- | scene/3d/cpu_particles_3d.cpp | 1 | ||||
-rw-r--r-- | scene/3d/node_3d.cpp | 22 | ||||
-rw-r--r-- | scene/3d/skeleton_3d.cpp | 223 | ||||
-rw-r--r-- | scene/3d/skeleton_3d.h | 13 | ||||
-rw-r--r-- | scene/debugger/scene_debugger.cpp | 16 | ||||
-rw-r--r-- | scene/gui/code_edit.cpp | 6 | ||||
-rw-r--r-- | scene/main/viewport.cpp | 10 | ||||
-rw-r--r-- | scene/resources/immediate_mesh.cpp | 2 | ||||
-rw-r--r-- | scene/resources/material.cpp | 9 |
12 files changed, 258 insertions, 103 deletions
diff --git a/scene/2d/mesh_instance_2d.cpp b/scene/2d/mesh_instance_2d.cpp index ae45d431fe..1031a67343 100644 --- a/scene/2d/mesh_instance_2d.cpp +++ b/scene/2d/mesh_instance_2d.cpp @@ -54,7 +54,20 @@ void MeshInstance2D::_bind_methods() { } void MeshInstance2D::set_mesh(const Ref<Mesh> &p_mesh) { + if (mesh == p_mesh) { + return; + } + + if (mesh.is_valid()) { + mesh->disconnect_changed(callable_mp((CanvasItem *)this, &CanvasItem::queue_redraw)); + } + mesh = p_mesh; + + if (mesh.is_valid()) { + mesh->connect_changed(callable_mp((CanvasItem *)this, &CanvasItem::queue_redraw)); + } + queue_redraw(); } diff --git a/scene/2d/skeleton_2d.cpp b/scene/2d/skeleton_2d.cpp index 90bfb4c84c..39cfccf983 100644 --- a/scene/2d/skeleton_2d.cpp +++ b/scene/2d/skeleton_2d.cpp @@ -159,7 +159,7 @@ void Bone2D::_notification(int p_what) { case NOTIFICATION_EXIT_TREE: { if (skeleton) { - for (int i = 0; i < skeleton->bones.size(); i++) { + for (uint32_t i = 0; i < skeleton->bones.size(); i++) { if (skeleton->bones[i].bone == this) { skeleton->bones.remove_at(i); break; @@ -555,17 +555,17 @@ void Skeleton2D::_update_bone_setup() { bones.sort(); //sorting so that they are always in the same order/index - for (int i = 0; i < bones.size(); i++) { - bones.write[i].rest_inverse = bones[i].bone->get_skeleton_rest().affine_inverse(); //bind pose - bones.write[i].bone->skeleton_index = i; + for (uint32_t i = 0; i < bones.size(); i++) { + bones[i].rest_inverse = bones[i].bone->get_skeleton_rest().affine_inverse(); //bind pose + bones[i].bone->skeleton_index = i; Bone2D *parent_bone = Object::cast_to<Bone2D>(bones[i].bone->get_parent()); if (parent_bone) { - bones.write[i].parent_index = parent_bone->skeleton_index; + bones[i].parent_index = parent_bone->skeleton_index; } else { - bones.write[i].parent_index = -1; + bones[i].parent_index = -1; } - bones.write[i].local_pose_override = bones[i].bone->get_skeleton_rest(); + bones[i].local_pose_override = bones[i].bone->get_skeleton_rest(); } transform_dirty = true; @@ -594,16 +594,16 @@ void Skeleton2D::_update_transform() { transform_dirty = false; - for (int i = 0; i < bones.size(); i++) { - ERR_CONTINUE(bones[i].parent_index >= i); + for (uint32_t i = 0; i < bones.size(); i++) { + ERR_CONTINUE(bones[i].parent_index >= (int)i); if (bones[i].parent_index >= 0) { - bones.write[i].accum_transform = bones[bones[i].parent_index].accum_transform * bones[i].bone->get_transform(); + bones[i].accum_transform = bones[bones[i].parent_index].accum_transform * bones[i].bone->get_transform(); } else { - bones.write[i].accum_transform = bones[i].bone->get_transform(); + bones[i].accum_transform = bones[i].bone->get_transform(); } } - for (int i = 0; i < bones.size(); i++) { + for (uint32_t i = 0; i < bones.size(); i++) { Transform2D final_xform = bones[i].accum_transform * bones[i].rest_inverse; RS::get_singleton()->skeleton_bone_set_transform_2d(skeleton, i, final_xform); } @@ -621,7 +621,7 @@ int Skeleton2D::get_bone_count() const { Bone2D *Skeleton2D::get_bone(int p_idx) { ERR_FAIL_COND_V(!is_inside_tree(), nullptr); - ERR_FAIL_INDEX_V(p_idx, bones.size(), nullptr); + ERR_FAIL_INDEX_V(p_idx, (int)bones.size(), nullptr); return bones[p_idx].bone; } @@ -733,14 +733,14 @@ RID Skeleton2D::get_skeleton() const { } void Skeleton2D::set_bone_local_pose_override(int p_bone_idx, Transform2D p_override, real_t p_amount, bool p_persistent) { - ERR_FAIL_INDEX_MSG(p_bone_idx, bones.size(), "Bone index is out of range!"); - bones.write[p_bone_idx].local_pose_override = p_override; - bones.write[p_bone_idx].local_pose_override_amount = p_amount; - bones.write[p_bone_idx].local_pose_override_persistent = p_persistent; + ERR_FAIL_INDEX_MSG(p_bone_idx, (int)bones.size(), "Bone index is out of range!"); + bones[p_bone_idx].local_pose_override = p_override; + bones[p_bone_idx].local_pose_override_amount = p_amount; + bones[p_bone_idx].local_pose_override_persistent = p_persistent; } Transform2D Skeleton2D::get_bone_local_pose_override(int p_bone_idx) { - ERR_FAIL_INDEX_V_MSG(p_bone_idx, bones.size(), Transform2D(), "Bone index is out of range!"); + ERR_FAIL_INDEX_V_MSG(p_bone_idx, (int)bones.size(), Transform2D(), "Bone index is out of range!"); return bones[p_bone_idx].local_pose_override; } @@ -771,7 +771,7 @@ void Skeleton2D::execute_modifications(real_t p_delta, int p_execution_mode) { } // Do not cache the transform changes caused by the modifications! - for (int i = 0; i < bones.size(); i++) { + for (uint32_t i = 0; i < bones.size(); i++) { bones[i].bone->copy_transform_to_cache = false; } @@ -783,7 +783,7 @@ void Skeleton2D::execute_modifications(real_t p_delta, int p_execution_mode) { // Only apply the local pose override on _process. Otherwise, just calculate the local_pose_override and reset the transform. if (p_execution_mode == SkeletonModificationStack2D::EXECUTION_MODE::execution_mode_process) { - for (int i = 0; i < bones.size(); i++) { + for (uint32_t i = 0; i < bones.size(); i++) { if (bones[i].local_pose_override_amount > 0) { bones[i].bone->set_meta("_local_pose_override_enabled_", true); @@ -793,7 +793,7 @@ void Skeleton2D::execute_modifications(real_t p_delta, int p_execution_mode) { bones[i].bone->propagate_call("force_update_transform"); if (bones[i].local_pose_override_persistent) { - bones.write[i].local_pose_override_amount = 0.0; + bones[i].local_pose_override_amount = 0.0; } } else { // TODO: see if there is a way to undo the override without having to resort to setting every bone's transform. @@ -804,7 +804,7 @@ void Skeleton2D::execute_modifications(real_t p_delta, int p_execution_mode) { } // Cache any future transform changes - for (int i = 0; i < bones.size(); i++) { + for (uint32_t i = 0; i < bones.size(); i++) { bones[i].bone->copy_transform_to_cache = true; } diff --git a/scene/2d/skeleton_2d.h b/scene/2d/skeleton_2d.h index 033bdff41d..16bd6fd34b 100644 --- a/scene/2d/skeleton_2d.h +++ b/scene/2d/skeleton_2d.h @@ -123,7 +123,7 @@ class Skeleton2D : public Node2D { bool local_pose_override_persistent = false; }; - Vector<Bone> bones; + LocalVector<Bone> bones; bool bone_setup_dirty = true; void _make_bone_setup_dirty(); diff --git a/scene/3d/cpu_particles_3d.cpp b/scene/3d/cpu_particles_3d.cpp index acbc443a93..5b84bf903f 100644 --- a/scene/3d/cpu_particles_3d.cpp +++ b/scene/3d/cpu_particles_3d.cpp @@ -1479,6 +1479,7 @@ void CPUParticles3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_mesh"), &CPUParticles3D::get_mesh); ClassDB::bind_method(D_METHOD("restart"), &CPUParticles3D::restart); + ClassDB::bind_method(D_METHOD("capture_aabb"), &CPUParticles3D::capture_aabb); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emitting"), "set_emitting", "is_emitting"); ADD_PROPERTY(PropertyInfo(Variant::INT, "amount", PROPERTY_HINT_RANGE, "1,1000000,1,exp"), "set_amount", "get_amount"); diff --git a/scene/3d/node_3d.cpp b/scene/3d/node_3d.cpp index 86ce8a881a..85de85a9a6 100644 --- a/scene/3d/node_3d.cpp +++ b/scene/3d/node_3d.cpp @@ -1181,15 +1181,16 @@ void Node3D::_validate_property(PropertyInfo &p_property) const { } bool Node3D::_property_can_revert(const StringName &p_name) const { - if (p_name == "basis") { + const String sname = p_name; + if (sname == "basis") { return true; - } else if (p_name == "scale") { + } else if (sname == "scale") { return true; - } else if (p_name == "quaternion") { + } else if (sname == "quaternion") { return true; - } else if (p_name == "rotation") { + } else if (sname == "rotation") { return true; - } else if (p_name == "position") { + } else if (sname == "position") { return true; } return false; @@ -1198,35 +1199,36 @@ bool Node3D::_property_can_revert(const StringName &p_name) const { bool Node3D::_property_get_revert(const StringName &p_name, Variant &r_property) const { bool valid = false; - if (p_name == "basis") { + const String sname = p_name; + if (sname == "basis") { Variant variant = PropertyUtils::get_property_default_value(this, "transform", &valid); if (valid && variant.get_type() == Variant::Type::TRANSFORM3D) { r_property = Transform3D(variant).get_basis(); } else { r_property = Basis(); } - } else if (p_name == "scale") { + } else if (sname == "scale") { Variant variant = PropertyUtils::get_property_default_value(this, "transform", &valid); if (valid && variant.get_type() == Variant::Type::TRANSFORM3D) { r_property = Transform3D(variant).get_basis().get_scale(); } else { r_property = Vector3(1.0, 1.0, 1.0); } - } else if (p_name == "quaternion") { + } else if (sname == "quaternion") { Variant variant = PropertyUtils::get_property_default_value(this, "transform", &valid); if (valid && variant.get_type() == Variant::Type::TRANSFORM3D) { r_property = Quaternion(Transform3D(variant).get_basis().get_rotation_quaternion()); } else { r_property = Quaternion(); } - } else if (p_name == "rotation") { + } else if (sname == "rotation") { Variant variant = PropertyUtils::get_property_default_value(this, "transform", &valid); if (valid && variant.get_type() == Variant::Type::TRANSFORM3D) { r_property = Transform3D(variant).get_basis().get_euler_normalized(data.euler_rotation_order); } else { r_property = Vector3(); } - } else if (p_name == "position") { + } else if (sname == "position") { Variant variant = PropertyUtils::get_property_default_value(this, "transform", &valid); if (valid) { r_property = Transform3D(variant).get_origin(); diff --git a/scene/3d/skeleton_3d.cpp b/scene/3d/skeleton_3d.cpp index db9c4db30d..9e4c9b1832 100644 --- a/scene/3d/skeleton_3d.cpp +++ b/scene/3d/skeleton_3d.cpp @@ -81,7 +81,7 @@ bool Skeleton3D::_set(const StringName &p_path, const Variant &p_value) { return false; } - int which = path.get_slicec('/', 1).to_int(); + uint32_t which = path.get_slicec('/', 1).to_int(); String what = path.get_slicec('/', 2); if (which == bones.size() && what == "name") { @@ -89,7 +89,7 @@ bool Skeleton3D::_set(const StringName &p_path, const Variant &p_value) { return true; } - ERR_FAIL_INDEX_V(which, bones.size(), false); + ERR_FAIL_UNSIGNED_INDEX_V(which, bones.size(), false); if (what == "parent") { set_bone_parent(which, p_value); @@ -153,10 +153,10 @@ bool Skeleton3D::_get(const StringName &p_path, Variant &r_ret) const { return false; } - int which = path.get_slicec('/', 1).to_int(); + uint32_t which = path.get_slicec('/', 1).to_int(); String what = path.get_slicec('/', 2); - ERR_FAIL_INDEX_V(which, bones.size(), false); + ERR_FAIL_UNSIGNED_INDEX_V(which, bones.size(), false); if (what == "name") { r_ret = get_bone_name(which); @@ -182,7 +182,7 @@ bool Skeleton3D::_get(const StringName &p_path, Variant &r_ret) const { } void Skeleton3D::_get_property_list(List<PropertyInfo> *p_list) const { - for (int i = 0; i < bones.size(); i++) { + for (uint32_t i = 0; i < bones.size(); i++) { const String prep = vformat("%s/%d/", PNAME("bones"), i); p_list->push_back(PropertyInfo(Variant::STRING, prep + PNAME("name"), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); p_list->push_back(PropertyInfo(Variant::INT, prep + PNAME("parent"), PROPERTY_HINT_RANGE, "-1," + itos(bones.size() - 1) + ",1", PROPERTY_USAGE_NO_EDITOR)); @@ -241,7 +241,7 @@ void Skeleton3D::_update_process_order() { return; } - Bone *bonesptr = bones.ptrw(); + Bone *bonesptr = bones.ptr(); int len = bones.size(); parentless_bones.clear(); @@ -276,6 +276,8 @@ void Skeleton3D::_update_process_order() { concatenated_bone_names = StringName(); + _update_bones_nested_set(); + process_order_dirty = false; emit_signal("bone_list_changed"); @@ -283,7 +285,7 @@ void Skeleton3D::_update_process_order() { void Skeleton3D::_update_bone_names() const { String names; - for (int i = 0; i < bones.size(); i++) { + for (uint32_t i = 0; i < bones.size(); i++) { if (i > 0) { names += ","; } @@ -331,16 +333,21 @@ void Skeleton3D::_notification(int p_what) { updating = true; - Bone *bonesptr = bones.ptrw(); + Bone *bonesptr = bones.ptr(); int len = bones.size(); + thread_local LocalVector<bool> bone_global_pose_dirty_backup; + // Process modifiers. _find_modifiers(); if (!modifiers.is_empty()) { // Store unmodified bone poses. - for (int i = 0; i < bones.size(); i++) { + for (uint32_t i = 0; i < bones.size(); i++) { bones_backup[i].save(bones[i]); } + // Store dirty flags for global bone poses. + bone_global_pose_dirty_backup = bone_global_pose_dirty; + _process_modifiers(); } @@ -412,9 +419,11 @@ void Skeleton3D::_notification(int p_what) { if (!modifiers.is_empty()) { // Restore unmodified bone poses. - for (int i = 0; i < bones.size(); i++) { - bones_backup[i].restore(bones.write[i]); + for (uint32_t i = 0; i < bones.size(); i++) { + bones_backup[i].restore(bones[i]); } + // Restore dirty flags for global bone poses. + bone_global_pose_dirty = bone_global_pose_dirty_backup; } updating = false; @@ -457,10 +466,111 @@ void Skeleton3D::_make_modifiers_dirty() { _update_deferred(UPDATE_FLAG_MODIFIER); } +void Skeleton3D::_update_bones_nested_set() { + nested_set_offset_to_bone_index.resize(bones.size()); + bone_global_pose_dirty.resize(bones.size()); + _make_bone_global_poses_dirty(); + + int offset = 0; + for (int bone : parentless_bones) { + offset += _update_bone_nested_set(bone, offset); + } +} + +int Skeleton3D::_update_bone_nested_set(int p_bone, int p_offset) { + Bone &bone = bones[p_bone]; + int offset = p_offset + 1; + int span = 1; + + for (int child_bone : bone.child_bones) { + int subspan = _update_bone_nested_set(child_bone, offset); + offset += subspan; + span += subspan; + } + + nested_set_offset_to_bone_index[p_offset] = p_bone; + bone.nested_set_offset = p_offset; + bone.nested_set_span = span; + + return span; +} + +void Skeleton3D::_make_bone_global_poses_dirty() { + for (uint32_t i = 0; i < bone_global_pose_dirty.size(); i++) { + bone_global_pose_dirty[i] = true; + } +} + +void Skeleton3D::_make_bone_global_pose_subtree_dirty(int p_bone) { + if (process_order_dirty) { + return; + } + + const Bone &bone = bones[p_bone]; + int span_offset = bone.nested_set_offset; + // No need to make subtree dirty when bone is already dirty. + if (bone_global_pose_dirty[span_offset]) { + return; + } + + // Make global poses of subtree dirty. + int span_end = span_offset + bone.nested_set_span; + for (int i = span_offset; i < span_end; i++) { + bone_global_pose_dirty[i] = true; + } +} + +void Skeleton3D::_update_bone_global_pose(int p_bone) { + const int bone_size = bones.size(); + ERR_FAIL_INDEX(p_bone, bone_size); + + _update_process_order(); + + // Global pose is already calculated. + int nested_set_offset = bones[p_bone].nested_set_offset; + if (!bone_global_pose_dirty[nested_set_offset]) { + return; + } + + thread_local LocalVector<int> bone_list; + bone_list.clear(); + Transform3D global_pose; + + // Create list of parent bones for which the global pose needs to be recalculated. + for (int bone = p_bone; bone >= 0; bone = bones[bone].parent) { + int offset = bones[bone].nested_set_offset; + // Stop searching when global pose is not dirty. + if (!bone_global_pose_dirty[offset]) { + global_pose = bones[bone].global_pose; + break; + } + + bone_list.push_back(bone); + } + + // Calculate global poses for all parent bones and the current bone. + for (int i = bone_list.size() - 1; i >= 0; i--) { + int bone_idx = bone_list[i]; + Bone &bone = bones[bone_idx]; + bool bone_enabled = bone.enabled && !show_rest_only; + Transform3D bone_pose = bone_enabled ? get_bone_pose(bone_idx) : get_bone_rest(bone_idx); + + global_pose *= bone_pose; +#ifndef DISABLE_DEPRECATED + if (bone.global_pose_override_amount >= CMP_EPSILON) { + global_pose = global_pose.interpolate_with(bone.global_pose_override, bone.global_pose_override_amount); + } +#endif // _DISABLE_DEPRECATED + + bone.global_pose = global_pose; + bone_global_pose_dirty[bone.nested_set_offset] = false; + } +} + Transform3D Skeleton3D::get_bone_global_pose(int p_bone) const { const int bone_size = bones.size(); ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D()); - const_cast<Skeleton3D *>(this)->force_update_all_dirty_bones(); + const_cast<Skeleton3D *>(this)->_update_bone_global_pose(p_bone); return bones[p_bone].global_pose; } @@ -534,7 +644,7 @@ void Skeleton3D::set_bone_name(int p_bone, const String &p_name) { } name_to_bone_index.erase(bones[p_bone].name); - bones.write[p_bone].name = p_name; + bones[p_bone].name = p_name; name_to_bone_index.insert(p_name, p_bone); version++; @@ -582,13 +692,13 @@ void Skeleton3D::set_bone_meta(int p_bone, const StringName &p_key, const Varian ERR_FAIL_INDEX(p_bone, bone_size); if (p_value.get_type() == Variant::NIL) { - if (bones.write[p_bone].metadata.has(p_key)) { - bones.write[p_bone].metadata.erase(p_key); + if (bones[p_bone].metadata.has(p_key)) { + bones[p_bone].metadata.erase(p_key); } return; } - bones.write[p_bone].metadata.insert(p_key, p_value, false); + bones[p_bone].metadata.insert(p_key, p_value, false); } bool Skeleton3D::is_bone_parent_of(int p_bone, int p_parent_bone_id) const { @@ -615,7 +725,7 @@ void Skeleton3D::set_bone_parent(int p_bone, int p_parent) { ERR_FAIL_COND(p_parent != -1 && (p_parent < 0)); ERR_FAIL_COND(p_bone == p_parent); - bones.write[p_bone].parent = p_parent; + bones[p_bone].parent = p_parent; process_order_dirty = true; rest_dirty = true; _make_dirty(); @@ -629,11 +739,11 @@ void Skeleton3D::unparent_bone_and_rest(int p_bone) { int parent = bones[p_bone].parent; while (parent >= 0) { - bones.write[p_bone].rest = bones[parent].rest * bones[p_bone].rest; + bones[p_bone].rest = bones[parent].rest * bones[p_bone].rest; parent = bones[parent].parent; } - bones.write[p_bone].parent = -1; + bones[p_bone].parent = -1; process_order_dirty = true; rest_dirty = true; @@ -669,9 +779,10 @@ void Skeleton3D::set_bone_rest(int p_bone, const Transform3D &p_rest) { const int bone_size = bones.size(); ERR_FAIL_INDEX(p_bone, bone_size); - bones.write[p_bone].rest = p_rest; + bones[p_bone].rest = p_rest; rest_dirty = true; _make_dirty(); + _make_bone_global_pose_subtree_dirty(p_bone); } Transform3D Skeleton3D::get_bone_rest(int p_bone) const { const int bone_size = bones.size(); @@ -692,9 +803,10 @@ void Skeleton3D::set_bone_enabled(int p_bone, bool p_enabled) { const int bone_size = bones.size(); ERR_FAIL_INDEX(p_bone, bone_size); - bones.write[p_bone].enabled = p_enabled; + bones[p_bone].enabled = p_enabled; emit_signal(SceneStringName(bone_enabled_changed), p_bone); _make_dirty(); + _make_bone_global_pose_subtree_dirty(p_bone); } bool Skeleton3D::is_bone_enabled(int p_bone) const { @@ -707,6 +819,7 @@ void Skeleton3D::set_show_rest_only(bool p_enabled) { show_rest_only = p_enabled; emit_signal(SceneStringName(show_rest_only_changed)); _make_dirty(); + _make_bone_global_poses_dirty(); } bool Skeleton3D::is_show_rest_only() const { @@ -727,12 +840,13 @@ void Skeleton3D::set_bone_pose(int p_bone, const Transform3D &p_pose) { const int bone_size = bones.size(); ERR_FAIL_INDEX(p_bone, bone_size); - bones.write[p_bone].pose_position = p_pose.origin; - bones.write[p_bone].pose_rotation = p_pose.basis.get_rotation_quaternion(); - bones.write[p_bone].pose_scale = p_pose.basis.get_scale(); - bones.write[p_bone].pose_cache_dirty = true; + bones[p_bone].pose_position = p_pose.origin; + bones[p_bone].pose_rotation = p_pose.basis.get_rotation_quaternion(); + bones[p_bone].pose_scale = p_pose.basis.get_scale(); + bones[p_bone].pose_cache_dirty = true; if (is_inside_tree()) { _make_dirty(); + _make_bone_global_pose_subtree_dirty(p_bone); } } @@ -740,30 +854,33 @@ void Skeleton3D::set_bone_pose_position(int p_bone, const Vector3 &p_position) { const int bone_size = bones.size(); ERR_FAIL_INDEX(p_bone, bone_size); - bones.write[p_bone].pose_position = p_position; - bones.write[p_bone].pose_cache_dirty = true; + bones[p_bone].pose_position = p_position; + bones[p_bone].pose_cache_dirty = true; if (is_inside_tree()) { _make_dirty(); + _make_bone_global_pose_subtree_dirty(p_bone); } } void Skeleton3D::set_bone_pose_rotation(int p_bone, const Quaternion &p_rotation) { const int bone_size = bones.size(); ERR_FAIL_INDEX(p_bone, bone_size); - bones.write[p_bone].pose_rotation = p_rotation; - bones.write[p_bone].pose_cache_dirty = true; + bones[p_bone].pose_rotation = p_rotation; + bones[p_bone].pose_cache_dirty = true; if (is_inside_tree()) { _make_dirty(); + _make_bone_global_pose_subtree_dirty(p_bone); } } void Skeleton3D::set_bone_pose_scale(int p_bone, const Vector3 &p_scale) { const int bone_size = bones.size(); ERR_FAIL_INDEX(p_bone, bone_size); - bones.write[p_bone].pose_scale = p_scale; - bones.write[p_bone].pose_cache_dirty = true; + bones[p_bone].pose_scale = p_scale; + bones[p_bone].pose_cache_dirty = true; if (is_inside_tree()) { _make_dirty(); + _make_bone_global_pose_subtree_dirty(p_bone); } } @@ -794,7 +911,7 @@ void Skeleton3D::reset_bone_pose(int p_bone) { } void Skeleton3D::reset_bone_poses() { - for (int i = 0; i < bones.size(); i++) { + for (uint32_t i = 0; i < bones.size(); i++) { reset_bone_pose(i); } } @@ -802,7 +919,7 @@ void Skeleton3D::reset_bone_poses() { Transform3D Skeleton3D::get_bone_pose(int p_bone) const { const int bone_size = bones.size(); ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D()); - const_cast<Skeleton3D *>(this)->bones.write[p_bone].update_pose_cache(); + const_cast<Skeleton3D *>(this)->bones[p_bone].update_pose_cache(); return bones[p_bone].pose_cache; } @@ -853,7 +970,7 @@ Ref<Skin> Skeleton3D::create_skin_from_rest_transforms() { // Pose changed, rebuild cache of inverses. const Bone *bonesptr = bones.ptr(); - int len = bones.size(); + uint32_t len = bones.size(); // Calculate global rests and invert them. LocalVector<int> bones_to_process; @@ -877,7 +994,7 @@ Ref<Skin> Skeleton3D::create_skin_from_rest_transforms() { } } - for (int i = 0; i < len; i++) { + for (uint32_t i = 0; i < len; i++) { // The inverse is what is actually required. skin->set_bind_bone(i, i); skin->set_bind_pose(i, skin->get_bind_pose(i).affine_inverse()); @@ -937,15 +1054,15 @@ void Skeleton3D::force_update_bone_children_transforms(int p_bone_idx) { const int bone_size = bones.size(); ERR_FAIL_INDEX(p_bone_idx, bone_size); - Bone *bonesptr = bones.ptrw(); - thread_local LocalVector<int> bones_to_process; - bones_to_process.clear(); - bones_to_process.push_back(p_bone_idx); + Bone *bonesptr = bones.ptr(); - uint32_t index = 0; - while (index < bones_to_process.size()) { - int current_bone_idx = bones_to_process[index]; + // Loop through nested set. + for (int offset = 0; offset < bone_size; offset++) { + if (!bone_global_pose_dirty[offset]) { + continue; + } + int current_bone_idx = nested_set_offset_to_bone_index[offset]; Bone &b = bonesptr[current_bone_idx]; bool bone_enabled = b.enabled && !show_rest_only; @@ -992,13 +1109,7 @@ void Skeleton3D::force_update_bone_children_transforms(int p_bone_idx) { } #endif // _DISABLE_DEPRECATED - // Add the bone's children to the list of bones to be processed. - int child_bone_size = b.child_bones.size(); - for (int i = 0; i < child_bone_size; i++) { - bones_to_process.push_back(b.child_bones[i]); - } - - index++; + bone_global_pose_dirty[offset] = false; } } @@ -1171,20 +1282,22 @@ void Skeleton3D::_bind_methods() { #ifndef DISABLE_DEPRECATED void Skeleton3D::clear_bones_global_pose_override() { - for (int i = 0; i < bones.size(); i += 1) { - bones.write[i].global_pose_override_amount = 0; - bones.write[i].global_pose_override_reset = true; + for (uint32_t i = 0; i < bones.size(); i += 1) { + bones[i].global_pose_override_amount = 0; + bones[i].global_pose_override_reset = true; } _make_dirty(); + _make_bone_global_poses_dirty(); } void Skeleton3D::set_bone_global_pose_override(int p_bone, const Transform3D &p_pose, real_t p_amount, bool p_persistent) { const int bone_size = bones.size(); ERR_FAIL_INDEX(p_bone, bone_size); - 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; + bones[p_bone].global_pose_override_amount = p_amount; + bones[p_bone].global_pose_override = p_pose; + bones[p_bone].global_pose_override_reset = !p_persistent; _make_dirty(); + _make_bone_global_pose_subtree_dirty(p_bone); } Transform3D Skeleton3D::get_bone_global_pose_override(int p_bone) const { diff --git a/scene/3d/skeleton_3d.h b/scene/3d/skeleton_3d.h index 07bdeccf2f..0db12600c3 100644 --- a/scene/3d/skeleton_3d.h +++ b/scene/3d/skeleton_3d.h @@ -107,6 +107,8 @@ private: Quaternion pose_rotation; Vector3 pose_scale = Vector3(1, 1, 1); Transform3D global_pose; + int nested_set_offset = 0; // Offset in nested set of bone hierarchy. + int nested_set_span = 0; // Subtree span in nested set of bone hierarchy. void update_pose_cache() { if (pose_cache_dirty) { @@ -153,7 +155,7 @@ private: HashSet<SkinReference *> skin_bindings; void _skin_changed(); - Vector<Bone> bones; + LocalVector<Bone> bones; bool process_order_dirty = false; Vector<int> parentless_bones; @@ -183,6 +185,15 @@ private: void _make_modifiers_dirty(); LocalVector<BonePoseBackup> bones_backup; + // Global bone pose calculation. + LocalVector<int> nested_set_offset_to_bone_index; // Map from Bone::nested_set_offset to bone index. + LocalVector<bool> bone_global_pose_dirty; // Indexable with Bone::nested_set_offset. + void _update_bones_nested_set(); + int _update_bone_nested_set(int p_bone, int p_offset); + void _make_bone_global_poses_dirty(); + void _make_bone_global_pose_subtree_dirty(int p_bone); + void _update_bone_global_pose(int p_bone); + #ifndef DISABLE_DEPRECATED void _add_bone_bind_compat_88791(const String &p_name); diff --git a/scene/debugger/scene_debugger.cpp b/scene/debugger/scene_debugger.cpp index 07c32eef13..22e5238fae 100644 --- a/scene/debugger/scene_debugger.cpp +++ b/scene/debugger/scene_debugger.cpp @@ -535,7 +535,21 @@ SceneDebuggerTree::SceneDebuggerTree(Node *p_root) { } } } - nodes.push_back(RemoteNode(count, n->get_name(), n->get_class(), n->get_instance_id(), n->get_scene_file_path(), view_flags)); + + String class_name; + ScriptInstance *script_instance = n->get_script_instance(); + if (script_instance) { + Ref<Script> script = script_instance->get_script(); + if (script.is_valid()) { + class_name = script->get_global_name(); + + if (class_name.is_empty()) { + // If there is no class_name in this script we just take the script path. + class_name = script->get_path(); + } + } + } + nodes.push_back(RemoteNode(count, n->get_name(), class_name.is_empty() ? n->get_class() : class_name, n->get_instance_id(), n->get_scene_file_path(), view_flags)); } } diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp index 635228670d..7346c9dcd3 100644 --- a/scene/gui/code_edit.cpp +++ b/scene/gui/code_edit.cpp @@ -277,15 +277,17 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) { code_completion_force_item_center = -1; queue_redraw(); } - code_completion_pan_offset += 1.0f; + code_completion_pan_offset = 0; } else if (code_completion_pan_offset >= +1.0) { if (code_completion_current_selected < code_completion_options.size() - 1) { code_completion_current_selected++; code_completion_force_item_center = -1; queue_redraw(); } - code_completion_pan_offset -= 1.0f; + code_completion_pan_offset = 0; } + accept_event(); + return; } Ref<InputEventMouseButton> mb = p_gui_input; diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 169a5dcb01..0cdb23618f 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -1448,7 +1448,11 @@ void Viewport::_gui_show_tooltip() { &tooltip_owner); gui.tooltip_text = gui.tooltip_text.strip_edges(); - if (gui.tooltip_text.is_empty()) { + // Controls can implement `make_custom_tooltip` to provide their own tooltip. + // This should be a Control node which will be added as child to a TooltipPanel. + Control *base_tooltip = tooltip_owner ? tooltip_owner->make_custom_tooltip(gui.tooltip_text) : nullptr; + + if (gui.tooltip_text.is_empty() && !base_tooltip) { return; // Nothing to show. } @@ -1469,10 +1473,6 @@ void Viewport::_gui_show_tooltip() { // Ensure no opaque background behind the panel as its StyleBox can be partially transparent (e.g. corners). panel->set_transparent_background(true); - // Controls can implement `make_custom_tooltip` to provide their own tooltip. - // This should be a Control node which will be added as child to a TooltipPanel. - Control *base_tooltip = tooltip_owner->make_custom_tooltip(gui.tooltip_text); - // If no custom tooltip is given, use a default implementation. if (!base_tooltip) { gui.tooltip_label = memnew(Label); diff --git a/scene/resources/immediate_mesh.cpp b/scene/resources/immediate_mesh.cpp index 907c0ab4ca..072542f0ad 100644 --- a/scene/resources/immediate_mesh.cpp +++ b/scene/resources/immediate_mesh.cpp @@ -312,6 +312,8 @@ void ImmediateMesh::surface_end() { uses_uv2s = false; surface_active = false; + + emit_changed(); } void ImmediateMesh::clear_surfaces() { diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp index 9df009ec28..ecc1982aa5 100644 --- a/scene/resources/material.cpp +++ b/scene/resources/material.cpp @@ -382,14 +382,11 @@ void ShaderMaterial::_get_property_list(List<PropertyInfo> *p_list) const { bool ShaderMaterial::_property_can_revert(const StringName &p_name) const { if (shader.is_valid()) { - const StringName *pr = remap_cache.getptr(p_name); - if (pr) { - Variant default_value = RenderingServer::get_singleton()->shader_get_parameter_default(shader->get_rid(), *pr); - Variant current_value = get_shader_parameter(*pr); - return default_value.get_type() != Variant::NIL && default_value != current_value; - } else if (p_name == "render_priority" || p_name == "next_pass") { + if (remap_cache.has(p_name)) { return true; } + const String sname = p_name; + return sname == "render_priority" || sname == "next_pass"; } return false; } |