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