diff options
32 files changed, 6368 insertions, 93 deletions
diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index d8ce2bc024..17f383c8ae 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -92,7 +92,7 @@ void EditorProperty::_notification(int p_what) { Rect2 bottom_rect; { - int child_room = size.width / 2; + int child_room = size.width * (1.0 - split_ratio); Ref<Font> font = get_font("font", "Tree"); int height = font->get_height(); @@ -691,6 +691,15 @@ bool EditorProperty::is_selectable() const { return selectable; } +void EditorProperty::set_name_split_ratio(float p_ratio) { + split_ratio = p_ratio; +} + +float EditorProperty::get_name_split_ratio() const { + + return split_ratio; +} + void EditorProperty::set_object_and_property(Object *p_object, const StringName &p_property) { object = p_object; property = p_property; @@ -744,6 +753,7 @@ void EditorProperty::_bind_methods() { EditorProperty::EditorProperty() { + split_ratio = 0.5; selectable = true; text_size = 0; read_only = false; @@ -1114,6 +1124,30 @@ EditorInspectorSection::EditorInspectorSection() { Ref<EditorInspectorPlugin> EditorInspector::inspector_plugins[MAX_PLUGINS]; int EditorInspector::inspector_plugin_count = 0; +EditorProperty *EditorInspector::instantiate_property_editor(Object *p_object, Variant::Type p_type, const String &p_path, PropertyHint p_hint, const String &p_hint_text, int p_usage) { + + for (int i = inspector_plugin_count - 1; i >= 0; i--) { + + inspector_plugins[i]->parse_property(p_object, p_type, p_path, p_hint, p_hint_text, p_usage); + if (inspector_plugins[i]->added_editors.size()) { + for (int j = 1; j < inspector_plugins[i]->added_editors.size(); j++) { //only keep first one + memdelete(inspector_plugins[i]->added_editors[j].property_editor); + } + + EditorProperty *prop = Object::cast_to<EditorProperty>(inspector_plugins[i]->added_editors[0].property_editor); + if (prop) { + + inspector_plugins[i]->added_editors.clear(); + return prop; + } else { + memdelete(inspector_plugins[i]->added_editors[0].property_editor); + inspector_plugins[i]->added_editors.clear(); + } + } + } + return NULL; +} + void EditorInspector::add_inspector_plugin(const Ref<EditorInspectorPlugin> &p_plugin) { ERR_FAIL_COND(inspector_plugin_count == MAX_PLUGINS); diff --git a/editor/editor_inspector.h b/editor/editor_inspector.h index 383cb458ec..b7a492f114 100644 --- a/editor/editor_inspector.h +++ b/editor/editor_inspector.h @@ -76,6 +76,8 @@ private: bool selected; int selected_focusable; + float split_ratio; + Vector<Control *> focusables; Control *label_reference; Control *bottom_editor; @@ -134,6 +136,9 @@ public: void set_selectable(bool p_selectable); bool is_selectable() const; + void set_name_split_ratio(float p_ratio); + float get_name_split_ratio() const; + void set_object_and_property(Object *p_object, const StringName &p_property); EditorProperty(); }; @@ -285,6 +290,8 @@ public: static void remove_inspector_plugin(const Ref<EditorInspectorPlugin> &p_plugin); static void cleanup_plugins(); + static EditorProperty *instantiate_property_editor(Object *p_object, Variant::Type p_type, const String &p_path, PropertyHint p_hint, const String &p_hint_text, int p_usage); + void set_undo_redo(UndoRedo *p_undo_redo); String get_selected_path() const; diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 8d039f8cc0..72adb9aa47 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -116,6 +116,7 @@ #include "editor/plugins/theme_editor_plugin.h" #include "editor/plugins/tile_map_editor_plugin.h" #include "editor/plugins/tile_set_editor_plugin.h" +#include "editor/plugins/visual_shader_editor_plugin.h" #include "editor/pvrtc_compress.h" #include "editor/register_exporters.h" #include "editor/script_editor_debugger.h" @@ -4605,6 +4606,10 @@ EditorNode::EditorNode() { Ref<EditorInspectorRootMotionPlugin> rmp; rmp.instance(); EditorInspector::add_inspector_plugin(rmp); + + Ref<EditorInspectorShaderModePlugin> smp; + smp.instance(); + EditorInspector::add_inspector_plugin(smp); } _pvrtc_register_compressors(); @@ -4646,7 +4651,8 @@ EditorNode::EditorNode() { EDITOR_DEF("interface/scene_tabs/show_thumbnail_on_hover", true); EDITOR_DEF("interface/inspector/capitalize_properties", true); EDITOR_DEF("interface/inspector/disable_folding", false); - EDITOR_DEF("interface/inspector/open_resources_in_new_inspector", false); + EDITOR_DEF("interface/inspector/open_resources_in_current_inspector", true); + EDITOR_DEF("interface/inspector/resources_types_to_open_in_new_inspector", "Material,Mesh"); EDITOR_DEF("run/auto_save/save_before_running", true); theme_base = memnew(Control); @@ -5367,8 +5373,7 @@ EditorNode::EditorNode() { raise_bottom_panel_item(AnimationPlayerEditor::singleton); add_editor_plugin(memnew(ShaderEditorPlugin(this))); - // FIXME: Disabled for Godot 3.0 as made incompatible, it needs to be ported to the new API. - //add_editor_plugin(memnew(ShaderGraphEditorPlugin(this))); + add_editor_plugin(memnew(VisualShaderEditorPlugin(this))); add_editor_plugin(memnew(AnimationNodeBlendTreeEditorPlugin(this))); add_editor_plugin(memnew(AnimationNodeBlendSpace1DEditorPlugin(this))); add_editor_plugin(memnew(AnimationNodeBlendSpace2DEditorPlugin(this))); diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp index 064569dea0..fefadde64e 100644 --- a/editor/editor_properties.cpp +++ b/editor/editor_properties.cpp @@ -405,6 +405,10 @@ void EditorPropertyEnum::setup(const Vector<String> &p_options) { } } +void EditorPropertyEnum::set_option_button_clip(bool p_enable) { + options->set_clip_text(p_enable); +} + void EditorPropertyEnum::_bind_methods() { ClassDB::bind_method(D_METHOD("_option_selected"), &EditorPropertyEnum::_option_selected); @@ -2266,6 +2270,10 @@ void EditorPropertyResource::drop_data_fw(const Point2 &p_point, const Variant & } } +void EditorPropertyResource::set_use_sub_inspector(bool p_enable) { + use_sub_inspector = p_enable; +} + void EditorPropertyResource::_bind_methods() { ClassDB::bind_method(D_METHOD("_file_selected"), &EditorPropertyResource::_file_selected); @@ -2288,7 +2296,8 @@ EditorPropertyResource::EditorPropertyResource() { sub_inspector = NULL; sub_inspector_vbox = NULL; - use_sub_inspector = !bool(EDITOR_GET("interface/inspector/open_resources_in_new_inspector")); + use_sub_inspector = bool(EDITOR_GET("interface/inspector/open_resources_in_current_inspector")); + HBoxContainer *hbc = memnew(HBoxContainer); add_child(hbc); assign = memnew(Button); @@ -2691,6 +2700,22 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ case Variant::OBJECT: { EditorPropertyResource *editor = memnew(EditorPropertyResource); editor->setup(p_hint == PROPERTY_HINT_RESOURCE_TYPE ? p_hint_text : "Resource"); + + if (p_hint == PROPERTY_HINT_RESOURCE_TYPE) { + String open_in_new = EDITOR_GET("interface/inspector/resources_types_to_open_in_new_inspector"); + for (int i = 0; i < open_in_new.get_slice_count(","); i++) { + String type = open_in_new.get_slicec(',', i).strip_edges(); + for (int j = 0; j < p_hint_text.get_slice_count(","); j++) { + String inherits = p_hint_text.get_slicec(',', j); + + if (ClassDB::is_parent_class(inherits, type)) { + + editor->set_use_sub_inspector(false); + } + } + } + } + add_property_editor(p_path, editor); } break; diff --git a/editor/editor_properties.h b/editor/editor_properties.h index c67eccb60e..ecccd7274d 100644 --- a/editor/editor_properties.h +++ b/editor/editor_properties.h @@ -178,6 +178,7 @@ protected: public: void setup(const Vector<String> &p_options); virtual void update_property(); + void set_option_button_clip(bool p_enable); EditorPropertyEnum(); }; @@ -531,6 +532,8 @@ public: void collapse_all_folding(); void expand_all_folding(); + void set_use_sub_inspector(bool p_enable); + EditorPropertyResource(); }; diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp index 98402d8df5..0534a398f4 100644 --- a/editor/editor_themes.cpp +++ b/editor/editor_themes.cpp @@ -72,10 +72,11 @@ static Ref<StyleBoxFlat> make_flat_stylebox(Color p_color, float p_margin_left = return style; } -static Ref<StyleBoxLine> make_line_stylebox(Color p_color, int p_thickness = 1, float p_grow = 1, bool p_vertical = false) { +static Ref<StyleBoxLine> make_line_stylebox(Color p_color, int p_thickness = 1, float p_grow_begin = 1, float p_grow_end = 1, bool p_vertical = false) { Ref<StyleBoxLine> style(memnew(StyleBoxLine)); style->set_color(p_color); - style->set_grow(p_grow); + style->set_grow_begin(p_grow_begin); + style->set_grow_end(p_grow_end); style->set_thickness(p_thickness); style->set_vertical(p_vertical); return style; @@ -462,9 +463,20 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { Ref<StyleBoxLine> style_popup_separator(memnew(StyleBoxLine)); style_popup_separator->set_color(separator_color); - style_popup_separator->set_grow(popup_margin_size - MAX(EDSCALE, border_width)); + style_popup_separator->set_grow_begin(popup_margin_size - MAX(EDSCALE, border_width)); + style_popup_separator->set_grow_end(popup_margin_size - MAX(EDSCALE, border_width)); style_popup_separator->set_thickness(MAX(EDSCALE, border_width)); + Ref<StyleBoxLine> style_popup_labeled_separator_left(memnew(StyleBoxLine)); + style_popup_labeled_separator_left->set_grow_begin(popup_margin_size - MAX(EDSCALE, border_width)); + style_popup_labeled_separator_left->set_color(separator_color); + style_popup_labeled_separator_left->set_thickness(MAX(EDSCALE, border_width)); + + Ref<StyleBoxLine> style_popup_labeled_separator_right(memnew(StyleBoxLine)); + style_popup_labeled_separator_right->set_grow_end(popup_margin_size - MAX(EDSCALE, border_width)); + style_popup_labeled_separator_right->set_color(separator_color); + style_popup_labeled_separator_right->set_thickness(MAX(EDSCALE, border_width)); + Ref<StyleBoxEmpty> style_empty = make_empty_stylebox(default_margin_size, default_margin_size, default_margin_size, default_margin_size); // Tabs @@ -578,6 +590,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { theme->set_icon("arrow", "OptionButton", theme->get_icon("GuiOptionArrow", "EditorIcons")); theme->set_constant("arrow_margin", "OptionButton", default_margin_size * EDSCALE); theme->set_constant("modulate_arrow", "OptionButton", true); + theme->set_constant("hseparation", "OptionButton", 4 * EDSCALE); // CheckButton theme->set_stylebox("normal", "CheckButton", style_menu); @@ -626,6 +639,9 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { Ref<StyleBoxFlat> style_popup_menu = style_popup; theme->set_stylebox("panel", "PopupMenu", style_popup_menu); theme->set_stylebox("separator", "PopupMenu", style_popup_separator); + theme->set_stylebox("labeled_separator_left", "PopupMenu", style_popup_labeled_separator_left); + theme->set_stylebox("labeled_separator_right", "PopupMenu", style_popup_labeled_separator_right); + theme->set_color("font_color", "PopupMenu", font_color); theme->set_color("font_color_hover", "PopupMenu", font_color_hl); theme->set_color("font_color_accel", "PopupMenu", font_color_disabled); @@ -780,7 +796,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { // Separators theme->set_stylebox("separator", "HSeparator", make_line_stylebox(separator_color, border_width)); - theme->set_stylebox("separator", "VSeparator", make_line_stylebox(separator_color, border_width, 0, true)); + theme->set_stylebox("separator", "VSeparator", make_line_stylebox(separator_color, border_width, 0, 0, true)); // Debugger @@ -1005,6 +1021,8 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { theme->set_constant("title_h_offset", "GraphNode", -16 * EDSCALE); theme->set_constant("close_h_offset", "GraphNode", 20 * EDSCALE); theme->set_constant("close_offset", "GraphNode", 20 * EDSCALE); + theme->set_constant("separation", "GraphNode", 1 * EDSCALE); + theme->set_icon("close", "GraphNode", theme->get_icon("GuiCloseCustomizable", "EditorIcons")); theme->set_icon("resizer", "GraphNode", theme->get_icon("GuiResizer", "EditorIcons")); theme->set_icon("port", "GraphNode", theme->get_icon("GuiGraphNodePort", "EditorIcons")); diff --git a/editor/icons/icon_visual_shader.svg b/editor/icons/icon_visual_shader.svg new file mode 100644 index 0000000000..e2c4f128b2 --- /dev/null +++ b/editor/icons/icon_visual_shader.svg @@ -0,0 +1,105 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="16" + height="16" + version="1.1" + viewBox="0 0 16 16" + id="svg20" + sodipodi:docname="icon_visual_shader.svg" + inkscape:version="0.92.3 (2405546, 2018-03-11)"> + <metadata + id="metadata26"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title></dc:title> + </cc:Work> + </rdf:RDF> + </metadata> + <defs + id="defs24" /> + <sodipodi:namedview + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1" + objecttolerance="10" + gridtolerance="10" + guidetolerance="10" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:window-width="640" + inkscape:window-height="480" + id="namedview22" + showgrid="false" + inkscape:zoom="14.75" + inkscape:cx="8" + inkscape:cy="8" + inkscape:window-x="67" + inkscape:window-y="27" + inkscape:window-maximized="0" + inkscape:current-layer="svg20" /> + <g + id="g18" + transform="matrix(1,0,0,0.50605327,0,0.49394673)"> + <path + d="M 2,1 C 1.44774,1.0001 1.00006,1.4477 1,2 v 12 c 5.52e-5,0.5523 0.44774,0.9999 1,1 h 12 c 0.55226,-10e-5 0.99994,-0.4477 1,-1 V 6 L 10,1 Z m 1,2 h 6 v 3 c 0,0.554 0.44599,1 1,1 h 3 v 6 H 3 Z" + id="path2" + inkscape:connector-curvature="0" + style="fill:#e0e0e0" /> + <path + d="m 10,11 h 2 v 1 h -2 z" + id="path4" + inkscape:connector-curvature="0" + style="fill:#9f70ff" /> + <path + d="M 4,6 H 6 V 7 H 4 Z" + id="path6" + inkscape:connector-curvature="0" + style="fill:#ffeb70" /> + <path + d="m 8,8 h 4 V 9 H 8 Z" + id="path8" + inkscape:connector-curvature="0" + style="fill:#9dff70" /> + <path + d="M 7,6 H 8 V 7 H 7 Z" + id="path10" + inkscape:connector-curvature="0" + style="fill:#70deff" /> + <path + d="m 4,11 h 5 v 1 H 4 Z" + id="path12" + inkscape:connector-curvature="0" + style="fill:#ff70ac" /> + <path + d="M 4,4 H 7 V 5 H 4 Z" + id="path14" + inkscape:connector-curvature="0" + style="fill:#ff7070" /> + <path + d="M 4,8 H 7 V 9 H 4 Z" + id="path16" + inkscape:connector-curvature="0" + style="fill:#70ffb9" /> + </g> + <path + inkscape:connector-curvature="0" + style="fill:#e0e0e0" + d="m 2.8642321,9 v 6 h 2 a 3,3 0 0 0 3,-3 V 9 h -2 v 3 a 1,1 0 0 1 -1,1 V 9 Z" + id="path1394" /> + <path + inkscape:connector-curvature="0" + style="fill:#e0e0e0" + d="m 10.864232,9 a 2,2 0 0 0 -1.7323999,1 2,2 0 0 0 0,2 2,2 0 0 0 1.7323999,1 H 8.8642321 v 2 h 1.9999999 a 2,2 0 0 0 1.7324,-1 2,2 0 0 0 0,-2 2,2 0 0 0 -1.7324,-1 h 2 V 9 Z" + id="path30" /> +</svg> diff --git a/editor/plugins/animation_blend_tree_editor_plugin.cpp b/editor/plugins/animation_blend_tree_editor_plugin.cpp index 3efb2736b5..c00ad451fa 100644 --- a/editor/plugins/animation_blend_tree_editor_plugin.cpp +++ b/editor/plugins/animation_blend_tree_editor_plugin.cpp @@ -246,7 +246,7 @@ void AnimationNodeBlendTreeEditor::_add_node(int p_idx) { Point2 instance_pos = graph->get_scroll_ofs() + graph->get_size() * 0.5; - anode->set_position(instance_pos); + anode->set_position(instance_pos / EDSCALE); String base_name = add_options[p_idx].name; int base = 1; diff --git a/editor/plugins/shader_editor_plugin.cpp b/editor/plugins/shader_editor_plugin.cpp index 9b31e1a421..4b7f27c0c1 100644 --- a/editor/plugins/shader_editor_plugin.cpp +++ b/editor/plugins/shader_editor_plugin.cpp @@ -130,9 +130,9 @@ void ShaderTextEditor::_load_theme_settings() { } } - for (const Set<String>::Element *E = ShaderTypes::get_singleton()->get_modes(VisualServer::ShaderMode(shader->get_mode())).front(); E; E = E->next()) { + for (int i = 0; i < ShaderTypes::get_singleton()->get_modes(VisualServer::ShaderMode(shader->get_mode())).size(); i++) { - keywords.push_back(E->get()); + keywords.push_back(ShaderTypes::get_singleton()->get_modes(VisualServer::ShaderMode(shader->get_mode()))[i]); } } diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp new file mode 100644 index 0000000000..1302a89e3f --- /dev/null +++ b/editor/plugins/visual_shader_editor_plugin.cpp @@ -0,0 +1,1203 @@ +#include "visual_shader_editor_plugin.h" + +#include "core/io/resource_loader.h" +#include "core/project_settings.h" +#include "editor/editor_properties.h" +#include "os/input.h" +#include "os/keyboard.h" +#include "scene/animation/animation_player.h" +#include "scene/gui/menu_button.h" +#include "scene/gui/panel.h" +#include "scene/main/viewport.h" + +Control *VisualShaderNodePlugin::create_editor(const Ref<VisualShaderNode> &p_node) { + + if (get_script_instance()) { + return get_script_instance()->call("create_editor", p_node); + } + return NULL; +} + +void VisualShaderNodePlugin::_bind_methods() { + + BIND_VMETHOD(MethodInfo(Variant::OBJECT, "create_editor", PropertyInfo(Variant::OBJECT, "for_node", PROPERTY_HINT_RESOURCE_TYPE, "VisualShaderNode"))); +} + +/////////////////// + +void VisualShaderEditor::edit(VisualShader *p_visual_shader) { + + if (p_visual_shader) { + visual_shader = Ref<VisualShader>(p_visual_shader); + } else { + visual_shader.unref(); + } + + if (visual_shader.is_null()) { + hide(); + } else { + _update_graph(); + } +} + +void VisualShaderEditor::add_plugin(const Ref<VisualShaderNodePlugin> &p_plugin) { + if (plugins.find(p_plugin) != -1) + return; + plugins.push_back(p_plugin); +} + +void VisualShaderEditor::remove_plugin(const Ref<VisualShaderNodePlugin> &p_plugin) { + plugins.erase(p_plugin); +} + +void VisualShaderEditor::add_custom_type(const String &p_name, const String &p_category, const Ref<Script> &p_script) { + + for (int i = 0; i < add_options.size(); i++) { + ERR_FAIL_COND(add_options[i].script == p_script); + } + + AddOption ao; + ao.name = p_name; + ao.script = p_script; + ao.category = p_category; + add_options.push_back(ao); + + _update_options_menu(); +} + +void VisualShaderEditor::remove_custom_type(const Ref<Script> &p_script) { + + for (int i = 0; i < add_options.size(); i++) { + if (add_options[i].script == p_script) { + add_options.remove(i); + return; + } + } + + _update_options_menu(); +} + +void VisualShaderEditor::_update_options_menu() { + + String prev_category; + add_node->get_popup()->clear(); + for (int i = 0; i < add_options.size(); i++) { + if (prev_category != add_options[i].category) { + add_node->get_popup()->add_separator(add_options[i].category); + } + add_node->get_popup()->add_item(add_options[i].name, i); + prev_category = add_options[i].category; + } +} + +Size2 VisualShaderEditor::get_minimum_size() const { + + return Size2(10, 200); +} + +void VisualShaderEditor::_draw_color_over_button(Object *obj, Color p_color) { + + Button *button = Object::cast_to<Button>(obj); + if (!button) + return; + + Ref<StyleBox> normal = get_stylebox("normal", "Button"); + button->draw_rect(Rect2(normal->get_offset(), button->get_size() - normal->get_minimum_size()), p_color); +} + +static Ref<StyleBoxEmpty> make_empty_stylebox(float p_margin_left = -1, float p_margin_top = -1, float p_margin_right = -1, float p_margin_bottom = -1) { + Ref<StyleBoxEmpty> style(memnew(StyleBoxEmpty)); + style->set_default_margin(MARGIN_LEFT, p_margin_left * EDSCALE); + style->set_default_margin(MARGIN_RIGHT, p_margin_right * EDSCALE); + style->set_default_margin(MARGIN_BOTTOM, p_margin_bottom * EDSCALE); + style->set_default_margin(MARGIN_TOP, p_margin_top * EDSCALE); + return style; +} + +void VisualShaderEditor::_update_graph() { + + if (updating) + return; + + graph->set_scroll_ofs(visual_shader->get_graph_offset() * EDSCALE); + + VisualShader::Type type = VisualShader::Type(edit_type->get_selected()); + graph->clear_connections(); + //erase all nodes + for (int i = 0; i < graph->get_child_count(); i++) { + + if (Object::cast_to<GraphNode>(graph->get_child(i))) { + memdelete(graph->get_child(i)); + i--; + } + } + + static const Color type_color[3] = { + Color::html("#61daf4"), + Color::html("#d67dee"), + Color::html("#f6a86e") + }; + + List<VisualShader::Connection> connections; + visual_shader->get_node_connections(type, &connections); + + Ref<StyleBoxEmpty> label_style = make_empty_stylebox(2, 1, 2, 1); + + Vector<int> nodes = visual_shader->get_node_list(type); + + for (int n_i = 0; n_i < nodes.size(); n_i++) { + + Vector2 position = visual_shader->get_node_position(type, nodes[n_i]); + Ref<VisualShaderNode> vsnode = visual_shader->get_node(type, nodes[n_i]); + + GraphNode *node = memnew(GraphNode); + graph->add_child(node); + + /*if (!vsnode->is_connected("changed", this, "_node_changed")) { + vsnode->connect("changed", this, "_node_changed", varray(vsnode->get_instance_id()), CONNECT_DEFERRED); + }*/ + + node->set_offset(position * EDSCALE); + + node->set_title(vsnode->get_caption()); + node->set_name(itos(nodes[n_i])); + + if (nodes[n_i] >= 2) { + node->set_show_close_button(true); + node->connect("close_request", this, "_delete_request", varray(nodes[n_i]), CONNECT_DEFERRED); + } + + node->connect("dragged", this, "_node_dragged", varray(nodes[n_i])); + + Control *custom_editor = NULL; + int port_offset = 0; + + Ref<VisualShaderNodeUniform> uniform = vsnode; + if (uniform.is_valid()) { + LineEdit *uniform_name = memnew(LineEdit); + uniform_name->set_text(uniform->get_uniform_name()); + node->add_child(uniform_name); + uniform_name->connect("text_entered", this, "_line_edit_changed", varray(uniform_name, nodes[n_i])); + uniform_name->connect("focus_exited", this, "_line_edit_focus_out", varray(uniform_name, nodes[n_i])); + + if (vsnode->get_input_port_count() == 0 && vsnode->get_output_port_count() == 1 && vsnode->get_output_port_name(0) == "") { + //shortcut + VisualShaderNode::PortType port_right = vsnode->get_output_port_type(0); + node->set_slot(0, false, VisualShaderNode::PORT_TYPE_SCALAR, Color(), true, port_right, type_color[port_right]); + continue; + } + port_offset++; + } + + for (int i = 0; i < plugins.size(); i++) { + custom_editor = plugins[i]->create_editor(vsnode); + if (custom_editor) { + break; + } + } + + if (custom_editor && vsnode->get_output_port_count() > 0 && vsnode->get_output_port_name(0) == "" && (vsnode->get_input_port_count() == 0 || vsnode->get_input_port_name(0) == "")) { + //will be embedded in first port + } else if (custom_editor) { + port_offset++; + node->add_child(custom_editor); + custom_editor = NULL; + } + + for (int i = 0; i < MAX(vsnode->get_input_port_count(), vsnode->get_output_port_count()); i++) { + + if (vsnode->is_port_separator(i)) { + node->add_child(memnew(HSeparator)); + port_offset++; + } + + bool valid_left = i < vsnode->get_input_port_count(); + VisualShaderNode::PortType port_left = VisualShaderNode::PORT_TYPE_SCALAR; + bool port_left_used = false; + String name_left; + if (valid_left) { + name_left = vsnode->get_input_port_name(i); + port_left = vsnode->get_input_port_type(i); + for (List<VisualShader::Connection>::Element *E = connections.front(); E; E = E->next()) { + if (E->get().to_node == nodes[n_i] && E->get().to_port == i) { + port_left_used = true; + } + } + } + + bool valid_right = i < vsnode->get_output_port_count(); + VisualShaderNode::PortType port_right = VisualShaderNode::PORT_TYPE_SCALAR; + String name_right; + if (valid_right) { + name_right = vsnode->get_output_port_name(i); + port_right = vsnode->get_output_port_type(i); + } + + HBoxContainer *hb = memnew(HBoxContainer); + + Variant default_value; + + if (valid_left && !port_left_used) { + default_value = vsnode->get_input_port_default_value(i); + } + + if (default_value.get_type() != Variant::NIL) { // only a label + Button *button = memnew(Button); + hb->add_child(button); + button->connect("pressed", this, "_edit_port_default_input", varray(button, nodes[n_i], i)); + + switch (default_value.get_type()) { + + case Variant::COLOR: { + button->set_custom_minimum_size(Size2(30, 0) * EDSCALE); + button->connect("draw", this, "_draw_color_over_button", varray(button, default_value)); + } break; + case Variant::INT: + case Variant::REAL: { + button->set_text(String::num(default_value, 4)); + } break; + case Variant::VECTOR3: { + Vector3 v = default_value; + button->set_text(String::num(v.x, 3) + "," + String::num(v.y, 3) + "," + String::num(v.z, 3)); + } break; + default: {} + } + } + + if (i == 0 && custom_editor) { + + hb->add_child(custom_editor); + custom_editor->set_h_size_flags(SIZE_EXPAND_FILL); + } else { + + if (valid_left) { + + Label *label = memnew(Label); + label->set_text(name_left); + label->add_style_override("normal", label_style); //more compact + hb->add_child(label); + } + + hb->add_spacer(); + + if (valid_right) { + + Label *label = memnew(Label); + label->set_text(name_right); + label->set_align(Label::ALIGN_RIGHT); + label->add_style_override("normal", label_style); //more compact + hb->add_child(label); + } + } + + if (valid_right && edit_type->get_selected() == VisualShader::TYPE_FRAGMENT) { + TextureButton *preview = memnew(TextureButton); + preview->set_toggle_mode(true); + preview->set_normal_texture(get_icon("GuiVisibilityHidden", "EditorIcons")); + preview->set_pressed_texture(get_icon("GuiVisibilityVisible", "EditorIcons")); + preview->set_v_size_flags(SIZE_SHRINK_CENTER); + + if (vsnode->get_output_port_for_preview() == i) { + preview->set_pressed(true); + } + + preview->connect("pressed", this, "_preview_select_port", varray(nodes[n_i], i), CONNECT_DEFERRED); + hb->add_child(preview); + } + + node->add_child(hb); + + node->set_slot(i + port_offset, valid_left, port_left, type_color[port_left], valid_right, port_right, type_color[port_right]); + } + + if (vsnode->get_output_port_for_preview() >= 0) { + VisualShaderNodePortPreview *port_preview = memnew(VisualShaderNodePortPreview); + port_preview->setup(visual_shader, type, nodes[n_i], vsnode->get_output_port_for_preview()); + port_preview->set_h_size_flags(SIZE_SHRINK_CENTER); + node->add_child(port_preview); + } + + String error = vsnode->get_warning(visual_shader->get_mode(), type); + if (error != String()) { + Label *error_label = memnew(Label); + error_label->add_color_override("font_color", get_color("error_color", "Editor")); + error_label->set_text(error); + node->add_child(error_label); + } + } + + for (List<VisualShader::Connection>::Element *E = connections.front(); E; E = E->next()) { + + int from = E->get().from_node; + int from_idx = E->get().from_port; + int to = E->get().to_node; + int to_idx = E->get().to_port; + + graph->connect_node(itos(from), from_idx, itos(to), to_idx); + } +} + +void VisualShaderEditor::_preview_select_port(int p_node, int p_port) { + + VisualShader::Type type = VisualShader::Type(edit_type->get_selected()); + Ref<VisualShaderNode> node = visual_shader->get_node(type, p_node); + if (node.is_null()) { + return; + } + + if (node->get_output_port_for_preview() == p_port) { + p_port = -1; //toggle it + } + undo_redo->create_action("Set Uniform Name"); + undo_redo->add_do_method(node.ptr(), "set_output_port_for_preview", p_port); + undo_redo->add_undo_method(node.ptr(), "set_output_port_for_preview", node->get_output_port_for_preview()); + undo_redo->add_do_method(this, "_update_graph"); + undo_redo->add_undo_method(this, "_update_graph"); + undo_redo->commit_action(); +} + +void VisualShaderEditor::_line_edit_changed(const String &p_text, Object *line_edit, int p_node_id) { + + VisualShader::Type type = VisualShader::Type(edit_type->get_selected()); + + Ref<VisualShaderNodeUniform> node = visual_shader->get_node(type, p_node_id); + ERR_FAIL_COND(!node.is_valid()); + + String validated_name = visual_shader->validate_uniform_name(p_text, node); + + updating = true; + undo_redo->create_action("Set Uniform Name"); + undo_redo->add_do_method(node.ptr(), "set_uniform_name", validated_name); + undo_redo->add_undo_method(node.ptr(), "set_uniform_name", node->get_uniform_name()); + undo_redo->add_do_method(this, "_update_graph"); + undo_redo->add_undo_method(this, "_update_graph"); + undo_redo->commit_action(); + updating = false; + + Object::cast_to<LineEdit>(line_edit)->set_text(validated_name); +} + +void VisualShaderEditor::_line_edit_focus_out(Object *line_edit, int p_node_id) { + + String text = Object::cast_to<LineEdit>(line_edit)->get_text(); + _line_edit_changed(text, line_edit, p_node_id); +} + +void VisualShaderEditor::_port_edited() { + + VisualShader::Type type = VisualShader::Type(edit_type->get_selected()); + + Variant value = property_editor->get_variant(); + Ref<VisualShaderNode> vsn = visual_shader->get_node(type, editing_node); + ERR_FAIL_COND(!vsn.is_valid()); + + undo_redo->create_action("Set Input Default Port"); + undo_redo->add_do_method(vsn.ptr(), "set_input_port_default_value", editing_port, value); + undo_redo->add_undo_method(vsn.ptr(), "set_input_port_default_value", editing_port, vsn->get_input_port_default_value(editing_port)); + undo_redo->add_do_method(this, "_update_graph"); + undo_redo->add_undo_method(this, "_update_graph"); + undo_redo->commit_action(); + + property_editor->hide(); +} + +void VisualShaderEditor::_edit_port_default_input(Object *p_button, int p_node, int p_port) { + + VisualShader::Type type = VisualShader::Type(edit_type->get_selected()); + + Ref<VisualShaderNode> vsn = visual_shader->get_node(type, p_node); + + Button *button = Object::cast_to<Button>(p_button); + ERR_FAIL_COND(!button); + Variant value = vsn->get_input_port_default_value(p_port); + property_editor->set_global_position(button->get_global_position() + Vector2(0, button->get_size().height)); + property_editor->edit(NULL, "", value.get_type(), value, 0, ""); + property_editor->popup(); + editing_node = p_node; + editing_port = p_port; +} + +void VisualShaderEditor::_add_node(int p_idx) { + + ERR_FAIL_INDEX(p_idx, add_options.size()); + + Ref<VisualShaderNode> vsnode; + + if (add_options[p_idx].type != String()) { + VisualShaderNode *vsn = Object::cast_to<VisualShaderNode>(ClassDB::instance(add_options[p_idx].type)); + ERR_FAIL_COND(!vsn); + vsnode = Ref<VisualShaderNode>(vsn); + } else { + ERR_FAIL_COND(add_options[p_idx].script.is_null()); + String base_type = add_options[p_idx].script->get_instance_base_type(); + VisualShaderNode *vsn = Object::cast_to<VisualShaderNode>(ClassDB::instance(base_type)); + ERR_FAIL_COND(!vsn); + vsnode = Ref<VisualShaderNode>(vsn); + vsnode->set_script(add_options[p_idx].script.get_ref_ptr()); + } + + Point2 position = (graph->get_scroll_ofs() + graph->get_size() * 0.5) / EDSCALE; + + VisualShader::Type type = VisualShader::Type(edit_type->get_selected()); + + int id_to_use = visual_shader->get_valid_node_id(type); + + undo_redo->create_action("Add Node to Visual Shader"); + undo_redo->add_do_method(visual_shader.ptr(), "add_node", type, vsnode, position, id_to_use); + undo_redo->add_undo_method(visual_shader.ptr(), "remove_node", type, id_to_use); + undo_redo->add_do_method(this, "_update_graph"); + undo_redo->add_undo_method(this, "_update_graph"); + undo_redo->commit_action(); +} + +void VisualShaderEditor::_node_dragged(const Vector2 &p_from, const Vector2 &p_to, int p_node) { + + VisualShader::Type type = VisualShader::Type(edit_type->get_selected()); + + updating = true; + undo_redo->create_action("Node Moved"); + undo_redo->add_do_method(visual_shader.ptr(), "set_node_position", type, p_node, p_to); + undo_redo->add_undo_method(visual_shader.ptr(), "set_node_position", type, p_node, p_from); + undo_redo->add_do_method(this, "_update_graph"); + undo_redo->add_undo_method(this, "_update_graph"); + undo_redo->commit_action(); + updating = false; +} + +void VisualShaderEditor::_connection_request(const String &p_from, int p_from_index, const String &p_to, int p_to_index) { + + VisualShader::Type type = VisualShader::Type(edit_type->get_selected()); + + int from = p_from.to_int(); + int to = p_to.to_int(); + + if (!visual_shader->can_connect_nodes(type, from, p_from_index, to, p_to_index)) { + EditorNode::get_singleton()->show_warning(TTR("Unable to connect, port may be in use or connection may be invalid.")); + return; + } + + undo_redo->create_action("Nodes Connected"); + undo_redo->add_do_method(visual_shader.ptr(), "connect_nodes", type, from, p_from_index, to, p_to_index); + undo_redo->add_undo_method(visual_shader.ptr(), "disconnect_nodes", type, from, p_from_index, to, p_to_index); + undo_redo->add_do_method(this, "_update_graph"); + undo_redo->add_undo_method(this, "_update_graph"); + undo_redo->commit_action(); +} + +void VisualShaderEditor::_disconnection_request(const String &p_from, int p_from_index, const String &p_to, int p_to_index) { + + graph->disconnect_node(p_from, p_from_index, p_to, p_to_index); + + VisualShader::Type type = VisualShader::Type(edit_type->get_selected()); + + int from = p_from.to_int(); + int to = p_to.to_int(); + + //updating = true; seems graph edit can handle this, no need to protect + undo_redo->create_action("Nodes Disconnected"); + undo_redo->add_do_method(visual_shader.ptr(), "disconnect_nodes", type, from, p_from_index, to, p_to_index); + undo_redo->add_undo_method(visual_shader.ptr(), "connect_nodes", type, from, p_from_index, to, p_to_index); + undo_redo->add_do_method(this, "_update_graph"); + undo_redo->add_undo_method(this, "_update_graph"); + undo_redo->commit_action(); + //updating = false; +} + +void VisualShaderEditor::_connection_to_empty(const String &p_from, int p_from_slot, const Vector2 &p_release_position) { +} + +void VisualShaderEditor::_delete_request(int which) { + + VisualShader::Type type = VisualShader::Type(edit_type->get_selected()); + + undo_redo->create_action("Delete Node"); + undo_redo->add_do_method(visual_shader.ptr(), "remove_node", type, which); + undo_redo->add_undo_method(visual_shader.ptr(), "add_node", type, visual_shader->get_node(type, which), visual_shader->get_node_position(type, which), which); + + List<VisualShader::Connection> conns; + visual_shader->get_node_connections(type, &conns); + + for (List<VisualShader::Connection>::Element *E = conns.front(); E; E = E->next()) { + if (E->get().from_node == which || E->get().to_node == which) { + 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(this, "_update_graph"); + undo_redo->add_undo_method(this, "_update_graph"); + undo_redo->commit_action(); +} + +void VisualShaderEditor::_node_selected(Object *p_node) { + + VisualShader::Type type = VisualShader::Type(edit_type->get_selected()); + + GraphNode *gn = Object::cast_to<GraphNode>(p_node); + ERR_FAIL_COND(!gn); + + int id = String(gn->get_name()).to_int(); + + Ref<VisualShaderNode> vsnode = visual_shader->get_node(type, id); + ERR_FAIL_COND(!vsnode.is_valid()); + + //do not rely on this, makes editor more complex + //EditorNode::get_singleton()->push_item(vsnode.ptr(), "", true); +} + +void VisualShaderEditor::_notification(int p_what) { + + if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) { + + error_panel->add_style_override("panel", get_stylebox("bg", "Tree")); + error_label->add_color_override("font_color", get_color("error_color", "Editor")); + } + + if (p_what == NOTIFICATION_PROCESS) { + } +} + +void VisualShaderEditor::_scroll_changed(const Vector2 &p_scroll) { + if (updating) + return; + updating = true; + visual_shader->set_graph_offset(p_scroll / EDSCALE); + updating = false; +} + +void VisualShaderEditor::_node_changed(int p_id) { + if (updating) + return; + + if (is_visible_in_tree()) { + _update_graph(); + } +} + +void VisualShaderEditor::_duplicate_nodes() { + + VisualShader::Type type = VisualShader::Type(edit_type->get_selected()); + + List<int> nodes; + + for (int i = 0; i < graph->get_child_count(); i++) { + + if (Object::cast_to<GraphNode>(graph->get_child(i))) { + int id = String(graph->get_child(i)->get_name()).to_int(); + Ref<VisualShaderNode> node = visual_shader->get_node(type, id); + Ref<VisualShaderNodeOutput> output = node; + if (output.is_valid()) //cant duplicate output + continue; + if (node.is_valid()) { + nodes.push_back(id); + } + } + } + + if (nodes.empty()) + return; + + undo_redo->create_action("Duplicate Nodes"); + + int base_id = visual_shader->get_valid_node_id(type); + int id_from = base_id; + Map<int, int> connection_remap; + + for (List<int>::Element *E = nodes.front(); E; E = E->next()) { + + connection_remap[E->get()] = id_from; + Ref<VisualShaderNode> node = visual_shader->get_node(type, E->get()); + + Ref<VisualShaderNode> dupli = node->duplicate(); + + undo_redo->add_do_method(visual_shader.ptr(), "add_node", type, dupli, visual_shader->get_node_position(type, E->get()) + Vector2(10, 10) * EDSCALE, id_from); + undo_redo->add_undo_method(visual_shader.ptr(), "remove_node", type, id_from); + + id_from++; + } + + List<VisualShader::Connection> conns; + visual_shader->get_node_connections(type, &conns); + + for (List<VisualShader::Connection>::Element *E = conns.front(); E; E = E->next()) { + if (connection_remap.has(E->get().from_node) && connection_remap.has(E->get().to_node)) { + undo_redo->add_do_method(visual_shader.ptr(), "connect_nodes", type, connection_remap[E->get().from_node], E->get().from_port, connection_remap[E->get().to_node], E->get().to_port); + } + } + + undo_redo->add_do_method(this, "_update_graph"); + undo_redo->add_undo_method(this, "_update_graph"); + undo_redo->commit_action(); + + //reselect + for (int i = 0; i < graph->get_child_count(); i++) { + + if (Object::cast_to<GraphNode>(graph->get_child(i))) { + int id = String(graph->get_child(i)->get_name()).to_int(); + if (nodes.find(id)) { + Object::cast_to<GraphNode>(graph->get_child(i))->set_selected(true); + } else { + Object::cast_to<GraphNode>(graph->get_child(i))->set_selected(false); + } + } + } +} + +void VisualShaderEditor::_mode_selected(int p_id) { + _update_graph(); +} + +void VisualShaderEditor::_input_select_item(Ref<VisualShaderNodeInput> input, String name) { + + String prev_name = input->get_input_name(); + + if (name == prev_name) + return; + + bool type_changed = input->get_input_type_by_name(name) != input->get_input_type_by_name(prev_name); + + UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo(); + undo_redo->create_action("Visual Shader Input Type Changed"); + + undo_redo->add_do_method(input.ptr(), "set_input_name", name); + undo_redo->add_undo_method(input.ptr(), "set_input_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, input); + List<VisualShader::Connection> conns; + visual_shader->get_node_connections(type, &conns); + for (List<VisualShader::Connection>::Element *E = conns.front(); E; E = E->next()) { + if (E->get().from_node == id) { + 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::_bind_methods() { + + ClassDB::bind_method("_update_graph", &VisualShaderEditor::_update_graph); + ClassDB::bind_method("_add_node", &VisualShaderEditor::_add_node); + ClassDB::bind_method("_node_dragged", &VisualShaderEditor::_node_dragged); + ClassDB::bind_method("_connection_request", &VisualShaderEditor::_connection_request); + ClassDB::bind_method("_disconnection_request", &VisualShaderEditor::_disconnection_request); + ClassDB::bind_method("_node_selected", &VisualShaderEditor::_node_selected); + ClassDB::bind_method("_scroll_changed", &VisualShaderEditor::_scroll_changed); + ClassDB::bind_method("_delete_request", &VisualShaderEditor::_delete_request); + ClassDB::bind_method("_node_changed", &VisualShaderEditor::_node_changed); + ClassDB::bind_method("_edit_port_default_input", &VisualShaderEditor::_edit_port_default_input); + ClassDB::bind_method("_port_edited", &VisualShaderEditor::_port_edited); + ClassDB::bind_method("_connection_to_empty", &VisualShaderEditor::_connection_to_empty); + ClassDB::bind_method("_line_edit_focus_out", &VisualShaderEditor::_line_edit_focus_out); + ClassDB::bind_method("_line_edit_changed", &VisualShaderEditor::_line_edit_changed); + ClassDB::bind_method("_duplicate_nodes", &VisualShaderEditor::_duplicate_nodes); + ClassDB::bind_method("_mode_selected", &VisualShaderEditor::_mode_selected); + ClassDB::bind_method("_input_select_item", &VisualShaderEditor::_input_select_item); + ClassDB::bind_method("_preview_select_port", &VisualShaderEditor::_preview_select_port); +} + +VisualShaderEditor *VisualShaderEditor::singleton = NULL; + +VisualShaderEditor::VisualShaderEditor() { + + singleton = this; + updating = false; + + graph = memnew(GraphEdit); + add_child(graph); + graph->add_valid_right_disconnect_type(VisualShaderNode::PORT_TYPE_SCALAR); + graph->add_valid_right_disconnect_type(VisualShaderNode::PORT_TYPE_VECTOR); + graph->add_valid_right_disconnect_type(VisualShaderNode::PORT_TYPE_TRANSFORM); + //graph->add_valid_left_disconnect_type(0); + graph->set_v_size_flags(SIZE_EXPAND_FILL); + graph->connect("connection_request", this, "_connection_request", varray(), CONNECT_DEFERRED); + graph->connect("disconnection_request", this, "_disconnection_request", varray(), CONNECT_DEFERRED); + graph->connect("node_selected", this, "_node_selected"); + graph->connect("scroll_offset_changed", this, "_scroll_changed"); + graph->connect("duplicate_nodes_request", this, "_duplicate_nodes"); + graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_SCALAR, VisualShaderNode::PORT_TYPE_SCALAR); + graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_SCALAR, VisualShaderNode::PORT_TYPE_VECTOR); + //graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_VECTOR, VisualShaderNode::PORT_TYPE_SCALAR); + graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_VECTOR, VisualShaderNode::PORT_TYPE_VECTOR); + graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_TRANSFORM, VisualShaderNode::PORT_TYPE_TRANSFORM); + + VSeparator *vs = memnew(VSeparator); + graph->get_zoom_hbox()->add_child(vs); + graph->get_zoom_hbox()->move_child(vs, 0); + + edit_type = memnew(OptionButton); + edit_type->add_item(TTR("Vertex")); + edit_type->add_item(TTR("Fragment")); + edit_type->add_item(TTR("Light")); + edit_type->select(1); + edit_type->connect("item_selected", this, "_mode_selected"); + graph->get_zoom_hbox()->add_child(edit_type); + graph->get_zoom_hbox()->move_child(edit_type, 0); + + add_node = memnew(MenuButton); + graph->get_zoom_hbox()->add_child(add_node); + add_node->set_text(TTR("Add Node..")); + graph->get_zoom_hbox()->move_child(add_node, 0); + add_node->get_popup()->connect("id_pressed", this, "_add_node"); + + add_options.push_back(AddOption("Scalar", "Constants", "VisualShaderNodeScalarConstant")); + add_options.push_back(AddOption("Vector", "Constants", "VisualShaderNodeVec3Constant")); + add_options.push_back(AddOption("Color", "Constants", "VisualShaderNodeColorConstant")); + add_options.push_back(AddOption("Transform", "Constants", "VisualShaderNodeTransformConstant")); + add_options.push_back(AddOption("Texture", "Constants", "VisualShaderNodeTexture")); + add_options.push_back(AddOption("CubeMap", "Constants", "VisualShaderNodeCubeMap")); + add_options.push_back(AddOption("ScalarOp", "Operators", "VisualShaderNodeScalarOp")); + add_options.push_back(AddOption("VectorOp", "Operators", "VisualShaderNodeVectorOp")); + add_options.push_back(AddOption("ColorOp", "Operators", "VisualShaderNodeColorOp")); + add_options.push_back(AddOption("TransformMult", "Operators", "VisualShaderNodeTransformMult")); + add_options.push_back(AddOption("TransformVecMult", "Operators", "VisualShaderNodeTransformVecMult")); + add_options.push_back(AddOption("ScalarFunc", "Functions", "VisualShaderNodeScalarFunc")); + add_options.push_back(AddOption("VectorFunc", "Functions", "VisualShaderNodeVectorFunc")); + add_options.push_back(AddOption("DotProduct", "Functions", "VisualShaderNodeDotProduct")); + add_options.push_back(AddOption("VectorLen", "Functions", "VisualShaderNodeVectorLen")); + add_options.push_back(AddOption("ScalarInterp", "Interpolation", "VisualShaderNodeScalarInterp")); + add_options.push_back(AddOption("VectorInterp", "Interpolation", "VisualShaderNodeVectorInterp")); + add_options.push_back(AddOption("VectorConstruct", "Construct", "VisualShaderNodeVectorConstruct")); + add_options.push_back(AddOption("TransformConstruct", "Construct", "VisualShaderNodeTransformConstruct")); + add_options.push_back(AddOption("VectorDestruct", "Destruct", "VisualShaderNodeVectorDestruct")); + add_options.push_back(AddOption("TransformDestruct", "Destruct", "VisualShaderNodeTransformDestruct")); + add_options.push_back(AddOption("Scalar", "Uniforms", "VisualShaderNodeScalarUniform")); + add_options.push_back(AddOption("Vector", "Uniforms", "VisualShaderNodeVec3Uniform")); + add_options.push_back(AddOption("Color", "Uniforms", "VisualShaderNodeColorUniform")); + add_options.push_back(AddOption("Transform", "Uniforms", "VisualShaderNodeTransformUniform")); + add_options.push_back(AddOption("Texture", "Uniforms", "VisualShaderNodeTextureUniform")); + add_options.push_back(AddOption("CubeMap", "Uniforms", "VisualShaderNodeCubeMapUniform")); + add_options.push_back(AddOption("Input", "Inputs", "VisualShaderNodeInput")); + + _update_options_menu(); + + error_panel = memnew(PanelContainer); + add_child(error_panel); + error_label = memnew(Label); + error_panel->add_child(error_label); + error_label->set_text("eh"); + error_panel->hide(); + + undo_redo = EditorNode::get_singleton()->get_undo_redo(); + + Ref<VisualShaderNodePluginDefault> default_plugin; + default_plugin.instance(); + add_plugin(default_plugin); + + property_editor = memnew(CustomPropertyEditor); + add_child(property_editor); + + property_editor->connect("variant_changed", this, "_port_edited"); +} + +void VisualShaderEditorPlugin::edit(Object *p_object) { + + visual_shader_editor->edit(Object::cast_to<VisualShader>(p_object)); +} + +bool VisualShaderEditorPlugin::handles(Object *p_object) const { + + return p_object->is_class("VisualShader"); +} + +void VisualShaderEditorPlugin::make_visible(bool p_visible) { + + if (p_visible) { + //editor->hide_animation_player_editors(); + //editor->animation_panel_make_visible(true); + button->show(); + editor->make_bottom_panel_item_visible(visual_shader_editor); + //visual_shader_editor->set_process(true); + } else { + + if (visual_shader_editor->is_visible_in_tree()) + editor->hide_bottom_panel(); + button->hide(); + //visual_shader_editor->set_process(false); + } +} + +VisualShaderEditorPlugin::VisualShaderEditorPlugin(EditorNode *p_node) { + + editor = p_node; + visual_shader_editor = memnew(VisualShaderEditor); + visual_shader_editor->set_custom_minimum_size(Size2(0, 300)); + + button = editor->add_bottom_panel_item(TTR("VisualShader"), visual_shader_editor); + button->hide(); +} + +VisualShaderEditorPlugin::~VisualShaderEditorPlugin() { +} + +//////////////// + +class VisualShaderNodePluginInputEditor : public OptionButton { + GDCLASS(VisualShaderNodePluginInputEditor, OptionButton) + + Ref<VisualShaderNodeInput> input; + +protected: + static void _bind_methods() { + ClassDB::bind_method("_item_selected", &VisualShaderNodePluginInputEditor::_item_selected); + } + +public: + void _notification(int p_what) { + if (p_what == NOTIFICATION_READY) { + connect("item_selected", this, "_item_selected"); + } + } + + void _item_selected(int p_item) { + VisualShaderEditor::get_singleton()->call_deferred("_input_select_item", input, get_item_text(p_item)); + } + + void setup(const Ref<VisualShaderNodeInput> &p_input) { + input = p_input; + Ref<Texture> type_icon[3] = { + EditorNode::get_singleton()->get_gui_base()->get_icon("float", "EditorIcons"), + EditorNode::get_singleton()->get_gui_base()->get_icon("Vector3", "EditorIcons"), + EditorNode::get_singleton()->get_gui_base()->get_icon("Transform", "EditorIcons"), + }; + + add_item("[None]"); + int to_select = -1; + for (int i = 0; i < input->get_input_index_count(); i++) { + if (input->get_input_name() == input->get_input_index_name(i)) { + to_select = i + 1; + } + add_icon_item(type_icon[input->get_input_index_type(i)], input->get_input_index_name(i)); + } + + if (to_select >= 0) { + select(to_select); + } + } +}; + +class VisualShaderNodePluginDefaultEditor : public VBoxContainer { + GDCLASS(VisualShaderNodePluginDefaultEditor, VBoxContainer) +public: + void _property_changed(const String &prop, const Variant &p_value) { + + UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo(); + + updating = true; + undo_redo->create_action("Edit Visual Property: " + prop, UndoRedo::MERGE_ENDS); + undo_redo->add_do_property(node.ptr(), prop, p_value); + undo_redo->add_undo_property(node.ptr(), prop, node->get(prop)); + undo_redo->commit_action(); + updating = false; + } + + void _node_changed() { + if (updating) + return; + for (int i = 0; i < properties.size(); i++) { + properties[i]->update_property(); + } + } + + void _refresh_request() { + VisualShaderEditor::get_singleton()->call_deferred("_update_graph"); + } + + bool updating; + Ref<VisualShaderNode> node; + Vector<EditorProperty *> properties; + + void setup(Vector<EditorProperty *> p_properties, const Vector<StringName> &p_names, Ref<VisualShaderNode> p_node) { + updating = false; + node = p_node; + properties = p_properties; + + for (int i = 0; i < p_properties.size(); i++) { + + add_child(p_properties[i]); + + properties[i]->connect("property_changed", this, "_property_changed"); + properties[i]->set_object_and_property(node.ptr(), p_names[i]); + properties[i]->update_property(); + properties[i]->set_name_split_ratio(0); + } + node->connect("changed", this, "_node_changed"); + node->connect("editor_refresh_request", this, "_refresh_request", varray(), CONNECT_DEFERRED); + } + + static void _bind_methods() { + ClassDB::bind_method("_property_changed", &VisualShaderNodePluginDefaultEditor::_property_changed); + ClassDB::bind_method("_node_changed", &VisualShaderNodePluginDefaultEditor::_node_changed); + ClassDB::bind_method("_refresh_request", &VisualShaderNodePluginDefaultEditor::_refresh_request); + } +}; + +Control *VisualShaderNodePluginDefault::create_editor(const Ref<VisualShaderNode> &p_node) { + + if (p_node->is_class("VisualShaderNodeInput")) { + //create input + VisualShaderNodePluginInputEditor *input_editor = memnew(VisualShaderNodePluginInputEditor); + input_editor->setup(p_node); + return input_editor; + } + + Vector<StringName> properties = p_node->get_editable_properties(); + if (properties.size() == 0) { + return NULL; + } + + List<PropertyInfo> props; + p_node->get_property_list(&props); + + Vector<PropertyInfo> pinfo; + + for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) { + + for (int i = 0; i < properties.size(); i++) { + if (E->get().name == String(properties[i])) { + pinfo.push_back(E->get()); + } + } + } + + if (pinfo.size() == 0) + return NULL; + + properties.clear(); + + Ref<VisualShaderNode> node = p_node; + Vector<EditorProperty *> editors; + + for (int i = 0; i < pinfo.size(); i++) { + + EditorProperty *prop = EditorInspector::instantiate_property_editor(node.ptr(), pinfo[i].type, pinfo[i].name, pinfo[i].hint, pinfo[i].hint_string, pinfo[i].usage); + if (!prop) + return NULL; + + if (Object::cast_to<EditorPropertyResource>(prop)) { + Object::cast_to<EditorPropertyResource>(prop)->set_use_sub_inspector(false); + prop->set_custom_minimum_size(Size2(100 * EDSCALE, 0)); + } else if (Object::cast_to<EditorPropertyTransform>(prop)) { + prop->set_custom_minimum_size(Size2(250 * EDSCALE, 0)); + } else if (Object::cast_to<EditorPropertyFloat>(prop) || Object::cast_to<EditorPropertyVector3>(prop)) { + prop->set_custom_minimum_size(Size2(100 * EDSCALE, 0)); + } else if (Object::cast_to<EditorPropertyEnum>(prop)) { + prop->set_custom_minimum_size(Size2(100 * EDSCALE, 0)); + Object::cast_to<EditorPropertyEnum>(prop)->set_option_button_clip(false); + } + + editors.push_back(prop); + properties.push_back(pinfo[i].name); + } + VisualShaderNodePluginDefaultEditor *editor = memnew(VisualShaderNodePluginDefaultEditor); + editor->setup(editors, properties, p_node); + return editor; +} + +void EditorPropertyShaderMode::_option_selected(int p_which) { + + //will not use this, instead will do all the logic setting manually + //emit_signal("property_changed", get_edited_property(), p_which); + + Ref<VisualShader> visual_shader(Object::cast_to<VisualShader>(get_edited_object())); + + if (visual_shader->get_mode() == p_which) + return; + + UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo(); + undo_redo->create_action("Visual Shader Mode Changed"); + //do is easy + undo_redo->add_do_method(visual_shader.ptr(), "set_mode", p_which); + undo_redo->add_undo_method(visual_shader.ptr(), "set_mode", visual_shader->get_mode()); + //now undo is hell + + //1. restore connections to output + for (int i = 0; i < VisualShader::TYPE_MAX; i++) { + + VisualShader::Type type = VisualShader::Type(i); + List<VisualShader::Connection> conns; + visual_shader->get_node_connections(type, &conns); + for (List<VisualShader::Connection>::Element *E = conns.front(); E; E = E->next()) { + if (E->get().to_node == VisualShader::NODE_ID_OUTPUT) { + 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); + } + } + } + //2. restore input indices + for (int i = 0; i < VisualShader::TYPE_MAX; i++) { + + VisualShader::Type type = VisualShader::Type(i); + Vector<int> nodes = visual_shader->get_node_list(type); + for (int i = 0; i < nodes.size(); i++) { + Ref<VisualShaderNodeInput> input = visual_shader->get_node(type, nodes[i]); + if (!input.is_valid()) { + continue; + } + + undo_redo->add_undo_method(input.ptr(), "set_input_name", input->get_input_name()); + } + } + + //3. restore enums and flags + List<PropertyInfo> props; + visual_shader->get_property_list(&props); + + for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) { + + if (E->get().name.begins_with("flags/") || E->get().name.begins_with("modes/")) { + undo_redo->add_undo_property(visual_shader.ptr(), E->get().name, visual_shader->get(E->get().name)); + } + } + + //update graph + 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 EditorPropertyShaderMode::update_property() { + + int which = get_edited_object()->get(get_edited_property()); + options->select(which); +} + +void EditorPropertyShaderMode::setup(const Vector<String> &p_options) { + for (int i = 0; i < p_options.size(); i++) { + options->add_item(p_options[i], i); + } +} + +void EditorPropertyShaderMode::set_option_button_clip(bool p_enable) { + options->set_clip_text(p_enable); +} + +void EditorPropertyShaderMode::_bind_methods() { + + ClassDB::bind_method(D_METHOD("_option_selected"), &EditorPropertyShaderMode::_option_selected); +} + +EditorPropertyShaderMode::EditorPropertyShaderMode() { + options = memnew(OptionButton); + options->set_clip_text(true); + add_child(options); + add_focusable(options); + options->connect("item_selected", this, "_option_selected"); +} + +bool EditorInspectorShaderModePlugin::can_handle(Object *p_object) { + return true; //can handle everything +} + +void EditorInspectorShaderModePlugin::parse_begin(Object *p_object) { + //do none +} + +bool EditorInspectorShaderModePlugin::parse_property(Object *p_object, Variant::Type p_type, const String &p_path, PropertyHint p_hint, const String &p_hint_text, int p_usage) { + + if (p_path == "mode" && p_object->is_class("VisualShader") && p_type == Variant::INT) { + + EditorPropertyShaderMode *editor = memnew(EditorPropertyShaderMode); + Vector<String> options = p_hint_text.split(","); + editor->setup(options); + add_property_editor(p_path, editor); + + return true; + } + + return false; //can be overriden, although it will most likely be last anyway +} + +void EditorInspectorShaderModePlugin::parse_end() { + //do none +} +////////////////////////////////// + +void VisualShaderNodePortPreview::_shader_changed() { + if (shader.is_null()) { + return; + } + + Vector<VisualShader::DefaultTextureParam> default_textures; + String shader_code = shader->generate_preview_shader(type, node, port, default_textures); + + Ref<Shader> preview_shader; + preview_shader.instance(); + preview_shader->set_code(shader_code); + for (int i = 0; i < default_textures.size(); i++) { + preview_shader->set_default_texture_param(default_textures[i].name, default_textures[i].param); + } + + Ref<ShaderMaterial> material; + material.instance(); + material->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_history()->get_path_size() - 1; i >= 0; i--) { + Object *object = ObjectDB::get_instance(EditorNode::get_singleton()->get_editor_history()->get_path_object(i)); + if (!object) + continue; + ShaderMaterial *src_mat = Object::cast_to<ShaderMaterial>(object); + if (src_mat && src_mat->get_shader().is_valid()) { + + List<PropertyInfo> params; + src_mat->get_shader()->get_param_list(¶ms); + for (List<PropertyInfo>::Element *E = params.front(); E; E = E->next()) { + material->set(E->get().name, src_mat->get(E->get().name)); + } + } + } + + set_material(material); +} + +void VisualShaderNodePortPreview::setup(const Ref<VisualShader> &p_shader, VisualShader::Type p_type, int p_node, int p_port) { + + shader = p_shader; + shader->connect("changed", this, "_shader_changed"); + type = p_type; + port = p_port; + node = p_node; + update(); + _shader_changed(); +} + +Size2 VisualShaderNodePortPreview::get_minimum_size() const { + return Size2(100, 100) * EDSCALE; +} + +void VisualShaderNodePortPreview::_notification(int p_what) { + if (p_what == NOTIFICATION_DRAW) { + Vector<Vector2> points; + Vector<Vector2> uvs; + Vector<Color> colors; + points.push_back(Vector2()); + uvs.push_back(Vector2(0, 0)); + colors.push_back(Color(1, 1, 1, 1)); + points.push_back(Vector2(get_size().width, 0)); + uvs.push_back(Vector2(1, 0)); + colors.push_back(Color(1, 1, 1, 1)); + points.push_back(get_size()); + uvs.push_back(Vector2(1, 1)); + colors.push_back(Color(1, 1, 1, 1)); + points.push_back(Vector2(0, get_size().height)); + uvs.push_back(Vector2(0, 1)); + colors.push_back(Color(1, 1, 1, 1)); + + draw_primitive(points, colors, uvs); + } +} + +void VisualShaderNodePortPreview::_bind_methods() { + ClassDB::bind_method("_shader_changed", &VisualShaderNodePortPreview::_shader_changed); +} + +VisualShaderNodePortPreview::VisualShaderNodePortPreview() { +} diff --git a/editor/plugins/visual_shader_editor_plugin.h b/editor/plugins/visual_shader_editor_plugin.h new file mode 100644 index 0000000000..f89d073dde --- /dev/null +++ b/editor/plugins/visual_shader_editor_plugin.h @@ -0,0 +1,186 @@ +#ifndef VISUAL_SHADER_EDITOR_PLUGIN_H +#define VISUAL_SHADER_EDITOR_PLUGIN_H + +#include "editor/editor_node.h" +#include "editor/editor_plugin.h" +#include "editor/property_editor.h" +#include "scene/gui/button.h" +#include "scene/gui/graph_edit.h" +#include "scene/gui/popup.h" +#include "scene/gui/tree.h" +#include "scene/resources/visual_shader.h" + +class VisualShaderNodePlugin : public Reference { + + GDCLASS(VisualShaderNodePlugin, Reference) +protected: + static void _bind_methods(); + +public: + virtual Control *create_editor(const Ref<VisualShaderNode> &p_node); +}; + +class VisualShaderEditor : public VBoxContainer { + + GDCLASS(VisualShaderEditor, VBoxContainer); + + CustomPropertyEditor *property_editor; + int editing_node; + int editing_port; + + Ref<VisualShader> visual_shader; + GraphEdit *graph; + MenuButton *add_node; + + OptionButton *edit_type; + + PanelContainer *error_panel; + Label *error_label; + + UndoRedo *undo_redo; + + void _update_graph(); + + struct AddOption { + String name; + String category; + String type; + Ref<Script> script; + AddOption(const String &p_name = String(), const String &p_category = String(), const String &p_type = String()) { + name = p_name; + type = p_type; + category = p_category; + } + }; + + Vector<AddOption> add_options; + + void _draw_color_over_button(Object *obj, Color p_color); + + void _add_node(int p_idx); + void _update_options_menu(); + + static VisualShaderEditor *singleton; + + void _node_dragged(const Vector2 &p_from, const Vector2 &p_to, int p_node); + bool updating; + + void _connection_request(const String &p_from, int p_from_index, const String &p_to, int p_to_index); + void _disconnection_request(const String &p_from, int p_from_index, const String &p_to, int p_to_index); + + void _scroll_changed(const Vector2 &p_scroll); + void _node_selected(Object *p_node); + + void _delete_request(int); + + void _removed_from_graph(); + + void _node_changed(int p_id); + + void _edit_port_default_input(Object *p_button, int p_node, int p_port); + void _port_edited(); + + void _connection_to_empty(const String &p_from, int p_from_slot, const Vector2 &p_release_position); + + void _line_edit_changed(const String &p_text, Object *line_edit, int p_node_id); + void _line_edit_focus_out(Object *line_edit, int p_node_id); + + void _duplicate_nodes(); + + Vector<Ref<VisualShaderNodePlugin> > plugins; + + void _mode_selected(int p_id); + + void _input_select_item(Ref<VisualShaderNodeInput> input, String name); + + void _preview_select_port(int p_node, int p_port); + +protected: + void _notification(int p_what); + static void _bind_methods(); + +public: + void add_plugin(const Ref<VisualShaderNodePlugin> &p_plugin); + void remove_plugin(const Ref<VisualShaderNodePlugin> &p_plugin); + + static VisualShaderEditor *get_singleton() { return singleton; } + + void add_custom_type(const String &p_name, const String &p_category, const Ref<Script> &p_script); + void remove_custom_type(const Ref<Script> &p_script); + + virtual Size2 get_minimum_size() const; + void edit(VisualShader *p_visual_shader); + VisualShaderEditor(); +}; + +class VisualShaderEditorPlugin : public EditorPlugin { + + GDCLASS(VisualShaderEditorPlugin, EditorPlugin); + + VisualShaderEditor *visual_shader_editor; + EditorNode *editor; + Button *button; + +public: + virtual String get_name() const { return "VisualShader"; } + bool has_main_screen() const { return false; } + virtual void edit(Object *p_object); + virtual bool handles(Object *p_object) const; + virtual void make_visible(bool p_visible); + + VisualShaderEditorPlugin(EditorNode *p_node); + ~VisualShaderEditorPlugin(); +}; + +class VisualShaderNodePluginDefault : public VisualShaderNodePlugin { + + GDCLASS(VisualShaderNodePluginDefault, VisualShaderNodePlugin) + +public: + virtual Control *create_editor(const Ref<VisualShaderNode> &p_node); +}; + +class EditorPropertyShaderMode : public EditorProperty { + GDCLASS(EditorPropertyShaderMode, EditorProperty) + OptionButton *options; + + void _option_selected(int p_which); + +protected: + static void _bind_methods(); + +public: + void setup(const Vector<String> &p_options); + virtual void update_property(); + void set_option_button_clip(bool p_enable); + EditorPropertyShaderMode(); +}; + +class EditorInspectorShaderModePlugin : public EditorInspectorPlugin { + GDCLASS(EditorInspectorShaderModePlugin, EditorInspectorPlugin) + +public: + virtual bool can_handle(Object *p_object); + virtual void parse_begin(Object *p_object); + virtual bool parse_property(Object *p_object, Variant::Type p_type, const String &p_path, PropertyHint p_hint, const String &p_hint_text, int p_usage); + virtual void parse_end(); +}; + +class VisualShaderNodePortPreview : public Control { + GDCLASS(VisualShaderNodePortPreview, Control) + Ref<VisualShader> shader; + VisualShader::Type type; + int node; + int port; + void _shader_changed(); //must regen +protected: + void _notification(int p_what); + static void _bind_methods(); + +public: + virtual Size2 get_minimum_size() const; + void setup(const Ref<VisualShader> &p_shader, VisualShader::Type p_type, int p_node, int p_port); + VisualShaderNodePortPreview(); +}; + +#endif // VISUAL_SHADER_EDITOR_PLUGIN_H diff --git a/main/tests/test_shader_lang.cpp b/main/tests/test_shader_lang.cpp index 63032597ed..7103b436e1 100644 --- a/main/tests/test_shader_lang.cpp +++ b/main/tests/test_shader_lang.cpp @@ -321,8 +321,8 @@ MainLoop *test() { dt["fragment"].built_ins["ALBEDO"] = SL::TYPE_VEC3; dt["fragment"].can_discard = true; - Set<String> rm; - rm.insert("popo"); + Vector<StringName> rm; + rm.push_back("popo"); Set<String> types; types.insert("spatial"); diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp index c5e4149782..2901176a69 100644 --- a/scene/gui/option_button.cpp +++ b/scene/gui/option_button.cpp @@ -36,7 +36,7 @@ Size2 OptionButton::get_minimum_size() const { Size2 minsize = Button::get_minimum_size(); if (has_icon("arrow")) - minsize.width += Control::get_icon("arrow")->get_width(); + minsize.width += Control::get_icon("arrow")->get_width() + get_constant("hseparation"); return minsize; } diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp index f5890fa2ee..ebec61ee6d 100644 --- a/scene/gui/popup_menu.cpp +++ b/scene/gui/popup_menu.cpp @@ -430,6 +430,8 @@ void PopupMenu::_notification(int p_what) { Ref<Texture> uncheck[] = { get_icon("unchecked"), get_icon("radio_unchecked") }; Ref<Texture> submenu = get_icon("submenu"); Ref<StyleBox> separator = get_stylebox("separator"); + Ref<StyleBox> labeled_separator_left = get_stylebox("labeled_separator_left"); + Ref<StyleBox> labeled_separator_right = get_stylebox("labeled_separator_right"); style->draw(ci, Rect2(Point2(), get_size())); Point2 ofs = style->get_offset(); @@ -466,10 +468,25 @@ void PopupMenu::_notification(int p_what) { hover->draw(ci, Rect2(item_ofs + Point2(-hseparation, -vseparation / 2), Size2(get_size().width - style->get_minimum_size().width + hseparation * 2, h + vseparation))); } + String text = items[i].shortcut.is_valid() ? String(tr(items[i].shortcut->get_name())) : items[i].xl_text; + if (items[i].separator) { int sep_h = separator->get_center_size().height + separator->get_minimum_size().height; - separator->draw(ci, Rect2(item_ofs + Point2(0, Math::floor((h - sep_h) / 2.0)), Size2(get_size().width - style->get_minimum_size().width, sep_h))); + if (text != String()) { + int ss = font->get_string_size(text).width; + int center = (get_size().width) / 2; + int l = center - ss / 2; + int r = center + ss / 2; + if (l > item_ofs.x) { + labeled_separator_left->draw(ci, Rect2(item_ofs + Point2(0, Math::floor((h - sep_h) / 2.0)), Size2(MAX(0, l - item_ofs.x), sep_h))); + } + if (r < get_size().width - style->get_margin(MARGIN_RIGHT)) { + labeled_separator_right->draw(ci, Rect2(Point2(r, item_ofs.y + Math::floor((h - sep_h) / 2.0)), Size2(MAX(0, get_size().width - style->get_margin(MARGIN_RIGHT) - r), sep_h))); + } + } else { + separator->draw(ci, Rect2(item_ofs + Point2(0, Math::floor((h - sep_h) / 2.0)), Size2(get_size().width - style->get_minimum_size().width, sep_h))); + } } if (items[i].checkable_type) { @@ -489,8 +506,13 @@ void PopupMenu::_notification(int p_what) { } item_ofs.y += font->get_ascent(); - String text = items[i].shortcut.is_valid() ? String(tr(items[i].shortcut->get_name())) : items[i].xl_text; - if (!items[i].separator) { + if (items[i].separator) { + + if (text != String()) { + int center = (get_size().width - font->get_string_size(text).width) / 2; + font->draw(ci, Point2(center, item_ofs.y + Math::floor((h - font_h) / 2.0)), text, font_color_disabled); + } + } else { font->draw(ci, item_ofs + Point2(0, Math::floor((h - font_h) / 2.0)), text, items[i].disabled ? font_color_disabled : (i == mouse_over ? font_color_hover : font_color)); } @@ -1073,11 +1095,15 @@ void PopupMenu::remove_item(int p_idx) { update(); } -void PopupMenu::add_separator() { +void PopupMenu::add_separator(const String &p_text) { Item sep; sep.separator = true; sep.ID = -1; + if (p_text != String()) { + sep.text = p_text; + sep.xl_text = tr(p_text); + } items.push_back(sep); update(); } @@ -1310,7 +1336,7 @@ void PopupMenu::_bind_methods() { ClassDB::bind_method(D_METHOD("remove_item", "idx"), &PopupMenu::remove_item); - ClassDB::bind_method(D_METHOD("add_separator"), &PopupMenu::add_separator); + ClassDB::bind_method(D_METHOD("add_separator", "label"), &PopupMenu::add_separator, DEFVAL(String())); ClassDB::bind_method(D_METHOD("clear"), &PopupMenu::clear); ClassDB::bind_method(D_METHOD("_set_items"), &PopupMenu::_set_items); diff --git a/scene/gui/popup_menu.h b/scene/gui/popup_menu.h index 44f02a9d3d..8ec51c7d3a 100644 --- a/scene/gui/popup_menu.h +++ b/scene/gui/popup_menu.h @@ -180,7 +180,7 @@ public: void remove_item(int p_idx); - void add_separator(); + void add_separator(const String &p_text = String()); void clear(); diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index a0bac09442..2064bf9b04 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -159,6 +159,8 @@ #include "scene/resources/texture.h" #include "scene/resources/tile_set.h" #include "scene/resources/video_stream.h" +#include "scene/resources/visual_shader.h" +#include "scene/resources/visual_shader_nodes.h" #include "scene/resources/world.h" #include "scene/resources/world_2d.h" #include "scene/scene_string_names.h" @@ -456,6 +458,39 @@ void register_scene_types() { AcceptDialog::set_swap_ok_cancel(GLOBAL_DEF("gui/common/swap_ok_cancel", bool(OS::get_singleton()->get_swap_ok_cancel()))); ClassDB::register_class<Shader>(); + ClassDB::register_class<VisualShader>(); + ClassDB::register_virtual_class<VisualShaderNode>(); + ClassDB::register_class<VisualShaderNodeInput>(); + ClassDB::register_virtual_class<VisualShaderNodeOutput>(); + ClassDB::register_class<VisualShaderNodeScalarConstant>(); + ClassDB::register_class<VisualShaderNodeColorConstant>(); + ClassDB::register_class<VisualShaderNodeVec3Constant>(); + ClassDB::register_class<VisualShaderNodeTransformConstant>(); + ClassDB::register_class<VisualShaderNodeScalarOp>(); + ClassDB::register_class<VisualShaderNodeVectorOp>(); + ClassDB::register_class<VisualShaderNodeColorOp>(); + ClassDB::register_class<VisualShaderNodeTransformMult>(); + ClassDB::register_class<VisualShaderNodeTransformVecMult>(); + ClassDB::register_class<VisualShaderNodeScalarFunc>(); + ClassDB::register_class<VisualShaderNodeVectorFunc>(); + ClassDB::register_class<VisualShaderNodeDotProduct>(); + ClassDB::register_class<VisualShaderNodeVectorLen>(); + ClassDB::register_class<VisualShaderNodeScalarInterp>(); + ClassDB::register_class<VisualShaderNodeVectorInterp>(); + ClassDB::register_class<VisualShaderNodeVectorConstruct>(); + ClassDB::register_class<VisualShaderNodeTransformConstruct>(); + ClassDB::register_class<VisualShaderNodeVectorDestruct>(); + ClassDB::register_class<VisualShaderNodeTransformDestruct>(); + ClassDB::register_class<VisualShaderNodeTexture>(); + ClassDB::register_class<VisualShaderNodeCubeMap>(); + ClassDB::register_virtual_class<VisualShaderNodeUniform>(); + ClassDB::register_class<VisualShaderNodeScalarUniform>(); + ClassDB::register_class<VisualShaderNodeColorUniform>(); + ClassDB::register_class<VisualShaderNodeVec3Uniform>(); + ClassDB::register_class<VisualShaderNodeTransformUniform>(); + ClassDB::register_class<VisualShaderNodeTextureUniform>(); + ClassDB::register_class<VisualShaderNodeCubeMapUniform>(); + ClassDB::register_class<ShaderMaterial>(); ClassDB::register_virtual_class<CanvasItem>(); ClassDB::register_class<CanvasItemMaterial>(); diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp index 702953fa40..d4432b969f 100644 --- a/scene/resources/default_theme/default_theme.cpp +++ b/scene/resources/default_theme/default_theme.cpp @@ -585,6 +585,8 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_stylebox("panel_disabled", "PopupMenu", make_stylebox(popup_bg_disabled_png, 4, 4, 4, 4)); theme->set_stylebox("hover", "PopupMenu", selected); theme->set_stylebox("separator", "PopupMenu", make_stylebox(vseparator_png, 3, 3, 3, 3)); + theme->set_stylebox("labeled_separator_left", "PopupMenu", make_stylebox(vseparator_png, 0, 0, 0, 0)); + theme->set_stylebox("labeled_separator_right", "PopupMenu", make_stylebox(vseparator_png, 0, 0, 0, 0)); theme->set_icon("checked", "PopupMenu", make_icon(checked_png)); theme->set_icon("unchecked", "PopupMenu", make_icon(unchecked_png)); diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp index 654d7b884e..90e103c96b 100644 --- a/scene/resources/material.cpp +++ b/scene/resources/material.cpp @@ -145,11 +145,17 @@ void ShaderMaterial::_get_property_list(List<PropertyInfo> *p_list) const { void ShaderMaterial::set_shader(const Ref<Shader> &p_shader) { + if (shader.is_valid()) { + shader->disconnect("changed", this, "_shader_changed"); + } + shader = p_shader; RID rid; - if (shader.is_valid()) + if (shader.is_valid()) { rid = shader->get_rid(); + shader->connect("changed", this, "_shader_changed"); + } VS::get_singleton()->material_set_shader(_get_material(), rid); _change_notify(); //properties for shader exposed @@ -171,12 +177,17 @@ Variant ShaderMaterial::get_shader_param(const StringName &p_param) const { return VS::get_singleton()->material_get_param(_get_material(), p_param); } +void ShaderMaterial::_shader_changed() { + _change_notify(); //update all properties +} + void ShaderMaterial::_bind_methods() { ClassDB::bind_method(D_METHOD("set_shader", "shader"), &ShaderMaterial::set_shader); ClassDB::bind_method(D_METHOD("get_shader"), &ShaderMaterial::get_shader); ClassDB::bind_method(D_METHOD("set_shader_param", "param", "value"), &ShaderMaterial::set_shader_param); ClassDB::bind_method(D_METHOD("get_shader_param", "param"), &ShaderMaterial::get_shader_param); + ClassDB::bind_method(D_METHOD("_shader_changed"), &ShaderMaterial::_shader_changed); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shader", PROPERTY_HINT_RESOURCE_TYPE, "Shader"), "set_shader", "get_shader"); } diff --git a/scene/resources/material.h b/scene/resources/material.h index 87594213bc..3b9d1be260 100644 --- a/scene/resources/material.h +++ b/scene/resources/material.h @@ -92,6 +92,8 @@ protected: virtual bool _can_do_next_pass() const; + void _shader_changed(); + public: void set_shader(const Ref<Shader> &p_shader); Ref<Shader> get_shader() const; diff --git a/scene/resources/scene_format_text.cpp b/scene/resources/scene_format_text.cpp index 597866eb74..9df117d09c 100644 --- a/scene/resources/scene_format_text.cpp +++ b/scene/resources/scene_format_text.cpp @@ -896,7 +896,7 @@ static void bs_save_unicode_string(FileAccess *f, const String &p_string, bool p CharString utf8 = p_string.utf8(); if (p_bit_on_len) { - f->store_32(utf8.length() + 1 | 0x80000000); + f->store_32((utf8.length() + 1) | 0x80000000); } else { f->store_32(utf8.length() + 1); } diff --git a/scene/resources/shader.cpp b/scene/resources/shader.cpp index 36740a307b..f53f03c1c8 100644 --- a/scene/resources/shader.cpp +++ b/scene/resources/shader.cpp @@ -54,16 +54,20 @@ void Shader::set_code(const String &p_code) { VisualServer::get_singleton()->shader_set_code(shader, p_code); params_cache_dirty = true; - emit_signal(SceneStringNames::get_singleton()->changed); + + emit_changed(); } String Shader::get_code() const { + _update_shader(); return VisualServer::get_singleton()->shader_get_code(shader); } void Shader::get_param_list(List<PropertyInfo> *p_params) const { + _update_shader(); + List<PropertyInfo> local; VisualServer::get_singleton()->shader_get_param_list(shader, &local); params_cache.clear(); @@ -72,6 +76,9 @@ void Shader::get_param_list(List<PropertyInfo> *p_params) const { for (List<PropertyInfo>::Element *E = local.front(); E; E = E->next()) { PropertyInfo pi = E->get(); + if (default_textures.has(pi.name)) { //do not show default textures + continue; + } pi.name = "shader_param/" + pi.name; params_cache[pi.name] = E->get().name; if (p_params) { @@ -86,6 +93,8 @@ void Shader::get_param_list(List<PropertyInfo> *p_params) const { RID Shader::get_rid() const { + _update_shader(); + return shader; } @@ -98,6 +107,8 @@ void Shader::set_default_texture_param(const StringName &p_param, const Ref<Text default_textures.erase(p_param); VS::get_singleton()->shader_set_default_texture_param(shader, p_param, RID()); } + + emit_changed(); } Ref<Texture> Shader::get_default_texture_param(const StringName &p_param) const { @@ -120,6 +131,9 @@ bool Shader::has_param(const StringName &p_param) const { return params_cache.has(p_param); } +void Shader::_update_shader() const { +} + void Shader::_bind_methods() { ClassDB::bind_method(D_METHOD("get_mode"), &Shader::get_mode); @@ -227,5 +241,5 @@ void ResourceFormatSaverShader::get_recognized_extensions(const RES &p_resource, } bool ResourceFormatSaverShader::recognize(const RES &p_resource) const { - return Object::cast_to<Shader>(*p_resource) != NULL; + return p_resource->get_class_name() == "Shader"; //only shader, not inherited } diff --git a/scene/resources/shader.h b/scene/resources/shader.h index 248a6f0125..efc5da7753 100644 --- a/scene/resources/shader.h +++ b/scene/resources/shader.h @@ -61,12 +61,13 @@ private: mutable Map<StringName, StringName> params_cache; //map a shader param to a material param.. Map<StringName, Ref<Texture> > default_textures; + virtual void _update_shader() const; //used for visual shader protected: static void _bind_methods(); public: //void set_mode(Mode p_mode); - Mode get_mode() const; + virtual Mode get_mode() const; void set_code(const String &p_code); String get_code() const; diff --git a/scene/resources/style_box.cpp b/scene/resources/style_box.cpp index ebad00b068..fb81375b0a 100644 --- a/scene/resources/style_box.cpp +++ b/scene/resources/style_box.cpp @@ -905,12 +905,20 @@ bool StyleBoxLine::is_vertical() const { return vertical; } -void StyleBoxLine::set_grow(float p_grow) { - grow = p_grow; +void StyleBoxLine::set_grow_end(float p_grow_end) { + grow_end = p_grow_end; emit_changed(); } -float StyleBoxLine::get_grow() const { - return grow; +float StyleBoxLine::get_grow_end() const { + return grow_end; +} + +void StyleBoxLine::set_grow_begin(float p_grow_begin) { + grow_begin = p_grow_begin; + emit_changed(); +} +float StyleBoxLine::get_grow_begin() const { + return grow_begin; } void StyleBoxLine::_bind_methods() { @@ -919,13 +927,16 @@ void StyleBoxLine::_bind_methods() { ClassDB::bind_method(D_METHOD("get_color"), &StyleBoxLine::get_color); ClassDB::bind_method(D_METHOD("set_thickness", "thickness"), &StyleBoxLine::set_thickness); ClassDB::bind_method(D_METHOD("get_thickness"), &StyleBoxLine::get_thickness); - ClassDB::bind_method(D_METHOD("set_grow", "grow"), &StyleBoxLine::set_grow); - ClassDB::bind_method(D_METHOD("get_grow"), &StyleBoxLine::get_grow); + ClassDB::bind_method(D_METHOD("set_grow_begin", "offset"), &StyleBoxLine::set_grow_begin); + ClassDB::bind_method(D_METHOD("get_grow_begin"), &StyleBoxLine::get_grow_begin); + ClassDB::bind_method(D_METHOD("set_grow_end", "offset"), &StyleBoxLine::set_grow_end); + ClassDB::bind_method(D_METHOD("get_grow_end"), &StyleBoxLine::get_grow_end); ClassDB::bind_method(D_METHOD("set_vertical", "vertical"), &StyleBoxLine::set_vertical); ClassDB::bind_method(D_METHOD("is_vertical"), &StyleBoxLine::is_vertical); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_color", "get_color"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "grow", PROPERTY_HINT_RANGE, "-300,300,1"), "set_grow", "get_grow"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "grow_begin", PROPERTY_HINT_RANGE, "-300,300,1"), "set_grow_begin", "get_grow_begin"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "grow_end", PROPERTY_HINT_RANGE, "-300,300,1"), "set_grow_end", "get_grow_end"); ADD_PROPERTY(PropertyInfo(Variant::INT, "thickness", PROPERTY_HINT_RANGE, "0,10"), "set_thickness", "get_thickness"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "vertical"), "set_vertical", "is_vertical"); } @@ -941,12 +952,12 @@ void StyleBoxLine::draw(RID p_canvas_item, const Rect2 &p_rect) const { Rect2i r = p_rect; if (vertical) { - r.position.y -= grow; - r.size.y += grow * 2; + r.position.y -= grow_begin; + r.size.y += (grow_begin + grow_end); r.size.x = thickness; } else { - r.position.x -= grow; - r.size.x += grow * 2; + r.position.x -= grow_begin; + r.size.x += (grow_begin + grow_end); r.size.y = thickness; } @@ -954,7 +965,8 @@ void StyleBoxLine::draw(RID p_canvas_item, const Rect2 &p_rect) const { } StyleBoxLine::StyleBoxLine() { - grow = 1.0; + grow_begin = 1.0; + grow_end = 1.0; thickness = 1; color = Color(0.0, 0.0, 0.0); vertical = false; diff --git a/scene/resources/style_box.h b/scene/resources/style_box.h index c1d84fe19f..ed193a1ab4 100644 --- a/scene/resources/style_box.h +++ b/scene/resources/style_box.h @@ -236,7 +236,8 @@ class StyleBoxLine : public StyleBox { Color color; int thickness; bool vertical; - float grow; + float grow_begin; + float grow_end; protected: virtual float get_style_margin(Margin p_margin) const; @@ -252,8 +253,11 @@ public: void set_vertical(bool p_vertical); bool is_vertical() const; - void set_grow(float p_grow); - float get_grow() const; + void set_grow_begin(float p_grow); + float get_grow_begin() const; + + void set_grow_end(float p_grow); + float get_grow_end() const; virtual Size2 get_center_size() const; diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp new file mode 100644 index 0000000000..b7b7802d1b --- /dev/null +++ b/scene/resources/visual_shader.cpp @@ -0,0 +1,1555 @@ +#include "visual_shader.h" +#include "servers/visual/shader_types.h" +#include "vmap.h" + +void VisualShaderNode::set_output_port_for_preview(int p_index) { + + port_preview = p_index; +} + +int VisualShaderNode::get_output_port_for_preview() const { + + return port_preview; +} + +void VisualShaderNode::set_input_port_default_value(int p_port, const Variant &p_value) { + default_input_values[p_port] = p_value; + emit_changed(); +} + +Variant VisualShaderNode::get_input_port_default_value(int p_port) const { + if (default_input_values.has(p_port)) { + return default_input_values[p_port]; + } + + return Variant(); +} + +bool VisualShaderNode::is_port_separator(int p_index) const { + return false; +} + +Vector<VisualShader::DefaultTextureParam> VisualShaderNode::get_default_texture_parameters(VisualShader::Type p_type, int p_id) const { + return Vector<VisualShader::DefaultTextureParam>(); +} +String VisualShaderNode::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { + return String(); +} + +Vector<StringName> VisualShaderNode::get_editable_properties() const { + return Vector<StringName>(); +} + +Array VisualShaderNode::_get_default_input_values() const { + + Array ret; + for (Map<int, Variant>::Element *E = default_input_values.front(); E; E = E->next()) { + ret.push_back(E->key()); + ret.push_back(E->get()); + } + return ret; +} +void VisualShaderNode::_set_default_input_values(const Array &p_values) { + + if (p_values.size() % 2 == 0) { + for (int i = 0; i < p_values.size(); i += 2) { + default_input_values[p_values[i + 0]] = p_values[i + 1]; + } + } + + emit_changed(); +} + +String VisualShaderNode::get_warning(Shader::Mode p_mode, VisualShader::Type p_type) const { + return String(); +} + +void VisualShaderNode::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_output_port_for_preview", "port"), &VisualShaderNode::set_output_port_for_preview); + ClassDB::bind_method(D_METHOD("get_output_port_for_preview"), &VisualShaderNode::get_output_port_for_preview); + + ClassDB::bind_method(D_METHOD("set_input_port_default_value", "port", "value"), &VisualShaderNode::set_input_port_default_value); + ClassDB::bind_method(D_METHOD("get_input_port_default_value", "port"), &VisualShaderNode::get_input_port_default_value); + + ClassDB::bind_method(D_METHOD("_set_default_input_values", "values"), &VisualShaderNode::_set_default_input_values); + ClassDB::bind_method(D_METHOD("_get_default_input_values"), &VisualShaderNode::_get_default_input_values); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "output_port_for_preview"), "set_output_port_for_preview", "get_output_port_for_preview"); + ADD_PROPERTYNZ(PropertyInfo(Variant::ARRAY, "default_input_values", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "_set_default_input_values", "_get_default_input_values"); + ADD_SIGNAL(MethodInfo("editor_refresh_request")); +} + +VisualShaderNode::VisualShaderNode() { + port_preview = -1; +} + +///////////////////////////////////////////////////////// + +void VisualShader::add_node(Type p_type, const Ref<VisualShaderNode> &p_node, const Vector2 &p_position, int p_id) { + ERR_FAIL_COND(p_node.is_null()); + ERR_FAIL_COND(p_id < 2); + ERR_FAIL_INDEX(p_type, TYPE_MAX); + Graph *g = &graph[p_type]; + ERR_FAIL_COND(g->nodes.has(p_id)); + Node n; + n.node = p_node; + n.position = p_position; + + Ref<VisualShaderNodeUniform> uniform = n.node; + if (uniform.is_valid()) { + String valid_name = validate_uniform_name(uniform->get_uniform_name(), uniform); + uniform->set_uniform_name(valid_name); + } + + Ref<VisualShaderNodeInput> input = n.node; + if (input.is_valid()) { + input->shader_mode = shader_mode; + input->shader_type = p_type; + input->connect("input_type_changed", this, "_input_type_changed", varray(p_type, p_id)); + } + + n.node->connect("changed", this, "_queue_update"); + + g->nodes[p_id] = n; + + _queue_update(); +} + +void VisualShader::set_node_position(Type p_type, int p_id, const Vector2 &p_position) { + ERR_FAIL_INDEX(p_type, TYPE_MAX); + Graph *g = &graph[p_type]; + ERR_FAIL_COND(!g->nodes.has(p_id)); + g->nodes[p_id].position = p_position; +} + +Vector2 VisualShader::get_node_position(Type p_type, int p_id) const { + ERR_FAIL_INDEX_V(p_type, TYPE_MAX, Vector2()); + const Graph *g = &graph[p_type]; + ERR_FAIL_COND_V(!g->nodes.has(p_id), Vector2()); + return g->nodes[p_id].position; +} +Ref<VisualShaderNode> VisualShader::get_node(Type p_type, int p_id) const { + ERR_FAIL_INDEX_V(p_type, TYPE_MAX, Ref<VisualShaderNode>()); + const Graph *g = &graph[p_type]; + ERR_FAIL_COND_V(!g->nodes.has(p_id), Ref<VisualShaderNode>()); + return g->nodes[p_id].node; +} + +Vector<int> VisualShader::get_node_list(Type p_type) const { + ERR_FAIL_INDEX_V(p_type, TYPE_MAX, Vector<int>()); + const Graph *g = &graph[p_type]; + + Vector<int> ret; + for (Map<int, Node>::Element *E = g->nodes.front(); E; E = E->next()) { + ret.push_back(E->key()); + } + + return ret; +} +int VisualShader::get_valid_node_id(Type p_type) const { + ERR_FAIL_INDEX_V(p_type, TYPE_MAX, NODE_ID_INVALID); + const Graph *g = &graph[p_type]; + return g->nodes.size() ? MAX(2, g->nodes.back()->key() + 1) : 2; +} + +int VisualShader::find_node_id(Type p_type, const Ref<VisualShaderNode> &p_node) const { + for (const Map<int, Node>::Element *E = graph[p_type].nodes.front(); E; E = E->next()) { + if (E->get().node == p_node) + return E->key(); + } + + return NODE_ID_INVALID; +} + +void VisualShader::remove_node(Type p_type, int p_id) { + ERR_FAIL_INDEX(p_type, TYPE_MAX); + ERR_FAIL_COND(p_id < 2); + Graph *g = &graph[p_type]; + ERR_FAIL_COND(!g->nodes.has(p_id)); + + Ref<VisualShaderNodeInput> input = g->nodes[p_id].node; + if (input.is_valid()) { + input->disconnect("input_type_changed", this, "_input_type_changed"); + } + + g->nodes[p_id].node->disconnect("changed", this, "_queue_update"); + + g->nodes.erase(p_id); + + for (List<Connection>::Element *E = g->connections.front(); E;) { + List<Connection>::Element *N = E->next(); + if (E->get().from_node == p_id || E->get().to_node == p_id) { + g->connections.erase(E); + } + E = N; + } + + _queue_update(); +} + +bool VisualShader::is_node_connection(Type p_type, int p_from_node, int p_from_port, int p_to_node, int p_to_port) const { + ERR_FAIL_INDEX_V(p_type, TYPE_MAX, false); + const Graph *g = &graph[p_type]; + + for (const List<Connection>::Element *E = g->connections.front(); E; E = E->next()) { + + if (E->get().from_node == p_from_node && E->get().from_port == p_from_port && E->get().to_node == p_to_node && E->get().to_port == p_to_port) { + return true; + } + } + + return false; +} + +bool VisualShader::can_connect_nodes(Type p_type, int p_from_node, int p_from_port, int p_to_node, int p_to_port) const { + + ERR_FAIL_INDEX_V(p_type, TYPE_MAX, false); + const Graph *g = &graph[p_type]; + + if (!g->nodes.has(p_from_node)) + return false; + + if (p_from_port < 0 || p_from_port >= g->nodes[p_from_node].node->get_output_port_count()) + return false; + + if (!g->nodes.has(p_to_node)) + return false; + + if (p_to_port < 0 || p_to_port >= g->nodes[p_to_node].node->get_input_port_count()) + return false; + + VisualShaderNode::PortType from_port_type = g->nodes[p_from_node].node->get_output_port_type(p_from_port); + VisualShaderNode::PortType to_port_type = g->nodes[p_to_node].node->get_input_port_type(p_to_port); + + if (MAX(0, from_port_type - 1) != (MAX(0, to_port_type - 1))) { + return false; + } + + for (const List<Connection>::Element *E = g->connections.front(); E; E = E->next()) { + + if (E->get().from_node == p_from_node && E->get().from_port == p_from_port && E->get().to_node == p_to_node && E->get().to_port == p_to_port) { + return false; + } + } + + return true; +} + +Error VisualShader::connect_nodes(Type p_type, int p_from_node, int p_from_port, int p_to_node, int p_to_port) { + ERR_FAIL_INDEX_V(p_type, TYPE_MAX, ERR_CANT_CONNECT); + Graph *g = &graph[p_type]; + + ERR_FAIL_COND_V(!g->nodes.has(p_from_node), ERR_INVALID_PARAMETER); + ERR_FAIL_INDEX_V(p_from_port, g->nodes[p_from_node].node->get_output_port_count(), ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(!g->nodes.has(p_to_node), ERR_INVALID_PARAMETER); + ERR_FAIL_INDEX_V(p_to_port, g->nodes[p_to_node].node->get_input_port_count(), ERR_INVALID_PARAMETER); + + VisualShaderNode::PortType from_port_type = g->nodes[p_from_node].node->get_output_port_type(p_from_port); + VisualShaderNode::PortType to_port_type = g->nodes[p_to_node].node->get_input_port_type(p_to_port); + + if (MAX(0, from_port_type - 1) != (MAX(0, to_port_type - 1))) { + ERR_EXPLAIN("Incompatible port types (scalar/vec with transform"); + ERR_FAIL_V(ERR_INVALID_PARAMETER) + return ERR_INVALID_PARAMETER; + } + + for (List<Connection>::Element *E = g->connections.front(); E; E = E->next()) { + + if (E->get().from_node == p_from_node && E->get().from_port == p_from_port && E->get().to_node == p_to_node && E->get().to_port == p_to_port) { + ERR_FAIL_V(ERR_ALREADY_EXISTS); + } + } + + Connection c; + c.from_node = p_from_node; + c.from_port = p_from_port; + c.to_node = p_to_node; + c.to_port = p_to_port; + g->connections.push_back(c); + + _queue_update(); + return OK; +} +void VisualShader::disconnect_nodes(Type p_type, int p_from_node, int p_from_port, int p_to_node, int p_to_port) { + ERR_FAIL_INDEX(p_type, TYPE_MAX); + Graph *g = &graph[p_type]; + + for (List<Connection>::Element *E = g->connections.front(); E; E = E->next()) { + + if (E->get().from_node == p_from_node && E->get().from_port == p_from_port && E->get().to_node == p_to_node && E->get().to_port == p_to_port) { + g->connections.erase(E); + _queue_update(); + return; + } + } +} + +Array VisualShader::_get_node_connections(Type p_type) const { + ERR_FAIL_INDEX_V(p_type, TYPE_MAX, Array()); + const Graph *g = &graph[p_type]; + + Array ret; + for (const List<Connection>::Element *E = g->connections.front(); E; E = E->next()) { + Dictionary d; + d["from_node"] = E->get().from_node; + d["from_port"] = E->get().from_port; + d["to_node"] = E->get().to_node; + d["to_port"] = E->get().to_port; + ret.push_back(d); + } + + return ret; +} +void VisualShader::get_node_connections(Type p_type, List<Connection> *r_connections) const { + ERR_FAIL_INDEX(p_type, TYPE_MAX); + const Graph *g = &graph[p_type]; + + for (const List<Connection>::Element *E = g->connections.front(); E; E = E->next()) { + r_connections->push_back(E->get()); + } +} + +void VisualShader::set_mode(Mode p_mode) { + if (shader_mode == p_mode) { + return; + } + + //erase input/output connections + modes.clear(); + flags.clear(); + shader_mode = p_mode; + for (int i = 0; i < TYPE_MAX; i++) { + + for (Map<int, Node>::Element *E = graph[i].nodes.front(); E; E = E->next()) { + + Ref<VisualShaderNodeInput> input = E->get().node; + if (input.is_valid()) { + input->shader_mode = shader_mode; + //input->input_index = 0; + } + } + + Ref<VisualShaderNodeOutput> output = graph[i].nodes[NODE_ID_OUTPUT].node; + output->shader_mode = shader_mode; + + // clear connections since they are no longer valid + for (List<Connection>::Element *E = graph[i].connections.front(); E;) { + + bool keep = true; + + List<Connection>::Element *N = E->next(); + + int from = E->get().from_node; + int to = E->get().to_node; + + if (!graph[i].nodes.has(from)) { + keep = false; + } else { + Ref<VisualShaderNode> from_node = graph[i].nodes[from].node; + if (from_node->is_class("VisualShaderNodeOutput") || from_node->is_class("VisualShaderNodeInput")) { + keep = false; + } + } + + if (!graph[i].nodes.has(to)) { + keep = false; + } else { + Ref<VisualShaderNode> to_node = graph[i].nodes[to].node; + if (to_node->is_class("VisualShaderNodeOutput") || to_node->is_class("VisualShaderNodeInput")) { + keep = false; + } + } + + if (!keep) { + graph[i].connections.erase(E); + } + E = N; + } + } + + _queue_update(); + _change_notify(); +} + +void VisualShader::set_graph_offset(const Vector2 &p_offset) { + graph_offset = p_offset; +} + +Vector2 VisualShader::get_graph_offset() const { + return graph_offset; +} + +Shader::Mode VisualShader::get_mode() const { + return shader_mode; +} + +String VisualShader::generate_preview_shader(Type p_type, int p_node, int p_port, Vector<DefaultTextureParam> &default_tex_params) const { + + Ref<VisualShaderNode> node = get_node(p_type, p_node); + ERR_FAIL_COND_V(!node.is_valid(), String()); + ERR_FAIL_COND_V(p_port < 0 || p_port >= node->get_output_port_count(), String()); + ERR_FAIL_COND_V(node->get_output_port_type(p_port) == VisualShaderNode::PORT_TYPE_TRANSFORM, String()); + + StringBuilder global_code; + StringBuilder code; + + global_code += String() + "shader_type canvas_item;\n"; + + //make it faster to go around through shader + VMap<ConnectionKey, const List<Connection>::Element *> input_connections; + VMap<ConnectionKey, const List<Connection>::Element *> output_connections; + + for (const List<Connection>::Element *E = graph[p_type].connections.front(); E; E = E->next()) { + ConnectionKey from_key; + from_key.node = E->get().from_node; + from_key.port = E->get().from_port; + + output_connections.insert(from_key, E); + + ConnectionKey to_key; + to_key.node = E->get().to_node; + to_key.port = E->get().to_port; + + input_connections.insert(to_key, E); + } + + code += "\nvoid fragment() {\n"; + + Set<int> processed; + Error err = _write_node(p_type, global_code, code, default_tex_params, input_connections, output_connections, p_node, processed, true); + ERR_FAIL_COND_V(err != OK, String()); + + if (node->get_output_port_type(p_port) == VisualShaderNode::PORT_TYPE_SCALAR) { + code += "\tCOLOR.rgb = vec3( n_out" + itos(p_node) + "p" + itos(p_port) + " );\n"; + } else { + code += "\tCOLOR.rgb = n_out" + itos(p_node) + "p" + itos(p_port) + ";\n"; + } + code += "}\n"; + + //set code secretly + global_code += "\n\n"; + String final_code = global_code; + final_code += code; + //print_line(final_code); + return final_code; +} + +#define IS_INITIAL_CHAR(m_d) (((m_d) >= 'a' && (m_d) <= 'z') || ((m_d) >= 'A' && (m_d) <= 'Z')) + +#define IS_SYMBOL_CHAR(m_d) (((m_d) >= 'a' && (m_d) <= 'z') || ((m_d) >= 'A' && (m_d) <= 'Z') || ((m_d) >= '0' && (m_d) <= '9') || (m_d) == '_') + +String VisualShader::validate_uniform_name(const String &p_name, const Ref<VisualShaderNodeUniform> &p_uniform) const { + + String name = p_name; //validate name first + while (name.length() && !IS_INITIAL_CHAR(name[0])) { + name = name.substr(1, name.length() - 1); + } + if (name != String()) { + + String valid_name; + + for (int i = 0; i < name.length(); i++) { + if (IS_SYMBOL_CHAR(name[i])) { + valid_name += String::chr(name[i]); + } else if (name[i] == ' ') { + valid_name += "_"; + } + } + + name = valid_name; + } + + if (name == String()) { + name = p_uniform->get_caption(); + } + + int attempt = 1; + + while (true) { + + bool exists = false; + for (int i = 0; i < TYPE_MAX; i++) { + for (const Map<int, Node>::Element *E = graph[i].nodes.front(); E; E = E->next()) { + Ref<VisualShaderNodeUniform> node = E->get().node; + if (node == p_uniform) { //do not test on self + continue; + } + if (node.is_valid() && node->get_uniform_name() == name) { + exists = true; + break; + } + } + if (exists) { + break; + } + } + + if (exists) { + //remove numbers, put new and try again + attempt++; + while (name.length() && name[name.length() - 1] >= '0' && name[name.length() - 1] <= '9') { + name = name.substr(0, name.length() - 1); + } + ERR_FAIL_COND_V(name == String(), String()); + name += itos(attempt); + } else { + break; + } + } + + return name; +} + +VisualShader::RenderModeEnums VisualShader::render_mode_enums[] = { + { Shader::MODE_SPATIAL, "blend" }, + { Shader::MODE_SPATIAL, "depth_draw" }, + { Shader::MODE_SPATIAL, "cull" }, + { Shader::MODE_SPATIAL, "diffuse" }, + { Shader::MODE_SPATIAL, "specular" }, + { Shader::MODE_CANVAS_ITEM, "blend" }, + { Shader::MODE_CANVAS_ITEM, NULL } +}; + +static const char *type_string[VisualShader::TYPE_MAX] = { + "vertex", + "fragment", + "light" +}; +bool VisualShader::_set(const StringName &p_name, const Variant &p_value) { + + String name = p_name; + if (name == "mode") { + set_mode(Shader::Mode(int(p_value))); + return true; + } else if (name.begins_with("flags/")) { + StringName flag = name.get_slicec('/', 1); + bool enable = p_value; + if (enable) { + flags.insert(flag); + } else { + flags.erase(flag); + } + _queue_update(); + return true; + } else if (name.begins_with("modes/")) { + String mode = name.get_slicec('/', 1); + int value = p_value; + if (value == 0) { + modes.erase(mode); //means its default anyway, so dont store it + } else { + modes[mode] = value; + } + _queue_update(); + return true; + } else if (name.begins_with("nodes/")) { + String typestr = name.get_slicec('/', 1); + Type type = TYPE_VERTEX; + for (int i = 0; i < TYPE_MAX; i++) { + if (typestr == type_string[i]) { + type = Type(i); + break; + } + } + + String index = name.get_slicec('/', 2); + if (index == "connections") { + + Vector<int> conns = p_value; + if (conns.size() % 4 == 0) { + for (int i = 0; i < conns.size(); i += 4) { + connect_nodes(type, conns[i + 0], conns[i + 1], conns[i + 2], conns[i + 3]); + } + } + return true; + } + + int id = index.to_int(); + String what = name.get_slicec('/', 3); + + if (what == "node") { + add_node(type, p_value, Vector2(), id); + return true; + } else if (what == "position") { + set_node_position(type, id, p_value); + return true; + } + } + return false; +} + +bool VisualShader::_get(const StringName &p_name, Variant &r_ret) const { + + String name = p_name; + if (name == "mode") { + r_ret = get_mode(); + return true; + } else if (name.begins_with("flags/")) { + StringName flag = name.get_slicec('/', 1); + r_ret = flags.has(flag); + return true; + } else if (name.begins_with("modes/")) { + String mode = name.get_slicec('/', 1); + if (modes.has(mode)) { + r_ret = modes[mode]; + } else { + r_ret = 0; + } + return true; + } else if (name.begins_with("nodes/")) { + String typestr = name.get_slicec('/', 1); + Type type = TYPE_VERTEX; + for (int i = 0; i < TYPE_MAX; i++) { + if (typestr == type_string[i]) { + type = Type(i); + break; + } + } + + String index = name.get_slicec('/', 2); + if (index == "connections") { + + Vector<int> conns; + for (const List<Connection>::Element *E = graph[type].connections.front(); E; E = E->next()) { + conns.push_back(E->get().from_node); + conns.push_back(E->get().from_port); + conns.push_back(E->get().to_node); + conns.push_back(E->get().to_port); + } + + r_ret = conns; + return true; + } + + int id = index.to_int(); + String what = name.get_slicec('/', 3); + + if (what == "node") { + r_ret = get_node(type, id); + return true; + } else if (what == "position") { + r_ret = get_node_position(type, id); + return true; + } + } + return false; +} +void VisualShader::_get_property_list(List<PropertyInfo> *p_list) const { + + //mode + p_list->push_back(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Spatial,CanvasItem,Particles")); + //render modes + + Map<String, String> blend_mode_enums; + Set<String> toggles; + + for (int i = 0; i < ShaderTypes::get_singleton()->get_modes(VisualServer::ShaderMode(shader_mode)).size(); i++) { + + String mode = ShaderTypes::get_singleton()->get_modes(VisualServer::ShaderMode(shader_mode))[i]; + int idx = 0; + bool in_enum = false; + while (render_mode_enums[idx].string) { + if (mode.begins_with(render_mode_enums[idx].string)) { + String begin = render_mode_enums[idx].string; + String option = mode.replace_first(begin + "_", ""); + if (!blend_mode_enums.has(begin)) { + blend_mode_enums[begin] = option; + } else { + blend_mode_enums[begin] += "," + option; + } + in_enum = true; + break; + } + idx++; + } + + if (!in_enum) { + toggles.insert(mode); + } + } + + for (Map<String, String>::Element *E = blend_mode_enums.front(); E; E = E->next()) { + + p_list->push_back(PropertyInfo(Variant::INT, "modes/" + E->key(), PROPERTY_HINT_ENUM, E->get())); + } + + for (Set<String>::Element *E = toggles.front(); E; E = E->next()) { + p_list->push_back(PropertyInfo(Variant::BOOL, "flags/" + E->get())); + } + + for (int i = 0; i < TYPE_MAX; i++) { + for (Map<int, Node>::Element *E = graph[i].nodes.front(); E; E = E->next()) { + + String prop_name = "nodes/"; + prop_name += type_string[i]; + prop_name += "/" + itos(E->key()); + + if (E->key() != NODE_ID_OUTPUT) { + + p_list->push_back(PropertyInfo(Variant::OBJECT, prop_name + "/node", PROPERTY_HINT_RESOURCE_TYPE, "VisualShaderNode", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE)); + } + p_list->push_back(PropertyInfo(Variant::VECTOR2, prop_name + "/position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); + } + p_list->push_back(PropertyInfo(Variant::POOL_INT_ARRAY, "nodes/" + String(type_string[i]) + "/connections", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); + } +} + +Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBuilder &code, Vector<VisualShader::DefaultTextureParam> &def_tex_params, const VMap<ConnectionKey, const List<Connection>::Element *> &input_connections, const VMap<ConnectionKey, const List<Connection>::Element *> &output_connections, int node, Set<int> &processed, bool for_preview) const { + + const Ref<VisualShaderNode> vsnode = graph[type].nodes[node].node; + + //check inputs recursively first + int input_count = vsnode->get_input_port_count(); + for (int i = 0; i < input_count; i++) { + ConnectionKey ck; + ck.node = node; + ck.port = i; + + if (input_connections.has(ck)) { + int from_node = input_connections[ck]->get().from_node; + if (processed.has(from_node)) { + continue; + } + + Error err = _write_node(type, global_code, code, def_tex_params, input_connections, output_connections, from_node, processed, for_preview); + if (err) + return err; + } + } + + // then this node + + code += "// " + vsnode->get_caption() + ":" + itos(node) + "\n"; + Vector<String> input_vars; + + input_vars.resize(vsnode->get_input_port_count()); + String *inputs = input_vars.ptrw(); + + for (int i = 0; i < input_count; i++) { + ConnectionKey ck; + ck.node = node; + ck.port = i; + + if (input_connections.has(ck)) { + //connected to something, use that output + int from_node = input_connections[ck]->get().from_node; + int from_port = input_connections[ck]->get().from_port; + + VisualShaderNode::PortType in_type = vsnode->get_input_port_type(i); + VisualShaderNode::PortType out_type = graph[type].nodes[from_node].node->get_output_port_type(from_port); + + String src_var = "n_out" + itos(from_node) + "p" + itos(from_port); + + if (in_type == out_type) { + inputs[i] = src_var; + } else if (in_type == VisualShaderNode::PORT_TYPE_SCALAR && out_type == VisualShaderNode::PORT_TYPE_VECTOR) { + inputs[i] = "dot(" + src_var + ",vec3(0.333333,0.333333,0.333333))"; + } else if (in_type == VisualShaderNode::PORT_TYPE_VECTOR && out_type == VisualShaderNode::PORT_TYPE_SCALAR) { + inputs[i] = "vec3(" + src_var + ")"; + } + } else { + + Variant defval = vsnode->get_input_port_default_value(i); + if (defval.get_type() == Variant::REAL || defval.get_type() == Variant::INT) { + float val = defval; + inputs[i] = "n_in" + itos(node) + "p" + itos(i); + code += "\tfloat " + inputs[i] + " = " + vformat("%.5f", val) + ";\n"; + } else if (defval.get_type() == Variant::VECTOR3) { + Vector3 val = defval; + inputs[i] = "n_in" + itos(node) + "p" + itos(i); + code += "\tvec3 " + inputs[i] + " = " + vformat("vec3(%.5f,%.5f,%.5f);\n", val.x, val.y, val.z); + } else if (defval.get_type() == Variant::TRANSFORM) { + Transform val = defval; + val.basis.transpose(); + inputs[i] = "n_in" + itos(node) + "p" + itos(i); + Array values; + for (int i = 0; i < 3; i++) { + values.push_back(val.basis[i].x); + values.push_back(val.basis[i].y); + values.push_back(val.basis[i].z); + } + values.push_back(val.origin.x); + values.push_back(val.origin.y); + values.push_back(val.origin.z); + bool err = false; + code += "\tmat4 " + inputs[i] + " = " + String("mat4( vec4(%.5f,%.5f,%.5f,0.0),vec4(%.5f,%.5f,%.5f,0.0),vec4(%.5f,%.5f,%.5f,0.0),vec4(%.5f,%.5f,%.5f,1.0) );\n").sprintf(values, &err); + } else { + //will go empty, node is expected to know what it is doing at this point and handle it + } + } + } + + int output_count = vsnode->get_output_port_count(); + Vector<String> output_vars; + output_vars.resize(vsnode->get_output_port_count()); + String *outputs = output_vars.ptrw(); + + for (int i = 0; i < output_count; i++) { + + outputs[i] = "n_out" + itos(node) + "p" + itos(i); + switch (vsnode->get_output_port_type(i)) { + case VisualShaderNode::PORT_TYPE_SCALAR: code += String() + "\tfloat " + outputs[i] + ";\n"; break; + case VisualShaderNode::PORT_TYPE_VECTOR: code += String() + "\tvec3 " + outputs[i] + ";\n"; break; + case VisualShaderNode::PORT_TYPE_TRANSFORM: code += String() + "\tmat4 " + outputs[i] + ";\n"; break; + default: {} + } + } + + Vector<VisualShader::DefaultTextureParam> params = vsnode->get_default_texture_parameters(type, node); + for (int i = 0; i < params.size(); i++) { + def_tex_params.push_back(params[i]); + } + + Ref<VisualShaderNodeInput> input = vsnode; + + if (input.is_valid() && for_preview) { + //handle for preview + code += input->generate_code_for_preview(type, node, inputs, outputs); + } else { + //handle normally + global_code += vsnode->generate_global(get_mode(), type, node); + code += vsnode->generate_code(get_mode(), type, node, inputs, outputs); + } + code += "\n"; // + processed.insert(node); + + return OK; +} + +void VisualShader::_update_shader() const { + if (!dirty) + return; + + dirty = false; + + StringBuilder global_code; + StringBuilder code; + Vector<VisualShader::DefaultTextureParam> default_tex_params; + static const char *shader_mode_str[Shader::MODE_MAX] = { "spatial", "canvas_item", "particles" }; + + global_code += String() + "shader_type " + shader_mode_str[shader_mode] + ";\n"; + + String render_mode; + + { + //fill render mode enums + int idx = 0; + while (render_mode_enums[idx].string) { + + if (shader_mode == render_mode_enums[idx].mode) { + + if (modes.has(render_mode_enums[idx].string)) { + + int which = modes[render_mode_enums[idx].string]; + int count = 0; + for (int i = 0; i < ShaderTypes::get_singleton()->get_modes(VisualServer::ShaderMode(shader_mode)).size(); i++) { + String mode = ShaderTypes::get_singleton()->get_modes(VisualServer::ShaderMode(shader_mode))[i]; + if (mode.begins_with(render_mode_enums[idx].string)) { + if (count == which) { + if (render_mode != String()) { + render_mode += ", "; + } + render_mode += mode; + break; + } + count++; + } + } + } + } + idx++; + } + + //fill render mode flags + for (int i = 0; i < ShaderTypes::get_singleton()->get_modes(VisualServer::ShaderMode(shader_mode)).size(); i++) { + + String mode = ShaderTypes::get_singleton()->get_modes(VisualServer::ShaderMode(shader_mode))[i]; + if (flags.has(mode)) { + if (render_mode != String()) { + render_mode += ", "; + } + render_mode += mode; + } + } + } + + if (render_mode != String()) { + + global_code += "render_mode " + render_mode + ";\n\n"; + } + + static const char *func_name[TYPE_MAX] = { "vertex", "fragment", "light" }; + + for (int i = 0; i < TYPE_MAX; i++) { + + //make it faster to go around through shader + VMap<ConnectionKey, const List<Connection>::Element *> input_connections; + VMap<ConnectionKey, const List<Connection>::Element *> output_connections; + + for (const List<Connection>::Element *E = graph[i].connections.front(); E; E = E->next()) { + ConnectionKey from_key; + from_key.node = E->get().from_node; + from_key.port = E->get().from_port; + + output_connections.insert(from_key, E); + + ConnectionKey to_key; + to_key.node = E->get().to_node; + to_key.port = E->get().to_port; + + input_connections.insert(to_key, E); + } + + code += "\nvoid " + String(func_name[i]) + "() {\n"; + + Set<int> processed; + Error err = _write_node(Type(i), global_code, code, default_tex_params, input_connections, output_connections, NODE_ID_OUTPUT, processed, false); + ERR_FAIL_COND(err != OK); + + code += "}\n"; + } + + //set code secretly + global_code += "\n\n"; + String final_code = global_code; + final_code += code; + const_cast<VisualShader *>(this)->set_code(final_code); + //print_line(final_code); + for (int i = 0; i < default_tex_params.size(); i++) { + const_cast<VisualShader *>(this)->set_default_texture_param(default_tex_params[i].name, default_tex_params[i].param); + } +} + +void VisualShader::_queue_update() { + if (dirty) { + return; + } + + dirty = true; + call_deferred("_update_shader"); +} + +void VisualShader::_input_type_changed(Type p_type, int p_id) { + //erase connections using this input, as type changed + Graph *g = &graph[p_type]; + + for (List<Connection>::Element *E = g->connections.front(); E;) { + List<Connection>::Element *N = E->next(); + if (E->get().from_node == p_id) { + g->connections.erase(E); + } + E = N; + } +} + +void VisualShader::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_mode", "mode"), &VisualShader::set_mode); + + ClassDB::bind_method(D_METHOD("add_node", "type", "node", "position", "id"), &VisualShader::add_node); + ClassDB::bind_method(D_METHOD("set_node_position", "type", "id", "position"), &VisualShader::set_node_position); + + ClassDB::bind_method(D_METHOD("get_node_position", "type", "id"), &VisualShader::get_node_position); + ClassDB::bind_method(D_METHOD("get_node", "type"), &VisualShader::get_node); + + ClassDB::bind_method(D_METHOD("get_node_list", "type"), &VisualShader::get_node_list); + ClassDB::bind_method(D_METHOD("get_valid_node_id", "type"), &VisualShader::get_valid_node_id); + + ClassDB::bind_method(D_METHOD("remove_node", "type", "id"), &VisualShader::remove_node); + + ClassDB::bind_method(D_METHOD("is_node_connection", "type", "from_node", "from_port", "to_node", "to_port"), &VisualShader::is_node_connection); + ClassDB::bind_method(D_METHOD("can_connect_nodes", "type", "from_node", "from_port", "to_node", "to_port"), &VisualShader::is_node_connection); + + ClassDB::bind_method(D_METHOD("connect_nodes", "type", "from_node", "from_port", "to_node", "to_port"), &VisualShader::connect_nodes); + ClassDB::bind_method(D_METHOD("disconnect_nodes", "type", "from_node", "from_port", "to_node", "to_port"), &VisualShader::disconnect_nodes); + + ClassDB::bind_method(D_METHOD("get_node_connections", "type"), &VisualShader::_get_node_connections); + + ClassDB::bind_method(D_METHOD("set_graph_offset", "offset"), &VisualShader::set_graph_offset); + ClassDB::bind_method(D_METHOD("get_graph_offset"), &VisualShader::get_graph_offset); + + ClassDB::bind_method(D_METHOD("_queue_update"), &VisualShader::_queue_update); + ClassDB::bind_method(D_METHOD("_update_shader"), &VisualShader::_update_shader); + + ClassDB::bind_method(D_METHOD("_input_type_changed"), &VisualShader::_input_type_changed); + + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "graph_offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_graph_offset", "get_graph_offset"); + + BIND_ENUM_CONSTANT(TYPE_VERTEX); + BIND_ENUM_CONSTANT(TYPE_FRAGMENT); + BIND_ENUM_CONSTANT(TYPE_LIGHT); + BIND_ENUM_CONSTANT(TYPE_MAX); + + BIND_CONSTANT(NODE_ID_INVALID); + BIND_CONSTANT(NODE_ID_OUTPUT); +} + +VisualShader::VisualShader() { + shader_mode = Shader::MODE_SPATIAL; + + for (int i = 0; i < TYPE_MAX; i++) { + Ref<VisualShaderNodeOutput> output; + output.instance(); + output->shader_type = Type(i); + output->shader_mode = shader_mode; + graph[i].nodes[NODE_ID_OUTPUT].node = output; + graph[i].nodes[NODE_ID_OUTPUT].position = Vector2(400, 150); + } + + dirty = true; +} + +/////////////////////////////////////////////////////////// + +const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = { + // Spatial, Vertex + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "vertex", "VERTEX" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "NORMAL" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "tangent", "TANGENT" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "binormal", "BINORMAL" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV,0.0)" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "uv2", "vec3(UV2,0.0)" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "point_size", "POINT_SIZE" }, + + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "world", "WORLD_MATRIX" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "inv_camera", "INV_CAMERA_MATRIX" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "projection", "PROJECTION" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "vp_size", "vec3(VIEWPORT_SIZE, 0)" }, + + // Spatial, Fragment + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "vertex", "VERTEX" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "NORMAL" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "tangent", "TANGENT" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "binormal", "BINORMAL" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV,0.0)" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "uv2", "vec3(UV2,0.0)" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "point_coord", "vec2(POINT_COORD,0.0)" }, + + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(SCREEN_UV,0.0)" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "side", "float(FRONT_FACING ? 1.0 : 0.0)" }, + + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_TRANSFORM, "world", "WORLD_MATRIX" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_TRANSFORM, "inv_camera", "INV_CAMERA_MATRIX" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_TRANSFORM, "projection", "PROJECTION" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "vp_size", "vec3(VIEWPORT_SIZE, 0.0)" }, + + // Spatial, Light + { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "NORMAL" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "view", "VIEW" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light", "LIGHT" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light_color", "LIGHT_COLOR" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "attenuation", "ATTENUATION" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "albedo", "ALBEDO" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "transmission", "TRANSMISSION" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "roughness", "ROUGHNESS" }, + + { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_TRANSFORM, "world", "WORLD_MATRIX" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_TRANSFORM, "inv_camera", "INV_CAMERA_MATRIX" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_TRANSFORM, "projection", "PROJECTION" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "vp_size", "vec3(VIEWPORT_SIZE, 0.0)" }, + // Canvas Item, Vertex + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "vertex", "vec3(VERTEX,0.0)" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV,0.0)" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "point_size", "POINT_SIZE" }, + + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "world", "WORLD_MATRIX" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "projection", "PROJECTION_MATRIX" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "extra", "EXTRA_MATRIX" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "light_pass", "float(AT_LIGHT_PASS ? 1.0 : 0.0)" }, + // Canvas Item, Fragment + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV,0.0)" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(SCREEN_UV,0.0)" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "point_coord", "vec2(POINT_COORD,0.0)" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "light_pass", "float(AT_LIGHT_PASS ? 1.0 : 0.0)" }, + // Canvas Item, Light + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV,0.0)" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "NORMAL" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light_vec", "vec3(LIGHT_VEC,0.0)" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "light_height", "LIGHT_HEIGHT" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light_color", "LIGHT_COLOR.rgb" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light_alpha", "LIGHT_COLOR.a" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light_uv", "vec3(LIGHT_UV,0.0)" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "shadow_color", "SHADOW_COLOR.rgb" }, + + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(SCREEN_UV,0.0)" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "point_coord", "vec2(POINT_COORD,0.0)" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, + // Particles, Vertex + { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "velocity", "VELOCITY" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "restart", "float(RESTART ? 1.0 : 0.0)" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "active", "float(ACTIVE ? 1.0 : 0.0)" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "custom", "CUSTOM.rgb" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "custom_alpha", "CUSTOM.a" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "transform", "TRANSFORM" }, + + { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "delta", "DELTA" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "lifetime", "LIFETIME" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "index", "float(INDEX)" }, + + { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "emission_transform", "EMISSION_TRANSFORM" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, + { Shader::MODE_MAX, VisualShader::TYPE_MAX, VisualShaderNode::PORT_TYPE_TRANSFORM, NULL, NULL }, +}; + +const VisualShaderNodeInput::Port VisualShaderNodeInput::preview_ports[] = { + + // Spatial, Fragment + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "vec3(0.0,0.0,1.0)" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "tangent", "vec3(0.0,1.0,0.0)" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "binormal", "vec3(1.0,0.0,0.0)" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV,0.0)" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "uv2", "vec3(UV,0.0)" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "vec3(1.0)" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "1.0" }, + + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(UV,0.0)" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "side", "1.0" }, + + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "vp_size", "vec3(1.0,1.0, 0.0)" }, + + // Spatial, Light + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "vec3(0.0,0.0,1.0)" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "vp_size", "vec3(1.0, 1.0, 0.0)" }, + // Canvas Item, Vertex + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "vertex", "vec3(VERTEX,0.0)" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV,0.0)" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "vec3(1.0)" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "1.0" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, + // Canvas Item, Fragment + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV,0.0)" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "vec3(1.0)" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "1.0" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(UV,0.0)" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, + // Canvas Item, Light + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV,0.0)" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "vec3(0.0,0.0,1.0)" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "vec3(1.0)" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "1.0" }, + + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(UV,0.0)" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, + // Particles, Vertex + { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "vec3(1.0)" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "1.0" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "velocity", "vec3(0.0,0.0,1.0)" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, + { Shader::MODE_MAX, VisualShader::TYPE_MAX, VisualShaderNode::PORT_TYPE_TRANSFORM, NULL, NULL }, +}; + +int VisualShaderNodeInput::get_input_port_count() const { + + return 0; +} +VisualShaderNodeInput::PortType VisualShaderNodeInput::get_input_port_type(int p_port) const { + + return PORT_TYPE_SCALAR; +} +String VisualShaderNodeInput::get_input_port_name(int p_port) const { + + return ""; +} + +int VisualShaderNodeInput::get_output_port_count() const { + + return 1; +} +VisualShaderNodeInput::PortType VisualShaderNodeInput::get_output_port_type(int p_port) const { + + return get_input_type_by_name(input_name); +} +String VisualShaderNodeInput::get_output_port_name(int p_port) const { + return ""; +} + +String VisualShaderNodeInput::get_caption() const { + return TTR("Input"); +} + +String VisualShaderNodeInput::generate_code_for_preview(VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const { + + int idx = 0; + + String code; + + while (preview_ports[idx].mode != Shader::MODE_MAX) { + if (preview_ports[idx].mode == shader_mode && preview_ports[idx].shader_type == shader_type && preview_ports[idx].name == input_name) { + code = "\t" + p_output_vars[0] + " = " + preview_ports[idx].string + ";\n"; + break; + } + idx++; + } + + if (code == String()) { + switch (get_output_port_type(0)) { + case PORT_TYPE_SCALAR: { + code = "\t" + p_output_vars[0] + " = 0.0;\n"; + } break; //default (none found) is scalar + case PORT_TYPE_VECTOR: { + code = "\t" + p_output_vars[0] + " = vec3(0.0);\n"; + } break; //default (none found) is scalar + case PORT_TYPE_TRANSFORM: { + code = "\t" + p_output_vars[0] + " = mat4( vec4(1.0,0.0,0.0,0.0), vec4(0.0,1.0,0.0,0.0), vec4(0.0,0.0,1.0,0.0), vec4(0.0,0.0,0.0,1.0) );\n"; + } break; //default (none found) is scalar + } + } + + return code; +} + +String VisualShaderNodeInput::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const { + + int idx = 0; + + String code; + + while (ports[idx].mode != Shader::MODE_MAX) { + if (ports[idx].mode == shader_mode && ports[idx].shader_type == shader_type && ports[idx].name == input_name) { + code = "\t" + p_output_vars[0] + " = " + ports[idx].string + ";\n"; + break; + } + idx++; + } + + if (code == String()) { + code = "\t" + p_output_vars[0] + " = 0.0;\n"; //default (none found) is scalar + } + + return code; +} + +void VisualShaderNodeInput::set_input_name(String p_name) { + PortType prev_type = get_input_type_by_name(input_name); + input_name = p_name; + emit_changed(); + if (get_input_type_by_name(input_name) != prev_type) { + emit_signal("input_type_changed"); + } +} + +String VisualShaderNodeInput::get_input_name() const { + return input_name; +} + +VisualShaderNodeInput::PortType VisualShaderNodeInput::get_input_type_by_name(String p_name) const { + + int idx = 0; + + while (ports[idx].mode != Shader::MODE_MAX) { + if (ports[idx].mode == shader_mode && ports[idx].shader_type == shader_type && ports[idx].name == p_name) { + return ports[idx].type; + } + idx++; + } + + return PORT_TYPE_SCALAR; +} + +int VisualShaderNodeInput::get_input_index_count() const { + int idx = 0; + int count = 0; + + while (ports[idx].mode != Shader::MODE_MAX) { + if (ports[idx].mode == shader_mode && ports[idx].shader_type == shader_type) { + count++; + } + idx++; + } + + return count; +} + +VisualShaderNodeInput::PortType VisualShaderNodeInput::get_input_index_type(int p_index) const { + int idx = 0; + int count = 0; + + while (ports[idx].mode != Shader::MODE_MAX) { + if (ports[idx].mode == shader_mode && ports[idx].shader_type == shader_type) { + if (count == p_index) { + return ports[idx].type; + } + count++; + } + idx++; + } + + return PORT_TYPE_SCALAR; +} + +String VisualShaderNodeInput::get_input_index_name(int p_index) const { + int idx = 0; + int count = 0; + + while (ports[idx].mode != Shader::MODE_MAX) { + if (ports[idx].mode == shader_mode && ports[idx].shader_type == shader_type) { + if (count == p_index) { + return ports[idx].name; + } + count++; + } + idx++; + } + + return ""; +} + +void VisualShaderNodeInput::_validate_property(PropertyInfo &property) const { + + if (property.name == "input_name") { + String port_list; + + int idx = 0; + + while (ports[idx].mode != Shader::MODE_MAX) { + if (ports[idx].mode == shader_mode && ports[idx].shader_type == shader_type) { + if (port_list != String()) { + port_list += ","; + } + port_list += ports[idx].name; + } + idx++; + } + + if (port_list == "") { + port_list = TTR("None"); + } + property.hint_string = port_list; + } +} + +Vector<StringName> VisualShaderNodeInput::get_editable_properties() const { + Vector<StringName> props; + props.push_back("input_name"); + return props; +} + +void VisualShaderNodeInput::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_input_name", "name"), &VisualShaderNodeInput::set_input_name); + ClassDB::bind_method(D_METHOD("get_input_name"), &VisualShaderNodeInput::get_input_name); + + ADD_PROPERTY(PropertyInfo(Variant::STRING, "input_name", PROPERTY_HINT_ENUM, ""), "set_input_name", "get_input_name"); + ADD_SIGNAL(MethodInfo("input_type_changed")); +} +VisualShaderNodeInput::VisualShaderNodeInput() { + input_name = "[None]"; + // changed when set + shader_type = VisualShader::TYPE_MAX; + shader_mode = Shader::MODE_MAX; +} + +//////////////////////////////////////////// + +const VisualShaderNodeOutput::Port VisualShaderNodeOutput::ports[] = { + // Spatial, Vertex + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "vertex", "VERTEX" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "NORMAL" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "tangent", "TANGENT" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "binormal", "BINORMAL" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "UV:xy" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "uv2", "UV2:xy" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "roughness", "ROUGHNESS" }, + // Spatial, Fragment + + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "albedo", "ALBEDO" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "ALPHA" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "metallic", "METALLIC" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "roughness", "ROUGHNESS" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "specular", "SPECULAR" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "emission", "EMISSION" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "ao", "AO" }, + + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "NORMAL" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "normalmap", "NORMALMAP" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "normalmap_depth", "NORMALMAP_DEPTH" }, + + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "rim", "RIM" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "rim_tint", "RIM_TINT" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "clearcoat", "CLEARCOAT" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "clearcoat_gloss", "CLEARCOAT_GLOSS" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "anisotropy", "ANISOTROPY" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "anisotropy_flow", "ANISOTROPY_FLOW:xy" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "subsurf_scatter", "SSS_STRENGTH" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "transmission", "TRANSMISSION" }, + + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha_scissor", "ALPHA_SCISSOR" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "ao_light_affect", "AO_LIGHT_AFFECT" }, + + // Spatial, Light + { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "diffuse", "DIFFUSE_LIGHT" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "specular", "SPECULAR_LIGHT" }, + // Canvas Item, Vertex + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "vertex", "VERTEX:xy" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "UV:xy" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" }, + // Canvas Item, Fragment + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "NORMAL" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "normalmap", "NORMALMAP" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "normalmap_depth", "NORMALMAP_DEPTH" }, + // Canvas Item, Light + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light", "LIGHT.rgb" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "light_alpha", "LIGHT.rgb" }, + // Particles, Vertex + { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "velocity", "VELOCITY" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "custom", "CUSTOM.rgb" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "custom_alpha", "CUSTOM.a" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "transform", "TRANSFORM" }, + { Shader::MODE_MAX, VisualShader::TYPE_MAX, VisualShaderNode::PORT_TYPE_TRANSFORM, NULL, NULL }, +}; + +int VisualShaderNodeOutput::get_input_port_count() const { + + int idx = 0; + int count = 0; + + while (ports[idx].mode != Shader::MODE_MAX) { + if (ports[idx].mode == shader_mode && ports[idx].shader_type == shader_type) { + count++; + } + idx++; + } + + return count; +} + +VisualShaderNodeOutput::PortType VisualShaderNodeOutput::get_input_port_type(int p_port) const { + + int idx = 0; + int count = 0; + + while (ports[idx].mode != Shader::MODE_MAX) { + if (ports[idx].mode == shader_mode && ports[idx].shader_type == shader_type) { + if (count == p_port) { + return ports[idx].type; + } + count++; + } + idx++; + } + + return PORT_TYPE_SCALAR; +} + +String VisualShaderNodeOutput::get_input_port_name(int p_port) const { + + int idx = 0; + int count = 0; + + while (ports[idx].mode != Shader::MODE_MAX) { + if (ports[idx].mode == shader_mode && ports[idx].shader_type == shader_type) { + if (count == p_port) { + return String(ports[idx].name).capitalize(); + } + count++; + } + idx++; + } + + return String(); +} + +Variant VisualShaderNodeOutput::get_input_port_default_value(int p_port) const { + return Variant(); +} + +int VisualShaderNodeOutput::get_output_port_count() const { + + return 0; +} +VisualShaderNodeOutput::PortType VisualShaderNodeOutput::get_output_port_type(int p_port) const { + return PORT_TYPE_SCALAR; +} +String VisualShaderNodeOutput::get_output_port_name(int p_port) const { + return String(); +} + +bool VisualShaderNodeOutput::is_port_separator(int p_index) const { + + if (shader_mode == Shader::MODE_SPATIAL && shader_type == VisualShader::TYPE_FRAGMENT) { + String name = get_input_port_name(p_index); + return (name == "Normal" || name == "Rim" || name == "Alpha Scissor"); + } + return false; +} + +String VisualShaderNodeOutput::get_caption() const { + return TTR("Output"); +} + +String VisualShaderNodeOutput::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const { + + int idx = 0; + int count = 0; + + String code; + while (ports[idx].mode != Shader::MODE_MAX) { + if (ports[idx].mode == shader_mode && ports[idx].shader_type == shader_type) { + + if (p_input_vars[count] != String()) { + String s = ports[idx].string; + if (s.find(":") != -1) { + code += "\t" + s.get_slicec(':', 0) + " = " + p_input_vars[count] + "." + s.get_slicec(':', 1) + ";\n"; + } else { + code += "\t" + s + " = " + p_input_vars[count] + ";\n"; + } + } + count++; + } + idx++; + } + + return code; +} + +VisualShaderNodeOutput::VisualShaderNodeOutput() { +} + +/////////////////////////// + +void VisualShaderNodeUniform::set_uniform_name(const String &p_name) { + uniform_name = p_name; + emit_signal("name_changed"); + emit_changed(); +} + +String VisualShaderNodeUniform::get_uniform_name() const { + return uniform_name; +} + +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); + + ADD_PROPERTY(PropertyInfo(Variant::STRING, "uniform_name"), "set_uniform_name", "get_uniform_name"); +} + +VisualShaderNodeUniform::VisualShaderNodeUniform() { +} diff --git a/scene/resources/visual_shader.h b/scene/resources/visual_shader.h new file mode 100644 index 0000000000..6ff1c9a9fe --- /dev/null +++ b/scene/resources/visual_shader.h @@ -0,0 +1,284 @@ +#ifndef VISUAL_SHADER_H +#define VISUAL_SHADER_H + +#include "scene/resources/shader.h" +#include "string_builder.h" + +class VisualShaderNodeUniform; +class VisualShaderNode; + +class VisualShader : public Shader { + GDCLASS(VisualShader, Shader) +public: + enum Type { + TYPE_VERTEX, + TYPE_FRAGMENT, + TYPE_LIGHT, + TYPE_MAX + }; + + struct Connection { + int from_node; + int from_port; + int to_node; + int to_port; + }; + + struct DefaultTextureParam { + StringName name; + Ref<Texture> param; + }; + +private: + struct Node { + Ref<VisualShaderNode> node; + Vector2 position; + }; + + struct Graph { + Map<int, Node> nodes; + List<Connection> connections; + } graph[TYPE_MAX]; + + Shader::Mode shader_mode; + + Array _get_node_connections(Type p_type) const; + + Vector2 graph_offset; + + struct RenderModeEnums { + Shader::Mode mode; + const char *string; + }; + + HashMap<String, int> modes; + Set<StringName> flags; + + static RenderModeEnums render_mode_enums[]; + + volatile mutable bool dirty; + void _queue_update(); + + union ConnectionKey { + + struct { + uint64_t node : 32; + uint64_t port : 32; + }; + uint64_t key; + bool operator<(const ConnectionKey &p_key) const { + return key < p_key.key; + } + }; + + Error _write_node(Type p_type, StringBuilder &global_code, StringBuilder &code, Vector<DefaultTextureParam> &def_tex_params, const VMap<ConnectionKey, const List<Connection>::Element *> &input_connections, const VMap<ConnectionKey, const List<Connection>::Element *> &output_connections, int node, Set<int> &processed, bool for_preview) const; + + void _input_type_changed(Type p_type, int p_id); + +protected: + virtual void _update_shader() const; + static void _bind_methods(); + + bool _set(const StringName &p_name, const Variant &p_value); + bool _get(const StringName &p_name, Variant &r_ret) const; + void _get_property_list(List<PropertyInfo> *p_list) const; + +public: + enum { + NODE_ID_INVALID = -1, + NODE_ID_OUTPUT = 0, + }; + + void add_node(Type p_type, const Ref<VisualShaderNode> &p_node, const Vector2 &p_position, int p_id); + void set_node_position(Type p_type, int p_id, const Vector2 &p_position); + + Vector2 get_node_position(Type p_type, int p_id) const; + Ref<VisualShaderNode> get_node(Type p_type, int p_id) const; + + Vector<int> get_node_list(Type p_type) const; + int get_valid_node_id(Type p_type) const; + + int find_node_id(Type p_type, const Ref<VisualShaderNode> &p_node) const; + void remove_node(Type p_type, int p_id); + + bool is_node_connection(Type p_type, int p_from_node, int p_from_port, int p_to_node, int p_to_port) const; + bool can_connect_nodes(Type p_type, int p_from_node, int p_from_port, int p_to_node, int p_to_port) const; + Error connect_nodes(Type p_type, int p_from_node, int p_from_port, int p_to_node, int p_to_port); + void disconnect_nodes(Type p_type, int p_from_node, int p_from_port, int p_to_node, int p_to_port); + + void get_node_connections(Type p_type, List<Connection> *r_connections) const; + + void set_mode(Mode p_mode); + virtual Mode get_mode() const; + + void set_graph_offset(const Vector2 &p_offset); + Vector2 get_graph_offset() const; + + String generate_preview_shader(Type p_type, int p_node, int p_port, Vector<DefaultTextureParam> &r_default_tex_params) const; + + String validate_uniform_name(const String &p_name, const Ref<VisualShaderNodeUniform> &p_uniform) const; + + VisualShader(); +}; + +VARIANT_ENUM_CAST(VisualShader::Type) +/// +/// +/// + +class VisualShaderNode : public Resource { + GDCLASS(VisualShaderNode, Resource) + + int port_preview; + + Map<int, Variant> default_input_values; + + Array _get_default_input_values() const; + void _set_default_input_values(const Array &p_values); + +protected: + static void _bind_methods(); + +public: + enum PortType { + PORT_TYPE_SCALAR, + PORT_TYPE_VECTOR, + PORT_TYPE_TRANSFORM, + }; + + virtual String get_caption() const = 0; + + virtual int get_input_port_count() const = 0; + virtual PortType get_input_port_type(int p_port) const = 0; + virtual String get_input_port_name(int p_port) const = 0; + + void set_input_port_default_value(int p_port, const Variant &p_value); + Variant get_input_port_default_value(int p_port) const; // if NIL (default if node does not set anything) is returned, it means no default value is wanted if disconnected, thus no input var must be supplied (empty string will be supplied) + + virtual int get_output_port_count() const = 0; + virtual PortType get_output_port_type(int p_port) const = 0; + virtual String get_output_port_name(int p_port) const = 0; + + void set_output_port_for_preview(int p_index); + int get_output_port_for_preview() const; + + virtual bool is_port_separator(int p_index) const; + + virtual Vector<StringName> get_editable_properties() const; + + virtual Vector<VisualShader::DefaultTextureParam> get_default_texture_parameters(VisualShader::Type p_type, int p_id) const; + virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const; + 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) const = 0; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty + + virtual String get_warning(Shader::Mode p_mode, VisualShader::Type p_type) const; + + VisualShaderNode(); +}; +///// + +class VisualShaderNodeInput : public VisualShaderNode { + GDCLASS(VisualShaderNodeInput, VisualShaderNode) + + friend class VisualShader; + VisualShader::Type shader_type; + Shader::Mode shader_mode; + + struct Port { + Shader::Mode mode; + VisualShader::Type shader_type; + PortType type; + const char *name; + const char *string; + }; + + static const Port ports[]; + static const Port preview_ports[]; + + String input_name; + +protected: + static void _bind_methods(); + void _validate_property(PropertyInfo &property) const; + +public: + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + virtual String get_caption() const; + + virtual String generate_code_for_preview(VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const; + 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) const; + + void set_input_name(String p_name); + String get_input_name() const; + + int get_input_index_count() const; + PortType get_input_index_type(int p_index) const; + String get_input_index_name(int p_index) const; + + PortType get_input_type_by_name(String p_name) const; + + virtual Vector<StringName> get_editable_properties() const; + + VisualShaderNodeInput(); +}; + +/// + +class VisualShaderNodeOutput : public VisualShaderNode { + GDCLASS(VisualShaderNodeOutput, VisualShaderNode) +public: + friend class VisualShader; + VisualShader::Type shader_type; + Shader::Mode shader_mode; + + struct Port { + Shader::Mode mode; + VisualShader::Type shader_type; + PortType type; + const char *name; + const char *string; + }; + + static const Port ports[]; + +public: + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + Variant get_input_port_default_value(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + virtual bool is_port_separator(int p_index) const; + + virtual String get_caption() const; + + 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) const; + + VisualShaderNodeOutput(); +}; + +class VisualShaderNodeUniform : public VisualShaderNode { + GDCLASS(VisualShaderNodeUniform, VisualShaderNode) + + String uniform_name; + +protected: + static void _bind_methods(); + +public: + void set_uniform_name(const String &p_name); + String get_uniform_name() const; + + VisualShaderNodeUniform(); +}; + +#endif // VISUAL_SHADER_H diff --git a/scene/resources/visual_shader_nodes.cpp b/scene/resources/visual_shader_nodes.cpp new file mode 100644 index 0000000000..90af312870 --- /dev/null +++ b/scene/resources/visual_shader_nodes.cpp @@ -0,0 +1,1879 @@ +#include "visual_shader_nodes.h" +////////////// Scalar + +String VisualShaderNodeScalarConstant::get_caption() const { + return "Scalar"; +} + +int VisualShaderNodeScalarConstant::get_input_port_count() const { + return 0; +} +VisualShaderNodeScalarConstant::PortType VisualShaderNodeScalarConstant::get_input_port_type(int p_port) const { + return PORT_TYPE_SCALAR; +} +String VisualShaderNodeScalarConstant::get_input_port_name(int p_port) const { + return String(); +} + +int VisualShaderNodeScalarConstant::get_output_port_count() const { + return 1; +} +VisualShaderNodeScalarConstant::PortType VisualShaderNodeScalarConstant::get_output_port_type(int p_port) const { + return PORT_TYPE_SCALAR; +} +String VisualShaderNodeScalarConstant::get_output_port_name(int p_port) const { + return ""; //no output port means the editor will be used as port +} + +String VisualShaderNodeScalarConstant::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const { + return "\t" + p_output_vars[0] + " = " + vformat("%.6f", constant) + ";\n"; +} + +void VisualShaderNodeScalarConstant::set_constant(float p_value) { + + constant = p_value; + emit_changed(); +} + +float VisualShaderNodeScalarConstant::get_constant() const { + + return constant; +} + +Vector<StringName> VisualShaderNodeScalarConstant::get_editable_properties() const { + Vector<StringName> props; + props.push_back("constant"); + return props; +} + +void VisualShaderNodeScalarConstant::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_constant", "value"), &VisualShaderNodeScalarConstant::set_constant); + ClassDB::bind_method(D_METHOD("get_constant"), &VisualShaderNodeScalarConstant::get_constant); + + ADD_PROPERTY(PropertyInfo(Variant::REAL, "constant"), "set_constant", "get_constant"); +} + +VisualShaderNodeScalarConstant::VisualShaderNodeScalarConstant() { + constant = 0; +} + +////////////// Color + +String VisualShaderNodeColorConstant::get_caption() const { + return "Color"; +} + +int VisualShaderNodeColorConstant::get_input_port_count() const { + return 0; +} +VisualShaderNodeColorConstant::PortType VisualShaderNodeColorConstant::get_input_port_type(int p_port) const { + return PORT_TYPE_VECTOR; +} +String VisualShaderNodeColorConstant::get_input_port_name(int p_port) const { + return String(); +} + +int VisualShaderNodeColorConstant::get_output_port_count() const { + return 2; +} +VisualShaderNodeColorConstant::PortType VisualShaderNodeColorConstant::get_output_port_type(int p_port) const { + return p_port == 0 ? PORT_TYPE_VECTOR : PORT_TYPE_SCALAR; +} +String VisualShaderNodeColorConstant::get_output_port_name(int p_port) const { + return p_port == 0 ? "" : "alpha"; //no output port means the editor will be used as port +} + +String VisualShaderNodeColorConstant::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const { + + String code; + code += "\t" + p_output_vars[0] + " = " + vformat("vec3(%.6f,%.6f,%.6f)", constant.r, constant.g, constant.b) + ";\n"; + code += "\t" + p_output_vars[1] + " = " + vformat("%.6f", constant.a) + ";\n"; + + return code; +} + +void VisualShaderNodeColorConstant::set_constant(Color p_value) { + + constant = p_value; + emit_changed(); +} + +Color VisualShaderNodeColorConstant::get_constant() const { + + return constant; +} + +Vector<StringName> VisualShaderNodeColorConstant::get_editable_properties() const { + Vector<StringName> props; + props.push_back("constant"); + return props; +} + +void VisualShaderNodeColorConstant::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_constant", "value"), &VisualShaderNodeColorConstant::set_constant); + ClassDB::bind_method(D_METHOD("get_constant"), &VisualShaderNodeColorConstant::get_constant); + + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "constant"), "set_constant", "get_constant"); +} + +VisualShaderNodeColorConstant::VisualShaderNodeColorConstant() { + constant = Color(1, 1, 1, 1); +} + +////////////// Vector + +String VisualShaderNodeVec3Constant::get_caption() const { + return "Vector"; +} + +int VisualShaderNodeVec3Constant::get_input_port_count() const { + return 0; +} +VisualShaderNodeVec3Constant::PortType VisualShaderNodeVec3Constant::get_input_port_type(int p_port) const { + return PORT_TYPE_VECTOR; +} +String VisualShaderNodeVec3Constant::get_input_port_name(int p_port) const { + return String(); +} + +int VisualShaderNodeVec3Constant::get_output_port_count() const { + return 1; +} +VisualShaderNodeVec3Constant::PortType VisualShaderNodeVec3Constant::get_output_port_type(int p_port) const { + return PORT_TYPE_VECTOR; +} +String VisualShaderNodeVec3Constant::get_output_port_name(int p_port) const { + return ""; //no output port means the editor will be used as port +} + +String VisualShaderNodeVec3Constant::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const { + return "\t" + p_output_vars[0] + " = " + vformat("vec3(%.6f,%.6f,%.6f)", constant.x, constant.y, constant.z) + ";\n"; +} + +void VisualShaderNodeVec3Constant::set_constant(Vector3 p_value) { + + constant = p_value; + emit_changed(); +} + +Vector3 VisualShaderNodeVec3Constant::get_constant() const { + + return constant; +} + +Vector<StringName> VisualShaderNodeVec3Constant::get_editable_properties() const { + Vector<StringName> props; + props.push_back("constant"); + return props; +} + +void VisualShaderNodeVec3Constant::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_constant", "value"), &VisualShaderNodeVec3Constant::set_constant); + ClassDB::bind_method(D_METHOD("get_constant"), &VisualShaderNodeVec3Constant::get_constant); + + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "constant"), "set_constant", "get_constant"); +} + +VisualShaderNodeVec3Constant::VisualShaderNodeVec3Constant() { +} + +////////////// Transform + +String VisualShaderNodeTransformConstant::get_caption() const { + return "Transform4x3"; +} + +int VisualShaderNodeTransformConstant::get_input_port_count() const { + return 0; +} +VisualShaderNodeTransformConstant::PortType VisualShaderNodeTransformConstant::get_input_port_type(int p_port) const { + return PORT_TYPE_VECTOR; +} +String VisualShaderNodeTransformConstant::get_input_port_name(int p_port) const { + return String(); +} + +int VisualShaderNodeTransformConstant::get_output_port_count() const { + return 1; +} +VisualShaderNodeTransformConstant::PortType VisualShaderNodeTransformConstant::get_output_port_type(int p_port) const { + return PORT_TYPE_TRANSFORM; +} +String VisualShaderNodeTransformConstant::get_output_port_name(int p_port) const { + return ""; //no output port means the editor will be used as port +} + +String VisualShaderNodeTransformConstant::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const { + Transform t = constant; + t.basis.transpose(); + + String code = "\t" + p_output_vars[0] + " = mat4("; + code += vformat("vec4(%.6f,%.6f,%.6f,0.0),", t.basis[0].x, t.basis[0].y, t.basis[0].z); + code += vformat("vec4(%.6f,%.6f,%.6f,0.0),", t.basis[1].x, t.basis[1].y, t.basis[1].z); + code += vformat("vec4(%.6f,%.6f,%.6f,0.0),", t.basis[2].x, t.basis[2].y, t.basis[2].z); + code += vformat("vec4(%.6f,%.6f,%.6f,1.0) );\n", t.origin.x, t.origin.y, t.origin.z); + return code; +} + +void VisualShaderNodeTransformConstant::set_constant(Transform p_value) { + + constant = p_value; + emit_changed(); +} + +Transform VisualShaderNodeTransformConstant::get_constant() const { + + return constant; +} + +Vector<StringName> VisualShaderNodeTransformConstant::get_editable_properties() const { + Vector<StringName> props; + props.push_back("constant"); + return props; +} + +void VisualShaderNodeTransformConstant::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_constant", "value"), &VisualShaderNodeTransformConstant::set_constant); + ClassDB::bind_method(D_METHOD("get_constant"), &VisualShaderNodeTransformConstant::get_constant); + + ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM, "constant"), "set_constant", "get_constant"); +} + +VisualShaderNodeTransformConstant::VisualShaderNodeTransformConstant() { +} + +////////////// Texture + +String VisualShaderNodeTexture::get_caption() const { + return "Texture"; +} + +int VisualShaderNodeTexture::get_input_port_count() const { + return 2; +} +VisualShaderNodeTexture::PortType VisualShaderNodeTexture::get_input_port_type(int p_port) const { + return p_port == 0 ? PORT_TYPE_VECTOR : PORT_TYPE_SCALAR; +} +String VisualShaderNodeTexture::get_input_port_name(int p_port) const { + return p_port == 0 ? "uv" : "lod"; +} + +int VisualShaderNodeTexture::get_output_port_count() const { + return 2; +} +VisualShaderNodeTexture::PortType VisualShaderNodeTexture::get_output_port_type(int p_port) const { + return p_port == 0 ? PORT_TYPE_VECTOR : PORT_TYPE_SCALAR; +} +String VisualShaderNodeTexture::get_output_port_name(int p_port) const { + return p_port == 0 ? "rgb" : "alpha"; +} + +static String make_unique_id(VisualShader::Type p_type, int p_id, const String &p_name) { + + static const char *typepf[VisualShader::TYPE_MAX] = { "vtx", "frg", "lgt" }; + return p_name + "_" + String(typepf[p_type]) + "_" + itos(p_id); +} + +Vector<VisualShader::DefaultTextureParam> VisualShaderNodeTexture::get_default_texture_parameters(VisualShader::Type p_type, int p_id) const { + VisualShader::DefaultTextureParam dtp; + dtp.name = make_unique_id(p_type, p_id, "tex"); + dtp.param = texture; + Vector<VisualShader::DefaultTextureParam> ret; + ret.push_back(dtp); + return ret; +} + +String VisualShaderNodeTexture::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { + + if (source == SOURCE_TEXTURE) { + + String u = "uniform sampler2D " + make_unique_id(p_type, p_id, "tex"); + switch (texture_type) { + case TYPE_DATA: break; + case TYPE_COLOR: u += " : hint_color"; break; + case TYPE_NORMALMAP: u += " : hint_normal"; break; + } + return u + ";"; + } + + return String(); +} + +String VisualShaderNodeTexture::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const { + + if (source == SOURCE_TEXTURE) { + String id = make_unique_id(p_type, p_id, "tex"); + String code; + if (p_input_vars[0] == String()) { //none bound, do nothing + + code += "\tvec4 " + id + "_read = vec4(0.0);\n"; + + } else if (p_input_vars[1] == String()) { + //no lod + code += "\tvec4 " + id + "_read = texture( " + id + " , " + p_input_vars[0] + ".xy );\n"; + } else { + code += "\tvec4 " + id + "_read = textureLod( " + id + " , " + p_input_vars[0] + ".xy , " + p_input_vars[1] + " );\n"; + } + + code += "\t" + p_output_vars[0] + " = " + id + "_read.rgb;\n"; + code += "\t" + p_output_vars[1] + " = " + id + "_read.a;\n"; + return code; + } + + if (source == SOURCE_SCREEN && (p_mode == Shader::MODE_SPATIAL || p_mode == Shader::MODE_CANVAS_ITEM) && p_type == VisualShader::TYPE_FRAGMENT) { + + String code = "\t{\n"; + if (p_input_vars[0] == String()) { //none bound, do nothing + + code += "\t\tvec4 _tex_read = vec4(0.0);\n"; + + } else if (p_input_vars[1] == String()) { + //no lod + code += "\t\tvec4 _tex_read = textureLod( SCREEN_TEXTURE , " + p_input_vars[0] + ".xy, 0.0 );\n"; + } else { + code += "\t\tvec4 _tex_read = textureLod( SCREEN_TEXTURE , " + p_input_vars[0] + ".xy , " + p_input_vars[1] + " );\n"; + } + + code += "\t\t" + p_output_vars[0] + " = _tex_read.rgb;\n"; + code += "\t\t" + p_output_vars[1] + " = _tex_read.a;\n"; + code += "\t}\n"; + return code; + } + + if (source == SOURCE_2D_TEXTURE && p_mode == Shader::MODE_CANVAS_ITEM && p_type == VisualShader::TYPE_FRAGMENT) { + + String code = "\t{\n"; + if (p_input_vars[0] == String()) { //none bound, do nothing + + code += "\t\tvec4 _tex_read = vec4(0.0);\n"; + + } else if (p_input_vars[1] == String()) { + //no lod + code += "\t\tvec4 _tex_read = texture( TEXTURE , " + p_input_vars[0] + ".xy );\n"; + } else { + code += "\t\tvec4 _tex_read = textureLod( TEXTURE , " + p_input_vars[0] + ".xy , " + p_input_vars[1] + " );\n"; + } + + code += "\t\t" + p_output_vars[0] + " = _tex_read.rgb;\n"; + code += "\t\t" + p_output_vars[1] + " = _tex_read.a;\n"; + code += "\t}\n"; + return code; + } + + if (source == SOURCE_2D_NORMAL && p_mode == Shader::MODE_CANVAS_ITEM && p_type == VisualShader::TYPE_FRAGMENT) { + + String code = "\t{\n"; + if (p_input_vars[0] == String()) { //none bound, do nothing + + code += "\t\tvec4 _tex_read = vec4(0.0);\n"; + + } else if (p_input_vars[1] == String()) { + //no lod + code += "\t\tvec4 _tex_read = texture( NORMAL_TEXTURE , " + p_input_vars[0] + ".xy );\n"; + } else { + code += "\t\tvec4 _tex_read = textureLod( NORMAL_TEXTURE , " + p_input_vars[0] + ".xy , " + p_input_vars[1] + " );\n"; + } + + code += "\t\t" + p_output_vars[0] + " = _tex_read.rgb;\n"; + code += "\t\t" + p_output_vars[1] + " = _tex_read.a;\n"; + code += "\t}\n"; + return code; + } + + //none + String code; + code += "\t" + p_output_vars[0] + " = vec3(0.0);\n"; + code += "\t" + p_output_vars[1] + " = 1.0;\n"; + return code; +} + +void VisualShaderNodeTexture::set_source(Source p_source) { + source = p_source; + emit_changed(); + emit_signal("editor_refresh_request"); +} + +VisualShaderNodeTexture::Source VisualShaderNodeTexture::get_source() const { + return source; +} + +void VisualShaderNodeTexture::set_texture(Ref<Texture> p_value) { + + texture = p_value; + emit_changed(); +} + +Ref<Texture> VisualShaderNodeTexture::get_texture() const { + + return texture; +} + +void VisualShaderNodeTexture::set_texture_type(TextureType p_type) { + texture_type = p_type; + emit_changed(); +} + +VisualShaderNodeTexture::TextureType VisualShaderNodeTexture::get_texture_type() const { + return texture_type; +} + +Vector<StringName> VisualShaderNodeTexture::get_editable_properties() const { + Vector<StringName> props; + props.push_back("source"); + if (source == SOURCE_TEXTURE) { + props.push_back("texture"); + props.push_back("texture_type"); + } + return props; +} + +String VisualShaderNodeTexture::get_warning(Shader::Mode p_mode, VisualShader::Type p_type) const { + + if (source == SOURCE_TEXTURE) { + return String(); // all good + } + + if (source == SOURCE_SCREEN && (p_mode == Shader::MODE_SPATIAL || p_mode == Shader::MODE_CANVAS_ITEM) && p_type == VisualShader::TYPE_FRAGMENT) { + + return String(); // all good + } + + if (source == SOURCE_2D_TEXTURE && p_mode == Shader::MODE_CANVAS_ITEM && p_type == VisualShader::TYPE_FRAGMENT) { + + return String(); // all good + } + + if (source == SOURCE_2D_NORMAL && p_mode == Shader::MODE_CANVAS_ITEM) { + + return String(); // all good + } + + return TTR("Invalid source for shader."); +} + +void VisualShaderNodeTexture::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_source", "value"), &VisualShaderNodeTexture::set_source); + ClassDB::bind_method(D_METHOD("get_source"), &VisualShaderNodeTexture::get_source); + + ClassDB::bind_method(D_METHOD("set_texture", "value"), &VisualShaderNodeTexture::set_texture); + ClassDB::bind_method(D_METHOD("get_texture"), &VisualShaderNodeTexture::get_texture); + + ClassDB::bind_method(D_METHOD("set_texture_type", "value"), &VisualShaderNodeTexture::set_texture_type); + ClassDB::bind_method(D_METHOD("get_texture_type"), &VisualShaderNodeTexture::get_texture_type); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "source", PROPERTY_HINT_ENUM, "Texture,Screen,Texture2D,NormalMap2D"), "set_source", "get_source"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_type", PROPERTY_HINT_ENUM, "Data,Color,Normalmap"), "set_texture_type", "get_texture_type"); + + BIND_ENUM_CONSTANT(SOURCE_TEXTURE); + BIND_ENUM_CONSTANT(SOURCE_SCREEN); + BIND_ENUM_CONSTANT(SOURCE_2D_TEXTURE); + BIND_ENUM_CONSTANT(SOURCE_2D_NORMAL); + BIND_ENUM_CONSTANT(TYPE_DATA); + BIND_ENUM_CONSTANT(TYPE_COLOR); + BIND_ENUM_CONSTANT(TYPE_NORMALMAP); +} + +VisualShaderNodeTexture::VisualShaderNodeTexture() { + texture_type = TYPE_DATA; + source = SOURCE_TEXTURE; +} + +////////////// CubeMap + +String VisualShaderNodeCubeMap::get_caption() const { + return "CubeMap"; +} + +int VisualShaderNodeCubeMap::get_input_port_count() const { + return 2; +} +VisualShaderNodeCubeMap::PortType VisualShaderNodeCubeMap::get_input_port_type(int p_port) const { + return p_port == 0 ? PORT_TYPE_VECTOR : PORT_TYPE_SCALAR; +} +String VisualShaderNodeCubeMap::get_input_port_name(int p_port) const { + return p_port == 0 ? "uv" : "lod"; +} + +int VisualShaderNodeCubeMap::get_output_port_count() const { + return 2; +} +VisualShaderNodeCubeMap::PortType VisualShaderNodeCubeMap::get_output_port_type(int p_port) const { + return p_port == 0 ? PORT_TYPE_VECTOR : PORT_TYPE_SCALAR; +} +String VisualShaderNodeCubeMap::get_output_port_name(int p_port) const { + return p_port == 0 ? "rgb" : "alpha"; +} + +Vector<VisualShader::DefaultTextureParam> VisualShaderNodeCubeMap::get_default_texture_parameters(VisualShader::Type p_type, int p_id) const { + VisualShader::DefaultTextureParam dtp; + dtp.name = make_unique_id(p_type, p_id, "cube"); + dtp.param = cube_map; + Vector<VisualShader::DefaultTextureParam> ret; + ret.push_back(dtp); + return ret; +} + +String VisualShaderNodeCubeMap::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { + + String u = "uniform sampler2DCube " + make_unique_id(p_type, p_id, "cube"); + switch (texture_type) { + case TYPE_DATA: break; + case TYPE_COLOR: u += " : hint_color"; break; + case TYPE_NORMALMAP: u += " : hint_normal"; break; + } + return u + ";"; +} + +String VisualShaderNodeCubeMap::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const { + + String id = make_unique_id(p_type, p_id, "cube"); + String code; + if (p_input_vars[0] == String()) { //none bound, do nothing + + code += "\tvec4 " + id + "_read = vec4(0.0);\n"; + + } else if (p_input_vars[1] == String()) { + //no lod + code += "\tvec4 " + id + "_read = texture( " + id + " , " + p_input_vars[0] + " );\n"; + } else { + code += "\tvec4 " + id + "_read = textureLod( " + id + " , " + p_input_vars[0] + " , " + p_input_vars[1] + " );\n"; + } + + code += "\t" + p_output_vars[0] + " = " + id + "_read.rgb;\n"; + code += "\t" + p_output_vars[1] + " = " + id + "_read.a;\n"; + return code; +} + +void VisualShaderNodeCubeMap::set_cube_map(Ref<CubeMap> p_value) { + + cube_map = p_value; + emit_changed(); +} + +Ref<CubeMap> VisualShaderNodeCubeMap::get_cube_map() const { + + return cube_map; +} + +void VisualShaderNodeCubeMap::set_texture_type(TextureType p_type) { + texture_type = p_type; + emit_changed(); +} + +VisualShaderNodeCubeMap::TextureType VisualShaderNodeCubeMap::get_texture_type() const { + return texture_type; +} + +Vector<StringName> VisualShaderNodeCubeMap::get_editable_properties() const { + Vector<StringName> props; + props.push_back("cube_map"); + props.push_back("texture_type"); + return props; +} + +void VisualShaderNodeCubeMap::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_cube_map", "value"), &VisualShaderNodeCubeMap::set_cube_map); + ClassDB::bind_method(D_METHOD("get_cube_map"), &VisualShaderNodeCubeMap::get_cube_map); + + ClassDB::bind_method(D_METHOD("set_texture_type", "value"), &VisualShaderNodeCubeMap::set_texture_type); + ClassDB::bind_method(D_METHOD("get_texture_type"), &VisualShaderNodeCubeMap::get_texture_type); + + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "cube_map", PROPERTY_HINT_RESOURCE_TYPE, "CubeMap"), "set_cube_map", "get_cube_map"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_type", PROPERTY_HINT_ENUM, "Data,Color,Normalmap"), "set_texture_type", "get_texture_type"); +} + +VisualShaderNodeCubeMap::VisualShaderNodeCubeMap() { + texture_type = TYPE_DATA; +} +////////////// Scalar Op + +String VisualShaderNodeScalarOp::get_caption() const { + return "ScalarOp"; +} + +int VisualShaderNodeScalarOp::get_input_port_count() const { + return 2; +} +VisualShaderNodeScalarOp::PortType VisualShaderNodeScalarOp::get_input_port_type(int p_port) const { + return PORT_TYPE_SCALAR; +} +String VisualShaderNodeScalarOp::get_input_port_name(int p_port) const { + return p_port == 0 ? "a" : "b"; +} + +int VisualShaderNodeScalarOp::get_output_port_count() const { + return 1; +} +VisualShaderNodeScalarOp::PortType VisualShaderNodeScalarOp::get_output_port_type(int p_port) const { + return PORT_TYPE_SCALAR; +} +String VisualShaderNodeScalarOp::get_output_port_name(int p_port) const { + return "op"; //no output port means the editor will be used as port +} + +String VisualShaderNodeScalarOp::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const { + + String code = "\t" + p_output_vars[0] + " = "; + switch (op) { + + case OP_ADD: code += p_input_vars[0] + " + " + p_input_vars[1] + ";\n"; break; + case OP_SUB: code += p_input_vars[0] + " - " + p_input_vars[1] + ";\n"; break; + case OP_MUL: code += p_input_vars[0] + " * " + p_input_vars[1] + ";\n"; break; + case OP_DIV: code += p_input_vars[0] + " / " + p_input_vars[1] + ";\n"; break; + case OP_MOD: code += "mod( " + p_input_vars[0] + " , " + p_input_vars[1] + " );\n"; break; + case OP_POW: code += "pow( " + p_input_vars[0] + " , " + p_input_vars[1] + " );\n"; break; + case OP_MAX: code += "max( " + p_input_vars[0] + " , " + p_input_vars[1] + " );\n"; break; + case OP_MIN: code += "min( " + p_input_vars[0] + " , " + p_input_vars[1] + " );\n"; break; + case OP_ATAN2: code += "atan( " + p_input_vars[0] + " , " + p_input_vars[1] + " );\n"; break; + } + + return code; +} + +void VisualShaderNodeScalarOp::set_operator(Operator p_op) { + + op = p_op; + emit_changed(); +} + +VisualShaderNodeScalarOp::Operator VisualShaderNodeScalarOp::get_operator() const { + + return op; +} + +Vector<StringName> VisualShaderNodeScalarOp::get_editable_properties() const { + Vector<StringName> props; + props.push_back("operator"); + return props; +} + +void VisualShaderNodeScalarOp::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_operator", "op"), &VisualShaderNodeScalarOp::set_operator); + ClassDB::bind_method(D_METHOD("get_operator"), &VisualShaderNodeScalarOp::get_operator); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "operator", PROPERTY_HINT_ENUM, "Add,Sub,Multiply,Divide,Remainder,Power,Max,Min,Atan2"), "set_operator", "get_operator"); + + BIND_ENUM_CONSTANT(OP_ADD); + BIND_ENUM_CONSTANT(OP_SUB); + BIND_ENUM_CONSTANT(OP_MUL); + BIND_ENUM_CONSTANT(OP_DIV); + BIND_ENUM_CONSTANT(OP_MOD); + BIND_ENUM_CONSTANT(OP_POW); + BIND_ENUM_CONSTANT(OP_MAX); + BIND_ENUM_CONSTANT(OP_MIN); + BIND_ENUM_CONSTANT(OP_ATAN2); +} + +VisualShaderNodeScalarOp::VisualShaderNodeScalarOp() { + op = OP_ADD; + set_input_port_default_value(0, 0.0); + set_input_port_default_value(1, 0.0); +} + +////////////// Vector Op + +String VisualShaderNodeVectorOp::get_caption() const { + return "VecOp"; +} + +int VisualShaderNodeVectorOp::get_input_port_count() const { + return 2; +} +VisualShaderNodeVectorOp::PortType VisualShaderNodeVectorOp::get_input_port_type(int p_port) const { + return PORT_TYPE_VECTOR; +} +String VisualShaderNodeVectorOp::get_input_port_name(int p_port) const { + return p_port == 0 ? "a" : "b"; +} + +int VisualShaderNodeVectorOp::get_output_port_count() const { + return 1; +} +VisualShaderNodeVectorOp::PortType VisualShaderNodeVectorOp::get_output_port_type(int p_port) const { + return PORT_TYPE_VECTOR; +} +String VisualShaderNodeVectorOp::get_output_port_name(int p_port) const { + return "op"; //no output port means the editor will be used as port +} + +String VisualShaderNodeVectorOp::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const { + + String code = "\t" + p_output_vars[0] + " = "; + switch (op) { + + case OP_ADD: code += p_input_vars[0] + " + " + p_input_vars[1] + ";\n"; break; + case OP_SUB: code += p_input_vars[0] + " - " + p_input_vars[1] + ";\n"; break; + case OP_MUL: code += p_input_vars[0] + " * " + p_input_vars[1] + ";\n"; break; + case OP_DIV: code += p_input_vars[0] + " / " + p_input_vars[1] + ";\n"; break; + case OP_MOD: code += "mod( " + p_input_vars[0] + " , " + p_input_vars[1] + " );\n"; break; + case OP_POW: code += "pow( " + p_input_vars[0] + " , " + p_input_vars[1] + " );\n"; break; + case OP_MAX: code += "max( " + p_input_vars[0] + " , " + p_input_vars[1] + " );\n"; break; + case OP_MIN: code += "min( " + p_input_vars[0] + " , " + p_input_vars[1] + " );\n"; break; + case OP_CROSS: code += "cross( " + p_input_vars[0] + " , " + p_input_vars[1] + " );\n"; break; + } + + return code; +} + +void VisualShaderNodeVectorOp::set_operator(Operator p_op) { + + op = p_op; + emit_changed(); +} + +VisualShaderNodeVectorOp::Operator VisualShaderNodeVectorOp::get_operator() const { + + return op; +} + +Vector<StringName> VisualShaderNodeVectorOp::get_editable_properties() const { + Vector<StringName> props; + props.push_back("operator"); + return props; +} + +void VisualShaderNodeVectorOp::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_operator", "op"), &VisualShaderNodeVectorOp::set_operator); + ClassDB::bind_method(D_METHOD("get_operator"), &VisualShaderNodeVectorOp::get_operator); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "operator", PROPERTY_HINT_ENUM, "Add,Sub,Multiply,Divide,Remainder,Power,Max,Min,Cross"), "set_operator", "get_operator"); + + BIND_ENUM_CONSTANT(OP_ADD); + BIND_ENUM_CONSTANT(OP_SUB); + BIND_ENUM_CONSTANT(OP_MUL); + BIND_ENUM_CONSTANT(OP_DIV); + BIND_ENUM_CONSTANT(OP_MOD); + BIND_ENUM_CONSTANT(OP_POW); + BIND_ENUM_CONSTANT(OP_MAX); + BIND_ENUM_CONSTANT(OP_MIN); + BIND_ENUM_CONSTANT(OP_CROSS); +} + +VisualShaderNodeVectorOp::VisualShaderNodeVectorOp() { + op = OP_ADD; + set_input_port_default_value(0, Vector3()); + set_input_port_default_value(1, Vector3()); +} + +////////////// Color Op + +String VisualShaderNodeColorOp::get_caption() const { + return "ColorOp"; +} + +int VisualShaderNodeColorOp::get_input_port_count() const { + return 2; +} +VisualShaderNodeColorOp::PortType VisualShaderNodeColorOp::get_input_port_type(int p_port) const { + return PORT_TYPE_VECTOR; +} +String VisualShaderNodeColorOp::get_input_port_name(int p_port) const { + return p_port == 0 ? "a" : "b"; +} + +int VisualShaderNodeColorOp::get_output_port_count() const { + return 1; +} +VisualShaderNodeColorOp::PortType VisualShaderNodeColorOp::get_output_port_type(int p_port) const { + return PORT_TYPE_VECTOR; +} +String VisualShaderNodeColorOp::get_output_port_name(int p_port) const { + return "op"; //no output port means the editor will be used as port +} + +String VisualShaderNodeColorOp::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const { + + String code; + static const char *axisn[3] = { "x", "y", "z" }; + switch (op) { + case OP_SCREEN: { + + code += "\t" + p_output_vars[0] + "=vec3(1.0)-(vec3(1.0)-" + p_input_vars[0] + ")*(vec3(1.0)-" + p_input_vars[1] + ");\n"; + } break; + case OP_DIFFERENCE: { + + code += "\t" + p_output_vars[0] + "=abs(" + p_input_vars[0] + "-" + p_input_vars[1] + ");\n"; + } break; + case OP_DARKEN: { + + code += "\t" + p_output_vars[0] + "=min(" + p_input_vars[0] + "," + p_input_vars[1] + ");\n"; + } break; + case OP_LIGHTEN: { + + code += "\t" + p_output_vars[0] + "=max(" + p_input_vars[0] + "," + p_input_vars[1] + ");\n"; + + } break; + case OP_OVERLAY: { + + for (int i = 0; i < 3; i++) { + code += "\t{\n"; + code += "\t\tfloat base=" + p_input_vars[0] + "." + axisn[i] + ";\n"; + code += "\t\tfloat blend=" + p_input_vars[1] + "." + axisn[i] + ";\n"; + code += "\t\tif (base < 0.5) {\n"; + code += "\t\t\t" + p_output_vars[0] + "." + axisn[i] + " = 2.0 * base * blend;\n"; + code += "\t\t} else {\n"; + code += "\t\t\t" + p_output_vars[0] + "." + axisn[i] + " = 1.0 - 2.0 * (1.0 - blend) * (1.0 - base);\n"; + code += "\t\t}\n"; + code += "\t}\n"; + } + + } break; + case OP_DODGE: { + + code += "\t" + p_output_vars[0] + "=(" + p_input_vars[0] + ")/(vec3(1.0)-" + p_input_vars[1] + ");\n"; + + } break; + case OP_BURN: { + + code += "\t" + p_output_vars[0] + "=vec3(1.0)-(vec3(1.0)-" + p_input_vars[0] + ")/(" + p_input_vars[1] + ");\n"; + } break; + case OP_SOFT_LIGHT: { + + for (int i = 0; i < 3; i++) { + code += "\t{\n"; + code += "\t\tfloat base=" + p_input_vars[0] + "." + axisn[i] + ";\n"; + code += "\t\tfloat blend=" + p_input_vars[1] + "." + axisn[i] + ";\n"; + code += "\t\tif (base < 0.5) {\n"; + code += "\t\t\t" + p_output_vars[0] + "." + axisn[i] + " = (base * (blend+0.5));\n"; + code += "\t\t} else {\n"; + code += "\t\t\t" + p_output_vars[0] + "." + axisn[i] + " = (1.0 - (1.0-base) * (1.0-(blend-0.5)));\n"; + code += "\t\t}\n"; + code += "\t}\n"; + } + + } break; + case OP_HARD_LIGHT: { + + for (int i = 0; i < 3; i++) { + code += "\t{\n"; + code += "\t\tfloat base=" + p_input_vars[0] + "." + axisn[i] + ";\n"; + code += "\t\tfloat blend=" + p_input_vars[1] + "." + axisn[i] + ";\n"; + code += "\t\tif (base < 0.5) {\n"; + code += "\t\t\t" + p_output_vars[0] + "." + axisn[i] + " = (base * (2.0*blend));\n"; + code += "\t\t} else {\n"; + code += "\t\t\t" + p_output_vars[0] + "." + axisn[i] + " = (1.0 - (1.0-base) * (1.0-2.0*(blend-0.5)));\n"; + code += "\t\t}\n"; + code += "\t}\n"; + } + + } break; + } + + return code; +} + +void VisualShaderNodeColorOp::set_operator(Operator p_op) { + + op = p_op; + emit_changed(); +} + +VisualShaderNodeColorOp::Operator VisualShaderNodeColorOp::get_operator() const { + + return op; +} + +Vector<StringName> VisualShaderNodeColorOp::get_editable_properties() const { + Vector<StringName> props; + props.push_back("operator"); + return props; +} + +void VisualShaderNodeColorOp::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_operator", "op"), &VisualShaderNodeColorOp::set_operator); + ClassDB::bind_method(D_METHOD("get_operator"), &VisualShaderNodeColorOp::get_operator); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "operator", PROPERTY_HINT_ENUM, "Screen,Difference,Darken,Lighten,Overlay,Dodge,Burn,SoftLight,HardLight"), "set_operator", "get_operator"); +} + +VisualShaderNodeColorOp::VisualShaderNodeColorOp() { + op = OP_SCREEN; + set_input_port_default_value(0, Vector3()); + set_input_port_default_value(1, Vector3()); +} + +////////////// Transform Mult + +String VisualShaderNodeTransformMult::get_caption() const { + return "TransMult"; +} + +int VisualShaderNodeTransformMult::get_input_port_count() const { + return 2; +} +VisualShaderNodeTransformMult::PortType VisualShaderNodeTransformMult::get_input_port_type(int p_port) const { + return PORT_TYPE_TRANSFORM; +} +String VisualShaderNodeTransformMult::get_input_port_name(int p_port) const { + return p_port == 0 ? "a" : "b"; +} + +int VisualShaderNodeTransformMult::get_output_port_count() const { + return 1; +} +VisualShaderNodeTransformMult::PortType VisualShaderNodeTransformMult::get_output_port_type(int p_port) const { + return PORT_TYPE_TRANSFORM; +} +String VisualShaderNodeTransformMult::get_output_port_name(int p_port) const { + return "mult"; //no output port means the editor will be used as port +} + +String VisualShaderNodeTransformMult::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const { + + if (op == OP_AxB) { + return "\t" + p_output_vars[0] + " = " + p_input_vars[0] + " * " + p_input_vars[1] + ";\n"; + } else { + return "\t" + p_output_vars[0] + " = " + p_input_vars[1] + " * " + p_input_vars[0] + ";\n"; + } +} + +void VisualShaderNodeTransformMult::set_operator(Operator p_op) { + + op = p_op; + emit_changed(); +} + +VisualShaderNodeTransformMult::Operator VisualShaderNodeTransformMult::get_operator() const { + + return op; +} + +Vector<StringName> VisualShaderNodeTransformMult::get_editable_properties() const { + Vector<StringName> props; + props.push_back("operator"); + return props; +} + +void VisualShaderNodeTransformMult::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_operator", "op"), &VisualShaderNodeTransformMult::set_operator); + ClassDB::bind_method(D_METHOD("get_operator"), &VisualShaderNodeTransformMult::get_operator); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "operator", PROPERTY_HINT_ENUM, "A x B,B x A"), "set_operator", "get_operator"); +} + +VisualShaderNodeTransformMult::VisualShaderNodeTransformMult() { + op = OP_AxB; + set_input_port_default_value(0, Transform()); + set_input_port_default_value(1, Transform()); +} + +////////////// TransformVec Mult + +String VisualShaderNodeTransformVecMult::get_caption() const { + return "TransVecMult"; +} + +int VisualShaderNodeTransformVecMult::get_input_port_count() const { + return 2; +} +VisualShaderNodeTransformVecMult::PortType VisualShaderNodeTransformVecMult::get_input_port_type(int p_port) const { + return p_port == 0 ? PORT_TYPE_TRANSFORM : PORT_TYPE_VECTOR; +} +String VisualShaderNodeTransformVecMult::get_input_port_name(int p_port) const { + return p_port == 0 ? "a" : "b"; +} + +int VisualShaderNodeTransformVecMult::get_output_port_count() const { + return 1; +} +VisualShaderNodeTransformVecMult::PortType VisualShaderNodeTransformVecMult::get_output_port_type(int p_port) const { + return PORT_TYPE_VECTOR; +} +String VisualShaderNodeTransformVecMult::get_output_port_name(int p_port) const { + return ""; //no output port means the editor will be used as port +} + +String VisualShaderNodeTransformVecMult::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const { + if (op == OP_AxB) { + return "\t" + p_output_vars[0] + " = ( " + p_input_vars[0] + " * vec4(" + p_input_vars[1] + ", 1.0) ).xyz;\n"; + } else if (op == OP_BxA) { + return "\t" + p_output_vars[0] + " = ( vec4(" + p_input_vars[1] + ", 1.0) * " + p_input_vars[0] + " ).xyz;\n"; + } else if (op == OP_3x3_AxB) { + return "\t" + p_output_vars[0] + " = ( " + p_input_vars[0] + " * vec4(" + p_input_vars[1] + ", 0.0) ).xyz;\n"; + } else { + return "\t" + p_output_vars[0] + " = ( vec4(" + p_input_vars[1] + ", 0.0) * " + p_input_vars[0] + " ).xyz;\n"; + } +} + +void VisualShaderNodeTransformVecMult::set_operator(Operator p_op) { + + op = p_op; + emit_changed(); +} + +VisualShaderNodeTransformVecMult::Operator VisualShaderNodeTransformVecMult::get_operator() const { + + return op; +} + +Vector<StringName> VisualShaderNodeTransformVecMult::get_editable_properties() const { + Vector<StringName> props; + props.push_back("operator"); + return props; +} + +void VisualShaderNodeTransformVecMult::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_operator", "op"), &VisualShaderNodeTransformVecMult::set_operator); + ClassDB::bind_method(D_METHOD("get_operator"), &VisualShaderNodeTransformVecMult::get_operator); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "operator", PROPERTY_HINT_ENUM, "A x B,B x A,A x B (3x3),B x A (3x3)"), "set_operator", "get_operator"); + + BIND_ENUM_CONSTANT(OP_AxB); + BIND_ENUM_CONSTANT(OP_BxA); + BIND_ENUM_CONSTANT(OP_3x3_AxB); + BIND_ENUM_CONSTANT(OP_3x3_BxA); +} + +VisualShaderNodeTransformVecMult::VisualShaderNodeTransformVecMult() { + op = OP_AxB; + set_input_port_default_value(0, Transform()); + set_input_port_default_value(1, Vector3()); +} + +////////////// Scalar Func + +String VisualShaderNodeScalarFunc::get_caption() const { + return "ScalarFunc"; +} + +int VisualShaderNodeScalarFunc::get_input_port_count() const { + return 1; +} +VisualShaderNodeScalarFunc::PortType VisualShaderNodeScalarFunc::get_input_port_type(int p_port) const { + return PORT_TYPE_SCALAR; +} +String VisualShaderNodeScalarFunc::get_input_port_name(int p_port) const { + return ""; +} + +int VisualShaderNodeScalarFunc::get_output_port_count() const { + return 1; +} +VisualShaderNodeScalarFunc::PortType VisualShaderNodeScalarFunc::get_output_port_type(int p_port) const { + return PORT_TYPE_SCALAR; +} +String VisualShaderNodeScalarFunc::get_output_port_name(int p_port) const { + return ""; //no output port means the editor will be used as port +} + +String VisualShaderNodeScalarFunc::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const { + + static const char *scalar_func_id[FUNC_NEGATE + 1] = { + "sin($)", + "cos($)", + "tan($)", + "asin($)", + "acos($)", + "atan($)", + "sinh($)", + "cosh($)", + "tanh($)", + "log($)", + "exp($)", + "sqrt($)", + "abs($)", + "sign($)", + "floor($)", + "round($)", + "ceil($)", + "fract($)", + "min(max($,0),1)", + "-($)", + }; + + return "\t" + p_output_vars[0] + " = " + String(scalar_func_id[func]).replace("$", p_input_vars[0]) + ";\n"; +} + +void VisualShaderNodeScalarFunc::set_function(Function p_func) { + + func = p_func; + emit_changed(); +} + +VisualShaderNodeScalarFunc::Function VisualShaderNodeScalarFunc::get_function() const { + + return func; +} + +Vector<StringName> VisualShaderNodeScalarFunc::get_editable_properties() const { + Vector<StringName> props; + props.push_back("function"); + return props; +} + +void VisualShaderNodeScalarFunc::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_function", "func"), &VisualShaderNodeScalarFunc::set_function); + ClassDB::bind_method(D_METHOD("get_function"), &VisualShaderNodeScalarFunc::get_function); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "Sin,Cos,Tan,ASin,ACos,ATan,SinH,CosH,TanH,Log,Exp,Sqrt,Abs,Sign,Floor,Round,Ceil,Frac,Saturate,Negate"), "set_function", "get_function"); + + BIND_ENUM_CONSTANT(FUNC_SIN); + BIND_ENUM_CONSTANT(FUNC_COS); + BIND_ENUM_CONSTANT(FUNC_TAN); + BIND_ENUM_CONSTANT(FUNC_ASIN); + BIND_ENUM_CONSTANT(FUNC_ACOS); + BIND_ENUM_CONSTANT(FUNC_ATAN); + BIND_ENUM_CONSTANT(FUNC_SINH); + BIND_ENUM_CONSTANT(FUNC_COSH); + BIND_ENUM_CONSTANT(FUNC_TANH); + BIND_ENUM_CONSTANT(FUNC_LOG); + BIND_ENUM_CONSTANT(FUNC_EXP); + BIND_ENUM_CONSTANT(FUNC_SQRT); + BIND_ENUM_CONSTANT(FUNC_ABS); + BIND_ENUM_CONSTANT(FUNC_SIGN); + BIND_ENUM_CONSTANT(FUNC_FLOOR); + BIND_ENUM_CONSTANT(FUNC_ROUND); + BIND_ENUM_CONSTANT(FUNC_CEIL); + BIND_ENUM_CONSTANT(FUNC_FRAC); + BIND_ENUM_CONSTANT(FUNC_SATURATE); + BIND_ENUM_CONSTANT(FUNC_NEGATE); +} + +VisualShaderNodeScalarFunc::VisualShaderNodeScalarFunc() { + func = FUNC_SIGN; + set_input_port_default_value(0, 0.0); +} + +////////////// Vector Func + +String VisualShaderNodeVectorFunc::get_caption() const { + return "VecFunc"; +} + +int VisualShaderNodeVectorFunc::get_input_port_count() const { + return 1; +} +VisualShaderNodeVectorFunc::PortType VisualShaderNodeVectorFunc::get_input_port_type(int p_port) const { + return PORT_TYPE_VECTOR; +} +String VisualShaderNodeVectorFunc::get_input_port_name(int p_port) const { + return ""; +} + +int VisualShaderNodeVectorFunc::get_output_port_count() const { + return 1; +} +VisualShaderNodeVectorFunc::PortType VisualShaderNodeVectorFunc::get_output_port_type(int p_port) const { + return PORT_TYPE_VECTOR; +} +String VisualShaderNodeVectorFunc::get_output_port_name(int p_port) const { + return ""; //no output port means the editor will be used as port +} + +String VisualShaderNodeVectorFunc::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const { + + static const char *vec_func_id[FUNC_HSV2RGB + 1] = { + "normalize($)", + "max(min($,vec3(1.0)),vec3(0.0))", + "-($)", + "1.0/($)", + "", + "", + }; + + String code; + + if (func == FUNC_RGB2HSV) { + code += "\t{\n"; + code += "\t\tvec3 c = " + p_input_vars[0] + ";\n"; + code += "\t\tvec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);\n"; + code += "\t\tvec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));\n"; + code += "\t\tvec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));\n"; + code += "\t\tfloat d = q.x - min(q.w, q.y);\n"; + code += "\t\tfloat e = 1.0e-10;\n"; + code += "\t\t" + p_output_vars[0] + "=vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);\n"; + code += "\t}\n"; + } else if (func == FUNC_HSV2RGB) { + code += "\t{\n"; + code += "\t\tvec3 c = " + p_input_vars[0] + ";\n"; + code += "\t\tvec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);\n"; + code += "\t\tvec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);\n"; + code += "\t\t" + p_output_vars[0] + "=c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);\n"; + code += "\t}\n"; + + } else { + code += "\t" + p_output_vars[0] + "=" + String(vec_func_id[func]).replace("$", p_input_vars[0]) + ";\n"; + } + + return code; +} + +void VisualShaderNodeVectorFunc::set_function(Function p_func) { + + func = p_func; + emit_changed(); +} + +VisualShaderNodeVectorFunc::Function VisualShaderNodeVectorFunc::get_function() const { + + return func; +} + +Vector<StringName> VisualShaderNodeVectorFunc::get_editable_properties() const { + Vector<StringName> props; + props.push_back("function"); + return props; +} + +void VisualShaderNodeVectorFunc::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_function", "func"), &VisualShaderNodeVectorFunc::set_function); + ClassDB::bind_method(D_METHOD("get_function"), &VisualShaderNodeVectorFunc::get_function); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "Normalize,Saturate,Negate,Reciprocal,RGB2HSV,HSV2RGB"), "set_function", "get_function"); + + BIND_ENUM_CONSTANT(FUNC_NORMALIZE); + BIND_ENUM_CONSTANT(FUNC_SATURATE); + BIND_ENUM_CONSTANT(FUNC_NEGATE); + BIND_ENUM_CONSTANT(FUNC_RECIPROCAL); + BIND_ENUM_CONSTANT(FUNC_RGB2HSV); + BIND_ENUM_CONSTANT(FUNC_HSV2RGB); +} + +VisualShaderNodeVectorFunc::VisualShaderNodeVectorFunc() { + func = FUNC_NORMALIZE; + set_input_port_default_value(0, Vector3()); +} + +////////////// Dot Product + +String VisualShaderNodeDotProduct::get_caption() const { + return "DotProd"; +} + +int VisualShaderNodeDotProduct::get_input_port_count() const { + return 2; +} +VisualShaderNodeDotProduct::PortType VisualShaderNodeDotProduct::get_input_port_type(int p_port) const { + return PORT_TYPE_VECTOR; +} +String VisualShaderNodeDotProduct::get_input_port_name(int p_port) const { + return p_port == 0 ? "a" : "b"; +} + +int VisualShaderNodeDotProduct::get_output_port_count() const { + return 1; +} +VisualShaderNodeDotProduct::PortType VisualShaderNodeDotProduct::get_output_port_type(int p_port) const { + return PORT_TYPE_SCALAR; +} +String VisualShaderNodeDotProduct::get_output_port_name(int p_port) const { + return "dot"; +} + +String VisualShaderNodeDotProduct::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const { + return "\t" + p_output_vars[0] + " = dot( " + p_input_vars[0] + " , " + p_input_vars[1] + " );\n"; +} + +VisualShaderNodeDotProduct::VisualShaderNodeDotProduct() { + set_input_port_default_value(0, Vector3()); + set_input_port_default_value(1, Vector3()); +} + +////////////// Vector Len + +String VisualShaderNodeVectorLen::get_caption() const { + return "VecLen"; +} + +int VisualShaderNodeVectorLen::get_input_port_count() const { + return 1; +} +VisualShaderNodeVectorLen::PortType VisualShaderNodeVectorLen::get_input_port_type(int p_port) const { + return PORT_TYPE_VECTOR; +} +String VisualShaderNodeVectorLen::get_input_port_name(int p_port) const { + return ""; +} + +int VisualShaderNodeVectorLen::get_output_port_count() const { + return 1; +} +VisualShaderNodeVectorLen::PortType VisualShaderNodeVectorLen::get_output_port_type(int p_port) const { + return PORT_TYPE_SCALAR; +} +String VisualShaderNodeVectorLen::get_output_port_name(int p_port) const { + return "length"; +} + +String VisualShaderNodeVectorLen::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const { + return "\t" + p_output_vars[0] + " = length( " + p_input_vars[0] + " );\n"; +} + +VisualShaderNodeVectorLen::VisualShaderNodeVectorLen() { + set_input_port_default_value(0, Vector3()); +} + +////////////// Scalar Interp + +String VisualShaderNodeScalarInterp::get_caption() const { + return "ScalarInterp"; +} + +int VisualShaderNodeScalarInterp::get_input_port_count() const { + return 3; +} +VisualShaderNodeScalarInterp::PortType VisualShaderNodeScalarInterp::get_input_port_type(int p_port) const { + return PORT_TYPE_SCALAR; +} +String VisualShaderNodeScalarInterp::get_input_port_name(int p_port) const { + if (p_port == 0) { + return "a"; + } else if (p_port == 1) { + return "b"; + } else { + return "c"; + } +} + +int VisualShaderNodeScalarInterp::get_output_port_count() const { + return 1; +} +VisualShaderNodeScalarInterp::PortType VisualShaderNodeScalarInterp::get_output_port_type(int p_port) const { + return PORT_TYPE_SCALAR; +} +String VisualShaderNodeScalarInterp::get_output_port_name(int p_port) const { + return "mix"; +} + +String VisualShaderNodeScalarInterp::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const { + return "\t" + p_output_vars[0] + " = mix( " + p_input_vars[0] + " , " + p_input_vars[1] + " , " + p_input_vars[2] + " );\n"; +} + +VisualShaderNodeScalarInterp::VisualShaderNodeScalarInterp() { + set_input_port_default_value(0, 0.0); + set_input_port_default_value(1, 0.0); + set_input_port_default_value(2, 0.0); +} + +////////////// Vector Interp + +String VisualShaderNodeVectorInterp::get_caption() const { + return "VecInterp"; +} + +int VisualShaderNodeVectorInterp::get_input_port_count() const { + return 3; +} +VisualShaderNodeVectorInterp::PortType VisualShaderNodeVectorInterp::get_input_port_type(int p_port) const { + return PORT_TYPE_VECTOR; +} +String VisualShaderNodeVectorInterp::get_input_port_name(int p_port) const { + if (p_port == 0) { + return "a"; + } else if (p_port == 1) { + return "b"; + } else { + return "c"; + } +} + +int VisualShaderNodeVectorInterp::get_output_port_count() const { + return 1; +} +VisualShaderNodeVectorInterp::PortType VisualShaderNodeVectorInterp::get_output_port_type(int p_port) const { + return PORT_TYPE_VECTOR; +} +String VisualShaderNodeVectorInterp::get_output_port_name(int p_port) const { + return "mix"; +} + +String VisualShaderNodeVectorInterp::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const { + return "\t" + p_output_vars[0] + " = mix( " + p_input_vars[0] + " , " + p_input_vars[1] + " , " + p_input_vars[2] + " );\n"; +} + +VisualShaderNodeVectorInterp::VisualShaderNodeVectorInterp() { + set_input_port_default_value(0, Vector3()); + set_input_port_default_value(1, Vector3()); + set_input_port_default_value(2, Vector3()); +} + +////////////// Vector Construct +String VisualShaderNodeVectorConstruct::get_caption() const { + return "VecConstr"; +} + +int VisualShaderNodeVectorConstruct::get_input_port_count() const { + return 3; +} +VisualShaderNodeVectorConstruct::PortType VisualShaderNodeVectorConstruct::get_input_port_type(int p_port) const { + return PORT_TYPE_SCALAR; +} +String VisualShaderNodeVectorConstruct::get_input_port_name(int p_port) const { + if (p_port == 0) { + return "x"; + } else if (p_port == 1) { + return "y"; + } else { + return "z"; + } +} + +int VisualShaderNodeVectorConstruct::get_output_port_count() const { + return 1; +} +VisualShaderNodeVectorConstruct::PortType VisualShaderNodeVectorConstruct::get_output_port_type(int p_port) const { + return PORT_TYPE_VECTOR; +} +String VisualShaderNodeVectorConstruct::get_output_port_name(int p_port) const { + return "vec"; +} + +String VisualShaderNodeVectorConstruct::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const { + return "\t" + p_output_vars[0] + " = vec3( " + p_input_vars[0] + " , " + p_input_vars[1] + " , " + p_input_vars[2] + " );\n"; +} + +VisualShaderNodeVectorConstruct::VisualShaderNodeVectorConstruct() { + + set_input_port_default_value(0, 0.0); + set_input_port_default_value(1, 0.0); + set_input_port_default_value(2, 0.0); +} + +////////////// Transform Construct + +String VisualShaderNodeTransformConstruct::get_caption() const { + return "TransConstr"; +} + +int VisualShaderNodeTransformConstruct::get_input_port_count() const { + return 4; +} +VisualShaderNodeTransformConstruct::PortType VisualShaderNodeTransformConstruct::get_input_port_type(int p_port) const { + return PORT_TYPE_VECTOR; +} +String VisualShaderNodeTransformConstruct::get_input_port_name(int p_port) const { + if (p_port == 0) { + return "x"; + } else if (p_port == 1) { + return "y"; + } else if (p_port == 2) { + return "z"; + } else { + return "origin"; + } +} + +int VisualShaderNodeTransformConstruct::get_output_port_count() const { + return 1; +} +VisualShaderNodeTransformConstruct::PortType VisualShaderNodeTransformConstruct::get_output_port_type(int p_port) const { + return PORT_TYPE_TRANSFORM; +} +String VisualShaderNodeTransformConstruct::get_output_port_name(int p_port) const { + return "xform"; +} + +String VisualShaderNodeTransformConstruct::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const { + return "\t" + p_output_vars[0] + " = mat4( vec4(" + p_input_vars[0] + ", 0.0) , vec4(" + p_input_vars[1] + ", 0.0) , vec4(" + p_input_vars[2] + ",0.0), vec4(" + p_input_vars[3] + ",1.0) );\n"; +} + +VisualShaderNodeTransformConstruct::VisualShaderNodeTransformConstruct() { + + set_input_port_default_value(0, Vector3()); + set_input_port_default_value(1, Vector3()); + set_input_port_default_value(2, Vector3()); + set_input_port_default_value(3, Vector3()); +} + +////////////// Vector Destruct +String VisualShaderNodeVectorDestruct::get_caption() const { + return "VecDestr"; +} + +int VisualShaderNodeVectorDestruct::get_input_port_count() const { + return 1; +} +VisualShaderNodeVectorDestruct::PortType VisualShaderNodeVectorDestruct::get_input_port_type(int p_port) const { + return PORT_TYPE_VECTOR; +} +String VisualShaderNodeVectorDestruct::get_input_port_name(int p_port) const { + return "vec"; +} + +int VisualShaderNodeVectorDestruct::get_output_port_count() const { + return 3; +} +VisualShaderNodeVectorDestruct::PortType VisualShaderNodeVectorDestruct::get_output_port_type(int p_port) const { + return PORT_TYPE_SCALAR; +} +String VisualShaderNodeVectorDestruct::get_output_port_name(int p_port) const { + if (p_port == 0) { + return "x"; + } else if (p_port == 1) { + return "y"; + } else { + return "z"; + } +} + +String VisualShaderNodeVectorDestruct::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const { + String code; + code += "\t" + p_output_vars[0] + " = " + p_input_vars[0] + ".x;\n"; + code += "\t" + p_output_vars[1] + " = " + p_input_vars[0] + ".y;\n"; + code += "\t" + p_output_vars[2] + " = " + p_input_vars[0] + ".z;\n"; + return code; +} + +VisualShaderNodeVectorDestruct::VisualShaderNodeVectorDestruct() { + set_input_port_default_value(0, Vector3()); +} + +////////////// Transform Destruct + +String VisualShaderNodeTransformDestruct::get_caption() const { + return "TransDestr"; +} + +int VisualShaderNodeTransformDestruct::get_input_port_count() const { + return 1; +} +VisualShaderNodeTransformDestruct::PortType VisualShaderNodeTransformDestruct::get_input_port_type(int p_port) const { + return PORT_TYPE_TRANSFORM; +} +String VisualShaderNodeTransformDestruct::get_input_port_name(int p_port) const { + return "xform"; +} + +int VisualShaderNodeTransformDestruct::get_output_port_count() const { + return 4; +} +VisualShaderNodeTransformDestruct::PortType VisualShaderNodeTransformDestruct::get_output_port_type(int p_port) const { + return PORT_TYPE_VECTOR; +} +String VisualShaderNodeTransformDestruct::get_output_port_name(int p_port) const { + if (p_port == 0) { + return "x"; + } else if (p_port == 1) { + return "y"; + } else if (p_port == 2) { + return "z"; + } else { + return "origin"; + } +} + +String VisualShaderNodeTransformDestruct::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const { + String code; + code += "\t" + p_output_vars[0] + " = " + p_input_vars[0] + "[0].xyz;\n"; + code += "\t" + p_output_vars[1] + " = " + p_input_vars[0] + "[1].xyz;\n"; + code += "\t" + p_output_vars[2] + " = " + p_input_vars[0] + "[2].xyz;\n"; + code += "\t" + p_output_vars[3] + " = " + p_input_vars[0] + "[3].xyz;\n"; + return code; +} + +VisualShaderNodeTransformDestruct::VisualShaderNodeTransformDestruct() { + set_input_port_default_value(0, Transform()); +} + +////////////// Scalar Uniform + +String VisualShaderNodeScalarUniform::get_caption() const { + return "ScalarUniform"; +} + +int VisualShaderNodeScalarUniform::get_input_port_count() const { + return 0; +} +VisualShaderNodeScalarUniform::PortType VisualShaderNodeScalarUniform::get_input_port_type(int p_port) const { + return PORT_TYPE_SCALAR; +} +String VisualShaderNodeScalarUniform::get_input_port_name(int p_port) const { + return String(); +} + +int VisualShaderNodeScalarUniform::get_output_port_count() const { + return 1; +} +VisualShaderNodeScalarUniform::PortType VisualShaderNodeScalarUniform::get_output_port_type(int p_port) const { + return PORT_TYPE_SCALAR; +} +String VisualShaderNodeScalarUniform::get_output_port_name(int p_port) const { + return ""; //no output port means the editor will be used as port +} + +String VisualShaderNodeScalarUniform::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { + return "uniform float " + get_uniform_name() + ";\n"; +} +String VisualShaderNodeScalarUniform::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const { + return "\t" + p_output_vars[0] + " = " + get_uniform_name() + ";\n"; +} + +VisualShaderNodeScalarUniform::VisualShaderNodeScalarUniform() { +} + +////////////// Color Uniform + +String VisualShaderNodeColorUniform::get_caption() const { + return "ColorUniform"; +} + +int VisualShaderNodeColorUniform::get_input_port_count() const { + return 0; +} +VisualShaderNodeColorUniform::PortType VisualShaderNodeColorUniform::get_input_port_type(int p_port) const { + return PORT_TYPE_VECTOR; +} +String VisualShaderNodeColorUniform::get_input_port_name(int p_port) const { + return String(); +} + +int VisualShaderNodeColorUniform::get_output_port_count() const { + return 2; +} +VisualShaderNodeColorUniform::PortType VisualShaderNodeColorUniform::get_output_port_type(int p_port) const { + return p_port == 0 ? PORT_TYPE_VECTOR : PORT_TYPE_SCALAR; +} +String VisualShaderNodeColorUniform::get_output_port_name(int p_port) const { + return p_port == 0 ? "color" : "alpha"; //no output port means the editor will be used as port +} + +String VisualShaderNodeColorUniform::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { + + return "uniform vec4 " + get_uniform_name() + " : hint_color;\n"; +} + +String VisualShaderNodeColorUniform::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const { + 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; +} + +VisualShaderNodeColorUniform::VisualShaderNodeColorUniform() { +} + +////////////// Vector Uniform + +String VisualShaderNodeVec3Uniform::get_caption() const { + return "VecUniform"; +} + +int VisualShaderNodeVec3Uniform::get_input_port_count() const { + return 0; +} +VisualShaderNodeVec3Uniform::PortType VisualShaderNodeVec3Uniform::get_input_port_type(int p_port) const { + return PORT_TYPE_VECTOR; +} +String VisualShaderNodeVec3Uniform::get_input_port_name(int p_port) const { + return String(); +} + +int VisualShaderNodeVec3Uniform::get_output_port_count() const { + return 1; +} +VisualShaderNodeVec3Uniform::PortType VisualShaderNodeVec3Uniform::get_output_port_type(int p_port) const { + return PORT_TYPE_VECTOR; +} +String VisualShaderNodeVec3Uniform::get_output_port_name(int p_port) const { + return ""; //no output port means the editor will be used as port +} +String VisualShaderNodeVec3Uniform::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { + return "uniform vec3 " + get_uniform_name() + ";\n"; +} + +String VisualShaderNodeVec3Uniform::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const { + return "\t" + p_output_vars[0] + " = " + get_uniform_name() + ";\n"; +} + +VisualShaderNodeVec3Uniform::VisualShaderNodeVec3Uniform() { +} + +////////////// Transform Uniform + +String VisualShaderNodeTransformUniform::get_caption() const { + return "TransUniform"; +} + +int VisualShaderNodeTransformUniform::get_input_port_count() const { + return 0; +} +VisualShaderNodeTransformUniform::PortType VisualShaderNodeTransformUniform::get_input_port_type(int p_port) const { + return PORT_TYPE_VECTOR; +} +String VisualShaderNodeTransformUniform::get_input_port_name(int p_port) const { + return String(); +} + +int VisualShaderNodeTransformUniform::get_output_port_count() const { + return 1; +} +VisualShaderNodeTransformUniform::PortType VisualShaderNodeTransformUniform::get_output_port_type(int p_port) const { + return PORT_TYPE_TRANSFORM; +} +String VisualShaderNodeTransformUniform::get_output_port_name(int p_port) const { + return ""; //no output port means the editor will be used as port +} +String VisualShaderNodeTransformUniform::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { + return "uniform mat4 " + get_uniform_name() + ";\n"; +} + +String VisualShaderNodeTransformUniform::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const { + return "\t" + p_output_vars[0] + " = " + get_uniform_name() + ";\n"; +} + +VisualShaderNodeTransformUniform::VisualShaderNodeTransformUniform() { +} + +////////////// Texture Uniform + +String VisualShaderNodeTextureUniform::get_caption() const { + return "TexUniform"; +} + +int VisualShaderNodeTextureUniform::get_input_port_count() const { + return 2; +} +VisualShaderNodeTextureUniform::PortType VisualShaderNodeTextureUniform::get_input_port_type(int p_port) const { + return p_port == 0 ? PORT_TYPE_VECTOR : PORT_TYPE_SCALAR; +} +String VisualShaderNodeTextureUniform::get_input_port_name(int p_port) const { + return p_port == 0 ? "uv" : "lod"; +} + +int VisualShaderNodeTextureUniform::get_output_port_count() const { + return 2; +} +VisualShaderNodeTextureUniform::PortType VisualShaderNodeTextureUniform::get_output_port_type(int p_port) const { + return p_port == 0 ? PORT_TYPE_VECTOR : PORT_TYPE_SCALAR; +} +String VisualShaderNodeTextureUniform::get_output_port_name(int p_port) const { + return p_port == 0 ? "rgb" : "alpha"; +} + +String VisualShaderNodeTextureUniform::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { + String code = "uniform sampler2D " + get_uniform_name(); + + switch (texture_type) { + case TYPE_DATA: + if (color_default == COLOR_DEFAULT_BLACK) + code += " : hint_black;\n"; + else + code += ";\n"; + break; + case TYPE_COLOR: + if (color_default == COLOR_DEFAULT_BLACK) + code += " : hint_black_albedo;\n"; + else + code += " : hint_albedo;\n"; + break; + case TYPE_NORMALMAP: code += " : hint_normal;\n"; break; + case TYPE_ANISO: code += " : hint_aniso;\n"; break; + } + + return code; +} + +String VisualShaderNodeTextureUniform::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const { + + String id = get_uniform_name(); + String code = "\t{\n"; + if (p_input_vars[0] == String()) { //none bound, do nothing + + code += "\t\tvec4 n_tex_read = vec4(0.0);\n"; + } else if (p_input_vars[1] == String()) { + //no lod + code += "\t\tvec4 n_tex_read = texture( " + id + " , " + p_input_vars[0] + ".xy );\n"; + } else { + code += "\t\tvec4 n_tex_read = textureLod( " + id + " , " + p_input_vars[0] + ".xy , " + p_input_vars[1] + " );\n"; + } + + code += "\t\t" + p_output_vars[0] + " = n_tex_read.rgb;\n"; + code += "\t\t" + p_output_vars[1] + " = n_tex_read.a;\n"; + code += "\t}\n"; + return code; +} + +void VisualShaderNodeTextureUniform::set_texture_type(TextureType p_type) { + + texture_type = p_type; + emit_changed(); +} + +VisualShaderNodeTextureUniform::TextureType VisualShaderNodeTextureUniform::get_texture_type() const { + return texture_type; +} + +void VisualShaderNodeTextureUniform::set_color_default(ColorDefault p_default) { + color_default = p_default; + emit_changed(); +} +VisualShaderNodeTextureUniform::ColorDefault VisualShaderNodeTextureUniform::get_color_default() const { + return color_default; +} + +Vector<StringName> VisualShaderNodeTextureUniform::get_editable_properties() const { + Vector<StringName> props; + props.push_back("texture_type"); + props.push_back("color_default"); + return props; +} + +void VisualShaderNodeTextureUniform::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_texture_type", "type"), &VisualShaderNodeTextureUniform::set_texture_type); + ClassDB::bind_method(D_METHOD("get_texture_type"), &VisualShaderNodeTextureUniform::get_texture_type); + + ClassDB::bind_method(D_METHOD("set_color_default", "type"), &VisualShaderNodeTextureUniform::set_color_default); + ClassDB::bind_method(D_METHOD("get_color_default"), &VisualShaderNodeTextureUniform::get_color_default); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_type", PROPERTY_HINT_ENUM, "Data,Color,Normalmap,Aniso"), "set_texture_type", "get_texture_type"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "color_default", PROPERTY_HINT_ENUM, "White Default,Black Default"), "set_color_default", "get_color_default"); + + BIND_ENUM_CONSTANT(TYPE_DATA); + BIND_ENUM_CONSTANT(TYPE_COLOR); + BIND_ENUM_CONSTANT(TYPE_NORMALMAP); + BIND_ENUM_CONSTANT(TYPE_ANISO); + + BIND_ENUM_CONSTANT(COLOR_DEFAULT_WHITE); + BIND_ENUM_CONSTANT(COLOR_DEFAULT_BLACK); +} + +VisualShaderNodeTextureUniform::VisualShaderNodeTextureUniform() { + texture_type = TYPE_DATA; + color_default = COLOR_DEFAULT_WHITE; +} + +////////////// CubeMap Uniform + +String VisualShaderNodeCubeMapUniform::get_caption() const { + return "CubeMap"; +} + +int VisualShaderNodeCubeMapUniform::get_input_port_count() const { + return 2; +} +VisualShaderNodeCubeMapUniform::PortType VisualShaderNodeCubeMapUniform::get_input_port_type(int p_port) const { + return p_port == 0 ? PORT_TYPE_VECTOR : PORT_TYPE_SCALAR; +} +String VisualShaderNodeCubeMapUniform::get_input_port_name(int p_port) const { + return p_port == 0 ? "normal" : "lod"; +} + +int VisualShaderNodeCubeMapUniform::get_output_port_count() const { + return 2; +} +VisualShaderNodeCubeMapUniform::PortType VisualShaderNodeCubeMapUniform::get_output_port_type(int p_port) const { + return p_port == 0 ? PORT_TYPE_VECTOR : PORT_TYPE_SCALAR; +} +String VisualShaderNodeCubeMapUniform::get_output_port_name(int p_port) const { + return p_port == 0 ? "rgb" : "alpha"; +} + +String VisualShaderNodeCubeMapUniform::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const { + return String(); +} + +VisualShaderNodeCubeMapUniform::VisualShaderNodeCubeMapUniform() { +} diff --git a/scene/resources/visual_shader_nodes.h b/scene/resources/visual_shader_nodes.h new file mode 100644 index 0000000000..cf46ee3189 --- /dev/null +++ b/scene/resources/visual_shader_nodes.h @@ -0,0 +1,861 @@ +#ifndef VISUAL_SHADER_NODES_H +#define VISUAL_SHADER_NODES_H + +#include "scene/resources/visual_shader.h" + +/// CONSTANTS /// + +class VisualShaderNodeScalarConstant : public VisualShaderNode { + GDCLASS(VisualShaderNodeScalarConstant, VisualShaderNode) + float constant; + +protected: + static void _bind_methods(); + +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + 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) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty + + void set_constant(float p_value); + float get_constant() const; + + virtual Vector<StringName> get_editable_properties() const; + + VisualShaderNodeScalarConstant(); +}; + +class VisualShaderNodeColorConstant : public VisualShaderNode { + GDCLASS(VisualShaderNodeColorConstant, VisualShaderNode) + Color constant; + +protected: + static void _bind_methods(); + +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + 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) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty + + void set_constant(Color p_value); + Color get_constant() const; + + virtual Vector<StringName> get_editable_properties() const; + + VisualShaderNodeColorConstant(); +}; + +class VisualShaderNodeVec3Constant : public VisualShaderNode { + GDCLASS(VisualShaderNodeVec3Constant, VisualShaderNode) + Vector3 constant; + +protected: + static void _bind_methods(); + +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + 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) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty + + void set_constant(Vector3 p_value); + Vector3 get_constant() const; + + virtual Vector<StringName> get_editable_properties() const; + + VisualShaderNodeVec3Constant(); +}; + +class VisualShaderNodeTransformConstant : public VisualShaderNode { + GDCLASS(VisualShaderNodeTransformConstant, VisualShaderNode) + Transform constant; + +protected: + static void _bind_methods(); + +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + 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) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty + + void set_constant(Transform p_value); + Transform get_constant() const; + + virtual Vector<StringName> get_editable_properties() const; + + VisualShaderNodeTransformConstant(); +}; + +////////////////////////////////// + +class VisualShaderNodeTexture : public VisualShaderNode { + GDCLASS(VisualShaderNodeTexture, VisualShaderNode) + Ref<Texture> texture; + +public: + enum Source { + SOURCE_TEXTURE, + SOURCE_SCREEN, + SOURCE_2D_TEXTURE, + SOURCE_2D_NORMAL + }; + + enum TextureType { + TYPE_DATA, + TYPE_COLOR, + TYPE_NORMALMAP + }; + +private: + Source source; + TextureType texture_type; + +protected: + static void _bind_methods(); + +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + virtual Vector<VisualShader::DefaultTextureParam> get_default_texture_parameters(VisualShader::Type p_type, int p_id) const; + virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const; + 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) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty + + void set_source(Source p_source); + Source get_source() const; + + void set_texture(Ref<Texture> p_value); + Ref<Texture> get_texture() const; + + void set_texture_type(TextureType p_type); + TextureType get_texture_type() const; + + virtual Vector<StringName> get_editable_properties() const; + + virtual String get_warning(Shader::Mode p_mode, VisualShader::Type p_type) const; + + VisualShaderNodeTexture(); +}; + +VARIANT_ENUM_CAST(VisualShaderNodeTexture::TextureType) +VARIANT_ENUM_CAST(VisualShaderNodeTexture::Source) + +////////////////////////////////// + +class VisualShaderNodeCubeMap : public VisualShaderNode { + GDCLASS(VisualShaderNodeCubeMap, VisualShaderNode) + Ref<CubeMap> cube_map; + +public: + enum TextureType { + TYPE_DATA, + TYPE_COLOR, + TYPE_NORMALMAP + }; + +private: + TextureType texture_type; + +protected: + static void _bind_methods(); + +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + virtual Vector<VisualShader::DefaultTextureParam> get_default_texture_parameters(VisualShader::Type p_type, int p_id) const; + virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const; + 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) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty + + void set_cube_map(Ref<CubeMap> p_value); + Ref<CubeMap> get_cube_map() const; + + void set_texture_type(TextureType p_type); + TextureType get_texture_type() const; + + virtual Vector<StringName> get_editable_properties() const; + + VisualShaderNodeCubeMap(); +}; + +VARIANT_ENUM_CAST(VisualShaderNodeCubeMap::TextureType) +/////////////////////////////////////// + +class VisualShaderNodeScalarOp : public VisualShaderNode { + GDCLASS(VisualShaderNodeScalarOp, VisualShaderNode) + +public: + enum Operator { + OP_ADD, + OP_SUB, + OP_MUL, + OP_DIV, + OP_MOD, + OP_POW, + OP_MAX, + OP_MIN, + OP_ATAN2 + }; + +protected: + Operator op; + + static void _bind_methods(); + +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + 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) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty + + void set_operator(Operator p_op); + Operator get_operator() const; + + virtual Vector<StringName> get_editable_properties() const; + + VisualShaderNodeScalarOp(); +}; + +VARIANT_ENUM_CAST(VisualShaderNodeScalarOp::Operator) + +class VisualShaderNodeVectorOp : public VisualShaderNode { + GDCLASS(VisualShaderNodeVectorOp, VisualShaderNode) + +public: + enum Operator { + OP_ADD, + OP_SUB, + OP_MUL, + OP_DIV, + OP_MOD, + OP_POW, + OP_MAX, + OP_MIN, + OP_CROSS + + }; + +protected: + Operator op; + + static void _bind_methods(); + +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + 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) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty + + void set_operator(Operator p_op); + Operator get_operator() const; + + virtual Vector<StringName> get_editable_properties() const; + + VisualShaderNodeVectorOp(); +}; + +VARIANT_ENUM_CAST(VisualShaderNodeVectorOp::Operator) + +class VisualShaderNodeColorOp : public VisualShaderNode { + GDCLASS(VisualShaderNodeColorOp, VisualShaderNode) + +public: + enum Operator { + OP_SCREEN, + OP_DIFFERENCE, + OP_DARKEN, + OP_LIGHTEN, + OP_OVERLAY, + OP_DODGE, + OP_BURN, + OP_SOFT_LIGHT, + OP_HARD_LIGHT, + }; + +protected: + Operator op; + + static void _bind_methods(); + +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + 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) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty + + void set_operator(Operator p_op); + Operator get_operator() const; + + virtual Vector<StringName> get_editable_properties() const; + + VisualShaderNodeColorOp(); +}; + +VARIANT_ENUM_CAST(VisualShaderNodeColorOp::Operator) + +class VisualShaderNodeTransformMult : public VisualShaderNode { + GDCLASS(VisualShaderNodeTransformMult, VisualShaderNode) + +public: + enum Operator { + OP_AxB, + OP_BxA, + }; + +protected: + Operator op; + + static void _bind_methods(); + +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + 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) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty + + void set_operator(Operator p_op); + Operator get_operator() const; + + virtual Vector<StringName> get_editable_properties() const; + + VisualShaderNodeTransformMult(); +}; + +VARIANT_ENUM_CAST(VisualShaderNodeTransformMult::Operator) + +class VisualShaderNodeTransformVecMult : public VisualShaderNode { + GDCLASS(VisualShaderNodeTransformVecMult, VisualShaderNode) + +public: + enum Operator { + OP_AxB, + OP_BxA, + OP_3x3_AxB, + OP_3x3_BxA, + }; + +protected: + Operator op; + + static void _bind_methods(); + +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + 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) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty + + void set_operator(Operator p_op); + Operator get_operator() const; + + virtual Vector<StringName> get_editable_properties() const; + + VisualShaderNodeTransformVecMult(); +}; + +VARIANT_ENUM_CAST(VisualShaderNodeTransformVecMult::Operator) + +/////////////////////////////////////// + +class VisualShaderNodeScalarFunc : public VisualShaderNode { + GDCLASS(VisualShaderNodeScalarFunc, VisualShaderNode) + +public: + enum Function { + FUNC_SIN, + FUNC_COS, + FUNC_TAN, + FUNC_ASIN, + FUNC_ACOS, + FUNC_ATAN, + FUNC_SINH, + FUNC_COSH, + FUNC_TANH, + FUNC_LOG, + FUNC_EXP, + FUNC_SQRT, + FUNC_ABS, + FUNC_SIGN, + FUNC_FLOOR, + FUNC_ROUND, + FUNC_CEIL, + FUNC_FRAC, + FUNC_SATURATE, + FUNC_NEGATE, + }; + +protected: + Function func; + + static void _bind_methods(); + +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + 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) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty + + void set_function(Function p_func); + Function get_function() const; + + virtual Vector<StringName> get_editable_properties() const; + + VisualShaderNodeScalarFunc(); +}; + +VARIANT_ENUM_CAST(VisualShaderNodeScalarFunc::Function) + +/////////////////////////////////////// + +class VisualShaderNodeVectorFunc : public VisualShaderNode { + GDCLASS(VisualShaderNodeVectorFunc, VisualShaderNode) + +public: + enum Function { + FUNC_NORMALIZE, + FUNC_SATURATE, + FUNC_NEGATE, + FUNC_RECIPROCAL, + FUNC_RGB2HSV, + FUNC_HSV2RGB, + }; + +protected: + Function func; + + static void _bind_methods(); + +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + 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) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty + + void set_function(Function p_func); + Function get_function() const; + + virtual Vector<StringName> get_editable_properties() const; + + VisualShaderNodeVectorFunc(); +}; + +VARIANT_ENUM_CAST(VisualShaderNodeVectorFunc::Function) + +/////////////////////////////////////// + +class VisualShaderNodeDotProduct : public VisualShaderNode { + GDCLASS(VisualShaderNodeDotProduct, VisualShaderNode) + +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + 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) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty + + VisualShaderNodeDotProduct(); +}; + +/////////////////////////////////////// + +class VisualShaderNodeVectorLen : public VisualShaderNode { + GDCLASS(VisualShaderNodeVectorLen, VisualShaderNode) + +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + 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) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty + + VisualShaderNodeVectorLen(); +}; + +/////////////////////////////////////// + +class VisualShaderNodeScalarInterp : public VisualShaderNode { + GDCLASS(VisualShaderNodeScalarInterp, VisualShaderNode) + +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + 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) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty + + VisualShaderNodeScalarInterp(); +}; + +/////////////////////////////////////// + +class VisualShaderNodeVectorInterp : public VisualShaderNode { + GDCLASS(VisualShaderNodeVectorInterp, VisualShaderNode) + +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + 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) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty + + VisualShaderNodeVectorInterp(); +}; + +/////////////////////////////////////// + +class VisualShaderNodeVectorConstruct : public VisualShaderNode { + GDCLASS(VisualShaderNodeVectorConstruct, VisualShaderNode) + +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + 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) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty + + VisualShaderNodeVectorConstruct(); +}; + +/////////////////////////////////////// + +class VisualShaderNodeTransformConstruct : public VisualShaderNode { + GDCLASS(VisualShaderNodeTransformConstruct, VisualShaderNode) + +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + 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) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty + + VisualShaderNodeTransformConstruct(); +}; + +/////////////////////////////////////// + +class VisualShaderNodeVectorDestruct : public VisualShaderNode { + GDCLASS(VisualShaderNodeVectorDestruct, VisualShaderNode) + +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + 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) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty + + VisualShaderNodeVectorDestruct(); +}; + +/////////////////////////////////////// + +class VisualShaderNodeTransformDestruct : public VisualShaderNode { + GDCLASS(VisualShaderNodeTransformDestruct, VisualShaderNode) + +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + 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) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty + + VisualShaderNodeTransformDestruct(); +}; + +/////////////////////////////////////// + +class VisualShaderNodeScalarUniform : public VisualShaderNodeUniform { + GDCLASS(VisualShaderNodeScalarUniform, VisualShaderNodeUniform) + +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const; + 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) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty + + VisualShaderNodeScalarUniform(); +}; + +class VisualShaderNodeColorUniform : public VisualShaderNodeUniform { + GDCLASS(VisualShaderNodeColorUniform, VisualShaderNodeUniform) + +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const; + 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) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty + + VisualShaderNodeColorUniform(); +}; + +class VisualShaderNodeVec3Uniform : public VisualShaderNodeUniform { + GDCLASS(VisualShaderNodeVec3Uniform, VisualShaderNodeUniform) + +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const; + 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) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty + + VisualShaderNodeVec3Uniform(); +}; + +class VisualShaderNodeTransformUniform : public VisualShaderNodeUniform { + GDCLASS(VisualShaderNodeTransformUniform, VisualShaderNodeUniform) + +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const; + 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) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty + + VisualShaderNodeTransformUniform(); +}; + +////////////////////////////////// + +class VisualShaderNodeTextureUniform : public VisualShaderNodeUniform { + GDCLASS(VisualShaderNodeTextureUniform, VisualShaderNodeUniform) +public: + enum TextureType { + TYPE_DATA, + TYPE_COLOR, + TYPE_NORMALMAP, + TYPE_ANISO, + }; + + enum ColorDefault { + COLOR_DEFAULT_WHITE, + COLOR_DEFAULT_BLACK + }; + +private: + TextureType texture_type; + ColorDefault color_default; + +protected: + static void _bind_methods(); + +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const; + 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) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty + + Vector<StringName> get_editable_properties() const; + + void set_texture_type(TextureType p_type); + TextureType get_texture_type() const; + + void set_color_default(ColorDefault p_default); + ColorDefault get_color_default() const; + + VisualShaderNodeTextureUniform(); +}; + +VARIANT_ENUM_CAST(VisualShaderNodeTextureUniform::TextureType) +VARIANT_ENUM_CAST(VisualShaderNodeTextureUniform::ColorDefault) + +////////////////////////////////// + +class VisualShaderNodeCubeMapUniform : public VisualShaderNode { + GDCLASS(VisualShaderNodeCubeMapUniform, VisualShaderNode) + +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + 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) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty + + VisualShaderNodeCubeMapUniform(); +}; + +#endif // VISUAL_SHADER_NODES_H diff --git a/servers/visual/shader_language.cpp b/servers/visual/shader_language.cpp index 2069e64c43..346b04f070 100644 --- a/servers/visual/shader_language.cpp +++ b/servers/visual/shader_language.cpp @@ -3583,7 +3583,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui return OK; } -Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_functions, const Set<String> &p_render_modes, const Set<String> &p_shader_types) { +Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_functions, const Vector<StringName> &p_render_modes, const Set<String> &p_shader_types) { Token tk = _get_token(); @@ -3642,7 +3642,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct return ERR_PARSE_ERROR; } - if (!p_render_modes.has(mode)) { + if (p_render_modes.find(mode) == -1) { _set_error("Invalid render mode: '" + String(mode) + "'"); return ERR_PARSE_ERROR; } @@ -4097,7 +4097,7 @@ String ShaderLanguage::get_shader_type(const String &p_code) { return String(); } -Error ShaderLanguage::compile(const String &p_code, const Map<StringName, FunctionInfo> &p_functions, const Set<String> &p_render_modes, const Set<String> &p_shader_types) { +Error ShaderLanguage::compile(const String &p_code, const Map<StringName, FunctionInfo> &p_functions, const Vector<StringName> &p_render_modes, const Set<String> &p_shader_types) { clear(); @@ -4114,7 +4114,7 @@ Error ShaderLanguage::compile(const String &p_code, const Map<StringName, Functi return OK; } -Error ShaderLanguage::complete(const String &p_code, const Map<StringName, FunctionInfo> &p_functions, const Set<String> &p_render_modes, const Set<String> &p_shader_types, List<String> *r_options, String &r_call_hint) { +Error ShaderLanguage::complete(const String &p_code, const Map<StringName, FunctionInfo> &p_functions, const Vector<StringName> &p_render_modes, const Set<String> &p_shader_types, List<String> *r_options, String &r_call_hint) { clear(); @@ -4134,9 +4134,9 @@ Error ShaderLanguage::complete(const String &p_code, const Map<StringName, Funct return ERR_PARSE_ERROR; } break; case COMPLETION_RENDER_MODE: { - for (const Set<String>::Element *E = p_render_modes.front(); E; E = E->next()) { + for (int i = 0; i < p_render_modes.size(); i++) { - r_options->push_back(E->get()); + r_options->push_back(p_render_modes[i]); } return OK; diff --git a/servers/visual/shader_language.h b/servers/visual/shader_language.h index 720511e18d..9b84c5f195 100644 --- a/servers/visual/shader_language.h +++ b/servers/visual/shader_language.h @@ -650,7 +650,7 @@ private: Error _parse_block(BlockNode *p_block, const Map<StringName, BuiltInInfo> &p_builtin_types, bool p_just_one = false, bool p_can_break = false, bool p_can_continue = false); - Error _parse_shader(const Map<StringName, FunctionInfo> &p_functions, const Set<String> &p_render_modes, const Set<String> &p_shader_types); + Error _parse_shader(const Map<StringName, FunctionInfo> &p_functions, const Vector<StringName> &p_render_modes, const Set<String> &p_shader_types); public: //static void get_keyword_list(ShaderType p_type,List<String> *p_keywords); @@ -658,8 +658,8 @@ public: void clear(); static String get_shader_type(const String &p_code); - Error compile(const String &p_code, const Map<StringName, FunctionInfo> &p_functions, const Set<String> &p_render_modes, const Set<String> &p_shader_types); - Error complete(const String &p_code, const Map<StringName, FunctionInfo> &p_functions, const Set<String> &p_render_modes, const Set<String> &p_shader_types, List<String> *r_options, String &r_call_hint); + Error compile(const String &p_code, const Map<StringName, FunctionInfo> &p_functions, const Vector<StringName> &p_render_modes, const Set<String> &p_shader_types); + Error complete(const String &p_code, const Map<StringName, FunctionInfo> &p_functions, const Vector<StringName> &p_render_modes, const Set<String> &p_shader_types, List<String> *r_options, String &r_call_hint); String get_error_text(); int get_error_line(); diff --git a/servers/visual/shader_types.cpp b/servers/visual/shader_types.cpp index a92e1b06d2..92786ea740 100644 --- a/servers/visual/shader_types.cpp +++ b/servers/visual/shader_types.cpp @@ -35,7 +35,7 @@ const Map<StringName, ShaderLanguage::FunctionInfo> &ShaderTypes::get_functions( return shader_modes[p_mode].functions; } -const Set<String> &ShaderTypes::get_modes(VS::ShaderMode p_mode) { +const Vector<StringName> &ShaderTypes::get_modes(VS::ShaderMode p_mode) { return shader_modes[p_mode].modes; } @@ -140,43 +140,44 @@ ShaderTypes::ShaderTypes() { shader_modes[VS::SHADER_SPATIAL].functions["light"].can_discard = true; - shader_modes[VS::SHADER_SPATIAL].modes.insert("blend_mix"); - shader_modes[VS::SHADER_SPATIAL].modes.insert("blend_add"); - shader_modes[VS::SHADER_SPATIAL].modes.insert("blend_sub"); - shader_modes[VS::SHADER_SPATIAL].modes.insert("blend_mul"); + //order used puts first enum mode (default) first + shader_modes[VS::SHADER_SPATIAL].modes.push_back("blend_mix"); + shader_modes[VS::SHADER_SPATIAL].modes.push_back("blend_add"); + shader_modes[VS::SHADER_SPATIAL].modes.push_back("blend_sub"); + shader_modes[VS::SHADER_SPATIAL].modes.push_back("blend_mul"); - shader_modes[VS::SHADER_SPATIAL].modes.insert("depth_draw_opaque"); - shader_modes[VS::SHADER_SPATIAL].modes.insert("depth_draw_always"); - shader_modes[VS::SHADER_SPATIAL].modes.insert("depth_draw_never"); - shader_modes[VS::SHADER_SPATIAL].modes.insert("depth_draw_alpha_prepass"); + shader_modes[VS::SHADER_SPATIAL].modes.push_back("depth_draw_opaque"); + shader_modes[VS::SHADER_SPATIAL].modes.push_back("depth_draw_always"); + shader_modes[VS::SHADER_SPATIAL].modes.push_back("depth_draw_never"); + shader_modes[VS::SHADER_SPATIAL].modes.push_back("depth_draw_alpha_prepass"); - shader_modes[VS::SHADER_SPATIAL].modes.insert("depth_test_disable"); + shader_modes[VS::SHADER_SPATIAL].modes.push_back("depth_test_disable"); - shader_modes[VS::SHADER_SPATIAL].modes.insert("cull_front"); - shader_modes[VS::SHADER_SPATIAL].modes.insert("cull_back"); - shader_modes[VS::SHADER_SPATIAL].modes.insert("cull_disabled"); + shader_modes[VS::SHADER_SPATIAL].modes.push_back("cull_back"); + shader_modes[VS::SHADER_SPATIAL].modes.push_back("cull_front"); + shader_modes[VS::SHADER_SPATIAL].modes.push_back("cull_disabled"); - shader_modes[VS::SHADER_SPATIAL].modes.insert("unshaded"); + shader_modes[VS::SHADER_SPATIAL].modes.push_back("unshaded"); - shader_modes[VS::SHADER_SPATIAL].modes.insert("diffuse_lambert"); - shader_modes[VS::SHADER_SPATIAL].modes.insert("diffuse_lambert_wrap"); - shader_modes[VS::SHADER_SPATIAL].modes.insert("diffuse_oren_nayar"); - shader_modes[VS::SHADER_SPATIAL].modes.insert("diffuse_burley"); - shader_modes[VS::SHADER_SPATIAL].modes.insert("diffuse_toon"); + shader_modes[VS::SHADER_SPATIAL].modes.push_back("diffuse_lambert"); + shader_modes[VS::SHADER_SPATIAL].modes.push_back("diffuse_lambert_wrap"); + shader_modes[VS::SHADER_SPATIAL].modes.push_back("diffuse_oren_nayar"); + shader_modes[VS::SHADER_SPATIAL].modes.push_back("diffuse_burley"); + shader_modes[VS::SHADER_SPATIAL].modes.push_back("diffuse_toon"); - shader_modes[VS::SHADER_SPATIAL].modes.insert("specular_schlick_ggx"); - shader_modes[VS::SHADER_SPATIAL].modes.insert("specular_blinn"); - shader_modes[VS::SHADER_SPATIAL].modes.insert("specular_phong"); - shader_modes[VS::SHADER_SPATIAL].modes.insert("specular_toon"); - shader_modes[VS::SHADER_SPATIAL].modes.insert("specular_disabled"); + shader_modes[VS::SHADER_SPATIAL].modes.push_back("specular_schlick_ggx"); + shader_modes[VS::SHADER_SPATIAL].modes.push_back("specular_blinn"); + shader_modes[VS::SHADER_SPATIAL].modes.push_back("specular_phong"); + shader_modes[VS::SHADER_SPATIAL].modes.push_back("specular_toon"); + shader_modes[VS::SHADER_SPATIAL].modes.push_back("specular_disabled"); - shader_modes[VS::SHADER_SPATIAL].modes.insert("skip_vertex_transform"); - shader_modes[VS::SHADER_SPATIAL].modes.insert("world_vertex_coords"); - shader_modes[VS::SHADER_SPATIAL].modes.insert("ensure_correct_normals"); + shader_modes[VS::SHADER_SPATIAL].modes.push_back("skip_vertex_transform"); + shader_modes[VS::SHADER_SPATIAL].modes.push_back("world_vertex_coords"); + shader_modes[VS::SHADER_SPATIAL].modes.push_back("ensure_correct_normals"); - shader_modes[VS::SHADER_SPATIAL].modes.insert("shadows_disabled"); + shader_modes[VS::SHADER_SPATIAL].modes.push_back("shadows_disabled"); - shader_modes[VS::SHADER_SPATIAL].modes.insert("vertex_lighting"); + shader_modes[VS::SHADER_SPATIAL].modes.push_back("vertex_lighting"); /************ CANVAS ITEM **************************/ @@ -227,17 +228,17 @@ ShaderTypes::ShaderTypes() { shader_modes[VS::SHADER_CANVAS_ITEM].functions["light"].built_ins["TIME"] = constt(ShaderLanguage::TYPE_FLOAT); shader_modes[VS::SHADER_CANVAS_ITEM].functions["light"].can_discard = true; - shader_modes[VS::SHADER_CANVAS_ITEM].modes.insert("skip_vertex_transform"); + shader_modes[VS::SHADER_CANVAS_ITEM].modes.push_back("skip_vertex_transform"); - shader_modes[VS::SHADER_CANVAS_ITEM].modes.insert("blend_mix"); - shader_modes[VS::SHADER_CANVAS_ITEM].modes.insert("blend_add"); - shader_modes[VS::SHADER_CANVAS_ITEM].modes.insert("blend_sub"); - shader_modes[VS::SHADER_CANVAS_ITEM].modes.insert("blend_mul"); - shader_modes[VS::SHADER_CANVAS_ITEM].modes.insert("blend_premul_alpha"); - shader_modes[VS::SHADER_CANVAS_ITEM].modes.insert("blend_disabled"); + shader_modes[VS::SHADER_CANVAS_ITEM].modes.push_back("blend_mix"); + shader_modes[VS::SHADER_CANVAS_ITEM].modes.push_back("blend_add"); + shader_modes[VS::SHADER_CANVAS_ITEM].modes.push_back("blend_sub"); + shader_modes[VS::SHADER_CANVAS_ITEM].modes.push_back("blend_mul"); + shader_modes[VS::SHADER_CANVAS_ITEM].modes.push_back("blend_premul_alpha"); + shader_modes[VS::SHADER_CANVAS_ITEM].modes.push_back("blend_disabled"); - shader_modes[VS::SHADER_CANVAS_ITEM].modes.insert("unshaded"); - shader_modes[VS::SHADER_CANVAS_ITEM].modes.insert("light_only"); + shader_modes[VS::SHADER_CANVAS_ITEM].modes.push_back("unshaded"); + shader_modes[VS::SHADER_CANVAS_ITEM].modes.push_back("light_only"); /************ PARTICLES **************************/ @@ -257,9 +258,9 @@ ShaderTypes::ShaderTypes() { shader_modes[VS::SHADER_PARTICLES].functions["vertex"].built_ins["RANDOM_SEED"] = constt(ShaderLanguage::TYPE_UINT); shader_modes[VS::SHADER_PARTICLES].functions["vertex"].can_discard = false; - shader_modes[VS::SHADER_PARTICLES].modes.insert("disable_force"); - shader_modes[VS::SHADER_PARTICLES].modes.insert("disable_velocity"); - shader_modes[VS::SHADER_PARTICLES].modes.insert("keep_data"); + shader_modes[VS::SHADER_PARTICLES].modes.push_back("disable_force"); + shader_modes[VS::SHADER_PARTICLES].modes.push_back("disable_velocity"); + shader_modes[VS::SHADER_PARTICLES].modes.push_back("keep_data"); shader_types.insert("spatial"); shader_types.insert("canvas_item"); diff --git a/servers/visual/shader_types.h b/servers/visual/shader_types.h index 1f43ff9c92..0680ec8242 100644 --- a/servers/visual/shader_types.h +++ b/servers/visual/shader_types.h @@ -31,14 +31,16 @@ #ifndef SHADERTYPES_H #define SHADERTYPES_H +#include "ordered_hash_map.h" #include "servers/visual_server.h" #include "shader_language.h" + class ShaderTypes { struct Type { Map<StringName, ShaderLanguage::FunctionInfo> functions; - Set<String> modes; + Vector<StringName> modes; }; Map<VS::ShaderMode, Type> shader_modes; @@ -51,7 +53,7 @@ public: static ShaderTypes *get_singleton() { return singleton; } const Map<StringName, ShaderLanguage::FunctionInfo> &get_functions(VS::ShaderMode p_mode); - const Set<String> &get_modes(VS::ShaderMode p_mode); + const Vector<StringName> &get_modes(VS::ShaderMode p_mode); const Set<String> &get_types(); ShaderTypes(); |