diff --git a/core/object/object.h b/core/object/object.h index 02f2ed1f6a8..d401b86ea9d 100644 --- a/core/object/object.h +++ b/core/object/object.h @@ -91,6 +91,7 @@ enum PropertyHint { PROPERTY_HINT_DICTIONARY_TYPE, PROPERTY_HINT_TOOL_BUTTON, PROPERTY_HINT_ONESHOT, ///< the property will be changed by self after setting, such as AudioStreamPlayer.playing, Particles.emitting. + PROPERTY_HINT_NO_NODEPATH, /// < this property will not contain a NodePath, regardless of type (Array, Dictionary, List, etc.). Needed for SceneTreeDock. PROPERTY_HINT_MAX, }; diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml index b041c45d04b..b3cac6e64e0 100644 --- a/doc/classes/@GlobalScope.xml +++ b/doc/classes/@GlobalScope.xml @@ -2935,7 +2935,7 @@ Hints that a property will be changed on its own after setting, such as [member AudioStreamPlayer.playing] or [member GPUParticles3D.emitting]. - + Represents the size of the [enum PropertyHint] enum. diff --git a/doc/classes/Node.xml b/doc/classes/Node.xml index be080e2821d..df2799d54c4 100644 --- a/doc/classes/Node.xml +++ b/doc/classes/Node.xml @@ -1079,6 +1079,11 @@ Emitted when the node's editor description field changed. + + + Emitted when an attribute of the node that is relevant to the editor is changed. Only emitted in the editor. + + Emitted when the node is considered ready, after [method _ready] is called. diff --git a/doc/classes/TreeItem.xml b/doc/classes/TreeItem.xml index bd46c54990f..c5aba9f8123 100644 --- a/doc/classes/TreeItem.xml +++ b/doc/classes/TreeItem.xml @@ -36,6 +36,12 @@ Calls the [param method] on the actual TreeItem and its children recursively. Pass parameters as a comma separated list. + + + + Removes all buttons from all columns of this item. + + diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp index df362039b8a..4a58e1e6edb 100644 --- a/drivers/gles3/rasterizer_canvas_gles3.cpp +++ b/drivers/gles3/rasterizer_canvas_gles3.cpp @@ -689,6 +689,8 @@ void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_cou state.current_tex = RID(); + const uint64_t base_specialization = GLES3::Config::get_singleton()->float_texture_supported ? 0 : CanvasShaderGLES3::USE_RGBA_SHADOWS; + for (uint32_t i = 0; i <= state.current_batch_index; i++) { // Skipping when there is no instances. if (state.canvas_instance_batches[i].instance_count == 0) { @@ -707,10 +709,9 @@ void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_cou } GLES3::CanvasMaterialData *material_data = state.canvas_instance_batches[i].material_data; - CanvasShaderGLES3::ShaderVariant variant = state.canvas_instance_batches[i].shader_variant; - uint64_t specialization = 0; - specialization |= uint64_t(state.canvas_instance_batches[i].lights_disabled); - specialization |= uint64_t(!GLES3::Config::get_singleton()->float_texture_supported) << 1; + CanvasShaderGLES3::ShaderVariant variant = CanvasShaderGLES3::MODE_DEFAULT; + uint64_t specialization = state.canvas_instance_batches[i].specialization; + specialization |= base_specialization; RID shader_version = data.canvas_shader_default_version; if (material_data) { @@ -812,6 +813,7 @@ void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_cou void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_render_target, const Transform2D &p_canvas_transform_inverse, Item *¤t_clip, GLES3::CanvasShaderData::BlendMode p_blend_mode, Light *p_lights, uint32_t &r_index, bool &r_batch_broken, bool &r_sdf_used, const Point2 &p_repeat_offset) { RenderingServer::CanvasItemTextureFilter texture_filter = p_item->texture_filter == RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT ? state.default_filter : p_item->texture_filter; + const uint64_t specialization_command_mask = ~(CanvasShaderGLES3::USE_NINEPATCH | CanvasShaderGLES3::USE_PRIMITIVE | CanvasShaderGLES3::USE_ATTRIBUTES | CanvasShaderGLES3::USE_INSTANCING); if (texture_filter != state.canvas_instance_batches[state.current_batch_index].filter) { _new_batch(r_batch_broken); @@ -870,9 +872,9 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend bool lights_disabled = light_count == 0 && !state.using_directional_lights; - if (lights_disabled != state.canvas_instance_batches[state.current_batch_index].lights_disabled) { + if (lights_disabled != bool(state.canvas_instance_batches[state.current_batch_index].specialization & CanvasShaderGLES3::DISABLE_LIGHTING)) { _new_batch(r_batch_broken); - state.canvas_instance_batches[state.current_batch_index].lights_disabled = lights_disabled; + state.canvas_instance_batches[state.current_batch_index].specialization ^= CanvasShaderGLES3::DISABLE_LIGHTING; } const Item::Command *c = p_item->commands; @@ -938,7 +940,7 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend state.canvas_instance_batches[state.current_batch_index].tex = rect->texture; state.canvas_instance_batches[state.current_batch_index].command_type = Item::Command::TYPE_RECT; state.canvas_instance_batches[state.current_batch_index].command = c; - state.canvas_instance_batches[state.current_batch_index].shader_variant = CanvasShaderGLES3::MODE_QUAD; + state.canvas_instance_batches[state.current_batch_index].specialization &= specialization_command_mask; } _prepare_canvas_texture(rect->texture, state.canvas_instance_batches[state.current_batch_index].filter, state.canvas_instance_batches[state.current_batch_index].repeat, r_index, texpixel_size); @@ -1028,7 +1030,8 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend state.canvas_instance_batches[state.current_batch_index].tex = np->texture; state.canvas_instance_batches[state.current_batch_index].command_type = Item::Command::TYPE_NINEPATCH; state.canvas_instance_batches[state.current_batch_index].command = c; - state.canvas_instance_batches[state.current_batch_index].shader_variant = CanvasShaderGLES3::MODE_NINEPATCH; + state.canvas_instance_batches[state.current_batch_index].specialization &= specialization_command_mask; + state.canvas_instance_batches[state.current_batch_index].specialization |= CanvasShaderGLES3::USE_NINEPATCH; } _prepare_canvas_texture(np->texture, state.canvas_instance_batches[state.current_batch_index].filter, state.canvas_instance_batches[state.current_batch_index].repeat, r_index, texpixel_size); @@ -1094,7 +1097,8 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend state.canvas_instance_batches[state.current_batch_index].tex = polygon->texture; state.canvas_instance_batches[state.current_batch_index].command_type = Item::Command::TYPE_POLYGON; state.canvas_instance_batches[state.current_batch_index].command = c; - state.canvas_instance_batches[state.current_batch_index].shader_variant = CanvasShaderGLES3::MODE_ATTRIBUTES; + state.canvas_instance_batches[state.current_batch_index].specialization &= specialization_command_mask; + state.canvas_instance_batches[state.current_batch_index].specialization |= CanvasShaderGLES3::USE_ATTRIBUTES; _prepare_canvas_texture(polygon->texture, state.canvas_instance_batches[state.current_batch_index].filter, state.canvas_instance_batches[state.current_batch_index].repeat, r_index, texpixel_size); @@ -1121,7 +1125,8 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend state.canvas_instance_batches[state.current_batch_index].primitive_points = primitive->point_count; state.canvas_instance_batches[state.current_batch_index].command_type = Item::Command::TYPE_PRIMITIVE; state.canvas_instance_batches[state.current_batch_index].command = c; - state.canvas_instance_batches[state.current_batch_index].shader_variant = CanvasShaderGLES3::MODE_PRIMITIVE; + state.canvas_instance_batches[state.current_batch_index].specialization &= specialization_command_mask; + state.canvas_instance_batches[state.current_batch_index].specialization |= CanvasShaderGLES3::USE_PRIMITIVE; } _prepare_canvas_texture(state.canvas_instance_batches[state.current_batch_index].tex, state.canvas_instance_batches[state.current_batch_index].filter, state.canvas_instance_batches[state.current_batch_index].repeat, r_index, texpixel_size); @@ -1166,7 +1171,8 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend _new_batch(r_batch_broken); Color modulate(1, 1, 1, 1); - state.canvas_instance_batches[state.current_batch_index].shader_variant = CanvasShaderGLES3::MODE_ATTRIBUTES; + state.canvas_instance_batches[state.current_batch_index].specialization &= specialization_command_mask; + state.canvas_instance_batches[state.current_batch_index].specialization |= CanvasShaderGLES3::USE_ATTRIBUTES; if (c->type == Item::Command::TYPE_MESH) { const Item::CommandMesh *m = static_cast(c); state.canvas_instance_batches[state.current_batch_index].tex = m->texture; @@ -1176,7 +1182,7 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend } else if (c->type == Item::Command::TYPE_MULTIMESH) { const Item::CommandMultiMesh *mm = static_cast(c); state.canvas_instance_batches[state.current_batch_index].tex = mm->texture; - state.canvas_instance_batches[state.current_batch_index].shader_variant = CanvasShaderGLES3::MODE_INSTANCED; + state.canvas_instance_batches[state.current_batch_index].specialization |= CanvasShaderGLES3::USE_INSTANCING; if (GLES3::MeshStorage::get_singleton()->multimesh_uses_colors(mm->multimesh)) { state.instance_data_array[r_index].flags |= FLAGS_INSTANCING_HAS_COLORS; @@ -1191,7 +1197,7 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend const Item::CommandParticles *pt = static_cast(c); RID particles = pt->particles; state.canvas_instance_batches[state.current_batch_index].tex = pt->texture; - state.canvas_instance_batches[state.current_batch_index].shader_variant = CanvasShaderGLES3::MODE_INSTANCED; + state.canvas_instance_batches[state.current_batch_index].specialization |= CanvasShaderGLES3::USE_INSTANCING; state.instance_data_array[r_index].flags |= FLAGS_INSTANCING_HAS_COLORS; state.instance_data_array[r_index].flags |= FLAGS_INSTANCING_HAS_CUSTOM_DATA; diff --git a/drivers/gles3/rasterizer_canvas_gles3.h b/drivers/gles3/rasterizer_canvas_gles3.h index 3ff583a6d4c..c36c14a3955 100644 --- a/drivers/gles3/rasterizer_canvas_gles3.h +++ b/drivers/gles3/rasterizer_canvas_gles3.h @@ -275,14 +275,12 @@ class RasterizerCanvasGLES3 : public RendererCanvasRender { RID material; GLES3::CanvasMaterialData *material_data = nullptr; - CanvasShaderGLES3::ShaderVariant shader_variant = CanvasShaderGLES3::MODE_QUAD; uint64_t vertex_input_mask = RS::ARRAY_FORMAT_VERTEX | RS::ARRAY_FORMAT_COLOR | RS::ARRAY_FORMAT_TEX_UV; + uint64_t specialization = 0; const Item::Command *command = nullptr; Item::Command::Type command_type = Item::Command::TYPE_ANIMATION_SLICE; // Can default to any type that doesn't form a batch. uint32_t primitive_points = 0; - - bool lights_disabled = false; }; // DataBuffer contains our per-frame data. I.e. the resources that are updated each frame. diff --git a/drivers/gles3/shaders/canvas.glsl b/drivers/gles3/shaders/canvas.glsl index 76881c8032d..728d0d47f38 100644 --- a/drivers/gles3/shaders/canvas.glsl +++ b/drivers/gles3/shaders/canvas.glsl @@ -1,17 +1,16 @@ /* clang-format off */ #[modes] -mode_quad = -mode_ninepatch = #define USE_NINEPATCH -mode_primitive = #define USE_PRIMITIVE -mode_attributes = #define USE_ATTRIBUTES -mode_instanced = #define USE_ATTRIBUTES \n#define USE_INSTANCING +mode_default = #[specializations] DISABLE_LIGHTING = true USE_RGBA_SHADOWS = false -SINGLE_INSTANCE = false +USE_NINEPATCH = false +USE_PRIMITIVE = false +USE_ATTRIBUTES = false +USE_INSTANCING = false #[vertex] diff --git a/drivers/gles3/shaders/sky.glsl b/drivers/gles3/shaders/sky.glsl index f734e4b3554..fcaf2d111ae 100644 --- a/drivers/gles3/shaders/sky.glsl +++ b/drivers/gles3/shaders/sky.glsl @@ -2,17 +2,15 @@ #[modes] mode_background = -mode_half_res = #define USE_HALF_RES_PASS -mode_quarter_res = #define USE_QUARTER_RES_PASS mode_cubemap = #define USE_CUBEMAP_PASS -mode_cubemap_half_res = #define USE_CUBEMAP_PASS \n#define USE_HALF_RES_PASS -mode_cubemap_quarter_res = #define USE_CUBEMAP_PASS \n#define USE_QUARTER_RES_PASS #[specializations] USE_MULTIVIEW = false USE_INVERTED_Y = true APPLY_TONEMAPPING = true +USE_QUARTER_RES_PASS = false +USE_HALF_RES_PASS = false #[vertex] diff --git a/editor/connections_dialog.cpp b/editor/connections_dialog.cpp index d739d96f386..774b1fdbb49 100644 --- a/editor/connections_dialog.cpp +++ b/editor/connections_dialog.cpp @@ -725,6 +725,7 @@ ConnectDialog::ConnectDialog() { from_signal->set_editable(false); tree = memnew(SceneTreeEditor(false)); + tree->set_update_when_invisible(false); tree->set_connecting_signal(true); tree->set_show_enabled_subscene(true); tree->set_v_size_flags(Control::SIZE_FILL | Control::SIZE_EXPAND); diff --git a/editor/gui/scene_tree_editor.cpp b/editor/gui/scene_tree_editor.cpp index 0947cfcce32..36c55b6cd66 100644 --- a/editor/gui/scene_tree_editor.cpp +++ b/editor/gui/scene_tree_editor.cpp @@ -63,7 +63,7 @@ void SceneTreeEditor::_cell_button_pressed(Object *p_item, int p_column, int p_i } if (connect_to_script_mode) { - return; //don't do anything in this mode + return; // Don't do anything in this mode. } TreeItem *item = Object::cast_to(p_item); @@ -154,7 +154,8 @@ void SceneTreeEditor::_cell_button_pressed(Object *p_item, int p_column, int p_i const String line = all_warnings.substr(start, end - start); lines.append(line); } - all_warnings = String("\n").join(lines).indent(" ").replace(U" •", U"\n•").substr(2); // We don't want the first two newlines. + // We don't want the first two newlines. + all_warnings = String("\n").join(lines).indent(" ").replace(U" •", U"\n•").substr(2); warning->set_text(all_warnings); warning->popup_centered(); @@ -194,12 +195,35 @@ void SceneTreeEditor::_toggle_visible(Node *p_node) { } } -void SceneTreeEditor::_add_nodes(Node *p_node, TreeItem *p_parent) { +void SceneTreeEditor::_update_node_path(Node *p_node, bool p_recursive) { if (!p_node) { return; } - // only owned nodes are editable, since nodes can create their own (manually owned) child nodes, + HashMap::Iterator I = node_cache.get(p_node); + if (!I) { + return; + } + + I->value.item->set_metadata(0, p_node->get_path()); + + if (!p_recursive) { + return; + } + + int cc = p_node->get_child_count(false); + for (int i = 0; i < cc; i++) { + Node *c = p_node->get_child(i, false); + _update_node_path(c, p_recursive); + } +} + +void SceneTreeEditor::_update_node_subtree(Node *p_node, TreeItem *p_parent, bool p_force) { + if (!p_node) { + return; + } + + // Only owned nodes are editable, since nodes can create their own (manually owned) child nodes, // which the editor needs not to know about. bool part_of_subscene = false; @@ -207,41 +231,152 @@ void SceneTreeEditor::_add_nodes(Node *p_node, TreeItem *p_parent) { if (!display_foreign && p_node->get_owner() != get_scene_node() && p_node != get_scene_node()) { if ((show_enabled_subscene || can_open_instance) && p_node->get_owner() && (get_scene_node()->is_editable_instance(p_node->get_owner()))) { part_of_subscene = true; - //allow + // Allow. } else { + // Stale node, remove recursively. + node_cache.remove(p_node, true); return; } } else { part_of_subscene = p_node != get_scene_node() && get_scene_node()->get_scene_inherited_state().is_valid() && get_scene_node()->get_scene_inherited_state()->find_node_by_path(get_scene_node()->get_path_to(p_node)) >= 0; } - TreeItem *item = tree->create_item(p_parent); + HashMap::Iterator I = node_cache.get(p_node); + TreeItem *item = nullptr; + + bool is_new = false; + + if (I) { + item = I->value.item; + TreeItem *current_parent = item->get_parent(); + + // Our parent might be re-created because of a changed type. + if (p_parent && p_parent != current_parent) { + if (current_parent) { + current_parent->remove_child(item); + } + p_parent->add_child(item); + I->value.removed = false; + _move_node_item(p_parent, I); + } - item->set_text(0, p_node->get_name()); - if (can_rename && !part_of_subscene) { - item->set_editable(0, true); + if (I->value.has_moved_children) { + _move_node_children(I); + } + } else { + int index = -1; + // Check to see if there is a root node for us to reuse. + if (!p_parent) { + item = tree->get_root(); + if (!item) { + item = tree->create_item(nullptr); + index = 0; + } + } else { + index = p_node->get_index(false); + item = tree->create_item(p_parent, index); + } + + I = node_cache.add(p_node, item); + I->value.index = index; + is_new = true; + } + + if (!(p_force || I->value.dirty)) { + // Nothing to do. + return; + } + + _update_node(p_node, item, part_of_subscene); + I->value.dirty = false; + I->value.can_process = p_node->can_process(); + + // Force update all our children if we are new or if we were forced to update. + bool force_update_children = p_force || is_new; + // Update all our children. + for (int i = 0; i < p_node->get_child_count(false); i++) { + _update_node_subtree(p_node->get_child(i, false), item, force_update_children); + } + + if (valid_types.size()) { + bool valid = false; + for (const StringName &E : valid_types) { + if (p_node->is_class(E) || + EditorNode::get_singleton()->is_object_of_custom_type(p_node, E)) { + valid = true; + break; + } else { + Ref