diff options
Diffstat (limited to 'scene')
42 files changed, 607 insertions, 274 deletions
diff --git a/scene/2d/animated_sprite_2d.cpp b/scene/2d/animated_sprite_2d.cpp index d3783aadd1..4734f97e23 100644 --- a/scene/2d/animated_sprite_2d.cpp +++ b/scene/2d/animated_sprite_2d.cpp @@ -443,6 +443,17 @@ TypedArray<String> AnimatedSprite2D::get_configuration_warnings() const { return warnings; } +void AnimatedSprite2D::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const { + if (p_idx == 0 && p_function == "play" && frames.is_valid()) { + List<StringName> al; + frames->get_animation_list(&al); + for (const StringName &name : al) { + r_options->push_back(String(name).quote()); + } + } + Node::get_argument_options(p_function, p_idx, r_options); +} + void AnimatedSprite2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_sprite_frames", "sprite_frames"), &AnimatedSprite2D::set_sprite_frames); ClassDB::bind_method(D_METHOD("get_sprite_frames"), &AnimatedSprite2D::get_sprite_frames); diff --git a/scene/2d/animated_sprite_2d.h b/scene/2d/animated_sprite_2d.h index b3af931ea2..3a41f810dc 100644 --- a/scene/2d/animated_sprite_2d.h +++ b/scene/2d/animated_sprite_2d.h @@ -109,6 +109,8 @@ public: bool is_flipped_v() const; TypedArray<String> get_configuration_warnings() const override; + virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override; + AnimatedSprite2D(); }; diff --git a/scene/2d/camera_2d.cpp b/scene/2d/camera_2d.cpp index efde8d8a2b..f61dbc071d 100644 --- a/scene/2d/camera_2d.cpp +++ b/scene/2d/camera_2d.cpp @@ -57,7 +57,7 @@ void Camera2D::_update_scroll() { Size2 screen_size = _get_camera_screen_size(); Point2 screen_offset = (anchor_mode == ANCHOR_MODE_DRAG_CENTER ? (screen_size * 0.5) : Point2()); - get_tree()->call_group_flags(SceneTree::GROUP_CALL_REALTIME, group_name, "_camera_moved", xform, screen_offset); + get_tree()->call_group(group_name, "_camera_moved", xform, screen_offset); }; } @@ -421,7 +421,7 @@ bool Camera2D::is_current() const { void Camera2D::make_current() { if (is_inside_tree()) { - get_tree()->call_group_flags(SceneTree::GROUP_CALL_REALTIME, group_name, "_make_current", this); + get_tree()->call_group(group_name, "_make_current", this); } else { current = true; } @@ -430,7 +430,7 @@ void Camera2D::make_current() { void Camera2D::clear_current() { if (is_inside_tree()) { - get_tree()->call_group_flags(SceneTree::GROUP_CALL_REALTIME, group_name, "_make_current", (Object *)nullptr); + get_tree()->call_group(group_name, "_make_current", (Object *)nullptr); } else { current = false; } diff --git a/scene/2d/collision_polygon_2d.cpp b/scene/2d/collision_polygon_2d.cpp index c8986e3c94..20840f5aea 100644 --- a/scene/2d/collision_polygon_2d.cpp +++ b/scene/2d/collision_polygon_2d.cpp @@ -32,6 +32,7 @@ #include "collision_object_2d.h" #include "core/math/geometry_2d.h" +#include "scene/2d/area_2d.h" #include "scene/resources/concave_polygon_shape_2d.h" #include "scene/resources/convex_polygon_shape_2d.h" @@ -254,6 +255,9 @@ TypedArray<String> CollisionPolygon2D::get_configuration_warnings() const { warnings.push_back(RTR("Invalid polygon. At least 2 points are needed in 'Segments' build mode.")); } } + if (one_way_collision && Object::cast_to<Area2D>(get_parent())) { + warnings.push_back(RTR("The One Way Collision property will be ignored when the parent is an Area2D.")); + } return warnings; } @@ -276,6 +280,7 @@ void CollisionPolygon2D::set_one_way_collision(bool p_enable) { if (parent) { parent->shape_owner_set_one_way_collision(owner_id, p_enable); } + update_configuration_warnings(); } bool CollisionPolygon2D::is_one_way_collision_enabled() const { diff --git a/scene/2d/collision_shape_2d.cpp b/scene/2d/collision_shape_2d.cpp index dd47ae6cb5..5a25b1705a 100644 --- a/scene/2d/collision_shape_2d.cpp +++ b/scene/2d/collision_shape_2d.cpp @@ -31,6 +31,7 @@ #include "collision_shape_2d.h" #include "collision_object_2d.h" +#include "scene/2d/area_2d.h" #include "scene/resources/concave_polygon_shape_2d.h" #include "scene/resources/convex_polygon_shape_2d.h" @@ -176,6 +177,9 @@ TypedArray<String> CollisionShape2D::get_configuration_warnings() const { if (!shape.is_valid()) { warnings.push_back(RTR("A shape must be provided for CollisionShape2D to function. Please create a shape resource for it!")); } + if (one_way_collision && Object::cast_to<Area2D>(get_parent())) { + warnings.push_back(RTR("The One Way Collision property will be ignored when the parent is an Area2D.")); + } Ref<ConvexPolygonShape2D> convex = shape; Ref<ConcavePolygonShape2D> concave = shape; @@ -204,6 +208,7 @@ void CollisionShape2D::set_one_way_collision(bool p_enable) { if (parent) { parent->shape_owner_set_one_way_collision(owner_id, p_enable); } + update_configuration_warnings(); } bool CollisionShape2D::is_one_way_collision_enabled() const { diff --git a/scene/3d/camera_3d.cpp b/scene/3d/camera_3d.cpp index 4f05e80377..4c53776bba 100644 --- a/scene/3d/camera_3d.cpp +++ b/scene/3d/camera_3d.cpp @@ -217,8 +217,6 @@ void Camera3D::make_current() { } get_viewport()->_camera_3d_set(this); - - //get_scene()->call_group(SceneMainLoop::GROUP_CALL_REALTIME,camera_group,"_camera_make_current",this); } void Camera3D::clear_current(bool p_enable_next) { diff --git a/scene/3d/lightmap_gi.cpp b/scene/3d/lightmap_gi.cpp index 88d2c1ad69..5c63bdcf1d 100644 --- a/scene/3d/lightmap_gi.cpp +++ b/scene/3d/lightmap_gi.cpp @@ -318,12 +318,11 @@ void LightmapGI::_find_meshes_and_lights(Node *p_at_node, Vector<MeshesFound> &m int LightmapGI::_bsp_get_simplex_side(const Vector<Vector3> &p_points, const LocalVector<BSPSimplex> &p_simplices, const Plane &p_plane, uint32_t p_simplex) const { int over = 0; int under = 0; - int coplanar = 0; const BSPSimplex &s = p_simplices[p_simplex]; for (int i = 0; i < 4; i++) { const Vector3 v = p_points[s.vertices[i]]; - if (p_plane.has_point(v)) { //coplanar - coplanar++; + if (p_plane.has_point(v)) { + // Coplanar. } else if (p_plane.is_point_over(v)) { over++; } else { diff --git a/scene/3d/navigation_region_3d.cpp b/scene/3d/navigation_region_3d.cpp index 7f6ecbebb7..80be770dfc 100644 --- a/scene/3d/navigation_region_3d.cpp +++ b/scene/3d/navigation_region_3d.cpp @@ -165,13 +165,17 @@ void _bake_navigation_mesh(void *p_user_data) { } } -void NavigationRegion3D::bake_navigation_mesh() { +void NavigationRegion3D::bake_navigation_mesh(bool p_on_thread) { ERR_FAIL_COND_MSG(bake_thread.is_started(), "Unable to start another bake request. The navigation mesh bake thread is already baking a navigation mesh."); BakeThreadsArgs *args = memnew(BakeThreadsArgs); args->nav_region = this; - bake_thread.start(_bake_navigation_mesh, args); + if (p_on_thread) { + bake_thread.start(_bake_navigation_mesh, args); + } else { + _bake_navigation_mesh(args); + } } void NavigationRegion3D::_bake_finished(Ref<NavigationMesh> p_nav_mesh) { @@ -204,7 +208,7 @@ void NavigationRegion3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_region_rid"), &NavigationRegion3D::get_region_rid); - ClassDB::bind_method(D_METHOD("bake_navigation_mesh"), &NavigationRegion3D::bake_navigation_mesh); + ClassDB::bind_method(D_METHOD("bake_navigation_mesh", "on_thread"), &NavigationRegion3D::bake_navigation_mesh, DEFVAL(true)); ClassDB::bind_method(D_METHOD("_bake_finished", "nav_mesh"), &NavigationRegion3D::_bake_finished); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "navmesh", PROPERTY_HINT_RESOURCE_TYPE, "NavigationMesh"), "set_navigation_mesh", "get_navigation_mesh"); diff --git a/scene/3d/navigation_region_3d.h b/scene/3d/navigation_region_3d.h index 1c559bc31a..140dfebf6a 100644 --- a/scene/3d/navigation_region_3d.h +++ b/scene/3d/navigation_region_3d.h @@ -62,9 +62,9 @@ public: void set_navigation_mesh(const Ref<NavigationMesh> &p_navmesh); Ref<NavigationMesh> get_navigation_mesh() const; - /// Bakes the navigation mesh in a dedicated thread; once done, automatically + /// Bakes the navigation mesh; once done, automatically /// sets the new navigation mesh and emits a signal - void bake_navigation_mesh(); + void bake_navigation_mesh(bool p_on_thread); void _bake_finished(Ref<NavigationMesh> p_nav_mesh); TypedArray<String> get_configuration_warnings() const override; diff --git a/scene/3d/sprite_3d.cpp b/scene/3d/sprite_3d.cpp index 223da13b71..8cb5081047 100644 --- a/scene/3d/sprite_3d.cpp +++ b/scene/3d/sprite_3d.cpp @@ -1247,6 +1247,17 @@ TypedArray<String> AnimatedSprite3D::get_configuration_warnings() const { return warnings; } +void AnimatedSprite3D::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const { + if (p_idx == 0 && p_function == "play" && frames.is_valid()) { + List<StringName> al; + frames->get_animation_list(&al); + for (const StringName &name : al) { + r_options->push_back(String(name).quote()); + } + } + Node::get_argument_options(p_function, p_idx, r_options); +} + void AnimatedSprite3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_sprite_frames", "sprite_frames"), &AnimatedSprite3D::set_sprite_frames); ClassDB::bind_method(D_METHOD("get_sprite_frames"), &AnimatedSprite3D::get_sprite_frames); diff --git a/scene/3d/sprite_3d.h b/scene/3d/sprite_3d.h index 028720a783..6ac85a7bbc 100644 --- a/scene/3d/sprite_3d.h +++ b/scene/3d/sprite_3d.h @@ -248,6 +248,8 @@ public: virtual Rect2 get_item_rect() const override; virtual TypedArray<String> get_configuration_warnings() const override; + virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override; + AnimatedSprite3D(); }; diff --git a/scene/3d/voxelizer.cpp b/scene/3d/voxelizer.cpp index d6ac5ccf30..42a2a68e2d 100644 --- a/scene/3d/voxelizer.cpp +++ b/scene/3d/voxelizer.cpp @@ -592,7 +592,6 @@ void Voxelizer::_fixup_plot(int p_idx, int p_level) { bake_cells.write[p_idx].albedo[2] = 0; float alpha_average = 0; - int children_found = 0; for (int i = 0; i < 8; i++) { uint32_t child = bake_cells[p_idx].children[i]; @@ -603,8 +602,6 @@ void Voxelizer::_fixup_plot(int p_idx, int p_level) { _fixup_plot(child, p_level + 1); alpha_average += bake_cells[child].alpha; - - children_found++; } bake_cells.write[p_idx].alpha = alpha_average / 8.0; diff --git a/scene/3d/world_environment.cpp b/scene/3d/world_environment.cpp index f638644628..fe9d9ae4dd 100644 --- a/scene/3d/world_environment.cpp +++ b/scene/3d/world_environment.cpp @@ -71,7 +71,7 @@ void WorldEnvironment::_update_current_environment() { } else { get_viewport()->find_world_3d()->set_environment(Ref<Environment>()); } - get_tree()->call_group("_world_environment_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id()), "update_configuration_warnings"); + get_tree()->call_group_flags(SceneTree::GROUP_CALL_DEFERRED, "_world_environment_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id()), "update_configuration_warnings"); } void WorldEnvironment::_update_current_camera_effects() { @@ -82,7 +82,7 @@ void WorldEnvironment::_update_current_camera_effects() { get_viewport()->find_world_3d()->set_camera_effects(Ref<CameraEffects>()); } - get_tree()->call_group("_world_camera_effects_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id()), "update_configuration_warnings"); + get_tree()->call_group_flags(SceneTree::GROUP_CALL_DEFERRED, "_world_camera_effects_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id()), "update_configuration_warnings"); } void WorldEnvironment::set_environment(const Ref<Environment> &p_environment) { diff --git a/scene/animation/tween.cpp b/scene/animation/tween.cpp index a52902f8c6..5457da472f 100644 --- a/scene/animation/tween.cpp +++ b/scene/animation/tween.cpp @@ -335,7 +335,7 @@ bool Tween::can_process(bool p_tree_paused) const { if (is_bound && pause_mode == TWEEN_PAUSE_BOUND) { Node *bound_node = get_bound_node(); if (bound_node) { - return bound_node->can_process(); + return bound_node->is_inside_tree() && bound_node->can_process(); } } diff --git a/scene/debugger/scene_debugger.cpp b/scene/debugger/scene_debugger.cpp index 77c1c20499..17b573b776 100644 --- a/scene/debugger/scene_debugger.cpp +++ b/scene/debugger/scene_debugger.cpp @@ -100,7 +100,7 @@ public: } } - void tick(double p_frame_time, double p_idle_time, double p_physics_time, double p_physics_frame_time) { + void tick(double p_frame_time, double p_process_time, double p_physics_time, double p_physics_frame_time) { uint64_t pt = OS::get_singleton()->get_ticks_msec(); if (pt - last_profile_time > 100) { last_profile_time = pt; diff --git a/scene/gui/button.cpp b/scene/gui/button.cpp index b7c1e674dd..ff194f979d 100644 --- a/scene/gui/button.cpp +++ b/scene/gui/button.cpp @@ -258,7 +258,8 @@ void Button::_notification(int p_what) { if (expand_icon) { Size2 _size = get_size() - style->get_offset() * 2; - _size.width -= get_theme_constant(SNAME("h_separation")) + icon_ofs_region; + int icon_text_separation = text.is_empty() ? 0 : get_theme_constant(SNAME("h_separation")); + _size.width -= icon_text_separation + icon_ofs_region; if (!clip_text && icon_align_rtl_checked != HORIZONTAL_ALIGNMENT_CENTER) { _size.width -= text_buf->get_size().width; } diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index 4f2f853be9..54fa726260 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -1501,14 +1501,15 @@ void Control::_set_layout_mode(LayoutMode p_mode) { bool list_changed = false; if (p_mode == LayoutMode::LAYOUT_MODE_POSITION || p_mode == LayoutMode::LAYOUT_MODE_ANCHORS) { - if (has_meta("_edit_layout_mode") && (int)get_meta("_edit_layout_mode") != (int)p_mode) { + if ((int)get_meta("_edit_layout_mode", p_mode) != (int)p_mode) { list_changed = true; } set_meta("_edit_layout_mode", (int)p_mode); if (p_mode == LayoutMode::LAYOUT_MODE_POSITION) { - set_meta("_edit_use_custom_anchors", false); + remove_meta("_edit_layout_mode"); + remove_meta("_edit_use_custom_anchors"); set_anchors_and_offsets_preset(LayoutPreset::PRESET_TOP_LEFT, LayoutPresetMode::PRESET_MODE_KEEP_SIZE); set_grow_direction_preset(LayoutPreset::PRESET_TOP_LEFT); } @@ -1589,22 +1590,22 @@ void Control::set_anchor_and_offset(Side p_side, real_t p_anchor, real_t p_pos, void Control::_set_anchors_layout_preset(int p_preset) { bool list_changed = false; - if (has_meta("_edit_layout_mode") && (int)get_meta("_edit_layout_mode") != (int)LayoutMode::LAYOUT_MODE_ANCHORS) { + if (get_meta("_edit_layout_mode", LayoutMode::LAYOUT_MODE_ANCHORS).operator int() != LayoutMode::LAYOUT_MODE_ANCHORS) { list_changed = true; - set_meta("_edit_layout_mode", (int)LayoutMode::LAYOUT_MODE_ANCHORS); + set_meta("_edit_layout_mode", LayoutMode::LAYOUT_MODE_ANCHORS); } if (p_preset == -1) { - if (!has_meta("_edit_use_custom_anchors") || !(bool)get_meta("_edit_use_custom_anchors")) { + if (!get_meta("_edit_use_custom_anchors", false)) { set_meta("_edit_use_custom_anchors", true); notify_property_list_changed(); } return; // Keep settings as is. } - if (!has_meta("_edit_use_custom_anchors") || (bool)get_meta("_edit_use_custom_anchors")) { + if (get_meta("_edit_use_custom_anchors", true)) { list_changed = true; - set_meta("_edit_use_custom_anchors", false); + remove_meta("_edit_use_custom_anchors"); } LayoutPreset preset = (LayoutPreset)p_preset; @@ -1645,7 +1646,7 @@ void Control::_set_anchors_layout_preset(int p_preset) { int Control::_get_anchors_layout_preset() const { // If the custom preset was selected by user, use it. - if (has_meta("_edit_use_custom_anchors") && (bool)get_meta("_edit_use_custom_anchors")) { + if ((bool)get_meta("_edit_use_custom_anchors", false)) { return -1; } diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp index 05a5ac75d1..8b22f3722a 100644 --- a/scene/gui/item_list.cpp +++ b/scene/gui/item_list.cpp @@ -606,7 +606,7 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) { return; } - if (mb.is_valid() && (mb->get_button_index() == MouseButton::LEFT || (allow_rmb_select && mb->get_button_index() == MouseButton::RIGHT)) && mb->is_pressed()) { + if (mb.is_valid() && mb->is_pressed()) { search_string = ""; //any mousepress cancels Vector2 pos = mb->get_position(); Ref<StyleBox> bg = get_theme_stylebox(SNAME("bg")); @@ -631,7 +631,7 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) { } } - if (closest != -1) { + if (closest != -1 && (mb->get_button_index() == MouseButton::LEFT || (allow_rmb_select && mb->get_button_index() == MouseButton::RIGHT))) { int i = closest; if (select_mode == SELECT_MULTI && items[i].selected && mb->is_command_pressed()) { @@ -654,59 +654,38 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) { emit_signal(SNAME("multi_selected"), j, true); } } + emit_signal(SNAME("item_clicked"), i, get_local_mouse_position(), mb->get_button_index()); - if (mb->get_button_index() == MouseButton::RIGHT) { - if (!CAN_SELECT(i)) { - return; - } - emit_signal(SNAME("item_rmb_selected"), i, get_local_mouse_position()); - } } else { if (!mb->is_double_click() && !mb->is_command_pressed() && select_mode == SELECT_MULTI && items[i].selectable && !items[i].disabled && items[i].selected && mb->get_button_index() == MouseButton::LEFT) { defer_select_single = i; return; } - if (items[i].selected && mb->get_button_index() == MouseButton::RIGHT) { - if (!CAN_SELECT(i)) { - return; - } - emit_signal(SNAME("item_rmb_selected"), i, get_local_mouse_position()); - } else { - if (!CAN_SELECT(i)) { - return; - } - - bool selected = items[i].selected; - + if (!items[i].selected || allow_reselect) { select(i, select_mode == SELECT_SINGLE || !mb->is_command_pressed()); - if (!selected || allow_reselect) { - if (select_mode == SELECT_SINGLE) { - emit_signal(SNAME("item_selected"), i); - } else { - emit_signal(SNAME("multi_selected"), i, true); - } + if (select_mode == SELECT_SINGLE) { + emit_signal(SNAME("item_selected"), i); + } else { + emit_signal(SNAME("multi_selected"), i, true); } + } - if (mb->get_button_index() == MouseButton::RIGHT) { - emit_signal(SNAME("item_rmb_selected"), i, get_local_mouse_position()); - } else if (/*select_mode==SELECT_SINGLE &&*/ mb->is_double_click()) { - emit_signal(SNAME("item_activated"), i); - } + emit_signal(SNAME("item_clicked"), i, get_local_mouse_position(), mb->get_button_index()); + + if (mb->get_button_index() == MouseButton::LEFT && mb->is_double_click()) { + emit_signal(SNAME("item_activated"), i); } } return; + } else if (closest != -1) { + emit_signal(SNAME("item_clicked"), closest, get_local_mouse_position(), mb->get_button_index()); + } else { + // Since closest is null, more likely we clicked on empty space, so send signal to interested controls. Allows, for example, implement items deselecting. + emit_signal(SNAME("empty_clicked"), get_local_mouse_position(), mb->get_button_index()); } - if (mb->get_button_index() == MouseButton::RIGHT) { - emit_signal(SNAME("rmb_clicked"), mb->get_position()); - - return; - } - - // Since closest is null, more likely we clicked on empty space, so send signal to interested controls. Allows, for example, implement items deselecting. - emit_signal(SNAME("nothing_selected")); } if (mb.is_valid() && mb->get_button_index() == MouseButton::WHEEL_UP && mb->is_pressed()) { scroll_bar->set_value(scroll_bar->get_value() - scroll_bar->get_page() * mb->get_factor() / 8); @@ -1797,11 +1776,10 @@ void ItemList::_bind_methods() { BIND_ENUM_CONSTANT(SELECT_MULTI); ADD_SIGNAL(MethodInfo("item_selected", PropertyInfo(Variant::INT, "index"))); - ADD_SIGNAL(MethodInfo("item_rmb_selected", PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::VECTOR2, "at_position"))); + ADD_SIGNAL(MethodInfo("empty_clicked", PropertyInfo(Variant::VECTOR2, "at_position"), PropertyInfo(Variant::INT, "mouse_button_index"))); + ADD_SIGNAL(MethodInfo("item_clicked", PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::VECTOR2, "at_position"), PropertyInfo(Variant::INT, "mouse_button_index"))); ADD_SIGNAL(MethodInfo("multi_selected", PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::BOOL, "selected"))); ADD_SIGNAL(MethodInfo("item_activated", PropertyInfo(Variant::INT, "index"))); - ADD_SIGNAL(MethodInfo("rmb_clicked", PropertyInfo(Variant::VECTOR2, "at_position"))); - ADD_SIGNAL(MethodInfo("nothing_selected")); GLOBAL_DEF("gui/timers/incremental_search_max_interval_msec", 2000); ProjectSettings::get_singleton()->set_custom_property_info("gui/timers/incremental_search_max_interval_msec", PropertyInfo(Variant::INT, "gui/timers/incremental_search_max_interval_msec", PROPERTY_HINT_RANGE, "0,10000,1,or_greater")); // No negative numbers diff --git a/scene/gui/progress_bar.cpp b/scene/gui/progress_bar.cpp index 50ffb3ca67..f36682942f 100644 --- a/scene/gui/progress_bar.cpp +++ b/scene/gui/progress_bar.cpp @@ -62,15 +62,42 @@ void ProgressBar::_notification(int p_what) { Color font_color = get_theme_color(SNAME("font_color")); draw_style_box(bg, Rect2(Point2(), get_size())); + float r = get_as_ratio(); - int mp = fg->get_minimum_size().width; - int p = r * (get_size().width - mp); - if (p > 0) { - if (is_layout_rtl()) { - draw_style_box(fg, Rect2(Point2(p, 0), Size2(fg->get_minimum_size().width, get_size().height))); - } else { - draw_style_box(fg, Rect2(Point2(0, 0), Size2(p + fg->get_minimum_size().width, get_size().height))); - } + + switch (mode) { + case FILL_BEGIN_TO_END: + case FILL_END_TO_BEGIN: { + int mp = fg->get_minimum_size().width; + int p = round(r * (get_size().width - mp)); + // We want FILL_BEGIN_TO_END to map to right to left when UI layout is RTL, + // and left to right otherwise. And likewise for FILL_END_TO_BEGIN. + bool right_to_left = is_layout_rtl() ? (mode == FILL_BEGIN_TO_END) : (mode == FILL_END_TO_BEGIN); + if (p > 0) { + if (right_to_left) { + int p_remaining = round((1.0 - r) * (get_size().width - mp)); + draw_style_box(fg, Rect2(Point2(p_remaining, 0), Size2(p + fg->get_minimum_size().width, get_size().height))); + } else { + draw_style_box(fg, Rect2(Point2(0, 0), Size2(p + fg->get_minimum_size().width, get_size().height))); + } + } + } break; + case FILL_TOP_TO_BOTTOM: + case FILL_BOTTOM_TO_TOP: { + int mp = fg->get_minimum_size().height; + int p = round(r * (get_size().height - mp)); + + if (p > 0) { + if (mode == FILL_TOP_TO_BOTTOM) { + draw_style_box(fg, Rect2(Point2(0, 0), Size2(get_size().width, p + fg->get_minimum_size().height))); + } else { + int p_remaining = round((1.0 - r) * (get_size().height - mp)); + draw_style_box(fg, Rect2(Point2(0, p_remaining), Size2(get_size().width, p + fg->get_minimum_size().height))); + } + } + } break; + case FILL_MODE_MAX: + break; } if (percent_visible) { @@ -88,6 +115,16 @@ void ProgressBar::_notification(int p_what) { } } +void ProgressBar::set_fill_mode(int p_fill) { + ERR_FAIL_INDEX(p_fill, FILL_MODE_MAX); + mode = (FillMode)p_fill; + update(); +} + +int ProgressBar::get_fill_mode() { + return mode; +} + void ProgressBar::set_percent_visible(bool p_visible) { percent_visible = p_visible; update(); @@ -98,10 +135,18 @@ bool ProgressBar::is_percent_visible() const { } void ProgressBar::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_fill_mode", "mode"), &ProgressBar::set_fill_mode); + ClassDB::bind_method(D_METHOD("get_fill_mode"), &ProgressBar::get_fill_mode); ClassDB::bind_method(D_METHOD("set_percent_visible", "visible"), &ProgressBar::set_percent_visible); ClassDB::bind_method(D_METHOD("is_percent_visible"), &ProgressBar::is_percent_visible); + ADD_PROPERTY(PropertyInfo(Variant::INT, "fill_mode", PROPERTY_HINT_ENUM, "Begin to End,End to Begin,Top to Bottom,Bottom to Top"), "set_fill_mode", "get_fill_mode"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "percent_visible"), "set_percent_visible", "is_percent_visible"); + + BIND_ENUM_CONSTANT(FILL_BEGIN_TO_END); + BIND_ENUM_CONSTANT(FILL_END_TO_BEGIN); + BIND_ENUM_CONSTANT(FILL_TOP_TO_BOTTOM); + BIND_ENUM_CONSTANT(FILL_BOTTOM_TO_TOP); } ProgressBar::ProgressBar() { diff --git a/scene/gui/progress_bar.h b/scene/gui/progress_bar.h index 2d89163f78..5ba21ad7d5 100644 --- a/scene/gui/progress_bar.h +++ b/scene/gui/progress_bar.h @@ -43,11 +43,27 @@ protected: static void _bind_methods(); public: + enum FillMode { + FILL_BEGIN_TO_END, + FILL_END_TO_BEGIN, + FILL_TOP_TO_BOTTOM, + FILL_BOTTOM_TO_TOP, + FILL_MODE_MAX + }; + + void set_fill_mode(int p_fill); + int get_fill_mode(); + void set_percent_visible(bool p_visible); bool is_percent_visible() const; Size2 get_minimum_size() const override; ProgressBar(); + +private: + FillMode mode = FILL_BEGIN_TO_END; }; +VARIANT_ENUM_CAST(ProgressBar::FillMode); + #endif // PROGRESS_BAR_H diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp index 3a3a84b481..8299d73b68 100644 --- a/scene/gui/tab_container.cpp +++ b/scene/gui/tab_container.cpp @@ -523,7 +523,7 @@ void TabContainer::move_child_notify(Node *p_child) { Control *c = Object::cast_to<Control>(p_child); if (c && !c->is_set_as_top_level()) { int old_idx = -1; - String tab_name = c->has_meta("_tab_name") ? String(c->get_meta("_tab_name")) : String(c->get_name()); + String tab_name = String(c->get_meta("_tab_name", c->get_name())); // Find the previous tab index of the control. for (int i = 0; i < get_tab_count(); i++) { @@ -556,9 +556,7 @@ void TabContainer::remove_child_notify(Node *p_child) { update(); } - if (p_child->has_meta("_tab_name")) { - p_child->remove_meta("_tab_name"); - } + p_child->remove_meta("_tab_name"); p_child->disconnect("renamed", callable_mp(this, &TabContainer::_refresh_tab_names)); // TabBar won't emit the "tab_changed" signal when not inside the tree. @@ -679,9 +677,7 @@ void TabContainer::set_tab_title(int p_tab, const String &p_title) { tab_bar->set_tab_title(p_tab, p_title); if (p_title == child->get_name()) { - if (child->has_meta("_tab_name")) { - child->remove_meta("_tab_name"); - } + child->remove_meta("_tab_name"); } else { child->set_meta("_tab_name", p_title); } diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 1a439a5c1d..d7a07454de 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -2388,7 +2388,7 @@ void TextEdit::_move_caret_page_down(bool p_select) { } void TextEdit::_do_backspace(bool p_word, bool p_all_to_left) { - if (!editable || (caret.column == 0 && caret.line == 0)) { + if (!editable || (caret.column == 0 && caret.line == 0 && !has_selection())) { return; } diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index 89807dbe95..0ca9a66e08 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -1754,19 +1754,16 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 for (int j = p_item->cells[i].buttons.size() - 1; j >= 0; j--) { Ref<Texture2D> b = p_item->cells[i].buttons[j].texture; Size2 s = b->get_size() + cache.button_pressed->get_minimum_size(); - if (s.height < label_h) { - s.height = label_h; - } Point2i o = Point2i(ofs + w - s.width, p_pos.y) - cache.offset + p_draw_ofs; if (cache.click_type == Cache::CLICK_BUTTON && cache.click_item == p_item && cache.click_column == i && cache.click_index == j && !p_item->cells[i].buttons[j].disabled) { - //being pressed + // Being pressed. Point2 od = o; if (rtl) { od.x = get_size().width - od.x - s.x; } - cache.button_pressed->draw(get_canvas_item(), Rect2(od, s)); + cache.button_pressed->draw(get_canvas_item(), Rect2(od.x, od.y, s.width, MAX(s.height, label_h))); } o.y += (label_h - s.height) / 2; diff --git a/scene/main/missing_node.cpp b/scene/main/missing_node.cpp new file mode 100644 index 0000000000..6daa9dec6b --- /dev/null +++ b/scene/main/missing_node.cpp @@ -0,0 +1,98 @@ +/*************************************************************************/ +/* missing_node.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "missing_node.h" + +bool MissingNode::_set(const StringName &p_name, const Variant &p_value) { + if (is_recording_properties()) { + properties.insert(p_name, p_value); + return true; //always valid to set (add) + } else { + if (!properties.has(p_name)) { + return false; + } + + properties[p_name] = p_value; + return true; + } +} + +bool MissingNode::_get(const StringName &p_name, Variant &r_ret) const { + if (!properties.has(p_name)) { + return false; + } + r_ret = properties[p_name]; + return true; +} + +void MissingNode::_get_property_list(List<PropertyInfo> *p_list) const { + for (OrderedHashMap<StringName, Variant>::ConstElement E = properties.front(); E; E = E.next()) { + p_list->push_back(PropertyInfo(E.value().get_type(), E.key())); + } +} + +void MissingNode::set_original_class(const String &p_class) { + original_class = p_class; +} + +String MissingNode::get_original_class() const { + return original_class; +} + +void MissingNode::set_recording_properties(bool p_enable) { + recording_properties = p_enable; +} + +bool MissingNode::is_recording_properties() const { + return recording_properties; +} + +TypedArray<String> MissingNode::get_configuration_warnings() const { + // The mere existence of this node is warning. + TypedArray<String> ret; + ret.push_back(vformat(RTR("This node was saved as class type '%s', which was no longer available when this scene was loaded."), original_class)); + ret.push_back(RTR("Data from the original node is kept as a placeholder until this type of node is available again. It can hence be safely re-saved without risk of data loss.")); + return ret; +} + +void MissingNode::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_original_class", "name"), &MissingNode::set_original_class); + ClassDB::bind_method(D_METHOD("get_original_class"), &MissingNode::get_original_class); + + ClassDB::bind_method(D_METHOD("set_recording_properties", "enable"), &MissingNode::set_recording_properties); + ClassDB::bind_method(D_METHOD("is_recording_properties"), &MissingNode::is_recording_properties); + + // Expose, but not save. + ADD_PROPERTY(PropertyInfo(Variant::STRING, "original_class", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_original_class", "get_original_class"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "recording_properties", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_recording_properties", "is_recording_properties"); +} + +MissingNode::MissingNode() { +} diff --git a/scene/main/missing_node.h b/scene/main/missing_node.h new file mode 100644 index 0000000000..b0f9492456 --- /dev/null +++ b/scene/main/missing_node.h @@ -0,0 +1,63 @@ +/*************************************************************************/ +/* missing_node.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef MISSING_NODE_H +#define MISSING_NODE_H + +#include "core/io/missing_resource.h" +#include "scene/main/node.h" + +class MissingNode : public Node { + GDCLASS(MissingNode, Node) + OrderedHashMap<StringName, Variant> properties; + + String original_class; + bool recording_properties = false; + +protected: + bool _set(const StringName &p_name, const Variant &p_value); + bool _get(const StringName &p_name, Variant &r_ret) const; + void _get_property_list(List<PropertyInfo> *p_list) const; + + static void _bind_methods(); + +public: + void set_original_class(const String &p_class); + String get_original_class() const; + + void set_recording_properties(bool p_enable); + bool is_recording_properties() const; + + virtual TypedArray<String> get_configuration_warnings() const override; + + MissingNode(); +}; + +#endif // MISSING_NODE_H diff --git a/scene/main/node.cpp b/scene/main/node.cpp index f549b3dde2..8961b5ba54 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -2012,35 +2012,28 @@ Node *Node::get_deepest_editable_node(Node *p_start_node) const { #ifdef TOOLS_ENABLED void Node::set_property_pinned(const String &p_property, bool p_pinned) { bool current_pinned = false; - bool has_pinned = has_meta("_edit_pinned_properties_"); - Array pinned; - String psa = get_property_store_alias(p_property); - if (has_pinned) { - pinned = get_meta("_edit_pinned_properties_"); - current_pinned = pinned.has(psa); - } + Array pinned = get_meta("_edit_pinned_properties_", Array()); + StringName psa = get_property_store_alias(p_property); + current_pinned = pinned.has(psa); if (current_pinned != p_pinned) { if (p_pinned) { pinned.append(psa); - if (!has_pinned) { - set_meta("_edit_pinned_properties_", pinned); - } } else { pinned.erase(psa); - if (pinned.is_empty()) { - remove_meta("_edit_pinned_properties_"); - } } } + + if (pinned.is_empty()) { + remove_meta("_edit_pinned_properties_"); + } else { + set_meta("_edit_pinned_properties_", pinned); + } } bool Node::is_property_pinned(const StringName &p_property) const { - if (!has_meta("_edit_pinned_properties_")) { - return false; - } - Array pinned = get_meta("_edit_pinned_properties_"); - String psa = get_property_store_alias(p_property); + Array pinned = get_meta("_edit_pinned_properties_", Array()); + StringName psa = get_property_store_alias(p_property); return pinned.has(psa); } diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp index a151d3cb33..9d80b3cc0f 100644 --- a/scene/main/scene_tree.cpp +++ b/scene/main/scene_tree.cpp @@ -181,7 +181,7 @@ void SceneTree::_flush_ugc() { argptrs[i] = &E->get()[i]; } - call_group_flagsp(GROUP_CALL_REALTIME, E->key().group, E->key().call, argptrs, E->get().size()); + call_group_flagsp(GROUP_CALL_DEFAULT, E->key().group, E->key().call, argptrs, E->get().size()); unique_group_calls.erase(E); } @@ -220,7 +220,7 @@ void SceneTree::call_group_flagsp(uint32_t p_call_flags, const StringName &p_gro return; } - if (p_call_flags & GROUP_CALL_UNIQUE && !(p_call_flags & GROUP_CALL_REALTIME)) { + if (p_call_flags & GROUP_CALL_UNIQUE && p_call_flags & GROUP_CALL_DEFERRED) { ERR_FAIL_COND(ugc_locked); UGCall ug; @@ -254,7 +254,7 @@ void SceneTree::call_group_flagsp(uint32_t p_call_flags, const StringName &p_gro continue; } - if (p_call_flags & GROUP_CALL_REALTIME) { + if (!(p_call_flags & GROUP_CALL_DEFERRED)) { Callable::CallError ce; nodes[i]->callp(p_function, p_args, p_argcount, ce); } else { @@ -268,7 +268,7 @@ void SceneTree::call_group_flagsp(uint32_t p_call_flags, const StringName &p_gro continue; } - if (p_call_flags & GROUP_CALL_REALTIME) { + if (!(p_call_flags & GROUP_CALL_DEFERRED)) { Callable::CallError ce; nodes[i]->callp(p_function, p_args, p_argcount, ce); } else { @@ -307,7 +307,7 @@ void SceneTree::notify_group_flags(uint32_t p_call_flags, const StringName &p_gr continue; } - if (p_call_flags & GROUP_CALL_REALTIME) { + if (!(p_call_flags & GROUP_CALL_DEFERRED)) { nodes[i]->notification(p_notification); } else { MessageQueue::get_singleton()->push_notification(nodes[i], p_notification); @@ -320,7 +320,7 @@ void SceneTree::notify_group_flags(uint32_t p_call_flags, const StringName &p_gr continue; } - if (p_call_flags & GROUP_CALL_REALTIME) { + if (!(p_call_flags & GROUP_CALL_DEFERRED)) { nodes[i]->notification(p_notification); } else { MessageQueue::get_singleton()->push_notification(nodes[i], p_notification); @@ -358,7 +358,7 @@ void SceneTree::set_group_flags(uint32_t p_call_flags, const StringName &p_group continue; } - if (p_call_flags & GROUP_CALL_REALTIME) { + if (!(p_call_flags & GROUP_CALL_DEFERRED)) { nodes[i]->set(p_name, p_value); } else { MessageQueue::get_singleton()->push_set(nodes[i], p_name, p_value); @@ -371,7 +371,7 @@ void SceneTree::set_group_flags(uint32_t p_call_flags, const StringName &p_group continue; } - if (p_call_flags & GROUP_CALL_REALTIME) { + if (!(p_call_flags & GROUP_CALL_DEFERRED)) { nodes[i]->set(p_name, p_value); } else { MessageQueue::get_singleton()->push_set(nodes[i], p_name, p_value); @@ -390,7 +390,7 @@ void SceneTree::notify_group(const StringName &p_group, int p_notification) { } void SceneTree::set_group(const StringName &p_group, const String &p_name, const Variant &p_value) { - set_group_flags(0, p_group, p_name, p_value); + set_group_flags(GROUP_CALL_DEFAULT, p_group, p_name, p_value); } void SceneTree::initialize() { @@ -413,7 +413,7 @@ bool SceneTree::physics_process(double p_time) { emit_signal(SNAME("physics_frame")); _notify_group_pause(SNAME("physics_process_internal"), Node::NOTIFICATION_INTERNAL_PHYSICS_PROCESS); - call_group_flags(GROUP_CALL_REALTIME, SNAME("_picking_viewports"), SNAME("_process_picking")); + call_group(SNAME("_picking_viewports"), SNAME("_process_picking")); _notify_group_pause(SNAME("physics_process"), Node::NOTIFICATION_PHYSICS_PROCESS); _flush_ugc(); MessageQueue::get_singleton()->flush(); //small little hack @@ -944,7 +944,7 @@ void SceneTree::_call_group(const Variant **p_args, int p_argcount, Callable::Ca StringName group = *p_args[0]; StringName method = *p_args[1]; - call_group_flagsp(0, group, method, p_args + 2, p_argcount - 2); + call_group_flagsp(GROUP_CALL_DEFAULT, group, method, p_args + 2, p_argcount - 2); } int64_t SceneTree::get_frame() const { @@ -1277,7 +1277,7 @@ void SceneTree::_bind_methods() { BIND_ENUM_CONSTANT(GROUP_CALL_DEFAULT); BIND_ENUM_CONSTANT(GROUP_CALL_REVERSE); - BIND_ENUM_CONSTANT(GROUP_CALL_REALTIME); + BIND_ENUM_CONSTANT(GROUP_CALL_DEFERRED); BIND_ENUM_CONSTANT(GROUP_CALL_UNIQUE); } diff --git a/scene/main/scene_tree.h b/scene/main/scene_tree.h index 9d7757e0a3..d633fb38d0 100644 --- a/scene/main/scene_tree.h +++ b/scene/main/scene_tree.h @@ -151,7 +151,6 @@ private: int collision_debug_contacts; void _change_scene(Node *p_to); - //void _call_group(uint32_t p_call_flags,const StringName& p_group,const StringName& p_function,const Variant& p_arg1,const Variant& p_arg2); List<Ref<SceneTreeTimer>> timers; List<Ref<Tween>> tweens; @@ -225,7 +224,7 @@ public: enum GroupCallFlags { GROUP_CALL_DEFAULT = 0, GROUP_CALL_REVERSE = 1, - GROUP_CALL_REALTIME = 2, + GROUP_CALL_DEFERRED = 2, GROUP_CALL_UNIQUE = 4, }; @@ -235,17 +234,20 @@ public: void notify_group_flags(uint32_t p_call_flags, const StringName &p_group, int p_notification); void set_group_flags(uint32_t p_call_flags, const StringName &p_group, const String &p_name, const Variant &p_value); + // `notify_group()` is immediate by default since Godot 4.0. void notify_group(const StringName &p_group, int p_notification); + // `set_group()` is immediate by default since Godot 4.0. void set_group(const StringName &p_group, const String &p_name, const Variant &p_value); template <typename... VarArgs> + // `call_group()` is immediate by default since Godot 4.0. void call_group(const StringName &p_group, const StringName &p_function, VarArgs... p_args) { Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported. const Variant *argptrs[sizeof...(p_args) + 1]; for (uint32_t i = 0; i < sizeof...(p_args); i++) { argptrs[i] = &args[i]; } - call_group_flagsp(0, p_group, p_function, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args)); + call_group_flagsp(GROUP_CALL_DEFAULT, p_group, p_function, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args)); } template <typename... VarArgs> diff --git a/scene/main/shader_globals_override.cpp b/scene/main/shader_globals_override.cpp index 7c689bd436..ed08c45a01 100644 --- a/scene/main/shader_globals_override.cpp +++ b/scene/main/shader_globals_override.cpp @@ -267,7 +267,7 @@ void ShaderGlobalsOverride::_notification(int p_what) { remove_from_group(SceneStringNames::get_singleton()->shader_overrides_group_active); remove_from_group(SceneStringNames::get_singleton()->shader_overrides_group); - get_tree()->call_group(SceneStringNames::get_singleton()->shader_overrides_group, "_activate"); //another may want to activate when this is removed + get_tree()->call_group_flags(SceneTree::GROUP_CALL_DEFERRED, SceneStringNames::get_singleton()->shader_overrides_group, "_activate"); //another may want to activate when this is removed active = false; } break; } diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index d7e58ed707..e4037c2843 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -2186,7 +2186,7 @@ void Viewport::_gui_control_grab_focus(Control *p_control) { // No need for change. return; } - get_tree()->call_group_flags(SceneTree::GROUP_CALL_REALTIME, "_viewports", "_gui_remove_focus_for_window", (Node *)get_base_window()); + get_tree()->call_group("_viewports", "_gui_remove_focus_for_window", (Node *)get_base_window()); gui.key_focus = p_control; emit_signal(SNAME("gui_focus_changed"), p_control); p_control->notification(Control::NOTIFICATION_FOCUS_ENTER); diff --git a/scene/multiplayer/scene_replication_interface.cpp b/scene/multiplayer/scene_replication_interface.cpp index 0764f136e4..2952512462 100644 --- a/scene/multiplayer/scene_replication_interface.cpp +++ b/scene/multiplayer/scene_replication_interface.cpp @@ -309,6 +309,9 @@ Error SceneReplicationInterface::on_despawn_receive(int p_from, const uint8_t *p Error err = rep_state->peer_del_remote(p_from, net_id, &node); ERR_FAIL_COND_V(err != OK, err); ERR_FAIL_COND_V(!node, ERR_BUG); + if (node->get_parent() != nullptr) { + node->get_parent()->remove_child(node); + } node->queue_delete(); return OK; } diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 6c71898414..632952c2cb 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -129,6 +129,7 @@ #include "scene/main/canvas_layer.h" #include "scene/main/http_request.h" #include "scene/main/instance_placeholder.h" +#include "scene/main/missing_node.h" #include "scene/main/resource_preloader.h" #include "scene/main/scene_tree.h" #include "scene/main/timer.h" @@ -302,6 +303,7 @@ void register_scene_types() { GDREGISTER_CLASS(Object); GDREGISTER_CLASS(Node); + GDREGISTER_VIRTUAL_CLASS(MissingNode); GDREGISTER_ABSTRACT_CLASS(InstancePlaceholder); GDREGISTER_ABSTRACT_CLASS(Viewport); @@ -1093,8 +1095,6 @@ void register_scene_types() { SceneReplicationInterface::make_default(); SceneRPCInterface::make_default(); SceneCacheInterface::make_default(); - - NativeExtensionManager::get_singleton()->initialize_extensions(NativeExtension::INITIALIZATION_LEVEL_SCENE); } void initialize_theme() { @@ -1147,8 +1147,6 @@ void initialize_theme() { } void unregister_scene_types() { - NativeExtensionManager::get_singleton()->deinitialize_extensions(NativeExtension::INITIALIZATION_LEVEL_SCENE); - SceneDebugger::deinitialize(); clear_default_theme(); diff --git a/scene/resources/animation_library.cpp b/scene/resources/animation_library.cpp index 5d92c3b0c6..2a581fb126 100644 --- a/scene/resources/animation_library.cpp +++ b/scene/resources/animation_library.cpp @@ -30,15 +30,15 @@ #include "animation_library.h" -bool AnimationLibrary::is_valid_name(const String &p_name) { +bool AnimationLibrary::is_valid_animation_name(const String &p_name) { return !(p_name.is_empty() || p_name.contains("/") || p_name.contains(":") || p_name.contains(",") || p_name.contains("[")); } -String AnimationLibrary::validate_name(const String &p_name) { - if (p_name.is_empty()) { - return "_"; // Should always return a valid name. - } +bool AnimationLibrary::is_valid_library_name(const String &p_name) { + return !(p_name.contains("/") || p_name.contains(":") || p_name.contains(",") || p_name.contains("[")); +} +String AnimationLibrary::validate_library_name(const String &p_name) { String name = p_name; const char *characters = "/:,["; for (const char *p = characters; *p; p++) { @@ -48,7 +48,7 @@ String AnimationLibrary::validate_name(const String &p_name) { } Error AnimationLibrary::add_animation(const StringName &p_name, const Ref<Animation> &p_animation) { - ERR_FAIL_COND_V_MSG(!is_valid_name(p_name), ERR_INVALID_PARAMETER, "Invalid animation name: '" + String(p_name) + "'."); + ERR_FAIL_COND_V_MSG(!is_valid_animation_name(p_name), ERR_INVALID_PARAMETER, "Invalid animation name: '" + String(p_name) + "'."); ERR_FAIL_COND_V(p_animation.is_null(), ERR_INVALID_PARAMETER); if (animations.has(p_name)) { @@ -72,7 +72,7 @@ void AnimationLibrary::remove_animation(const StringName &p_name) { void AnimationLibrary::rename_animation(const StringName &p_name, const StringName &p_new_name) { ERR_FAIL_COND(!animations.has(p_name)); - ERR_FAIL_COND_MSG(!is_valid_name(p_new_name), "Invalid animation name: '" + String(p_new_name) + "'."); + ERR_FAIL_COND_MSG(!is_valid_animation_name(p_new_name), "Invalid animation name: '" + String(p_new_name) + "'."); ERR_FAIL_COND(animations.has(p_new_name)); animations.insert(p_new_name, animations[p_name]); diff --git a/scene/resources/animation_library.h b/scene/resources/animation_library.h index 0f327fb072..21f0162eb3 100644 --- a/scene/resources/animation_library.h +++ b/scene/resources/animation_library.h @@ -49,8 +49,9 @@ protected: static void _bind_methods(); public: - static bool is_valid_name(const String &p_name); - static String validate_name(const String &p_name); + static bool is_valid_animation_name(const String &p_name); + static bool is_valid_library_name(const String &p_name); + static String validate_library_name(const String &p_name); Error add_animation(const StringName &p_name, const Ref<Animation> &p_animation); void remove_animation(const StringName &p_name); diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp index 16fce5e08a..27e1590940 100644 --- a/scene/resources/material.cpp +++ b/scene/resources/material.cpp @@ -84,7 +84,7 @@ void Material::inspect_native_shader_code() { SceneTree *st = Object::cast_to<SceneTree>(OS::get_singleton()->get_main_loop()); RID shader = get_shader_rid(); if (st && shader.is_valid()) { - st->call_group("_native_shader_source_visualizer", "_inspect_shader", shader); + st->call_group_flags(SceneTree::GROUP_CALL_DEFERRED, "_native_shader_source_visualizer", "_inspect_shader", shader); } } diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp index b991cb1abe..eaf26c5225 100644 --- a/scene/resources/packed_scene.cpp +++ b/scene/resources/packed_scene.cpp @@ -33,11 +33,13 @@ #include "core/config/engine.h" #include "core/config/project_settings.h" #include "core/core_string_names.h" +#include "core/io/missing_resource.h" #include "core/io/resource_loader.h" #include "scene/2d/node_2d.h" #include "scene/3d/node_3d.h" #include "scene/gui/control.h" #include "scene/main/instance_placeholder.h" +#include "scene/main/missing_node.h" #include "scene/property_utils.h" #define PACKED_SCENE_VERSION 2 @@ -47,10 +49,7 @@ bool SceneState::can_instantiate() const { } static Array _sanitize_node_pinned_properties(Node *p_node) { - if (!p_node->has_meta("_edit_pinned_properties_")) { - return Array(); - } - Array pinned = p_node->get_meta("_edit_pinned_properties_"); + Array pinned = p_node->get_meta("_edit_pinned_properties_", Array()); if (pinned.is_empty()) { return Array(); } @@ -113,6 +112,7 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { const NodeData &n = nd[i]; Node *parent = nullptr; + String old_parent_path; if (i > 0) { ERR_FAIL_COND_V_MSG(n.parent == -1, nullptr, vformat("Invalid scene: node %s does not specify its parent node.", snames[n.name])); @@ -120,6 +120,8 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { #ifdef DEBUG_ENABLED if (!nparent && (n.parent & FLAG_ID_IS_PATH)) { WARN_PRINT(String("Parent path '" + String(node_paths[n.parent & FLAG_MASK]) + "' for node '" + String(snames[n.name]) + "' has vanished when instancing: '" + get_path() + "'.").ascii().get_data()); + old_parent_path = String(node_paths[n.parent & FLAG_MASK]).trim_prefix("./").replace("/", "@"); + nparent = ret_nodes[0]; } #endif parent = nparent; @@ -130,6 +132,7 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { } Node *node = nullptr; + MissingNode *missing_node = nullptr; if (i == 0 && base_scene_idx >= 0) { //scene inheritance on root node @@ -184,24 +187,33 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { memdelete(obj); obj = nullptr; } - WARN_PRINT(vformat("Node %s of type %s cannot be created. A placeholder will be created instead.", snames[n.name], snames[n.type]).ascii().get_data()); - if (n.parent >= 0 && n.parent < nc && ret_nodes[n.parent]) { - if (Object::cast_to<Control>(ret_nodes[n.parent])) { - obj = memnew(Control); - } else if (Object::cast_to<Node2D>(ret_nodes[n.parent])) { - obj = memnew(Node2D); + + if (ResourceLoader::is_creating_missing_resources_if_class_unavailable_enabled()) { + missing_node = memnew(MissingNode); + missing_node->set_original_class(snames[n.type]); + missing_node->set_recording_properties(true); + node = missing_node; + obj = missing_node; + } else { + WARN_PRINT(vformat("Node %s of type %s cannot be created. A placeholder will be created instead.", snames[n.name], snames[n.type]).ascii().get_data()); + if (n.parent >= 0 && n.parent < nc && ret_nodes[n.parent]) { + if (Object::cast_to<Control>(ret_nodes[n.parent])) { + obj = memnew(Control); + } else if (Object::cast_to<Node2D>(ret_nodes[n.parent])) { + obj = memnew(Node2D); #ifndef _3D_DISABLED - } else if (Object::cast_to<Node3D>(ret_nodes[n.parent])) { - obj = memnew(Node3D); + } else if (Object::cast_to<Node3D>(ret_nodes[n.parent])) { + obj = memnew(Node3D); #endif // _3D_DISABLED + } } - } - if (!obj) { - obj = memnew(Node); - } + if (!obj) { + obj = memnew(Node); + } - node = Object::cast_to<Node>(obj); + node = Object::cast_to<Node>(obj); + } } } @@ -214,6 +226,8 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { if (nprop_count) { const NodeData::Property *nprops = &n.properties[0]; + Dictionary missing_resource_properties; + for (int j = 0; j < nprop_count; j++) { bool valid; ERR_FAIL_INDEX_V(nprops[j].name, sname_count, nullptr); @@ -270,9 +284,24 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { } else if (p_edit_state == GEN_EDIT_STATE_INSTANCE) { value = value.duplicate(true); // Duplicate arrays and dictionaries for the editor } - node->set(snames[nprops[j].name], value, &valid); + + bool set_valid = true; + if (ResourceLoader::is_creating_missing_resources_if_class_unavailable_enabled() && value.get_type() == Variant::OBJECT) { + Ref<MissingResource> mr = value; + if (mr.is_valid()) { + missing_resource_properties[snames[nprops[j].name]] = mr; + set_valid = false; + } + } + + if (set_valid) { + node->set(snames[nprops[j].name], value, &valid); + } } } + if (!missing_resource_properties.is_empty()) { + node->set_meta(META_MISSING_RESOURCES, missing_resource_properties); + } } //name @@ -306,6 +335,10 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { } } + if (!old_parent_path.is_empty()) { + node->_set_name_nocheck(old_parent_path + "@" + node->get_name()); + } + if (n.owner >= 0) { NODE_FROM_ID(owner, n.owner); if (owner) { @@ -324,6 +357,10 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { } } + if (missing_node) { + missing_node->set_recording_properties(false); + } + ret_nodes[i] = node; if (node && gen_node_path_cache && ret_nodes[0]) { @@ -485,33 +522,36 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map p_node->get_property_list(&plist); Array pinned_props = _sanitize_node_pinned_properties(p_node); + Dictionary missing_resource_properties = p_node->get_meta(META_MISSING_RESOURCES, Dictionary()); for (const PropertyInfo &E : plist) { if (!(E.usage & PROPERTY_USAGE_STORAGE)) { continue; } - Variant forced_value; + if (E.name == META_PROPERTY_MISSING_RESOURCES) { + continue; // Ignore this property when packing. + } - // If instance or inheriting, not saving if property requested so, or it's meta - if (states_stack.size()) { + // If instance or inheriting, not saving if property requested so. + if (!states_stack.is_empty()) { if ((E.usage & PROPERTY_USAGE_NO_INSTANCE_STATE)) { continue; } - // Meta is normally not saved in instances/inherited (see GH-12838), but we need to save the pinned list - if (E.name == "__meta__") { - if (pinned_props.size()) { - Dictionary meta_override; - meta_override["_edit_pinned_properties_"] = pinned_props; - forced_value = meta_override; - } - } } StringName name = E.name; - Variant value = forced_value.get_type() == Variant::NIL ? p_node->get(name) : forced_value; + Variant value = p_node->get(name); + + if (E.type == Variant::OBJECT && missing_resource_properties.has(E.name)) { + // Was this missing resource overriden? If so do not save the old value. + Ref<Resource> ures = value; + if (ures.is_null()) { + value = missing_resource_properties[E.name]; + } + } - if (!pinned_props.has(name) && forced_value.get_type() == Variant::NIL) { + if (!pinned_props.has(name)) { bool is_valid_default = false; Variant default_value = PropertyUtils::get_property_default_value(p_node, name, &is_valid_default, &states_stack, true); if (is_valid_default && !PropertyUtils::is_property_value_different(value, default_value)) { @@ -566,11 +606,18 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map nd.owner = -1; } + MissingNode *missing_node = Object::cast_to<MissingNode>(p_node); + // Save the right type. If this node was created by an instance // then flag that the node should not be created but reused if (states_stack.is_empty() && !is_editable_instance) { //this node is not part of an instancing process, so save the type - nd.type = _nm_get_string(p_node->get_class(), name_map); + if (missing_node != nullptr) { + // Its a missing node (type non existant on load). + nd.type = _nm_get_string(missing_node->get_original_class(), name_map); + } else { + nd.type = _nm_get_string(p_node->get_class(), name_map); + } } else { // this node is part of an instantiated process, so do not save the type. // instead, save that it was instantiated diff --git a/scene/resources/polygon_path_finder.cpp b/scene/resources/polygon_path_finder.cpp index 882afdb43d..94e7f46ea5 100644 --- a/scene/resources/polygon_path_finder.cpp +++ b/scene/resources/polygon_path_finder.cpp @@ -137,7 +137,7 @@ Vector<Vector2> PolygonPathFinder::find_path(const Vector2 &p_from, const Vector Edge ignore_to_edge(-1, -1); if (!_is_point_inside(from)) { - float closest_dist = 1e20; + float closest_dist = 1e20f; Vector2 closest_point; for (Set<Edge>::Element *E = edges.front(); E; E = E->next()) { @@ -161,7 +161,7 @@ Vector<Vector2> PolygonPathFinder::find_path(const Vector2 &p_from, const Vector }; if (!_is_point_inside(to)) { - float closest_dist = 1e20; + float closest_dist = 1e20f; Vector2 closest_point; for (Set<Edge>::Element *E = edges.front(); E; E = E->next()) { @@ -489,7 +489,7 @@ bool PolygonPathFinder::is_point_inside(const Vector2 &p_point) const { } Vector2 PolygonPathFinder::get_closest_point(const Vector2 &p_point) const { - float closest_dist = 1e20; + float closest_dist = 1e20f; Vector2 closest_point; for (Set<Edge>::Element *E = edges.front(); E; E = E->next()) { @@ -508,7 +508,7 @@ Vector2 PolygonPathFinder::get_closest_point(const Vector2 &p_point) const { } } - ERR_FAIL_COND_V(closest_dist == 1e20, Vector2()); + ERR_FAIL_COND_V(Math::is_equal_approx(closest_dist, 1e20f), Vector2()); return closest_point; } diff --git a/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp index 04a6ad7675..ae321fd9a7 100644 --- a/scene/resources/resource_format_text.cpp +++ b/scene/resources/resource_format_text.cpp @@ -32,6 +32,7 @@ #include "core/config/project_settings.h" #include "core/io/dir_access.h" +#include "core/io/missing_resource.h" #include "core/io/resource_format_binary.h" #include "core/version.h" @@ -535,6 +536,8 @@ Error ResourceLoaderText::load() { } } + MissingResource *missing_resource = nullptr; + if (res.is_null()) { //not reuse if (cache_mode != ResourceFormatLoader::CACHE_MODE_IGNORE && ResourceCache::has(path)) { //only if it doesn't exist //cached, do not assign @@ -545,10 +548,17 @@ Error ResourceLoaderText::load() { Object *obj = ClassDB::instantiate(type); if (!obj) { - error_text += "Can't create sub resource of type: " + type; - _printerr(); - error = ERR_FILE_CORRUPT; - return error; + if (ResourceLoader::is_creating_missing_resources_if_class_unavailable_enabled()) { + missing_resource = memnew(MissingResource); + missing_resource->set_original_class(type); + missing_resource->set_recording_properties(true); + obj = missing_resource; + } else { + error_text += "Can't create sub resource of type: " + type; + _printerr(); + error = ERR_FILE_CORRUPT; + return error; + } } Resource *r = Object::cast_to<Resource>(obj); @@ -572,6 +582,8 @@ Error ResourceLoaderText::load() { res->set_scene_unique_id(id); } + Dictionary missing_resource_properties; + while (true) { String assign; Variant value; @@ -585,7 +597,23 @@ Error ResourceLoaderText::load() { if (!assign.is_empty()) { if (do_assign) { - res->set(assign, value); + bool set_valid = true; + + if (value.get_type() == Variant::OBJECT && missing_resource != nullptr) { + // If the property being set is a missing resource (and the parent is not), + // then setting it will most likely not work. + // Instead, save it as metadata. + + Ref<MissingResource> mr = value; + if (mr.is_valid()) { + missing_resource_properties[assign] = mr; + set_valid = false; + } + } + + if (set_valid) { + res->set(assign, value); + } } //it's assignment } else if (!next_tag.name.is_empty()) { @@ -599,6 +627,14 @@ Error ResourceLoaderText::load() { } } + if (missing_resource) { + missing_resource->set_recording_properties(false); + } + + if (!missing_resource_properties.is_empty()) { + res->set_meta(META_MISSING_RESOURCES, missing_resource_properties); + } + if (progress && resources_total > 0) { *progress = resource_current / float(resources_total); } @@ -624,13 +660,22 @@ Error ResourceLoaderText::load() { } } + MissingResource *missing_resource = nullptr; + if (!resource.is_valid()) { Object *obj = ClassDB::instantiate(res_type); if (!obj) { - error_text += "Can't create sub resource of type: " + res_type; - _printerr(); - error = ERR_FILE_CORRUPT; - return error; + if (ResourceLoader::is_creating_missing_resources_if_class_unavailable_enabled()) { + missing_resource = memnew(MissingResource); + missing_resource->set_original_class(res_type); + missing_resource->set_recording_properties(true); + obj = missing_resource; + } else { + error_text += "Can't create sub resource of type: " + res_type; + _printerr(); + error = ERR_FILE_CORRUPT; + return error; + } } Resource *r = Object::cast_to<Resource>(obj); @@ -646,6 +691,8 @@ Error ResourceLoaderText::load() { resource_current++; + Dictionary missing_resource_properties; + while (true) { String assign; Variant value; @@ -668,7 +715,23 @@ Error ResourceLoaderText::load() { } if (!assign.is_empty()) { - resource->set(assign, value); + bool set_valid = true; + + if (value.get_type() == Variant::OBJECT && missing_resource != nullptr) { + // If the property being set is a missing resource (and the parent is not), + // then setting it will most likely not work. + // Instead, save it as metadata. + + Ref<MissingResource> mr = value; + if (mr.is_valid()) { + missing_resource_properties[assign] = mr; + set_valid = false; + } + } + + if (set_valid) { + resource->set(assign, value); + } //it's assignment } else if (!next_tag.name.is_empty()) { error = ERR_FILE_CORRUPT; @@ -676,14 +739,24 @@ Error ResourceLoaderText::load() { _printerr(); return error; } else { - error = OK; - if (progress && resources_total > 0) { - *progress = resource_current / float(resources_total); - } - - return error; + break; } } + + if (missing_resource) { + missing_resource->set_recording_properties(false); + } + + if (!missing_resource_properties.is_empty()) { + resource->set_meta(META_MISSING_RESOURCES, missing_resource_properties); + } + + error = OK; + if (progress && resources_total > 0) { + *progress = resource_current / float(resources_total); + } + + return error; } //for scene files @@ -1593,6 +1666,15 @@ void ResourceFormatSaverTextInstance::_find_resources(const Variant &p_variant, } } +static String _resource_get_class(Ref<Resource> p_resource) { + Ref<MissingResource> missing_resource = p_resource; + if (missing_resource.is_valid()) { + return missing_resource->get_original_class(); + } else { + return p_resource->get_class(); + } +} + Error ResourceFormatSaverTextInstance::save(const String &p_path, const Ref<Resource> &p_resource, uint32_t p_flags) { if (p_path.ends_with(".tscn")) { packed_scene = p_resource; @@ -1634,7 +1716,7 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const Ref<Reso { String title = packed_scene.is_valid() ? "[gd_scene " : "[gd_resource "; if (packed_scene.is_null()) { - title += "type=\"" + p_resource->get_class() + "\" "; + title += "type=\"" + _resource_get_class(p_resource) + "\" "; } int load_steps = saved_resources.size() + external_resources.size(); @@ -1758,7 +1840,7 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const Ref<Reso if (res->get_scene_unique_id().is_empty()) { String new_id; while (true) { - new_id = res->get_class() + "_" + Resource::generate_scene_unique_id(); + new_id = _resource_get_class(res) + "_" + Resource::generate_scene_unique_id(); if (!used_unique_ids.has(new_id)) { break; @@ -1770,7 +1852,7 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const Ref<Reso } String id = res->get_scene_unique_id(); - line += "type=\"" + res->get_class() + "\" id=\"" + id; + line += "type=\"" + _resource_get_class(res) + "\" id=\"" + id; f->store_line(line + "\"]"); if (takeover_paths) { res->set_path(p_path + "::" + id, true); @@ -1782,12 +1864,17 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const Ref<Reso #endif } + Dictionary missing_resource_properties = p_resource->get_meta(META_MISSING_RESOURCES, Dictionary()); + List<PropertyInfo> property_list; res->get_property_list(&property_list); for (List<PropertyInfo>::Element *PE = property_list.front(); PE; PE = PE->next()) { if (skip_editor && PE->get().name.begins_with("__editor")) { continue; } + if (PE->get().name == META_PROPERTY_MISSING_RESOURCES) { + continue; + } if (PE->get().usage & PROPERTY_USAGE_STORAGE) { String name = PE->get().name; @@ -1802,6 +1889,15 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const Ref<Reso } else { value = res->get(name); } + + if (PE->get().type == Variant::OBJECT && missing_resource_properties.has(PE->get().name)) { + // Was this missing resource overriden? If so do not save the old value. + Ref<Resource> ures = value; + if (ures.is_null()) { + value = missing_resource_properties[PE->get().name]; + } + } + Variant default_value = ClassDB::class_get_default_property_value(res->get_class(), name); if (default_value.get_type() != Variant::NIL && bool(Variant::evaluate(Variant::OP_EQUAL, value, default_value))) { diff --git a/scene/resources/skeleton_modification_stack_2d.cpp b/scene/resources/skeleton_modification_stack_2d.cpp index b944c244b6..38ec19828f 100644 --- a/scene/resources/skeleton_modification_stack_2d.cpp +++ b/scene/resources/skeleton_modification_stack_2d.cpp @@ -263,7 +263,7 @@ void SkeletonModificationStack2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "get_enabled"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "strength", PROPERTY_HINT_RANGE, "0, 1, 0.001"), "set_strength", "get_strength"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "modification_count", PROPERTY_HINT_RANGE, "0, 100, 1"), "set_modification_count", "get_modification_count"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "modification_count", PROPERTY_HINT_RANGE, "0, 100, 1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_ARRAY, "Modifications,modifications/"), "set_modification_count", "get_modification_count"); } SkeletonModificationStack2D::SkeletonModificationStack2D() { diff --git a/scene/resources/skeleton_modification_stack_3d.cpp b/scene/resources/skeleton_modification_stack_3d.cpp index 7ccba1228c..44fbfc934e 100644 --- a/scene/resources/skeleton_modification_stack_3d.cpp +++ b/scene/resources/skeleton_modification_stack_3d.cpp @@ -217,7 +217,7 @@ void SkeletonModificationStack3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "get_enabled"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "strength", PROPERTY_HINT_RANGE, "0, 1, 0.001"), "set_strength", "get_strength"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "modification_count", PROPERTY_HINT_RANGE, "0, 100, 1"), "set_modification_count", "get_modification_count"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "modification_count", PROPERTY_HINT_RANGE, "0, 100, 1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_ARRAY, "Modifications,modifications/"), "set_modification_count", "get_modification_count"); } SkeletonModificationStack3D::SkeletonModificationStack3D() { diff --git a/scene/resources/visual_shader_nodes.cpp b/scene/resources/visual_shader_nodes.cpp index 99d5d5c5d5..6af34a8a82 100644 --- a/scene/resources/visual_shader_nodes.cpp +++ b/scene/resources/visual_shader_nodes.cpp @@ -2608,8 +2608,6 @@ String VisualShaderNodeVectorFunc::generate_code(Shader::Mode p_mode, VisualShad "", // FUNC_SATURATE "-($)", "1.0 / ($)", - "", // FUNC_RGB2HSV - "", // FUNC_HSV2RGB "abs($)", "acos($)", "acosh($)", @@ -2667,43 +2665,7 @@ String VisualShaderNodeVectorFunc::generate_code(Shader::Mode p_mode, VisualShad return " " + p_output_vars[0] + " = " + code.replace("$", p_input_vars[0]) + ";\n"; } - String code; - - if (func == FUNC_RGB2HSV) { - if (op_type == OP_TYPE_VECTOR_2D) { // Not supported. - return " " + p_output_vars[0] + " = vec2(0.0);\n"; - } - if (op_type == OP_TYPE_VECTOR_4D) { // Not supported. - return " " + p_output_vars[0] + " = vec4(0.0);\n"; - } - code += " {\n"; - code += " vec3 c = " + p_input_vars[0] + ";\n"; - code += " vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);\n"; - code += " vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));\n"; - code += " vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));\n"; - code += " float d = q.x - min(q.w, q.y);\n"; - code += " float e = 1.0e-10;\n"; - code += " " + p_output_vars[0] + " = vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);\n"; - code += " }\n"; - } else if (func == FUNC_HSV2RGB) { - if (op_type == OP_TYPE_VECTOR_2D) { // Not supported. - return " " + p_output_vars[0] + " = vec2(0.0);\n"; - } - if (op_type == OP_TYPE_VECTOR_4D) { // Not supported. - return " " + p_output_vars[0] + " = vec4(0.0);\n"; - } - code += " {\n"; - code += " vec3 c = " + p_input_vars[0] + ";\n"; - code += " vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);\n"; - code += " vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);\n"; - code += " " + p_output_vars[0] + " = c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);\n"; - code += " }\n"; - - } else { - code += " " + p_output_vars[0] + " = " + String(funcs[func]).replace("$", p_input_vars[0]) + ";\n"; - } - - return code; + return " " + p_output_vars[0] + " = " + String(funcs[func]).replace("$", p_input_vars[0]) + ";\n"; } void VisualShaderNodeVectorFunc::set_op_type(OpType p_op_type) { @@ -2733,13 +2695,6 @@ void VisualShaderNodeVectorFunc::set_function(Function p_func) { if (func == p_func) { return; } - if (p_func == FUNC_RGB2HSV) { - simple_decl = false; - } else if (p_func == FUNC_HSV2RGB) { - simple_decl = false; - } else { - simple_decl = true; - } func = p_func; emit_changed(); } @@ -2754,34 +2709,16 @@ Vector<StringName> VisualShaderNodeVectorFunc::get_editable_properties() const { return props; } -String VisualShaderNodeVectorFunc::get_warning(Shader::Mode p_mode, VisualShader::Type p_type) const { - bool invalid_type = false; - - if (op_type == OP_TYPE_VECTOR_2D || op_type == OP_TYPE_VECTOR_4D) { - if (func == FUNC_RGB2HSV || func == FUNC_HSV2RGB) { - invalid_type = true; - } - } - - if (invalid_type) { - return RTR("Invalid function for that type."); - } - - return String(); -} - void VisualShaderNodeVectorFunc::_bind_methods() { ClassDB::bind_method(D_METHOD("set_function", "func"), &VisualShaderNodeVectorFunc::set_function); ClassDB::bind_method(D_METHOD("get_function"), &VisualShaderNodeVectorFunc::get_function); - ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "Normalize,Saturate,Negate,Reciprocal,RGB2HSV,HSV2RGB,Abs,ACos,ACosH,ASin,ASinH,ATan,ATanH,Ceil,Cos,CosH,Degrees,Exp,Exp2,Floor,Frac,InverseSqrt,Log,Log2,Radians,Round,RoundEven,Sign,Sin,SinH,Sqrt,Tan,TanH,Trunc,OneMinus"), "set_function", "get_function"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "Normalize,Saturate,Negate,Reciprocal,Abs,ACos,ACosH,ASin,ASinH,ATan,ATanH,Ceil,Cos,CosH,Degrees,Exp,Exp2,Floor,Frac,InverseSqrt,Log,Log2,Radians,Round,RoundEven,Sign,Sin,SinH,Sqrt,Tan,TanH,Trunc,OneMinus"), "set_function", "get_function"); BIND_ENUM_CONSTANT(FUNC_NORMALIZE); BIND_ENUM_CONSTANT(FUNC_SATURATE); BIND_ENUM_CONSTANT(FUNC_NEGATE); BIND_ENUM_CONSTANT(FUNC_RECIPROCAL); - BIND_ENUM_CONSTANT(FUNC_RGB2HSV); - BIND_ENUM_CONSTANT(FUNC_HSV2RGB); BIND_ENUM_CONSTANT(FUNC_ABS); BIND_ENUM_CONSTANT(FUNC_ACOS); BIND_ENUM_CONSTANT(FUNC_ACOSH); @@ -2872,6 +2809,25 @@ String VisualShaderNodeColorFunc::generate_code(Shader::Mode p_mode, VisualShade code += " " + p_output_vars[0] + " = vec3(max2, max2, max2);\n"; code += " }\n"; break; + case FUNC_HSV2RGB: + code += " {\n"; + code += " vec3 c = " + p_input_vars[0] + ";\n"; + code += " vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);\n"; + code += " vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);\n"; + code += " " + p_output_vars[0] + " = c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);\n"; + code += " }\n"; + break; + case FUNC_RGB2HSV: + code += " {\n"; + code += " vec3 c = " + p_input_vars[0] + ";\n"; + code += " vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);\n"; + code += " vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));\n"; + code += " vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));\n"; + code += " float d = q.x - min(q.w, q.y);\n"; + code += " float e = 1.0e-10;\n"; + code += " " + p_output_vars[0] + " = vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);\n"; + code += " }\n"; + break; case FUNC_SEPIA: code += " {\n"; code += " vec3 c = " + p_input_vars[0] + ";\n"; @@ -2911,9 +2867,11 @@ void VisualShaderNodeColorFunc::_bind_methods() { ClassDB::bind_method(D_METHOD("set_function", "func"), &VisualShaderNodeColorFunc::set_function); ClassDB::bind_method(D_METHOD("get_function"), &VisualShaderNodeColorFunc::get_function); - ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "Grayscale,Sepia"), "set_function", "get_function"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "Grayscale,HSV2RGB,RGB2HSV,Sepia"), "set_function", "get_function"); BIND_ENUM_CONSTANT(FUNC_GRAYSCALE); + BIND_ENUM_CONSTANT(FUNC_HSV2RGB); + BIND_ENUM_CONSTANT(FUNC_RGB2HSV); BIND_ENUM_CONSTANT(FUNC_SEPIA); BIND_ENUM_CONSTANT(FUNC_MAX); } @@ -5104,7 +5062,7 @@ int VisualShaderNodeColorUniform::get_input_port_count() const { } VisualShaderNodeColorUniform::PortType VisualShaderNodeColorUniform::get_input_port_type(int p_port) const { - return PORT_TYPE_VECTOR_3D; + return PORT_TYPE_SCALAR; } String VisualShaderNodeColorUniform::get_input_port_name(int p_port) const { @@ -5112,15 +5070,22 @@ String VisualShaderNodeColorUniform::get_input_port_name(int p_port) const { } int VisualShaderNodeColorUniform::get_output_port_count() const { - return 2; + return 1; } VisualShaderNodeColorUniform::PortType VisualShaderNodeColorUniform::get_output_port_type(int p_port) const { - return p_port == 0 ? PORT_TYPE_VECTOR_3D : PORT_TYPE_SCALAR; + return p_port == 0 ? PORT_TYPE_VECTOR_4D : PORT_TYPE_SCALAR; } String VisualShaderNodeColorUniform::get_output_port_name(int p_port) const { - return p_port == 0 ? "color" : "alpha"; //no output port means the editor will be used as port + return "color"; +} + +bool VisualShaderNodeColorUniform::is_output_port_expandable(int p_port) const { + if (p_port == 0) { + return true; + } + return false; } void VisualShaderNodeColorUniform::set_default_value_enabled(bool p_enabled) { @@ -5157,9 +5122,7 @@ String VisualShaderNodeColorUniform::generate_global(Shader::Mode p_mode, Visual } String VisualShaderNodeColorUniform::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const { - String code = " " + p_output_vars[0] + " = " + get_uniform_name() + ".rgb;\n"; - code += " " + p_output_vars[1] + " = " + get_uniform_name() + ".a;\n"; - return code; + return " " + p_output_vars[0] + " = " + get_uniform_name() + ";\n"; } bool VisualShaderNodeColorUniform::is_show_prop_names() const { diff --git a/scene/resources/visual_shader_nodes.h b/scene/resources/visual_shader_nodes.h index 26c98bd2ea..338f1157d3 100644 --- a/scene/resources/visual_shader_nodes.h +++ b/scene/resources/visual_shader_nodes.h @@ -1039,8 +1039,6 @@ public: FUNC_SATURATE, FUNC_NEGATE, FUNC_RECIPROCAL, - FUNC_RGB2HSV, - FUNC_HSV2RGB, FUNC_ABS, FUNC_ACOS, FUNC_ACOSH, @@ -1095,7 +1093,6 @@ public: Function get_function() const; virtual Vector<StringName> get_editable_properties() const override; - String get_warning(Shader::Mode p_mode, VisualShader::Type p_type) const override; VisualShaderNodeVectorFunc(); }; @@ -1112,6 +1109,8 @@ class VisualShaderNodeColorFunc : public VisualShaderNode { public: enum Function { FUNC_GRAYSCALE, + FUNC_HSV2RGB, + FUNC_RGB2HSV, FUNC_SEPIA, FUNC_MAX, }; @@ -1922,6 +1921,8 @@ public: virtual PortType get_output_port_type(int p_port) const override; virtual String get_output_port_name(int p_port) const override; + bool is_output_port_expandable(int p_port) const override; + virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const override; virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override; |