From f202a3d24e2cea5d22f5b507c2c34311acd8de2b Mon Sep 17 00:00:00 2001 From: Yuri Rubinsky Date: Thu, 11 Jul 2024 16:20:25 +0300 Subject: [PATCH] Add a material preview to visual shader editor --- editor/plugins/material_editor_plugin.cpp | 33 +- editor/plugins/material_editor_plugin.h | 5 + .../plugins/visual_shader_editor_plugin.cpp | 417 ++++++++++++++---- editor/plugins/visual_shader_editor_plugin.h | 42 +- scene/resources/visual_shader.cpp | 76 +++- scene/resources/visual_shader.h | 9 +- 6 files changed, 491 insertions(+), 91 deletions(-) diff --git a/editor/plugins/material_editor_plugin.cpp b/editor/plugins/material_editor_plugin.cpp index 602e6f945c3..2702b6c9095 100644 --- a/editor/plugins/material_editor_plugin.cpp +++ b/editor/plugins/material_editor_plugin.cpp @@ -33,6 +33,7 @@ #include "core/config/project_settings.h" #include "editor/editor_node.h" #include "editor/editor_settings.h" +#include "editor/editor_string_names.h" #include "editor/editor_undo_redo_manager.h" #include "editor/themes/editor_scale.h" #include "scene/3d/camera_3d.h" @@ -41,6 +42,7 @@ #include "scene/gui/box_container.h" #include "scene/gui/button.h" #include "scene/gui/color_rect.h" +#include "scene/gui/label.h" #include "scene/gui/subviewport_container.h" #include "scene/main/viewport.h" #include "scene/resources/3d/fog_material.h" @@ -80,11 +82,15 @@ void MaterialEditor::_notification(int p_what) { sphere_switch->set_icon(theme_cache.sphere_icon); box_switch->set_icon(theme_cache.box_icon); + + error_label->add_theme_color_override(SceneStringName(font_color), get_theme_color(SNAME("error_color"), EditorStringName(Editor))); } break; case NOTIFICATION_DRAW: { - Size2 size = get_size(); - draw_texture_rect(theme_cache.checkerboard, Rect2(Point2(), size), true); + if (!is_unsupported_shader_mode) { + Size2 size = get_size(); + draw_texture_rect(theme_cache.checkerboard, Rect2(Point2(), size), true); + } } break; } } @@ -99,16 +105,20 @@ void MaterialEditor::_update_rotation() { void MaterialEditor::edit(Ref p_material, const Ref &p_env) { material = p_material; camera->set_environment(p_env); + + is_unsupported_shader_mode = false; if (!material.is_null()) { Shader::Mode mode = p_material->get_shader_mode(); switch (mode) { case Shader::MODE_CANVAS_ITEM: + layout_error->hide(); layout_3d->hide(); layout_2d->show(); vc->hide(); rect_instance->set_material(material); break; case Shader::MODE_SPATIAL: + layout_error->hide(); layout_2d->hide(); layout_3d->show(); vc->show(); @@ -116,6 +126,11 @@ void MaterialEditor::edit(Ref p_material, const Ref &p_en box_instance->set_material_override(material); break; default: + layout_error->show(); + layout_2d->hide(); + layout_3d->hide(); + vc->hide(); + is_unsupported_shader_mode = true; break; } } else { @@ -175,6 +190,20 @@ MaterialEditor::MaterialEditor() { layout_2d->set_visible(false); + layout_error = memnew(VBoxContainer); + layout_error->set_alignment(BoxContainer::ALIGNMENT_CENTER); + layout_error->set_anchors_and_offsets_preset(PRESET_FULL_RECT); + + error_label = memnew(Label); + error_label->set_text(TTR("Preview is not available for this shader mode.")); + error_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER); + error_label->set_vertical_alignment(VERTICAL_ALIGNMENT_CENTER); + error_label->set_autowrap_mode(TextServer::AUTOWRAP_WORD_SMART); + + layout_error->add_child(error_label); + layout_error->hide(); + add_child(layout_error); + // Spatial vc = memnew(SubViewportContainer); diff --git a/editor/plugins/material_editor_plugin.h b/editor/plugins/material_editor_plugin.h index fb6bafc0efb..28c59d27dba 100644 --- a/editor/plugins/material_editor_plugin.h +++ b/editor/plugins/material_editor_plugin.h @@ -45,6 +45,7 @@ class MeshInstance3D; class SubViewport; class SubViewportContainer; class Button; +class Label; class MaterialEditor : public Control { GDCLASS(MaterialEditor, Control); @@ -69,6 +70,10 @@ class MaterialEditor : public Control { Ref sphere_mesh; Ref box_mesh; + VBoxContainer *layout_error = nullptr; + Label *error_label = nullptr; + bool is_unsupported_shader_mode = false; + HBoxContainer *layout_3d = nullptr; Ref material; diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp index b2669cf1afd..bf4e90ab77f 100644 --- a/editor/plugins/visual_shader_editor_plugin.cpp +++ b/editor/plugins/visual_shader_editor_plugin.cpp @@ -43,6 +43,7 @@ #include "editor/filesystem_dock.h" #include "editor/inspector_dock.h" #include "editor/plugins/curve_editor_plugin.h" +#include "editor/plugins/material_editor_plugin.h" #include "editor/plugins/shader_editor_plugin.h" #include "editor/themes/editor_scale.h" #include "scene/animation/tween.h" @@ -50,12 +51,14 @@ #include "scene/gui/check_box.h" #include "scene/gui/code_edit.h" #include "scene/gui/color_picker.h" +#include "scene/gui/flow_container.h" #include "scene/gui/graph_edit.h" #include "scene/gui/menu_button.h" #include "scene/gui/option_button.h" #include "scene/gui/popup.h" #include "scene/gui/rich_text_label.h" #include "scene/gui/separator.h" +#include "scene/gui/split_container.h" #include "scene/gui/tree.h" #include "scene/gui/view_panner.h" #include "scene/main/window.h" @@ -255,7 +258,7 @@ void VisualShaderGraphPlugin::show_port_preview(VisualShader::Type p_type, int p vbox->add_child(offset); VisualShaderNodePortPreview *port_preview = memnew(VisualShaderNodePortPreview); - port_preview->setup(visual_shader, visual_shader->get_shader_type(), p_node_id, p_port_id, p_is_valid); + port_preview->setup(visual_shader, editor->preview_material, visual_shader->get_shader_type(), p_node_id, p_port_id, p_is_valid); port_preview->set_h_size_flags(Control::SIZE_SHRINK_CENTER); vbox->add_child(port_preview); link.preview_visible = true; @@ -1526,6 +1529,7 @@ void VisualShaderEditor::edit_shader(const Ref &p_shader) { visual_shader->set_graph_offset(graph->get_scroll_offset() / EDSCALE); _set_mode(visual_shader->get_mode()); + preview_material->set_shader(visual_shader); _update_nodes(); } else { if (visual_shader.is_valid()) { @@ -1949,6 +1953,96 @@ bool VisualShaderEditor::_is_available(int p_mode) { return (p_mode == -1 || (p_mode & current_mode) != 0); } +bool VisualShaderEditor::_update_preview_parameter_tree() { + bool found = false; + bool use_filter = !param_filter_name.is_empty(); + + parameters->clear(); + TreeItem *root = parameters->create_item(); + + for (const KeyValue &prop : parameter_props) { + String param_name = prop.value.name; + if (use_filter && !param_name.containsn(param_filter_name)) { + continue; + } + + TreeItem *item = parameters->create_item(root); + item->set_text(0, param_name); + item->set_meta("id", param_name); + + if (param_name == selected_param_id) { + parameters->set_selected(item); + found = true; + } + + if (prop.value.type == Variant::OBJECT) { + item->set_icon(0, get_editor_theme_icon(SNAME("ImageTexture"))); + } else { + item->set_icon(0, get_editor_theme_icon(Variant::get_type_name(prop.value.type))); + } + } + + return found; +} + +void VisualShaderEditor::_clear_preview_param() { + selected_param_id = ""; + current_prop = nullptr; + + if (param_vbox2->get_child_count() > 0) { + param_vbox2->remove_child(param_vbox2->get_child(0)); + } + + param_vbox->hide(); +} + +void VisualShaderEditor::_update_preview_parameter_list() { + material_editor->edit(preview_material.ptr(), env); + + List properties; + RenderingServer::get_singleton()->get_shader_parameter_list(visual_shader->get_rid(), &properties); + + HashSet params_to_remove; + for (const KeyValue &E : parameter_props) { + params_to_remove.insert(E.key); + } + parameter_props.clear(); + + for (const PropertyInfo &prop : properties) { + String param_name = prop.name; + + if (visual_shader->_has_preview_shader_parameter(param_name)) { + preview_material->set_shader_parameter(param_name, visual_shader->_get_preview_shader_parameter(param_name)); + } else { + preview_material->set_shader_parameter(param_name, RenderingServer::get_singleton()->shader_get_parameter_default(visual_shader->get_rid(), param_name)); + } + + parameter_props.insert(param_name, prop); + params_to_remove.erase(param_name); + + if (param_name == selected_param_id) { + current_prop->update_property(); + current_prop->update_editor_property_status(); + current_prop->update_cache(); + } + } + + _update_preview_parameter_tree(); + + // Removes invalid parameters. + for (const String ¶m_name : params_to_remove) { + preview_material->set_shader_parameter(param_name, Variant()); + + if (visual_shader->_has_preview_shader_parameter(param_name)) { + visual_shader->_set_preview_shader_parameter(param_name, Variant()); + } + + if (param_name == selected_param_id) { + _clear_preview_param(); + } + } +} + void VisualShaderEditor::_update_nodes() { clear_custom_types(); Dictionary added; @@ -4872,6 +4966,74 @@ void VisualShaderEditor::_sbox_input(const Ref &p_ie) { } } +void VisualShaderEditor::_param_filter_changed(const String &p_text) { + param_filter_name = p_text; + + if (!_update_preview_parameter_tree()) { + _clear_preview_param(); + } +} + +void VisualShaderEditor::_param_property_changed(const String &p_property, const Variant &p_value, const String &p_field, bool p_changing) { + if (p_changing) { + return; + } + String raw_prop_name = p_property.trim_prefix("shader_parameter/"); + + EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); + + undo_redo->create_action(vformat(TTR("Edit Preview Parameter: %s"), p_property)); + undo_redo->add_do_method(visual_shader.ptr(), "_set_preview_shader_parameter", raw_prop_name, p_value); + undo_redo->add_undo_method(visual_shader.ptr(), "_set_preview_shader_parameter", raw_prop_name, preview_material->get(p_property)); + undo_redo->add_do_method(this, "_update_current_param"); + undo_redo->add_undo_method(this, "_update_current_param"); + undo_redo->commit_action(); +} + +void VisualShaderEditor::_update_current_param() { + if (current_prop != nullptr) { + String name = current_prop->get_meta("id"); + preview_material->set("shader_parameter/" + name, visual_shader->_get_preview_shader_parameter(name)); + + current_prop->update_property(); + current_prop->update_editor_property_status(); + current_prop->update_cache(); + } +} + +void VisualShaderEditor::_param_selected() { + _clear_preview_param(); + + TreeItem *item = parameters->get_selected(); + selected_param_id = item->get_meta("id"); + + PropertyInfo pi = parameter_props.get(selected_param_id); + EditorProperty *prop = EditorInspector::instantiate_property_editor(preview_material.ptr(), pi.type, pi.name, pi.hint, pi.hint_string, pi.usage); + if (!prop) { + return; + } + prop->connect("property_changed", callable_mp(this, &VisualShaderEditor::_param_property_changed)); + prop->set_h_size_flags(SIZE_EXPAND_FILL); + prop->set_object_and_property(preview_material.ptr(), "shader_parameter/" + pi.name); + + prop->set_label(TTR("Value:")); + prop->update_property(); + prop->update_editor_property_status(); + prop->update_cache(); + + current_prop = prop; + current_prop->set_meta("id", selected_param_id); + + param_vbox2->add_child(prop); + param_vbox->show(); +} + +void VisualShaderEditor::_param_unselected() { + parameters->deselect_all(); + + _clear_preview_param(); +} + void VisualShaderEditor::_notification(int p_what) { switch (p_what) { case NOTIFICATION_POSTINITIALIZE: { @@ -4915,9 +5077,11 @@ void VisualShaderEditor::_notification(int p_what) { case NOTIFICATION_THEME_CHANGED: { highend_label->set_modulate(get_theme_color(SNAME("highend_color"), EditorStringName(Editor))); + param_filter->set_right_icon(Control::get_editor_theme_icon(SNAME("Search"))); node_filter->set_right_icon(Control::get_editor_theme_icon(SNAME("Search"))); - preview_shader->set_icon(Control::get_editor_theme_icon(SNAME("Shader"))); + code_preview_button->set_icon(Control::get_editor_theme_icon(SNAME("Shader"))); + shader_preview_button->set_icon(Control::get_editor_theme_icon(SNAME("SubViewport"))); { Color background_color = EDITOR_GET("text_editor/theme/highlighting/background_color"); @@ -4970,7 +5134,7 @@ void VisualShaderEditor::_notification(int p_what) { tools->set_icon(get_editor_theme_icon(SNAME("Tools"))); - if (p_what == NOTIFICATION_THEME_CHANGED && is_visible_in_tree()) { + if (is_visible_in_tree()) { _update_graph(); } } break; @@ -5907,14 +6071,14 @@ void VisualShaderEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da } void VisualShaderEditor::_show_preview_text() { - preview_showed = !preview_showed; - if (preview_showed) { - if (preview_first) { - preview_window->set_size(Size2(400 * EDSCALE, 600 * EDSCALE)); - preview_window->popup_centered(); - preview_first = false; + code_preview_showed = !code_preview_showed; + if (code_preview_showed) { + if (code_preview_first) { + code_preview_window->set_size(Size2(400 * EDSCALE, 600 * EDSCALE)); + code_preview_window->popup_centered(); + code_preview_first = false; } else { - preview_window->popup(); + code_preview_window->popup(); } _preview_size_changed(); @@ -5923,18 +6087,18 @@ void VisualShaderEditor::_show_preview_text() { pending_update_preview = false; } } else { - preview_window->hide(); + code_preview_window->hide(); } } void VisualShaderEditor::_preview_close_requested() { - preview_showed = false; - preview_window->hide(); - preview_shader->set_pressed(false); + code_preview_showed = false; + code_preview_window->hide(); + code_preview_button->set_pressed(false); } void VisualShaderEditor::_preview_size_changed() { - preview_vbox->set_custom_minimum_size(preview_window->get_size()); + code_preview_vbox->set_custom_minimum_size(code_preview_window->get_size()); } static ShaderLanguage::DataType _visual_shader_editor_get_global_shader_uniform_type(const StringName &p_variable) { @@ -5943,7 +6107,7 @@ static ShaderLanguage::DataType _visual_shader_editor_get_global_shader_uniform_ } void VisualShaderEditor::_update_preview() { - if (!preview_showed) { + if (!code_preview_showed) { pending_update_preview = true; return; } @@ -6035,14 +6199,25 @@ void VisualShaderEditor::_get_next_nodes_recursively(VisualShader::Type p_type, void VisualShaderEditor::_visibility_changed() { if (!is_visible()) { - if (preview_window->is_visible()) { - preview_shader->set_pressed(false); - preview_window->hide(); - preview_showed = false; + if (code_preview_window->is_visible()) { + code_preview_button->set_pressed(false); + code_preview_window->hide(); + code_preview_showed = false; } } } +void VisualShaderEditor::_show_shader_preview() { + shader_preview_showed = !shader_preview_showed; + if (shader_preview_showed) { + shader_preview_vbox->show(); + } else { + shader_preview_vbox->hide(); + + _param_unselected(); + } +} + void VisualShaderEditor::_bind_methods() { ClassDB::bind_method("_update_nodes", &VisualShaderEditor::_update_nodes); ClassDB::bind_method("_update_graph", &VisualShaderEditor::_update_graph); @@ -6057,6 +6232,7 @@ void VisualShaderEditor::_bind_methods() { ClassDB::bind_method("_update_constant", &VisualShaderEditor::_update_constant); ClassDB::bind_method("_update_parameter", &VisualShaderEditor::_update_parameter); ClassDB::bind_method("_update_next_previews", &VisualShaderEditor::_update_next_previews); + ClassDB::bind_method("_update_current_param", &VisualShaderEditor::_update_current_param); } VisualShaderEditor::VisualShaderEditor() { @@ -6065,14 +6241,19 @@ VisualShaderEditor::VisualShaderEditor() { FileSystemDock::get_singleton()->get_script_create_dialog()->connect("script_created", callable_mp(this, &VisualShaderEditor::_script_created)); FileSystemDock::get_singleton()->connect("resource_removed", callable_mp(this, &VisualShaderEditor::_resource_removed)); + HSplitContainer *main_box = memnew(HSplitContainer); + main_box->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT); + add_child(main_box); + graph = memnew(GraphEdit); - graph->get_menu_hbox()->set_h_size_flags(SIZE_EXPAND_FILL); - graph->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT); + graph->set_v_size_flags(SIZE_EXPAND_FILL); + graph->set_h_size_flags(SIZE_EXPAND_FILL); + graph->set_custom_minimum_size(Size2(200 * EDSCALE, 0)); graph->set_grid_pattern(GraphEdit::GridPattern::GRID_PATTERN_DOTS); int grid_pattern = EDITOR_GET("editors/visual_editors/grid_pattern"); graph->set_grid_pattern((GraphEdit::GridPattern)grid_pattern); graph->set_show_zoom_label(true); - add_child(graph); + main_box->add_child(graph); SET_DRAG_FORWARDING_GCD(graph, VisualShaderEditor); float graph_minimap_opacity = EDITOR_GET("editors/visual_editors/minimap_opacity"); graph->set_minimap_opacity(graph_minimap_opacity); @@ -6160,9 +6341,30 @@ VisualShaderEditor::VisualShaderEditor() { graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_TRANSFORM, VisualShaderNode::PORT_TYPE_TRANSFORM); graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_SAMPLER, VisualShaderNode::PORT_TYPE_SAMPLER); + PanelContainer *toolbar_panel = static_cast(graph->get_menu_hbox()->get_parent()); + toolbar_panel->set_anchors_and_offsets_preset(Control::PRESET_TOP_WIDE, PRESET_MODE_MINSIZE, 10); + toolbar_panel->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); + + HFlowContainer *toolbar = memnew(HFlowContainer); + { + LocalVector nodes; + for (int i = 0; i < graph->get_menu_hbox()->get_child_count(); i++) { + Node *child = graph->get_menu_hbox()->get_child(i); + nodes.push_back(child); + } + + for (Node *node : nodes) { + graph->get_menu_hbox()->remove_child(node); + toolbar->add_child(node); + } + + graph->get_menu_hbox()->hide(); + toolbar_panel->add_child(toolbar); + } + VSeparator *vs = memnew(VSeparator); - graph->get_menu_hbox()->add_child(vs); - graph->get_menu_hbox()->move_child(vs, 0); + toolbar->add_child(vs); + toolbar->move_child(vs, 0); custom_mode_box = memnew(CheckBox); custom_mode_box->set_text(TTR("Custom")); @@ -6196,22 +6398,22 @@ VisualShaderEditor::VisualShaderEditor() { edit_type = edit_type_standard; - graph->get_menu_hbox()->add_child(custom_mode_box); - graph->get_menu_hbox()->move_child(custom_mode_box, 0); - graph->get_menu_hbox()->add_child(edit_type_standard); - graph->get_menu_hbox()->move_child(edit_type_standard, 0); - graph->get_menu_hbox()->add_child(edit_type_particles); - graph->get_menu_hbox()->move_child(edit_type_particles, 0); - graph->get_menu_hbox()->add_child(edit_type_sky); - graph->get_menu_hbox()->move_child(edit_type_sky, 0); - graph->get_menu_hbox()->add_child(edit_type_fog); - graph->get_menu_hbox()->move_child(edit_type_fog, 0); + toolbar->add_child(custom_mode_box); + toolbar->move_child(custom_mode_box, 0); + toolbar->add_child(edit_type_standard); + toolbar->move_child(edit_type_standard, 0); + toolbar->add_child(edit_type_particles); + toolbar->move_child(edit_type_particles, 0); + toolbar->add_child(edit_type_sky); + toolbar->move_child(edit_type_sky, 0); + toolbar->add_child(edit_type_fog); + toolbar->move_child(edit_type_fog, 0); add_node = memnew(Button); add_node->set_flat(true); add_node->set_text(TTR("Add Node...")); - graph->get_menu_hbox()->add_child(add_node); - graph->get_menu_hbox()->move_child(add_node, 0); + toolbar->add_child(add_node); + toolbar->move_child(add_node, 0); add_node->connect(SceneStringName(pressed), callable_mp(this, &VisualShaderEditor::_show_members_dialog).bind(false, VisualShaderNode::PORT_TYPE_MAX, VisualShaderNode::PORT_TYPE_MAX)); graph->connect("graph_elements_linked_to_frame_request", callable_mp(this, &VisualShaderEditor::_nodes_linked_to_frame_request)); @@ -6220,46 +6422,54 @@ VisualShaderEditor::VisualShaderEditor() { varying_button = memnew(MenuButton); varying_button->set_text(TTR("Manage Varyings")); varying_button->set_switch_on_hover(true); - graph->get_menu_hbox()->add_child(varying_button); + toolbar->add_child(varying_button); PopupMenu *varying_menu = varying_button->get_popup(); varying_menu->add_item(TTR("Add Varying"), int(VaryingMenuOptions::ADD)); varying_menu->add_item(TTR("Remove Varying"), int(VaryingMenuOptions::REMOVE)); varying_menu->connect(SceneStringName(id_pressed), callable_mp(this, &VisualShaderEditor::_varying_menu_id_pressed)); - preview_shader = memnew(Button); - preview_shader->set_theme_type_variation("FlatButton"); - preview_shader->set_toggle_mode(true); - preview_shader->set_tooltip_text(TTR("Show generated shader code.")); - graph->get_menu_hbox()->add_child(preview_shader); - preview_shader->connect(SceneStringName(pressed), callable_mp(this, &VisualShaderEditor::_show_preview_text)); + code_preview_button = memnew(Button); + code_preview_button->set_theme_type_variation("FlatButton"); + code_preview_button->set_toggle_mode(true); + code_preview_button->set_tooltip_text(TTR("Show generated shader code.")); + toolbar->add_child(code_preview_button); + code_preview_button->connect(SceneStringName(pressed), callable_mp(this, &VisualShaderEditor::_show_preview_text)); + + shader_preview_button = memnew(Button); + shader_preview_button->set_theme_type_variation("FlatButton"); + shader_preview_button->set_toggle_mode(true); + shader_preview_button->set_tooltip_text(TTR("Toggle shader preview.")); + shader_preview_button->set_pressed(true); + toolbar->add_child(shader_preview_button); + shader_preview_button->connect(SceneStringName(pressed), callable_mp(this, &VisualShaderEditor::_show_shader_preview)); /////////////////////////////////////// - // PREVIEW WINDOW + // CODE PREVIEW /////////////////////////////////////// - preview_window = memnew(Window); - preview_window->set_title(TTR("Generated Shader Code")); - preview_window->set_visible(preview_showed); - preview_window->set_exclusive(true); - preview_window->connect("close_requested", callable_mp(this, &VisualShaderEditor::_preview_close_requested)); - preview_window->connect("size_changed", callable_mp(this, &VisualShaderEditor::_preview_size_changed)); - add_child(preview_window); + code_preview_window = memnew(Window); + code_preview_window->set_title(TTR("Generated Shader Code")); + code_preview_window->set_visible(code_preview_showed); + code_preview_window->set_exclusive(true); + code_preview_window->connect("close_requested", callable_mp(this, &VisualShaderEditor::_preview_close_requested)); + code_preview_window->connect("size_changed", callable_mp(this, &VisualShaderEditor::_preview_size_changed)); + add_child(code_preview_window); - preview_vbox = memnew(VBoxContainer); - preview_window->add_child(preview_vbox); - preview_vbox->add_theme_constant_override("separation", 0); + code_preview_vbox = memnew(VBoxContainer); + code_preview_window->add_child(code_preview_vbox); + code_preview_vbox->add_theme_constant_override("separation", 0); preview_text = memnew(CodeEdit); syntax_highlighter.instantiate(); - preview_vbox->add_child(preview_text); + code_preview_vbox->add_child(preview_text); preview_text->set_v_size_flags(Control::SIZE_EXPAND_FILL); preview_text->set_syntax_highlighter(syntax_highlighter); preview_text->set_draw_line_numbers(true); preview_text->set_editable(false); error_panel = memnew(PanelContainer); - preview_vbox->add_child(error_panel); + code_preview_vbox->add_child(error_panel); error_panel->set_visible(false); error_label = memnew(Label); @@ -6290,6 +6500,70 @@ VisualShaderEditor::VisualShaderEditor() { connection_popup_menu->add_item(TTR("Insert New Reroute"), ConnectionMenuOptions::INSERT_NEW_REROUTE); connection_popup_menu->connect(SceneStringName(id_pressed), callable_mp(this, &VisualShaderEditor::_connection_menu_id_pressed)); + /////////////////////////////////////// + // SHADER PREVIEW + /////////////////////////////////////// + + shader_preview_vbox = memnew(VBoxContainer); + shader_preview_vbox->set_custom_minimum_size(Size2(200 * EDSCALE, 0)); + main_box->add_child(shader_preview_vbox); + + VSplitContainer *preview_split = memnew(VSplitContainer); + preview_split->set_v_size_flags(SIZE_EXPAND_FILL); + shader_preview_vbox->add_child(preview_split); + + // Initialize material editor. + { + env.instantiate(); + Ref sky = memnew(Sky()); + env->set_sky(sky); + env->set_background(Environment::BG_COLOR); + env->set_ambient_source(Environment::AMBIENT_SOURCE_SKY); + env->set_reflection_source(Environment::REFLECTION_SOURCE_SKY); + + preview_material.instantiate(); + preview_material->connect(CoreStringName(property_list_changed), callable_mp(this, &VisualShaderEditor::_update_preview_parameter_list)); + + material_editor = memnew(MaterialEditor); + preview_split->add_child(material_editor); + } + + VBoxContainer *params_vbox = memnew(VBoxContainer); + preview_split->add_child(params_vbox); + + param_filter = memnew(LineEdit); + param_filter->connect(SceneStringName(text_changed), callable_mp(this, &VisualShaderEditor::_param_filter_changed)); + param_filter->set_h_size_flags(SIZE_EXPAND_FILL); + param_filter->set_placeholder(TTR("Filter Parameters")); + params_vbox->add_child(param_filter); + + ScrollContainer *sc = memnew(ScrollContainer); + sc->set_v_size_flags(SIZE_EXPAND_FILL); + params_vbox->add_child(sc); + + parameters = memnew(Tree); + parameters->set_hide_root(true); + parameters->set_allow_reselect(true); + parameters->set_hide_folding(false); + parameters->set_h_size_flags(SIZE_EXPAND_FILL); + parameters->set_v_size_flags(SIZE_EXPAND_FILL); + parameters->connect(SceneStringName(item_selected), callable_mp(this, &VisualShaderEditor::_param_selected)); + parameters->connect("nothing_selected", callable_mp(this, &VisualShaderEditor::_param_unselected)); + sc->add_child(parameters); + + param_vbox = memnew(VBoxContainer); + param_vbox->set_v_size_flags(SIZE_EXPAND_FILL); + param_vbox->hide(); + params_vbox->add_child(param_vbox); + + ScrollContainer *sc2 = memnew(ScrollContainer); + sc2->set_v_size_flags(SIZE_EXPAND_FILL); + param_vbox->add_child(sc2); + + param_vbox2 = memnew(VBoxContainer); + param_vbox2->set_h_size_flags(SIZE_EXPAND_FILL); + sc2->add_child(param_vbox2); + /////////////////////////////////////// // SHADER NODES TREE /////////////////////////////////////// @@ -7714,36 +7988,21 @@ void VisualShaderNodePortPreview::_shader_changed() { mat.instantiate(); mat->set_shader(preview_shader); - //find if a material is also being edited and copy parameters to this one - - for (int i = EditorNode::get_singleton()->get_editor_selection_history()->get_path_size() - 1; i >= 0; i--) { - Object *object = ObjectDB::get_instance(EditorNode::get_singleton()->get_editor_selection_history()->get_path_object(i)); - ShaderMaterial *src_mat; - if (!object) { - continue; - } - if (object->has_method("get_material_override")) { // trying getting material from MeshInstance - src_mat = Object::cast_to(object->call("get_material_override")); - } else if (object->has_method("get_material")) { // from CanvasItem/Node2D - src_mat = Object::cast_to(object->call("get_material")); - } else { - src_mat = Object::cast_to(object); - } - if (src_mat && src_mat->get_shader().is_valid()) { - List params; - src_mat->get_shader()->get_shader_uniform_list(¶ms); - for (const PropertyInfo &E : params) { - mat->set_shader_parameter(E.name, src_mat->get_shader_parameter(E.name)); - } + if (preview_mat.is_valid() && preview_mat->get_shader().is_valid()) { + List params; + preview_mat->get_shader()->get_shader_uniform_list(¶ms); + for (const PropertyInfo &E : params) { + mat->set_shader_parameter(E.name, preview_mat->get_shader_parameter(E.name)); } } set_material(mat); } -void VisualShaderNodePortPreview::setup(const Ref &p_shader, VisualShader::Type p_type, int p_node, int p_port, bool p_is_valid) { +void VisualShaderNodePortPreview::setup(const Ref &p_shader, Ref &p_preview_material, VisualShader::Type p_type, int p_node, int p_port, bool p_is_valid) { shader = p_shader; shader->connect_changed(callable_mp(this, &VisualShaderNodePortPreview::_shader_changed), CONNECT_DEFERRED); + preview_mat = p_preview_material; type = p_type; port = p_port; node = p_node; diff --git a/editor/plugins/visual_shader_editor_plugin.h b/editor/plugins/visual_shader_editor_plugin.h index 6ce096f8218..b17036e39f8 100644 --- a/editor/plugins/visual_shader_editor_plugin.h +++ b/editor/plugins/visual_shader_editor_plugin.h @@ -50,6 +50,7 @@ class RichTextLabel; class Tree; class VisualShaderEditor; +class MaterialEditor; class VisualShaderNodePlugin : public RefCounted { GDCLASS(VisualShaderNodePlugin, RefCounted); @@ -206,11 +207,18 @@ class VisualShaderEditor : public ShaderEditor { int editing_port = -1; Ref edited_property_holder; + MaterialEditor *material_editor = nullptr; Ref visual_shader; + Ref preview_material; + Ref env; + String param_filter_name; + EditorProperty *current_prop = nullptr; + VBoxContainer *shader_preview_vbox = nullptr; GraphEdit *graph = nullptr; Button *add_node = nullptr; MenuButton *varying_button = nullptr; - Button *preview_shader = nullptr; + Button *code_preview_button = nullptr; + Button *shader_preview_button = nullptr; OptionButton *edit_type = nullptr; OptionButton *edit_type_standard = nullptr; @@ -222,8 +230,8 @@ class VisualShaderEditor : public ShaderEditor { bool pending_update_preview = false; bool shader_error = false; - Window *preview_window = nullptr; - VBoxContainer *preview_vbox = nullptr; + Window *code_preview_window = nullptr; + VBoxContainer *code_preview_vbox = nullptr; CodeEdit *preview_text = nullptr; Ref syntax_highlighter = nullptr; PanelContainer *error_panel = nullptr; @@ -261,8 +269,17 @@ class VisualShaderEditor : public ShaderEditor { PopupPanel *frame_tint_color_pick_popup = nullptr; ColorPicker *frame_tint_color_picker = nullptr; - bool preview_first = true; - bool preview_showed = false; + bool code_preview_first = true; + bool code_preview_showed = false; + + bool shader_preview_showed = true; + + LineEdit *param_filter = nullptr; + String selected_param_id; + Tree *parameters = nullptr; + HashMap parameter_props; + VBoxContainer *param_vbox = nullptr; + VBoxContainer *param_vbox2 = nullptr; enum ShaderModeFlags { MODE_FLAGS_SPATIAL_CANVASITEM = 1, @@ -349,6 +366,10 @@ class VisualShaderEditor : public ShaderEditor { void _show_add_varying_dialog(); void _show_remove_varying_dialog(); + void _clear_preview_param(); + void _update_preview_parameter_list(); + bool _update_preview_parameter_tree(); + void _update_nodes(); void _update_graph(); @@ -414,6 +435,8 @@ class VisualShaderEditor : public ShaderEditor { void _get_next_nodes_recursively(VisualShader::Type p_type, int p_node_id, LocalVector &r_nodes) const; String _get_description(int p_idx); + void _show_shader_preview(); + Vector nodes_link_to_frame_buffer; // Contains the nodes that are requested to be linked to a frame. This is used to perform one Undo/Redo operation for dragging nodes. int frame_node_id_to_link_to = -1; @@ -592,6 +615,12 @@ class VisualShaderEditor : public ShaderEditor { void _resource_removed(const Ref &p_resource); void _resources_removed(); + void _param_property_changed(const String &p_property, const Variant &p_value, const String &p_field = "", bool p_changing = false); + void _update_current_param(); + void _param_filter_changed(const String &p_text); + void _param_selected(); + void _param_unselected(); + protected: void _notification(int p_what); static void _bind_methods(); @@ -652,6 +681,7 @@ public: class VisualShaderNodePortPreview : public Control { GDCLASS(VisualShaderNodePortPreview, Control); Ref shader; + Ref preview_mat; VisualShader::Type type = VisualShader::Type::TYPE_MAX; int node = 0; int port = 0; @@ -662,7 +692,7 @@ protected: public: virtual Size2 get_minimum_size() const override; - void setup(const Ref &p_shader, VisualShader::Type p_type, int p_node, int p_port, bool p_is_valid); + void setup(const Ref &p_shader, Ref &p_preview_material, VisualShader::Type p_type, int p_node, int p_port, bool p_is_valid); }; class VisualShaderConversionPlugin : public EditorResourceConversionPlugin { diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp index 1fa52b9c733..aee014a8827 100644 --- a/scene/resources/visual_shader.cpp +++ b/scene/resources/visual_shader.cpp @@ -32,6 +32,7 @@ #include "core/templates/rb_map.h" #include "core/templates/vmap.h" +#include "core/variant/variant_utility.h" #include "servers/rendering/shader_types.h" #include "visual_shader_nodes.h" #include "visual_shader_particle_nodes.h" @@ -897,6 +898,44 @@ VisualShader::VaryingType VisualShader::get_varying_type(const String &p_name) { return varyings[p_name].type; } +void VisualShader::_set_preview_shader_parameter(const String &p_name, const Variant &p_value) { +#ifdef TOOLS_ENABLED + if (Engine::get_singleton()->is_editor_hint()) { + if (p_value.get_type() == Variant::NIL) { + if (!preview_params.erase(p_name)) { + return; + } + } else { + Variant *var = preview_params.getptr(p_name); + if (var != nullptr && *var == p_value) { + return; + } + preview_params.insert(p_name, p_value); + } + emit_changed(); + } +#endif // TOOLS_ENABLED +} + +Variant VisualShader::_get_preview_shader_parameter(const String &p_name) const { +#ifdef TOOLS_ENABLED + if (Engine::get_singleton()->is_editor_hint()) { + ERR_FAIL_COND_V(!preview_params.has(p_name), Variant()); + return preview_params.get(p_name); + } +#endif // TOOLS_ENABLED + return Variant(); +} + +bool VisualShader::_has_preview_shader_parameter(const String &p_name) const { +#ifdef TOOLS_ENABLED + if (Engine::get_singleton()->is_editor_hint()) { + return preview_params.has(p_name); + } +#endif // TOOLS_ENABLED + return false; +} + void VisualShader::add_node(Type p_type, const Ref &p_node, const Vector2 &p_position, int p_id) { ERR_FAIL_COND(p_node.is_null()); ERR_FAIL_COND(p_id < 2); @@ -1695,7 +1734,16 @@ bool VisualShader::_set(const StringName &p_name, const Variant &p_value) { } _queue_update(); return true; - } else if (prop_name.begins_with("nodes/")) { + } +#ifdef TOOLS_ENABLED + else if (prop_name.begins_with("preview_params/") && Engine::get_singleton()->is_editor_hint()) { + String param_name = prop_name.get_slicec('/', 1); + Variant value = VariantUtilityFunctions::str_to_var(p_value); + preview_params[param_name] = value; + return true; + } +#endif + else if (prop_name.begins_with("nodes/")) { String typestr = prop_name.get_slicec('/', 1); Type type = TYPE_VERTEX; for (int i = 0; i < TYPE_MAX; i++) { @@ -1767,7 +1815,19 @@ bool VisualShader::_get(const StringName &p_name, Variant &r_ret) const { r_ret = String(); } return true; - } else if (prop_name.begins_with("nodes/")) { + } +#ifdef TOOLS_ENABLED + else if (prop_name.begins_with("preview_params/") && Engine::get_singleton()->is_editor_hint()) { + String param_name = prop_name.get_slicec('/', 1); + if (preview_params.has(param_name)) { + r_ret = VariantUtilityFunctions::var_to_str(preview_params[param_name]); + } else { + r_ret = String(); + } + return true; + } +#endif // TOOLS_ENABLED + else if (prop_name.begins_with("nodes/")) { String typestr = prop_name.get_slicec('/', 1); Type type = TYPE_VERTEX; for (int i = 0; i < TYPE_MAX; i++) { @@ -1864,6 +1924,14 @@ void VisualShader::_get_property_list(List *p_list) const { p_list->push_back(PropertyInfo(Variant::STRING, vformat("%s/%s", PNAME("varyings"), E.key), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); } +#ifdef TOOLS_ENABLED + if (Engine::get_singleton()->is_editor_hint()) { + for (const KeyValue &E : preview_params) { + p_list->push_back(PropertyInfo(Variant::STRING, vformat("%s/%s", PNAME("preview_params"), E.key), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); + } + } +#endif // TOOLS_ENABLED + for (int i = 0; i < TYPE_MAX; i++) { for (const KeyValue &E : graph[i].nodes) { String prop_name = "nodes/"; @@ -2943,6 +3011,10 @@ void VisualShader::_bind_methods() { ClassDB::bind_method(D_METHOD("remove_varying", "name"), &VisualShader::remove_varying); ClassDB::bind_method(D_METHOD("has_varying", "name"), &VisualShader::has_varying); + ClassDB::bind_method(D_METHOD("_set_preview_shader_parameter", "name", "value"), &VisualShader::_set_preview_shader_parameter); + ClassDB::bind_method(D_METHOD("_get_preview_shader_parameter", "name"), &VisualShader::_get_preview_shader_parameter); + ClassDB::bind_method(D_METHOD("_has_preview_shader_parameter", "name"), &VisualShader::_has_preview_shader_parameter); + ClassDB::bind_method(D_METHOD("_update_shader"), &VisualShader::_update_shader); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "graph_offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_graph_offset", "get_graph_offset"); diff --git a/scene/resources/visual_shader.h b/scene/resources/visual_shader.h index 9cd8f86d0fe..2b213948def 100644 --- a/scene/resources/visual_shader.h +++ b/scene/resources/visual_shader.h @@ -42,8 +42,6 @@ class VisualShaderNode; class VisualShader : public Shader { GDCLASS(VisualShader, Shader); - friend class VisualShaderNodeVersionChecker; - public: enum Type { TYPE_VERTEX, @@ -142,6 +140,9 @@ private: HashSet flags; HashMap varyings; +#ifdef TOOLS_ENABLED + HashMap preview_params; +#endif List varyings_list; mutable SafeFlag dirty; @@ -199,6 +200,10 @@ public: // internal methods void set_varying_type(const String &p_name, VaryingType p_type); VaryingType get_varying_type(const String &p_name); + void _set_preview_shader_parameter(const String &p_name, const Variant &p_value); + Variant _get_preview_shader_parameter(const String &p_name) const; + bool _has_preview_shader_parameter(const String &p_name) const; + Vector2 get_node_position(Type p_type, int p_id) const; Ref get_node(Type p_type, int p_id) const;