diff options
Diffstat (limited to 'scene')
| -rw-r--r-- | scene/animation/animation_node_state_machine.cpp | 3 | ||||
| -rw-r--r-- | scene/gui/control.cpp | 57 | ||||
| -rw-r--r-- | scene/gui/line_edit.cpp | 1 | ||||
| -rw-r--r-- | scene/gui/text_edit.cpp | 87 | ||||
| -rw-r--r-- | scene/gui/text_edit.h | 14 | ||||
| -rw-r--r-- | scene/gui/tree.cpp | 4 | ||||
| -rw-r--r-- | scene/register_scene_types.cpp | 2 | ||||
| -rw-r--r-- | scene/resources/default_theme/default_theme.cpp | 1 | ||||
| -rw-r--r-- | scene/resources/visual_shader.cpp | 661 | ||||
| -rw-r--r-- | scene/resources/visual_shader.h | 92 |
10 files changed, 882 insertions, 40 deletions
diff --git a/scene/animation/animation_node_state_machine.cpp b/scene/animation/animation_node_state_machine.cpp index 1e3470cd90..60f8806b25 100644 --- a/scene/animation/animation_node_state_machine.cpp +++ b/scene/animation/animation_node_state_machine.cpp @@ -314,8 +314,9 @@ float AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *sm, if (start_request_travel) { if (!playing) { + String node_name = start_request; start_request = StringName(); - ERR_EXPLAIN("Can't travel to '" + String(start_request) + "' if state machine is not active."); + ERR_EXPLAIN("Can't travel to '" + node_name + "' if state machine is not playing."); ERR_FAIL_V(0); } diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index 11fb92a946..76275c2420 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -289,7 +289,7 @@ void Control::_update_minimum_size() { Size2 minsize = get_combined_minimum_size(); if (minsize.x > data.size_cache.x || minsize.y > data.size_cache.y) { - _size_changed(); + set_size(data.size_cache); } data.updating_last_minimum_size = false; @@ -1840,53 +1840,72 @@ Rect2 Control::get_anchorable_rect() const { void Control::add_icon_override(const StringName &p_name, const Ref<Texture> &p_icon) { - ERR_FAIL_COND(p_icon.is_null()); if (data.icon_override.has(p_name)) { data.icon_override[p_name]->disconnect("changed", this, "_override_changed"); } - data.icon_override[p_name] = p_icon; - if (data.icon_override[p_name].is_valid()) { - data.icon_override[p_name]->connect("changed", this, "_override_changed", Vector<Variant>(), CONNECT_REFERENCE_COUNTED); + + // clear if "null" is passed instead of a icon + if (p_icon.is_null()) { + data.icon_override.erase(p_name); + } else { + data.icon_override[p_name] = p_icon; + if (data.icon_override[p_name].is_valid()) { + data.icon_override[p_name]->connect("changed", this, "_override_changed", Vector<Variant>(), CONNECT_REFERENCE_COUNTED); + } } notification(NOTIFICATION_THEME_CHANGED); } void Control::add_shader_override(const StringName &p_name, const Ref<Shader> &p_shader) { - ERR_FAIL_COND(p_shader.is_null()); + if (data.shader_override.has(p_name)) { data.shader_override[p_name]->disconnect("changed", this, "_override_changed"); } - data.shader_override[p_name] = p_shader; - if (data.shader_override[p_name].is_valid()) { - data.shader_override[p_name]->connect("changed", this, "_override_changed", Vector<Variant>(), CONNECT_REFERENCE_COUNTED); + + // clear if "null" is passed instead of a shader + if (p_shader.is_null()) { + data.shader_override.erase(p_name); + } else { + data.shader_override[p_name] = p_shader; + if (data.shader_override[p_name].is_valid()) { + data.shader_override[p_name]->connect("changed", this, "_override_changed", Vector<Variant>(), CONNECT_REFERENCE_COUNTED); + } } notification(NOTIFICATION_THEME_CHANGED); } void Control::add_style_override(const StringName &p_name, const Ref<StyleBox> &p_style) { - ERR_FAIL_COND(p_style.is_null()); if (data.style_override.has(p_name)) { data.style_override[p_name]->disconnect("changed", this, "_override_changed"); } - data.style_override[p_name] = p_style; - if (data.style_override[p_name].is_valid()) { - data.style_override[p_name]->connect("changed", this, "_override_changed", Vector<Variant>(), CONNECT_REFERENCE_COUNTED); - } + // clear if "null" is passed instead of a style + if (p_style.is_null()) { + data.style_override.erase(p_name); + } else { + data.style_override[p_name] = p_style; + if (data.style_override[p_name].is_valid()) { + data.style_override[p_name]->connect("changed", this, "_override_changed", Vector<Variant>(), CONNECT_REFERENCE_COUNTED); + } + } notification(NOTIFICATION_THEME_CHANGED); } void Control::add_font_override(const StringName &p_name, const Ref<Font> &p_font) { - ERR_FAIL_COND(p_font.is_null()); if (data.font_override.has(p_name)) { data.font_override[p_name]->disconnect("changed", this, "_override_changed"); } - data.font_override[p_name] = p_font; - if (data.font_override[p_name].is_valid()) { - data.font_override[p_name]->connect("changed", this, "_override_changed", Vector<Variant>(), CONNECT_REFERENCE_COUNTED); - } + // clear if "null" is passed instead of a font + if (p_font.is_null()) { + data.font_override.erase(p_name); + } else { + data.font_override[p_name] = p_font; + if (data.font_override[p_name].is_valid()) { + data.font_override[p_name]->connect("changed", this, "_override_changed", Vector<Variant>(), CONNECT_REFERENCE_COUNTED); + } + } notification(NOTIFICATION_THEME_CHANGED); } void Control::add_color_override(const StringName &p_name, const Color &p_color) { diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index 2d18a80833..ed74851755 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -1656,6 +1656,7 @@ LineEdit::LineEdit() { context_menu_enabled = true; menu = memnew(PopupMenu); add_child(menu); + editable = false; // initialise to opposite first, so we get past the early-out in set_editable set_editable(true); menu->connect("id_pressed", this, "menu_option"); expand_to_text_length = false; diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 8acd28a2f4..e039558abc 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -292,6 +292,7 @@ void TextEdit::Text::insert(int p_at, const String &p_text) { line.marked = false; line.safe = false; line.breakpoint = false; + line.bookmark = false; line.hidden = false; line.width_cache = -1; line.wrap_amount_cache = -1; @@ -346,7 +347,7 @@ void TextEdit::_update_scrollbars() { if (line_numbers) total_width += cache.line_number_w; - if (draw_breakpoint_gutter) { + if (draw_breakpoint_gutter || draw_bookmark_gutter) { total_width += cache.breakpoint_gutter_width; } @@ -605,7 +606,7 @@ void TextEdit::_notification(int p_what) { draw_caret = false; } - if (draw_breakpoint_gutter) { + if (draw_breakpoint_gutter || draw_bookmark_gutter) { breakpoint_gutter_width = (get_row_height() * 55) / 100; cache.breakpoint_gutter_width = breakpoint_gutter_width; } else { @@ -954,6 +955,16 @@ void TextEdit::_notification(int p_what) { #endif } + // draw bookmark marker + if (text.is_bookmark(line)) { + if (draw_bookmark_gutter) { + int vertical_gap = (get_row_height() * 40) / 100; + int horizontal_gap = (cache.breakpoint_gutter_width * 30) / 100; + int marker_radius = get_row_height() - (vertical_gap * 2); + VisualServer::get_singleton()->canvas_item_add_circle(ci, Point2(cache.style_normal->get_margin(MARGIN_LEFT) + horizontal_gap - 2 + marker_radius / 2, ofs_y + vertical_gap + marker_radius / 2), marker_radius, Color(cache.bookmark_color.r, cache.bookmark_color.g, cache.bookmark_color.b)); + } + } + // draw breakpoint marker if (text.is_breakpoint(line)) { if (draw_breakpoint_gutter) { @@ -1095,7 +1106,7 @@ void TextEdit::_notification(int p_what) { if (line == cursor.line && cursor_wrap_index == line_wrap_index && highlight_current_line) { // draw the wrap indent offset highlight if (line_wrap_index != 0 && j == 0) { - VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(char_ofs + char_margin - indent_px, ofs_y, (char_ofs + char_margin), get_row_height()), cache.current_line_color); + VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(char_ofs + char_margin - indent_px, ofs_y, indent_px, get_row_height()), cache.current_line_color); } // if its the last char draw to end of the line if (j == str.length() - 1) { @@ -4303,17 +4314,22 @@ Control::CursorShape TextEdit::get_cursor_shape(const Point2 &p_pos) const { void TextEdit::set_text(String p_text) { setting_text = true; - _clear(); - _insert_text_at_cursor(p_text); - clear_undo_history(); - cursor.column = 0; - cursor.line = 0; - cursor.x_ofs = 0; - cursor.line_ofs = 0; - cursor.wrap_ofs = 0; - cursor.last_fit_x = 0; - cursor_set_line(0); - cursor_set_column(0); + if (!undo_enabled) { + _clear(); + _insert_text_at_cursor(p_text); + } + + if (undo_enabled) { + cursor_set_line(0); + cursor_set_column(0); + + begin_complex_operation(); + _remove_text(0, 0, MAX(0, get_line_count() - 1), MAX(get_line(MAX(get_line_count() - 1, 0)).size() - 1, 0)); + _insert_text_at_cursor(p_text); + end_complex_operation(); + selection.active = false; + } + update(); setting_text = false; @@ -4499,6 +4515,7 @@ void TextEdit::_update_caches() { cache.mark_color = get_color("mark_color"); cache.current_line_color = get_color("current_line_color"); cache.line_length_guideline_color = get_color("line_length_guideline_color"); + cache.bookmark_color = get_color("bookmark_color"); cache.breakpoint_color = get_color("breakpoint_color"); cache.executing_line_color = get_color("executing_line_color"); cache.code_folding_color = get_color("code_folding_color"); @@ -5096,6 +5113,37 @@ void TextEdit::clear_executing_line() { update(); } +bool TextEdit::is_line_set_as_bookmark(int p_line) const { + + ERR_FAIL_INDEX_V(p_line, text.size(), false); + return text.is_bookmark(p_line); +} + +void TextEdit::set_line_as_bookmark(int p_line, bool p_bookmark) { + + ERR_FAIL_INDEX(p_line, text.size()); + text.set_bookmark(p_line, p_bookmark); + update(); +} + +void TextEdit::get_bookmarks(List<int> *p_bookmarks) const { + + for (int i = 0; i < text.size(); i++) { + if (text.is_bookmark(i)) + p_bookmarks->push_back(i); + } +} + +Array TextEdit::get_bookmarks_array() const { + + Array arr; + for (int i = 0; i < text.size(); i++) { + if (text.is_bookmark(i)) + arr.append(i); + } + return arr; +} + bool TextEdit::is_line_set_as_breakpoint(int p_line) const { ERR_FAIL_INDEX_V(p_line, text.size(), false); @@ -6183,6 +6231,15 @@ void TextEdit::set_line_length_guideline_column(int p_column) { update(); } +void TextEdit::set_bookmark_gutter_enabled(bool p_draw) { + draw_bookmark_gutter = p_draw; + update(); +} + +bool TextEdit::is_bookmark_gutter_enabled() const { + return draw_bookmark_gutter; +} + void TextEdit::set_breakpoint_gutter_enabled(bool p_draw) { draw_breakpoint_gutter = p_draw; update(); @@ -6579,6 +6636,7 @@ TextEdit::TextEdit() { line_numbers_zero_padded = false; line_length_guideline = false; line_length_guideline_col = 80; + draw_bookmark_gutter = false; draw_breakpoint_gutter = false; draw_fold_gutter = false; draw_info_gutter = false; @@ -6603,6 +6661,7 @@ TextEdit::TextEdit() { context_menu_enabled = true; menu = memnew(PopupMenu); add_child(menu); + readonly = true; // initialise to opposite first, so we get past the early-out in set_readonly set_readonly(false); menu->connect("id_pressed", this, "menu_option"); first_draw = true; diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h index eb9fb2cf57..64cbafc717 100644 --- a/scene/gui/text_edit.h +++ b/scene/gui/text_edit.h @@ -75,6 +75,7 @@ public: int width_cache : 24; bool marked : 1; bool breakpoint : 1; + bool bookmark : 1; bool hidden : 1; bool safe : 1; int wrap_amount_cache : 24; @@ -105,6 +106,8 @@ public: void set(int p_line, const String &p_text); void set_marked(int p_line, bool p_marked) { text.write[p_line].marked = p_marked; } bool is_marked(int p_line) const { return text[p_line].marked; } + void set_bookmark(int p_line, bool p_bookmark) { text.write[p_line].bookmark = p_bookmark; } + bool is_bookmark(int p_line) const { return text[p_line].bookmark; } void set_breakpoint(int p_line, bool p_breakpoint) { text.write[p_line].breakpoint = p_breakpoint; } bool is_breakpoint(int p_line) const { return text[p_line].breakpoint; } void set_hidden(int p_line, bool p_hidden) { text.write[p_line].hidden = p_hidden; } @@ -188,6 +191,7 @@ private: Color member_variable_color; Color selection_color; Color mark_color; + Color bookmark_color; Color breakpoint_color; Color executing_line_color; Color code_folding_color; @@ -300,6 +304,7 @@ private: bool line_numbers_zero_padded; bool line_length_guideline; int line_length_guideline_col; + bool draw_bookmark_gutter; bool draw_breakpoint_gutter; int breakpoint_gutter_width; bool draw_fold_gutter; @@ -490,6 +495,10 @@ public: void insert_at(const String &p_text, int at); int get_line_count() const; void set_line_as_marked(int p_line, bool p_marked); + void set_line_as_bookmark(int p_line, bool p_bookmark); + bool is_line_set_as_bookmark(int p_line) const; + void get_bookmarks(List<int> *p_bookmarks) const; + Array get_bookmarks_array() const; void set_line_as_breakpoint(int p_line, bool p_breakpoint); bool is_line_set_as_breakpoint(int p_line) const; void set_executing_line(int p_line); @@ -664,6 +673,9 @@ public: void set_show_line_length_guideline(bool p_show); void set_line_length_guideline_column(int p_column); + void set_bookmark_gutter_enabled(bool p_draw); + bool is_bookmark_gutter_enabled() const; + void set_breakpoint_gutter_enabled(bool p_draw); bool is_breakpoint_gutter_enabled() const; @@ -720,7 +732,7 @@ public: virtual void _update_cache() = 0; virtual Map<int, TextEdit::HighlighterInfo> _get_line_syntax_highlighting(int p_line) = 0; - virtual String get_name() = 0; + virtual String get_name() const = 0; virtual List<String> get_supported_languages() = 0; void set_text_editor(TextEdit *p_text_editor); diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index f22fe5b6a5..049cae9dd9 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -2266,7 +2266,7 @@ void Tree::_gui_input(Ref<InputEvent> p_event) { next = _n; } else { - return; + break; } } if (next == selected_item) @@ -2304,7 +2304,7 @@ void Tree::_gui_input(Ref<InputEvent> p_event) { prev = _n; } else { - return; + break; } } if (prev == selected_item) diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 8bfb50c68d..5440c88fa8 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -476,6 +476,7 @@ void register_scene_types() { ClassDB::register_virtual_class<VisualShaderNode>(); ClassDB::register_class<VisualShaderNodeInput>(); ClassDB::register_virtual_class<VisualShaderNodeOutput>(); + ClassDB::register_class<VisualShaderNodeGroupBase>(); ClassDB::register_class<VisualShaderNodeScalarConstant>(); ClassDB::register_class<VisualShaderNodeBooleanConstant>(); ClassDB::register_class<VisualShaderNodeColorConstant>(); @@ -524,6 +525,7 @@ void register_scene_types() { ClassDB::register_class<VisualShaderNodeIf>(); ClassDB::register_class<VisualShaderNodeSwitch>(); ClassDB::register_class<VisualShaderNodeFresnel>(); + ClassDB::register_class<VisualShaderNodeExpression>(); ClassDB::register_class<ShaderMaterial>(); ClassDB::register_virtual_class<CanvasItem>(); diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp index 0b88763300..bdb6c78782 100644 --- a/scene/resources/default_theme/default_theme.cpp +++ b/scene/resources/default_theme/default_theme.cpp @@ -441,6 +441,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_color("font_color_selected", "TextEdit", Color(0, 0, 0)); theme->set_color("selection_color", "TextEdit", font_color_selection); theme->set_color("mark_color", "TextEdit", Color(1.0, 0.4, 0.4, 0.4)); + theme->set_color("bookmark_color", "TextEdit", Color(0.08, 0.49, 0.98)); theme->set_color("breakpoint_color", "TextEdit", Color(0.8, 0.8, 0.4, 0.2)); theme->set_color("executing_line_color", "TextEdit", Color(0.2, 0.8, 0.2, 0.4)); theme->set_color("code_folding_color", "TextEdit", Color(0.8, 0.8, 0.8, 0.8)); diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp index b8f21948c3..1781777fcd 100644 --- a/scene/resources/visual_shader.cpp +++ b/scene/resources/visual_shader.cpp @@ -159,6 +159,7 @@ Vector2 VisualShader::get_node_position(Type p_type, int p_id) const { 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]; @@ -269,6 +270,18 @@ bool VisualShader::can_connect_nodes(Type p_type, int p_from_node, int p_from_po return true; } +void VisualShader::connect_nodes_forced(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]; + 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(); +} + 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]; @@ -304,6 +317,7 @@ Error VisualShader::connect_nodes(Type p_type, int p_from_node, int p_from_port, _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]; @@ -334,6 +348,7 @@ Array VisualShader::_get_node_connections(Type p_type) const { 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]; @@ -477,6 +492,54 @@ String VisualShader::generate_preview_shader(Type p_type, int p_node, int p_port #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_port_name(const String &p_name, const List<String> &p_input_ports, const List<String> &p_output_ports) const { + String name = p_name; + + 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; + } + + String valid_name = name; + bool is_equal = false; + + for (int i = 0; i < p_input_ports.size(); i++) { + if (name == p_input_ports[i]) { + is_equal = true; + break; + } + } + + if (!is_equal) { + for (int i = 0; i < p_output_ports.size(); i++) { + if (name == p_output_ports[i]) { + is_equal = true; + break; + } + } + } + + if (is_equal) { + name = ""; + } + + return name; +} + String VisualShader::validate_uniform_name(const String &p_name, const Ref<VisualShaderNodeUniform> &p_uniform) const { String name = p_name; //validate name first @@ -596,7 +659,7 @@ bool VisualShader::_set(const StringName &p_name, const Variant &p_value) { 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]); + connect_nodes_forced(type, conns[i + 0], conns[i + 1], conns[i + 2], conns[i + 3]); } } return true; @@ -611,6 +674,18 @@ bool VisualShader::_set(const StringName &p_name, const Variant &p_value) { } else if (what == "position") { set_node_position(type, id, p_value); return true; + } else if (what == "size") { + ((VisualShaderNodeGroupBase *)get_node(type, id).ptr())->set_size(p_value); + return true; + } else if (what == "input_ports") { + ((VisualShaderNodeGroupBase *)get_node(type, id).ptr())->set_inputs(p_value); + return true; + } else if (what == "output_ports") { + ((VisualShaderNodeGroupBase *)get_node(type, id).ptr())->set_outputs(p_value); + return true; + } else if (what == "expression") { + ((VisualShaderNodeExpression *)get_node(type, id).ptr())->set_expression(p_value); + return true; } } return false; @@ -668,6 +743,18 @@ bool VisualShader::_get(const StringName &p_name, Variant &r_ret) const { } else if (what == "position") { r_ret = get_node_position(type, id); return true; + } else if (what == "size") { + r_ret = ((VisualShaderNodeGroupBase *)get_node(type, id).ptr())->get_size(); + return true; + } else if (what == "input_ports") { + r_ret = ((VisualShaderNodeGroupBase *)get_node(type, id).ptr())->get_inputs(); + return true; + } else if (what == "output_ports") { + r_ret = ((VisualShaderNodeGroupBase *)get_node(type, id).ptr())->get_outputs(); + return true; + } else if (what == "expression") { + r_ret = ((VisualShaderNodeExpression *)get_node(type, id).ptr())->get_expression(); + return true; } } return false; @@ -727,6 +814,15 @@ void VisualShader::_get_property_list(List<PropertyInfo> *p_list) const { 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)); + + if (Object::cast_to<VisualShaderNodeGroupBase>(E->get().node.ptr()) != NULL) { + p_list->push_back(PropertyInfo(Variant::VECTOR2, prop_name + "/size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); + p_list->push_back(PropertyInfo(Variant::STRING, prop_name + "/input_ports", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); + p_list->push_back(PropertyInfo(Variant::STRING, prop_name + "/output_ports", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); + } + if (Object::cast_to<VisualShaderNodeExpression>(E->get().node.ptr()) != NULL) { + p_list->push_back(PropertyInfo(Variant::STRING, prop_name + "/expression", 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)); } @@ -993,26 +1089,33 @@ void VisualShader::_input_type_changed(Type p_type, int p_id) { } } +void VisualShader::rebuild() { + dirty = true; + _update_shader(); +} + 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", "type", "id"), &VisualShader::get_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_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("rebuild"), &VisualShader::rebuild); 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("connect_nodes_forced", "type", "from_node", "from_port", "to_node", "to_port"), &VisualShader::connect_nodes_forced); ClassDB::bind_method(D_METHOD("get_node_connections", "type"), &VisualShader::_get_node_connections); @@ -1627,3 +1730,555 @@ void VisualShaderNodeUniform::_bind_methods() { VisualShaderNodeUniform::VisualShaderNodeUniform() { } + +////////////// GroupBase + +String VisualShaderNodeGroupBase::get_caption() const { + return "Group"; +} + +void VisualShaderNodeGroupBase::set_size(const Vector2 &p_size) { + size = p_size; +} + +Vector2 VisualShaderNodeGroupBase::get_size() const { + return size; +} + +void VisualShaderNodeGroupBase::set_inputs(const String &p_inputs) { + + if (inputs == p_inputs) + return; + + clear_input_ports(); + + inputs = p_inputs; + + Vector<String> input_strings = inputs.split(";", false); + + int input_port_count = input_strings.size(); + + for (int i = 0; i < input_port_count; i++) { + + Vector<String> arr = input_strings[i].split(","); + + int port_idx = arr[0].to_int(); + int port_type = arr[1].to_int(); + String port_name = arr[2]; + + Port port; + port.type = (PortType)port_type; + port.name = port_name; + input_ports[port_idx] = port; + } +} + +String VisualShaderNodeGroupBase::get_inputs() const { + return inputs; +} + +void VisualShaderNodeGroupBase::set_outputs(const String &p_outputs) { + + if (outputs == p_outputs) + return; + + clear_output_ports(); + + outputs = p_outputs; + + Vector<String> output_strings = outputs.split(";", false); + + int output_port_count = output_strings.size(); + + for (int i = 0; i < output_port_count; i++) { + + Vector<String> arr = output_strings[i].split(","); + + int port_idx = arr[0].to_int(); + int port_type = arr[1].to_int(); + String port_name = arr[2]; + + Port port; + port.type = (PortType)port_type; + port.name = port_name; + output_ports[port_idx] = port; + } +} + +String VisualShaderNodeGroupBase::get_outputs() const { + return outputs; +} + +void VisualShaderNodeGroupBase::add_input_port(int p_id, int p_type, const String &p_name) { + + String str = itos(p_id) + "," + itos(p_type) + "," + p_name + ";"; + Vector<String> inputs_strings = inputs.split(";", false); + int index = 0; + if (p_id < inputs_strings.size()) { + for (int i = 0; i < inputs_strings.size(); i++) { + if (i == p_id) { + inputs = inputs.insert(index, str); + break; + } + index += inputs_strings[i].size(); + } + } else { + inputs += str; + } + + inputs_strings = inputs.split(";", false); + index = 0; + + for (int i = 0; i < inputs_strings.size(); i++) { + int count = 0; + for (int j = 0; j < inputs_strings[i].size(); j++) { + if (inputs_strings[i][j] == ',') { + break; + } + count++; + } + + inputs.erase(index, count); + inputs = inputs.insert(index, itos(i)); + index += inputs_strings[i].size(); + } + + _apply_port_changes(); +} + +void VisualShaderNodeGroupBase::remove_input_port(int p_id) { + + Vector<String> inputs_strings = inputs.split(";", false); + int count = 0; + int index = 0; + for (int i = 0; i < inputs_strings.size(); i++) { + Vector<String> arr = inputs_strings[i].split(","); + if (arr[0].to_int() == p_id) { + count = inputs_strings[i].size(); + break; + } + index += inputs_strings[i].size(); + } + inputs.erase(index, count); + + inputs_strings = inputs.split(";", false); + for (int i = p_id; i < inputs_strings.size(); i++) { + inputs = inputs.replace_first(inputs_strings[i].split(",")[0], itos(i)); + } + + _apply_port_changes(); +} + +int VisualShaderNodeGroupBase::get_input_port_count() const { + return input_ports.size(); +} + +bool VisualShaderNodeGroupBase::has_input_port(int p_id) const { + return input_ports.has(p_id); +} + +void VisualShaderNodeGroupBase::add_output_port(int p_id, int p_type, const String &p_name) { + + String str = itos(p_id) + "," + itos(p_type) + "," + p_name + ";"; + Vector<String> outputs_strings = outputs.split(";", false); + int index = 0; + if (p_id < outputs_strings.size()) { + for (int i = 0; i < outputs_strings.size(); i++) { + if (i == p_id) { + outputs = outputs.insert(index, str); + break; + } + index += outputs_strings[i].size(); + } + } else { + outputs += str; + } + + outputs_strings = outputs.split(";", false); + index = 0; + + for (int i = 0; i < outputs_strings.size(); i++) { + int count = 0; + for (int j = 0; j < outputs_strings[i].size(); j++) { + if (outputs_strings[i][j] == ',') { + break; + } + count++; + } + + outputs.erase(index, count); + outputs = outputs.insert(index, itos(i)); + index += outputs_strings[i].size(); + } + + _apply_port_changes(); +} + +void VisualShaderNodeGroupBase::remove_output_port(int p_id) { + + Vector<String> outputs_strings = outputs.split(";", false); + int count = 0; + int index = 0; + for (int i = 0; i < outputs_strings.size(); i++) { + Vector<String> arr = outputs_strings[i].split(","); + if (arr[0].to_int() == p_id) { + count = outputs_strings[i].size(); + break; + } + index += outputs_strings[i].size(); + } + outputs.erase(index, count); + + outputs_strings = outputs.split(";", false); + for (int i = p_id; i < outputs_strings.size(); i++) { + outputs = outputs.replace_first(outputs_strings[i].split(",")[0], itos(i)); + } + + _apply_port_changes(); +} + +int VisualShaderNodeGroupBase::get_output_port_count() const { + return output_ports.size(); +} + +bool VisualShaderNodeGroupBase::has_output_port(int p_id) const { + return output_ports.has(p_id); +} + +void VisualShaderNodeGroupBase::clear_input_ports() { + input_ports.clear(); +} + +void VisualShaderNodeGroupBase::clear_output_ports() { + output_ports.clear(); +} + +void VisualShaderNodeGroupBase::set_input_port_type(int p_id, int p_type) { + + if (input_ports[p_id].type == p_type) + return; + + Vector<String> inputs_strings = inputs.split(";", false); + int count = 0; + int index = 0; + for (int i = 0; i < inputs_strings.size(); i++) { + Vector<String> arr = inputs_strings[i].split(","); + if (arr[0].to_int() == p_id) { + index += arr[0].size(); + count = arr[1].size() - 1; + break; + } + index += inputs_strings[i].size(); + } + + inputs.erase(index, count); + + inputs = inputs.insert(index, itos(p_type)); + + _apply_port_changes(); +} + +VisualShaderNodeGroupBase::PortType VisualShaderNodeGroupBase::get_input_port_type(int p_id) const { + ERR_FAIL_COND_V(!input_ports.has(p_id), (PortType)0); + return input_ports[p_id].type; +} + +void VisualShaderNodeGroupBase::set_input_port_name(int p_id, const String &p_name) { + + if (input_ports[p_id].name == p_name) + return; + + Vector<String> inputs_strings = inputs.split(";", false); + int count = 0; + int index = 0; + for (int i = 0; i < inputs_strings.size(); i++) { + Vector<String> arr = inputs_strings[i].split(","); + if (arr[0].to_int() == p_id) { + index += arr[0].size() + arr[1].size(); + count = arr[2].size() - 1; + break; + } + index += inputs_strings[i].size(); + } + + inputs.erase(index, count); + + inputs = inputs.insert(index, p_name); + + _apply_port_changes(); +} + +String VisualShaderNodeGroupBase::get_input_port_name(int p_id) const { + ERR_FAIL_COND_V(!input_ports.has(p_id), ""); + return input_ports[p_id].name; +} + +void VisualShaderNodeGroupBase::set_output_port_type(int p_id, int p_type) { + + if (output_ports[p_id].type == p_type) + return; + + Vector<String> output_strings = outputs.split(";", false); + int count = 0; + int index = 0; + for (int i = 0; i < output_strings.size(); i++) { + Vector<String> arr = output_strings[i].split(","); + if (arr[0].to_int() == p_id) { + index += arr[0].size(); + count = arr[1].size() - 1; + break; + } + index += output_strings[i].size(); + } + + outputs.erase(index, count); + + outputs = outputs.insert(index, itos(p_type)); + + _apply_port_changes(); +} + +VisualShaderNodeGroupBase::PortType VisualShaderNodeGroupBase::get_output_port_type(int p_id) const { + ERR_FAIL_COND_V(!output_ports.has(p_id), (PortType)0); + return output_ports[p_id].type; +} + +void VisualShaderNodeGroupBase::set_output_port_name(int p_id, const String &p_name) { + if (output_ports[p_id].name == p_name) + return; + + Vector<String> output_strings = outputs.split(";", false); + int count = 0; + int index = 0; + for (int i = 0; i < output_strings.size(); i++) { + Vector<String> arr = output_strings[i].split(","); + if (arr[0].to_int() == p_id) { + index += arr[0].size() + arr[1].size(); + count = arr[2].size() - 1; + break; + } + index += output_strings[i].size(); + } + + outputs.erase(index, count); + + outputs = outputs.insert(index, p_name); + + _apply_port_changes(); +} + +String VisualShaderNodeGroupBase::get_output_port_name(int p_id) const { + ERR_FAIL_COND_V(!output_ports.has(p_id), ""); + return output_ports[p_id].name; +} + +int VisualShaderNodeGroupBase::get_free_input_port_id() const { + return input_ports.size(); +} + +int VisualShaderNodeGroupBase::get_free_output_port_id() const { + return output_ports.size(); +} + +void VisualShaderNodeGroupBase::set_control(Control *p_control, int p_index) { + controls[p_index] = p_control; +} + +Control *VisualShaderNodeGroupBase::get_control(int p_index) { + ERR_FAIL_COND_V(!controls.has(p_index), NULL); + return controls[p_index]; +} + +void VisualShaderNodeGroupBase::_apply_port_changes() { + + Vector<String> inputs_strings = inputs.split(";", false); + Vector<String> outputs_strings = outputs.split(";", false); + + clear_input_ports(); + clear_output_ports(); + + for (int i = 0; i < inputs_strings.size(); i++) { + Vector<String> arr = inputs_strings[i].split(","); + Port port; + port.type = (PortType)arr[1].to_int(); + port.name = arr[2]; + input_ports[i] = port; + } + for (int i = 0; i < outputs_strings.size(); i++) { + Vector<String> arr = outputs_strings[i].split(","); + Port port; + port.type = (PortType)arr[1].to_int(); + port.name = arr[2]; + output_ports[i] = port; + } +} + +void VisualShaderNodeGroupBase::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_size", "size"), &VisualShaderNodeGroupBase::set_size); + ClassDB::bind_method(D_METHOD("get_size"), &VisualShaderNodeGroupBase::get_size); + + ClassDB::bind_method(D_METHOD("set_inputs", "inputs"), &VisualShaderNodeGroupBase::set_inputs); + ClassDB::bind_method(D_METHOD("get_inputs"), &VisualShaderNodeGroupBase::get_inputs); + + ClassDB::bind_method(D_METHOD("set_outputs", "outputs"), &VisualShaderNodeGroupBase::set_outputs); + ClassDB::bind_method(D_METHOD("get_outputs"), &VisualShaderNodeGroupBase::get_outputs); + + ClassDB::bind_method(D_METHOD("add_input_port", "id", "type", "name"), &VisualShaderNodeGroupBase::add_input_port); + ClassDB::bind_method(D_METHOD("remove_input_port", "id"), &VisualShaderNodeGroupBase::remove_input_port); + ClassDB::bind_method(D_METHOD("get_input_port_count"), &VisualShaderNodeGroupBase::get_input_port_count); + ClassDB::bind_method(D_METHOD("has_input_port", "id"), &VisualShaderNodeGroupBase::has_input_port); + ClassDB::bind_method(D_METHOD("clear_input_ports"), &VisualShaderNodeGroupBase::clear_input_ports); + + ClassDB::bind_method(D_METHOD("add_output_port", "id", "type", "name"), &VisualShaderNodeGroupBase::add_output_port); + ClassDB::bind_method(D_METHOD("remove_output_port", "id"), &VisualShaderNodeGroupBase::remove_output_port); + ClassDB::bind_method(D_METHOD("get_output_port_count"), &VisualShaderNodeGroupBase::get_output_port_count); + ClassDB::bind_method(D_METHOD("has_output_port", "id"), &VisualShaderNodeGroupBase::has_output_port); + ClassDB::bind_method(D_METHOD("clear_output_ports"), &VisualShaderNodeGroupBase::clear_output_ports); + + ClassDB::bind_method(D_METHOD("set_input_port_name"), &VisualShaderNodeGroupBase::set_input_port_name); + ClassDB::bind_method(D_METHOD("set_input_port_type"), &VisualShaderNodeGroupBase::set_input_port_type); + ClassDB::bind_method(D_METHOD("set_output_port_name"), &VisualShaderNodeGroupBase::set_output_port_name); + ClassDB::bind_method(D_METHOD("set_output_port_type"), &VisualShaderNodeGroupBase::set_output_port_type); + + ClassDB::bind_method(D_METHOD("get_free_input_port_id"), &VisualShaderNodeGroupBase::get_free_input_port_id); + ClassDB::bind_method(D_METHOD("get_free_output_port_id"), &VisualShaderNodeGroupBase::get_free_output_port_id); + + ClassDB::bind_method(D_METHOD("set_control", "control", "index"), &VisualShaderNodeGroupBase::set_control); + ClassDB::bind_method(D_METHOD("get_control", "index"), &VisualShaderNodeGroupBase::get_control); +} + +String VisualShaderNodeGroupBase::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const { + return ""; +} + +VisualShaderNodeGroupBase::VisualShaderNodeGroupBase() { + size = Size2(0, 0); + inputs = ""; + outputs = ""; +} + +////////////// Expression + +String VisualShaderNodeExpression::get_caption() const { + return "Expression"; +} + +void VisualShaderNodeExpression::set_expression(const String &p_expression) { + expression = p_expression; +} + +void VisualShaderNodeExpression::build() { + emit_changed(); +} + +String VisualShaderNodeExpression::get_expression() const { + return expression; +} + +String VisualShaderNodeExpression::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const { + + String _expression = expression; + + _expression = _expression.insert(0, "\n"); + _expression = _expression.replace("\n", "\n\t\t"); + + static Vector<String> pre_symbols; + if (pre_symbols.empty()) { + pre_symbols.push_back("\t"); + pre_symbols.push_back("{"); + pre_symbols.push_back("["); + pre_symbols.push_back("("); + pre_symbols.push_back(" "); + pre_symbols.push_back("-"); + pre_symbols.push_back("*"); + pre_symbols.push_back("/"); + pre_symbols.push_back("+"); + pre_symbols.push_back("="); + pre_symbols.push_back("&"); + pre_symbols.push_back("|"); + pre_symbols.push_back("!"); + } + + static Vector<String> post_symbols; + if (post_symbols.empty()) { + post_symbols.push_back("\0"); + post_symbols.push_back("\t"); + post_symbols.push_back("\n"); + post_symbols.push_back(";"); + post_symbols.push_back("}"); + post_symbols.push_back("]"); + post_symbols.push_back(")"); + post_symbols.push_back(" "); + post_symbols.push_back("."); + post_symbols.push_back("-"); + post_symbols.push_back("*"); + post_symbols.push_back("/"); + post_symbols.push_back("+"); + post_symbols.push_back("="); + post_symbols.push_back("&"); + post_symbols.push_back("|"); + post_symbols.push_back("!"); + } + + for (int i = 0; i < get_input_port_count(); i++) { + for (int j = 0; j < pre_symbols.size(); j++) { + for (int k = 0; k < post_symbols.size(); k++) { + _expression = _expression.replace(pre_symbols[j] + get_input_port_name(i) + post_symbols[k], pre_symbols[j] + p_input_vars[i] + post_symbols[k]); + } + } + } + for (int i = 0; i < get_output_port_count(); i++) { + for (int j = 0; j < pre_symbols.size(); j++) { + for (int k = 0; k < post_symbols.size(); k++) { + _expression = _expression.replace(pre_symbols[j] + get_output_port_name(i) + post_symbols[k], pre_symbols[j] + p_output_vars[i] + post_symbols[k]); + } + } + } + + String output_initializer; + + for (int i = 0; i < get_output_port_count(); i++) { + int port_type = get_output_port_type(i); + String tk = ""; + switch (port_type) { + case PORT_TYPE_SCALAR: + tk = "0.0"; + break; + case PORT_TYPE_VECTOR: + tk = "vec3(0.0, 0.0, 0.0)"; + break; + case PORT_TYPE_BOOLEAN: + tk = "false"; + break; + case PORT_TYPE_TRANSFORM: + tk = "mat4(1.0)"; + break; + default: + continue; + } + output_initializer += "\t" + p_output_vars[i] + "=" + tk + ";\n"; + } + + String code; + code += output_initializer; + code += "\t{"; + code += _expression; + code += "\n\t}"; + + return code; +} + +void VisualShaderNodeExpression::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_expression", "expression"), &VisualShaderNodeExpression::set_expression); + ClassDB::bind_method(D_METHOD("get_expression"), &VisualShaderNodeExpression::get_expression); + + ClassDB::bind_method(D_METHOD("build"), &VisualShaderNodeExpression::build); + + ADD_PROPERTY(PropertyInfo(Variant::STRING, "expression"), "set_expression", "get_expression"); +} + +VisualShaderNodeExpression::VisualShaderNodeExpression() { + expression = ""; +} diff --git a/scene/resources/visual_shader.h b/scene/resources/visual_shader.h index cf10de9bf5..ea39d118e4 100644 --- a/scene/resources/visual_shader.h +++ b/scene/resources/visual_shader.h @@ -135,7 +135,9 @@ public: 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 connect_nodes_forced(Type p_type, int p_from_node, int p_from_port, int p_to_node, int p_to_port); + void rebuild(); void get_node_connections(Type p_type, List<Connection> *r_connections) const; void set_mode(Mode p_mode); @@ -148,6 +150,7 @@ public: String generate_preview_shader(Type p_type, int p_node, int p_port, Vector<DefaultTextureParam> &r_default_tex_params) const; + String validate_port_name(const String &p_name, const List<String> &p_input_ports, const List<String> &p_output_ports) const; String validate_uniform_name(const String &p_name, const Ref<VisualShaderNodeUniform> &p_uniform) const; VisualShader(); @@ -314,4 +317,93 @@ public: VisualShaderNodeUniform(); }; +class VisualShaderNodeGroupBase : public VisualShaderNode { + GDCLASS(VisualShaderNodeGroupBase, VisualShaderNode) +private: + void _apply_port_changes(); + +protected: + Vector2 size; + String inputs; + String outputs; + + struct Port { + PortType type; + String name; + }; + + Map<int, Port> input_ports; + Map<int, Port> output_ports; + Map<int, Control *> controls; + +protected: + static void _bind_methods(); + +public: + virtual String get_caption() const; + + void set_size(const Vector2 &p_size); + Vector2 get_size() const; + + void set_inputs(const String &p_inputs); + String get_inputs() const; + + void set_outputs(const String &p_outputs); + String get_outputs() const; + + void add_input_port(int p_id, int p_type, const String &p_name); + void remove_input_port(int p_id); + virtual int get_input_port_count() const; + bool has_input_port(int p_id) const; + void clear_input_ports(); + + void add_output_port(int p_id, int p_type, const String &p_name); + void remove_output_port(int p_id); + virtual int get_output_port_count() const; + bool has_output_port(int p_id) const; + void clear_output_ports(); + + void set_input_port_type(int p_id, int p_type); + virtual PortType get_input_port_type(int p_id) const; + void set_input_port_name(int p_id, const String &p_name); + virtual String get_input_port_name(int p_id) const; + + void set_output_port_type(int p_id, int p_type); + virtual PortType get_output_port_type(int p_id) const; + void set_output_port_name(int p_id, const String &p_name); + virtual String get_output_port_name(int p_id) const; + + int get_free_input_port_id() const; + int get_free_output_port_id() const; + + void set_control(Control *p_control, int p_index); + Control *get_control(int p_index); + + virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const; + + VisualShaderNodeGroupBase(); +}; + +class VisualShaderNodeExpression : public VisualShaderNodeGroupBase { + GDCLASS(VisualShaderNodeExpression, VisualShaderNodeGroupBase) + +private: + String expression; + +protected: + static void _bind_methods(); + +public: + virtual String get_caption() const; + + void set_expression(const String &p_expression); + String get_expression() const; + + void build(); + + virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const; + + VisualShaderNodeExpression(); +}; + #endif // VISUAL_SHADER_H |