From 7ddaff47a3fad50ec488226ab64fdaffdf2d172e Mon Sep 17 00:00:00 2001 From: Yuri Roubinsky Date: Tue, 28 Jul 2020 11:02:57 +0300 Subject: [PATCH] Added UniformRef visual shader node --- doc/classes/VisualShaderNodeUniformRef.xml | 20 ++ .../plugins/visual_shader_editor_plugin.cpp | 131 ++++++++++ editor/plugins/visual_shader_editor_plugin.h | 1 + scene/register_scene_types.cpp | 1 + scene/resources/visual_shader.cpp | 227 +++++++++++++++++- scene/resources/visual_shader.h | 60 +++++ 6 files changed, 439 insertions(+), 1 deletion(-) create mode 100644 doc/classes/VisualShaderNodeUniformRef.xml diff --git a/doc/classes/VisualShaderNodeUniformRef.xml b/doc/classes/VisualShaderNodeUniformRef.xml new file mode 100644 index 00000000000..db02e398abf --- /dev/null +++ b/doc/classes/VisualShaderNodeUniformRef.xml @@ -0,0 +1,20 @@ + + + + A reference to an existing [VisualShaderNodeUniform]. + + + Creating a reference to a [VisualShaderNodeUniform] allows you to reuse this uniform in different shaders or shader stages easily. + + + + + + + + The name of the uniform which this reference points to. + + + + + diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp index d987f6f7c09..5473b0c2842 100644 --- a/editor/plugins/visual_shader_editor_plugin.cpp +++ b/editor/plugins/visual_shader_editor_plugin.cpp @@ -489,6 +489,45 @@ void VisualShaderEditor::_update_graph() { Vector nodes = visual_shader->get_node_list(type); + VisualShaderNodeUniformRef::clear_uniforms(); + + // scan for all uniforms + + for (int t = 0; t < VisualShader::TYPE_MAX; t++) { + Vector tnodes = visual_shader->get_node_list((VisualShader::Type)t); + for (int i = 0; i < tnodes.size(); i++) { + Ref vsnode = visual_shader->get_node((VisualShader::Type)t, tnodes[i]); + Ref uniform = vsnode; + + if (uniform.is_valid()) { + Ref float_uniform = vsnode; + Ref int_uniform = vsnode; + Ref vec3_uniform = vsnode; + Ref color_uniform = vsnode; + Ref bool_uniform = vsnode; + Ref transform_uniform = vsnode; + + VisualShaderNodeUniformRef::UniformType uniform_type; + if (float_uniform.is_valid()) { + uniform_type = VisualShaderNodeUniformRef::UniformType::UNIFORM_TYPE_FLOAT; + } else if (int_uniform.is_valid()) { + uniform_type = VisualShaderNodeUniformRef::UniformType::UNIFORM_TYPE_INT; + } else if (bool_uniform.is_valid()) { + uniform_type = VisualShaderNodeUniformRef::UniformType::UNIFORM_TYPE_BOOLEAN; + } else if (vec3_uniform.is_valid()) { + uniform_type = VisualShaderNodeUniformRef::UniformType::UNIFORM_TYPE_VECTOR; + } else if (transform_uniform.is_valid()) { + uniform_type = VisualShaderNodeUniformRef::UniformType::UNIFORM_TYPE_TRANSFORM; + } else if (color_uniform.is_valid()) { + uniform_type = VisualShaderNodeUniformRef::UniformType::UNIFORM_TYPE_COLOR; + } else { + uniform_type = VisualShaderNodeUniformRef::UniformType::UNIFORM_TYPE_SAMPLER; + } + VisualShaderNodeUniformRef::add_uniform(uniform->get_uniform_name(), uniform_type); + } + } + } + Control *offset; for (int n_i = 0; n_i < nodes.size(); n_i++) { @@ -2035,6 +2074,41 @@ void VisualShaderEditor::_input_select_item(Ref input, St undo_redo->commit_action(); } +void VisualShaderEditor::_uniform_select_item(Ref p_uniform_ref, String p_name) { + String prev_name = p_uniform_ref->get_uniform_name(); + + if (p_name == prev_name) { + return; + } + + bool type_changed = p_uniform_ref->get_uniform_type_by_name(p_name) != p_uniform_ref->get_uniform_type_by_name(prev_name); + + UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo(); + undo_redo->create_action(TTR("UniformRef Name Changed")); + + undo_redo->add_do_method(p_uniform_ref.ptr(), "set_uniform_name", p_name); + undo_redo->add_undo_method(p_uniform_ref.ptr(), "set_uniform_name", prev_name); + + if (type_changed) { + //restore connections if type changed + VisualShader::Type type = VisualShader::Type(edit_type->get_selected()); + int id = visual_shader->find_node_id(type, p_uniform_ref); + List conns; + visual_shader->get_node_connections(type, &conns); + for (List::Element *E = conns.front(); E; E = E->next()) { + if (E->get().from_node == id) { + undo_redo->add_do_method(visual_shader.ptr(), "disconnect_nodes", type, E->get().from_node, E->get().from_port, E->get().to_node, E->get().to_port); + undo_redo->add_undo_method(visual_shader.ptr(), "connect_nodes", type, E->get().from_node, E->get().from_port, E->get().to_node, E->get().to_port); + } + } + } + + undo_redo->add_do_method(VisualShaderEditor::get_singleton(), "_update_graph"); + undo_redo->add_undo_method(VisualShaderEditor::get_singleton(), "_update_graph"); + + undo_redo->commit_action(); +} + void VisualShaderEditor::_member_filter_changed(const String &p_text) { _update_options_menu(); } @@ -2260,6 +2334,7 @@ void VisualShaderEditor::_bind_methods() { ClassDB::bind_method("_add_node", &VisualShaderEditor::_add_node); ClassDB::bind_method("_node_changed", &VisualShaderEditor::_node_changed); ClassDB::bind_method("_input_select_item", &VisualShaderEditor::_input_select_item); + ClassDB::bind_method("_uniform_select_item", &VisualShaderEditor::_uniform_select_item); ClassDB::bind_method("_set_node_size", &VisualShaderEditor::_set_node_size); ClassDB::bind_method("_clear_buffer", &VisualShaderEditor::_clear_buffer); @@ -2862,6 +2937,7 @@ VisualShaderEditor::VisualShaderEditor() { add_options.push_back(AddOption("Expression", "Special", "", "VisualShaderNodeExpression", TTR("Custom Godot Shader Language expression, with custom amount of input and output ports. This is a direct injection of code into the vertex/fragment/light function, do not use it to write the function declarations inside."))); add_options.push_back(AddOption("Fresnel", "Special", "", "VisualShaderNodeFresnel", TTR("Returns falloff based on the dot product of surface normal and view direction of camera (pass associated inputs to it)."), -1, VisualShaderNode::PORT_TYPE_SCALAR)); add_options.push_back(AddOption("GlobalExpression", "Special", "", "VisualShaderNodeGlobalExpression", TTR("Custom Godot Shader Language expression, which is placed on top of the resulted shader. You can place various function definitions inside and call it later in the Expressions. You can also declare varyings, uniforms and constants."))); + add_options.push_back(AddOption("UniformRef", "Special", "", "VisualShaderNodeUniformRef", TTR("A reference to an existing uniform."))); add_options.push_back(AddOption("ScalarDerivativeFunc", "Special", "Common", "VisualShaderNodeScalarDerivativeFunc", TTR("(Fragment/Light mode only) Scalar derivative function."), -1, VisualShaderNode::PORT_TYPE_SCALAR, VisualShader::TYPE_FRAGMENT | VisualShader::TYPE_LIGHT, -1, -1, true)); add_options.push_back(AddOption("VectorDerivativeFunc", "Special", "Common", "VisualShaderNodeVectorDerivativeFunc", TTR("(Fragment/Light mode only) Vector derivative function."), -1, VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_FRAGMENT | VisualShader::TYPE_LIGHT, -1, -1, true)); @@ -2980,6 +3056,54 @@ public: } }; +//////////////// + +class VisualShaderNodePluginUniformRefEditor : public OptionButton { + GDCLASS(VisualShaderNodePluginUniformRefEditor, OptionButton); + + Ref uniform_ref; + +public: + void _notification(int p_what) { + if (p_what == NOTIFICATION_READY) { + connect("item_selected", callable_mp(this, &VisualShaderNodePluginUniformRefEditor::_item_selected)); + } + } + + void _item_selected(int p_item) { + VisualShaderEditor::get_singleton()->call_deferred("_uniform_select_item", uniform_ref, get_item_text(p_item)); + } + + void setup(const Ref &p_uniform_ref) { + uniform_ref = p_uniform_ref; + + Ref type_icon[7] = { + EditorNode::get_singleton()->get_gui_base()->get_theme_icon("float", "EditorIcons"), + EditorNode::get_singleton()->get_gui_base()->get_theme_icon("int", "EditorIcons"), + EditorNode::get_singleton()->get_gui_base()->get_theme_icon("bool", "EditorIcons"), + EditorNode::get_singleton()->get_gui_base()->get_theme_icon("Vector3", "EditorIcons"), + EditorNode::get_singleton()->get_gui_base()->get_theme_icon("Transform", "EditorIcons"), + EditorNode::get_singleton()->get_gui_base()->get_theme_icon("Color", "EditorIcons"), + EditorNode::get_singleton()->get_gui_base()->get_theme_icon("ImageTexture", "EditorIcons"), + }; + + add_item("[None]"); + int to_select = -1; + for (int i = 0; i < p_uniform_ref->get_uniforms_count(); i++) { + if (p_uniform_ref->get_uniform_name() == p_uniform_ref->get_uniform_name_by_index(i)) { + to_select = i + 1; + } + add_icon_item(type_icon[p_uniform_ref->get_uniform_type_by_index(i)], p_uniform_ref->get_uniform_name_by_index(i)); + } + + if (to_select >= 0) { + select(to_select); + } + } +}; + +//////////////// + class VisualShaderNodePluginDefaultEditor : public VBoxContainer { GDCLASS(VisualShaderNodePluginDefaultEditor, VBoxContainer); Ref parent_resource; @@ -3095,6 +3219,13 @@ public: }; Control *VisualShaderNodePluginDefault::create_editor(const Ref &p_parent_resource, const Ref &p_node) { + if (p_node->is_class("VisualShaderNodeUniformRef")) { + //create input + VisualShaderNodePluginUniformRefEditor *uniform_editor = memnew(VisualShaderNodePluginUniformRefEditor); + uniform_editor->setup(p_node); + return uniform_editor; + } + if (p_node->is_class("VisualShaderNodeInput")) { //create input VisualShaderNodePluginInputEditor *input_editor = memnew(VisualShaderNodePluginInputEditor); diff --git a/editor/plugins/visual_shader_editor_plugin.h b/editor/plugins/visual_shader_editor_plugin.h index 0601b351310..9b80488b224 100644 --- a/editor/plugins/visual_shader_editor_plugin.h +++ b/editor/plugins/visual_shader_editor_plugin.h @@ -232,6 +232,7 @@ class VisualShaderEditor : public VBoxContainer { void _rebuild(); void _input_select_item(Ref input, String name); + void _uniform_select_item(Ref p_uniform, String p_name); void _add_input_port(int p_node, int p_port, int p_port_type, const String &p_name); void _remove_input_port(int p_node, int p_port); diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index db80dbe8142..47b8b6073bf 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -547,6 +547,7 @@ void register_scene_types() { ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_virtual_class(); + ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp index 0b8e435c195..8b84ce9e8cb 100644 --- a/scene/resources/visual_shader.cpp +++ b/scene/resources/visual_shader.cpp @@ -1151,7 +1151,10 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui bool skip_global = input.is_valid() && for_preview; if (!skip_global) { - global_code += vsnode->generate_global(get_mode(), type, node); + Ref uniform = vsnode; + if (!uniform.is_valid() || !uniform->is_global_code_generated()) { + global_code += vsnode->generate_global(get_mode(), type, node); + } String class_name = vsnode->get_class_name(); if (class_name == "VisualShaderNodeCustom") { @@ -1398,6 +1401,9 @@ void VisualShader::_update_shader() const { static const char *func_name[TYPE_MAX] = { "vertex", "fragment", "light" }; String global_expressions; + Set used_uniform_names; + List uniforms; + for (int i = 0, index = 0; i < TYPE_MAX; i++) { if (!ShaderTypes::get_singleton()->get_functions(RenderingServer::ShaderMode(shader_mode)).has(func_name[i])) { continue; @@ -1413,6 +1419,24 @@ void VisualShader::_update_shader() const { expr += "\n"; global_expressions += expr; } + Ref uniform_ref = Object::cast_to(E->get().node.ptr()); + if (uniform_ref.is_valid()) { + used_uniform_names.insert(uniform_ref->get_uniform_name()); + } + Ref uniform = Object::cast_to(E->get().node.ptr()); + if (uniform.is_valid()) { + uniforms.push_back(uniform.ptr()); + } + } + } + + for (int i = 0; i < uniforms.size(); i++) { + VisualShaderNodeUniform *uniform = uniforms[i]; + if (used_uniform_names.has(uniform->get_uniform_name())) { + global_code += uniform->generate_global(get_mode(), Type(i), -1); + const_cast(uniform)->set_global_code_generated(true); + } else { + const_cast(uniform)->set_global_code_generated(false); } } @@ -2002,6 +2026,199 @@ VisualShaderNodeInput::VisualShaderNodeInput() { shader_mode = Shader::MODE_MAX; } +////////////// UniformRef + +List uniforms; + +void VisualShaderNodeUniformRef::add_uniform(const String &p_name, UniformType p_type) { + uniforms.push_back({ p_name, p_type }); +} + +void VisualShaderNodeUniformRef::clear_uniforms() { + uniforms.clear(); +} + +String VisualShaderNodeUniformRef::get_caption() const { + return "UniformRef"; +} + +int VisualShaderNodeUniformRef::get_input_port_count() const { + return 0; +} + +VisualShaderNodeUniformRef::PortType VisualShaderNodeUniformRef::get_input_port_type(int p_port) const { + return PortType::PORT_TYPE_SCALAR; +} + +String VisualShaderNodeUniformRef::get_input_port_name(int p_port) const { + return ""; +} + +int VisualShaderNodeUniformRef::get_output_port_count() const { + if (uniform_name == "[None]") { + return 0; + } + + switch (uniform_type) { + case UniformType::UNIFORM_TYPE_FLOAT: + return 1; + case UniformType::UNIFORM_TYPE_INT: + return 1; + case UniformType::UNIFORM_TYPE_BOOLEAN: + return 1; + case UniformType::UNIFORM_TYPE_VECTOR: + return 1; + case UniformType::UNIFORM_TYPE_TRANSFORM: + return 1; + case UniformType::UNIFORM_TYPE_COLOR: + return 2; + case UniformType::UNIFORM_TYPE_SAMPLER: + return 1; + default: + break; + } + return 0; +} + +VisualShaderNodeUniformRef::PortType VisualShaderNodeUniformRef::get_output_port_type(int p_port) const { + switch (uniform_type) { + case UniformType::UNIFORM_TYPE_FLOAT: + return PortType::PORT_TYPE_SCALAR; + case UniformType::UNIFORM_TYPE_INT: + return PortType::PORT_TYPE_SCALAR_INT; + case UniformType::UNIFORM_TYPE_BOOLEAN: + return PortType::PORT_TYPE_BOOLEAN; + case UniformType::UNIFORM_TYPE_VECTOR: + return PortType::PORT_TYPE_VECTOR; + case UniformType::UNIFORM_TYPE_TRANSFORM: + return PortType::PORT_TYPE_TRANSFORM; + case UniformType::UNIFORM_TYPE_COLOR: + if (p_port == 0) { + return PortType::PORT_TYPE_VECTOR; + } else if (p_port == 1) { + return PORT_TYPE_SCALAR; + } + break; + case UniformType::UNIFORM_TYPE_SAMPLER: + return PortType::PORT_TYPE_SAMPLER; + default: + break; + } + return PORT_TYPE_SCALAR; +} + +String VisualShaderNodeUniformRef::get_output_port_name(int p_port) const { + switch (uniform_type) { + case UniformType::UNIFORM_TYPE_FLOAT: + return ""; + case UniformType::UNIFORM_TYPE_INT: + return ""; + case UniformType::UNIFORM_TYPE_BOOLEAN: + return ""; + case UniformType::UNIFORM_TYPE_VECTOR: + return ""; + case UniformType::UNIFORM_TYPE_TRANSFORM: + return ""; + case UniformType::UNIFORM_TYPE_COLOR: + if (p_port == 0) { + return "rgb"; + } else if (p_port == 1) { + return "alpha"; + } + break; + case UniformType::UNIFORM_TYPE_SAMPLER: + return ""; + break; + default: + break; + } + return ""; +} + +void VisualShaderNodeUniformRef::set_uniform_name(const String &p_name) { + uniform_name = p_name; + if (p_name != "[None]") { + uniform_type = get_uniform_type_by_name(p_name); + } else { + uniform_type = UniformType::UNIFORM_TYPE_FLOAT; + } + emit_changed(); +} + +String VisualShaderNodeUniformRef::get_uniform_name() const { + return uniform_name; +} + +int VisualShaderNodeUniformRef::get_uniforms_count() const { + return uniforms.size(); +} + +String VisualShaderNodeUniformRef::get_uniform_name_by_index(int p_idx) const { + if (p_idx >= 0 && p_idx < uniforms.size()) { + return uniforms[p_idx].name; + } + return ""; +} + +VisualShaderNodeUniformRef::UniformType VisualShaderNodeUniformRef::get_uniform_type_by_name(const String &p_name) const { + for (int i = 0; i < uniforms.size(); i++) { + if (uniforms[i].name == p_name) { + return uniforms[i].type; + } + } + return UniformType::UNIFORM_TYPE_FLOAT; +} + +VisualShaderNodeUniformRef::UniformType VisualShaderNodeUniformRef::get_uniform_type_by_index(int p_idx) const { + if (p_idx >= 0 && p_idx < uniforms.size()) { + return uniforms[p_idx].type; + } + return UniformType::UNIFORM_TYPE_FLOAT; +} + +String VisualShaderNodeUniformRef::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 { + switch (uniform_type) { + case UniformType::UNIFORM_TYPE_FLOAT: + return "\t" + p_output_vars[0] + " = " + get_uniform_name() + ";\n"; + case UniformType::UNIFORM_TYPE_INT: + return "\t" + p_output_vars[0] + " = " + get_uniform_name() + ";\n"; + case UniformType::UNIFORM_TYPE_BOOLEAN: + return "\t" + p_output_vars[0] + " = " + get_uniform_name() + ";\n"; + case UniformType::UNIFORM_TYPE_VECTOR: + return "\t" + p_output_vars[0] + " = " + get_uniform_name() + ";\n"; + case UniformType::UNIFORM_TYPE_TRANSFORM: + return "\t" + p_output_vars[0] + " = " + get_uniform_name() + ";\n"; + case UniformType::UNIFORM_TYPE_COLOR: { + String code = "\t" + p_output_vars[0] + " = " + get_uniform_name() + ".rgb;\n"; + code += "\t" + p_output_vars[1] + " = " + get_uniform_name() + ".a;\n"; + return code; + } break; + case UniformType::UNIFORM_TYPE_SAMPLER: + break; + default: + break; + } + return ""; +} + +void VisualShaderNodeUniformRef::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_uniform_name", "name"), &VisualShaderNodeUniformRef::set_uniform_name); + ClassDB::bind_method(D_METHOD("get_uniform_name"), &VisualShaderNodeUniformRef::get_uniform_name); + + ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "uniform_name", PROPERTY_HINT_ENUM, ""), "set_uniform_name", "get_uniform_name"); +} + +Vector VisualShaderNodeUniformRef::get_editable_properties() const { + Vector props; + props.push_back("uniform_name"); + return props; +} + +VisualShaderNodeUniformRef::VisualShaderNodeUniformRef() { + uniform_name = "[None]"; + uniform_type = UniformType::UNIFORM_TYPE_FLOAT; +} + //////////////////////////////////////////// const VisualShaderNodeOutput::Port VisualShaderNodeOutput::ports[] = { @@ -2195,6 +2412,14 @@ VisualShaderNodeUniform::Qualifier VisualShaderNodeUniform::get_qualifier() cons return qualifier; } +void VisualShaderNodeUniform::set_global_code_generated(bool p_enabled) { + global_code_generated = p_enabled; +} + +bool VisualShaderNodeUniform::is_global_code_generated() const { + return global_code_generated; +} + void VisualShaderNodeUniform::_bind_methods() { ClassDB::bind_method(D_METHOD("set_uniform_name", "name"), &VisualShaderNodeUniform::set_uniform_name); ClassDB::bind_method(D_METHOD("get_uniform_name"), &VisualShaderNodeUniform::get_uniform_name); diff --git a/scene/resources/visual_shader.h b/scene/resources/visual_shader.h index 6d3fda17440..d74269cfc6d 100644 --- a/scene/resources/visual_shader.h +++ b/scene/resources/visual_shader.h @@ -388,6 +388,7 @@ public: private: String uniform_name; Qualifier qualifier; + bool global_code_generated = false; protected: static void _bind_methods(); @@ -400,6 +401,9 @@ public: void set_qualifier(Qualifier p_qual); Qualifier get_qualifier() const; + void set_global_code_generated(bool p_enabled); + bool is_global_code_generated() const; + virtual bool is_qualifier_supported(Qualifier p_qual) const = 0; virtual Vector get_editable_properties() const override; @@ -410,6 +414,62 @@ public: VARIANT_ENUM_CAST(VisualShaderNodeUniform::Qualifier) +class VisualShaderNodeUniformRef : public VisualShaderNode { + GDCLASS(VisualShaderNodeUniformRef, VisualShaderNode); + +public: + enum UniformType { + UNIFORM_TYPE_FLOAT, + UNIFORM_TYPE_INT, + UNIFORM_TYPE_BOOLEAN, + UNIFORM_TYPE_VECTOR, + UNIFORM_TYPE_TRANSFORM, + UNIFORM_TYPE_COLOR, + UNIFORM_TYPE_SAMPLER, + }; + + struct Uniform { + String name; + UniformType type; + }; + +private: + String uniform_name; + UniformType uniform_type; + +protected: + static void _bind_methods(); + +public: + static void add_uniform(const String &p_name, UniformType p_type); + static void clear_uniforms(); + +public: + virtual String get_caption() const override; + + virtual int get_input_port_count() const override; + virtual PortType get_input_port_type(int p_port) const override; + virtual String get_input_port_name(int p_port) const override; + + virtual int get_output_port_count() const override; + virtual PortType get_output_port_type(int p_port) const override; + virtual String get_output_port_name(int p_port) const override; + + void set_uniform_name(const String &p_name); + String get_uniform_name() const; + + int get_uniforms_count() const; + String get_uniform_name_by_index(int p_idx) const; + UniformType get_uniform_type_by_name(const String &p_name) const; + UniformType get_uniform_type_by_index(int p_idx) const; + + virtual Vector get_editable_properties() 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; + + VisualShaderNodeUniformRef(); +}; + class VisualShaderNodeGroupBase : public VisualShaderNode { GDCLASS(VisualShaderNodeGroupBase, VisualShaderNode);