summaryrefslogtreecommitdiff
path: root/editor/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'editor/plugins')
-rw-r--r--editor/plugins/visual_shader_editor_plugin.cpp579
-rw-r--r--editor/plugins/visual_shader_editor_plugin.h24
2 files changed, 589 insertions, 14 deletions
diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp
index ad698be0a3..cdb1dbfdc4 100644
--- a/editor/plugins/visual_shader_editor_plugin.cpp
+++ b/editor/plugins/visual_shader_editor_plugin.cpp
@@ -358,7 +358,9 @@ void VisualShaderEditor::_update_graph() {
for (int i = 0; i < graph->get_child_count(); i++) {
if (Object::cast_to<GraphNode>(graph->get_child(i))) {
- memdelete(graph->get_child(i));
+ Node *node = graph->get_child(i);
+ graph->remove_child(node);
+ memdelete(node);
i--;
}
}
@@ -377,13 +379,33 @@ void VisualShaderEditor::_update_graph() {
Vector<int> nodes = visual_shader->get_node_list(type);
+ Control *offset;
+
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]);
+ Ref<VisualShaderNodeGroupBase> group_node = Object::cast_to<VisualShaderNodeGroupBase>(vsnode.ptr());
+ bool is_group = !group_node.is_null();
+ Size2 size = Size2(0, 0);
+
+ Ref<VisualShaderNodeExpression> expression_node = Object::cast_to<VisualShaderNodeExpression>(group_node.ptr());
+ bool is_expression = !expression_node.is_null();
+ String expression = "";
+
GraphNode *node = memnew(GraphNode);
+ if (is_group) {
+ size = group_node->get_size();
+
+ node->set_resizable(true);
+ node->connect("resize_request", this, "_node_resized", varray((int)type, nodes[n_i]));
+ }
+ if (is_expression) {
+ expression = expression_node->get_expression();
+ }
+
/*if (!vsnode->is_connected("changed", this, "_node_changed")) {
vsnode->connect("changed", this, "_node_changed", varray(vsnode->get_instance_id()), CONNECT_DEFERRED);
}*/
@@ -403,6 +425,10 @@ void VisualShaderEditor::_update_graph() {
Control *custom_editor = NULL;
int port_offset = 0;
+ if (is_group) {
+ port_offset++;
+ }
+
Ref<VisualShaderNodeUniform> uniform = vsnode;
if (uniform.is_valid()) {
graph->add_child(node);
@@ -438,6 +464,24 @@ void VisualShaderEditor::_update_graph() {
custom_editor = NULL;
}
+ if (is_group) {
+ HBoxContainer *hb2 = memnew(HBoxContainer);
+
+ Button *add_input_btn = memnew(Button);
+ add_input_btn->set_text(TTR("Add input +"));
+ add_input_btn->connect("pressed", this, "_add_input_port", varray(nodes[n_i], group_node->get_free_input_port_id(), VisualShaderNode::PORT_TYPE_VECTOR, "input" + itos(group_node->get_free_input_port_id())), CONNECT_DEFERRED);
+ hb2->add_child(add_input_btn);
+
+ hb2->add_spacer();
+
+ Button *add_output_btn = memnew(Button);
+ add_output_btn->set_text(TTR("Add output +"));
+ add_output_btn->connect("pressed", this, "_add_output_port", varray(nodes[n_i], group_node->get_free_output_port_id(), VisualShaderNode::PORT_TYPE_VECTOR, "output" + itos(group_node->get_free_output_port_id())), CONNECT_DEFERRED);
+ hb2->add_child(add_output_btn);
+
+ node->add_child(hb2);
+ }
+
for (int i = 0; i < MAX(vsnode->get_input_port_count(), vsnode->get_output_port_count()); i++) {
if (vsnode->is_port_separator(i)) {
@@ -507,21 +551,75 @@ void VisualShaderEditor::_update_graph() {
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);
+ if (is_group) {
+
+ OptionButton *type_box = memnew(OptionButton);
+ hb->add_child(type_box);
+ type_box->add_item(TTR("Scalar"));
+ type_box->add_item(TTR("Vector"));
+ type_box->add_item(TTR("Boolean"));
+ type_box->add_item(TTR("Transform"));
+ type_box->select(group_node->get_input_port_type(i));
+ type_box->set_custom_minimum_size(Size2(100 * EDSCALE, 0));
+ type_box->connect("item_selected", this, "_change_input_port_type", varray(nodes[n_i], i), CONNECT_DEFERRED);
+
+ LineEdit *name_box = memnew(LineEdit);
+ hb->add_child(name_box);
+ name_box->set_custom_minimum_size(Size2(60 * EDSCALE, 0));
+ name_box->set_text(name_left);
+ name_box->set_expand_to_text_length(true);
+ name_box->connect("text_entered", this, "_change_input_port_name", varray(name_box, nodes[n_i], i));
+ name_box->connect("focus_exited", this, "_port_name_focus_out", varray(name_box, nodes[n_i], i, false));
+
+ if (is_group) {
+ Button *remove_btn = memnew(Button);
+ remove_btn->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("Remove", "EditorIcons"));
+ remove_btn->set_tooltip(TTR("Remove") + " " + name_left);
+ remove_btn->connect("pressed", this, "_remove_input_port", varray(nodes[n_i], i), CONNECT_DEFERRED);
+ hb->add_child(remove_btn);
+ }
+ } else {
+
+ 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 (is_group) {
+ Button *remove_btn = memnew(Button);
+ remove_btn->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("Remove", "EditorIcons"));
+ remove_btn->set_tooltip(TTR("Remove") + " " + name_left);
+ remove_btn->connect("pressed", this, "_remove_output_port", varray(nodes[n_i], i), CONNECT_DEFERRED);
+ hb->add_child(remove_btn);
+
+ LineEdit *name_box = memnew(LineEdit);
+ hb->add_child(name_box);
+ name_box->set_custom_minimum_size(Size2(60 * EDSCALE, 0));
+ name_box->set_text(name_right);
+ name_box->set_expand_to_text_length(true);
+ name_box->connect("text_entered", this, "_change_output_port_name", varray(name_box, nodes[n_i], i));
+ name_box->connect("focus_exited", this, "_port_name_focus_out", varray(name_box, nodes[n_i], i, true));
+
+ OptionButton *type_box = memnew(OptionButton);
+ hb->add_child(type_box);
+ type_box->add_item(TTR("Scalar"));
+ type_box->add_item(TTR("Vector"));
+ type_box->add_item(TTR("Boolean"));
+ type_box->add_item(TTR("Transform"));
+ type_box->select(group_node->get_output_port_type(i));
+ type_box->set_custom_minimum_size(Size2(100 * EDSCALE, 0));
+ type_box->connect("item_selected", this, "_change_output_port_type", varray(nodes[n_i], i), CONNECT_DEFERRED);
+ } else {
+ Label *label = memnew(Label);
+ label->set_text(name_right);
+ label->add_style_override("normal", label_style); //more compact
+ hb->add_child(label);
+ }
}
}
@@ -540,18 +638,33 @@ void VisualShaderEditor::_update_graph() {
hb->add_child(preview);
}
+ if (is_group) {
+ offset = memnew(Control);
+ offset->set_custom_minimum_size(Size2(0, 5 * EDSCALE));
+ node->add_child(offset);
+ port_offset++;
+ }
+
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 && vsnode->get_output_port_type(vsnode->get_output_port_for_preview()) != VisualShaderNode::PORT_TYPE_TRANSFORM) {
+ offset = memnew(Control);
+ offset->set_custom_minimum_size(Size2(0, 5 * EDSCALE));
+ node->add_child(offset);
+
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);
}
+ offset = memnew(Control);
+ offset->set_custom_minimum_size(Size2(0, 5 * EDSCALE));
+ node->add_child(offset);
+
String error = vsnode->get_warning(visual_shader->get_mode(), type);
if (error != String()) {
Label *error_label = memnew(Label);
@@ -560,9 +673,42 @@ void VisualShaderEditor::_update_graph() {
node->add_child(error_label);
}
+ if (is_expression) {
+
+ TextEdit *expression_box = memnew(TextEdit);
+ expression_node->set_control(expression_box, 0);
+ node->add_child(expression_box);
+
+ Color text_color = EDITOR_GET("text_editor/highlighting/text_color");
+ Color keyword_color = EDITOR_GET("text_editor/highlighting/keyword_color");
+ Color comment_color = EDITOR_GET("text_editor/highlighting/comment_color");
+ Color symbol_color = EDITOR_GET("text_editor/highlighting/symbol_color");
+
+ expression_box->set_syntax_coloring(true);
+
+ for (List<String>::Element *E = keyword_list.front(); E; E = E->next()) {
+
+ expression_box->add_keyword_color(E->get(), keyword_color);
+ }
+
+ expression_box->add_font_override("font", get_font("expression", "EditorFonts"));
+ expression_box->add_color_override("font_color", text_color);
+ expression_box->add_color_override("symbol_color", symbol_color);
+ expression_box->add_color_region("/*", "*/", comment_color, false);
+ expression_box->add_color_region("//", "", comment_color, false);
+
+ expression_box->set_text(expression);
+ expression_box->set_context_menu_enabled(false);
+ expression_box->set_show_line_numbers(true);
+
+ expression_box->connect("text_changed", this, "_set_expression", varray(nodes[n_i]), CONNECT_DEFERRED);
+ }
+
if (!uniform.is_valid()) {
graph->add_child(node);
_update_created_node(node);
+ if (is_group)
+ call_deferred("_set_node_size", (int)type, nodes[n_i], size);
}
}
@@ -577,6 +723,290 @@ void VisualShaderEditor::_update_graph() {
}
}
+void VisualShaderEditor::_add_input_port(int p_node, int p_port, int p_port_type, const String &p_name) {
+
+ VisualShader::Type type = VisualShader::Type(edit_type->get_selected());
+ Ref<VisualShaderNodeExpression> node = visual_shader->get_node(type, p_node);
+ if (node.is_null()) {
+ return;
+ }
+
+ undo_redo->create_action(TTR("Add input port"));
+ undo_redo->add_do_method(node.ptr(), "add_input_port", p_port, p_port_type, p_name);
+ undo_redo->add_undo_method(node.ptr(), "remove_input_port", p_port);
+ undo_redo->add_do_method(this, "_update_graph");
+ undo_redo->add_undo_method(this, "_update_graph");
+ undo_redo->add_do_method(this, "_rebuild");
+ undo_redo->add_undo_method(this, "_rebuild");
+ undo_redo->commit_action();
+}
+
+void VisualShaderEditor::_add_output_port(int p_node, int p_port, int p_port_type, const String &p_name) {
+
+ VisualShader::Type type = VisualShader::Type(edit_type->get_selected());
+ Ref<VisualShaderNodeGroupBase> node = visual_shader->get_node(type, p_node);
+ if (node.is_null()) {
+ return;
+ }
+
+ undo_redo->create_action(TTR("Add output port"));
+ undo_redo->add_do_method(node.ptr(), "add_output_port", p_port, p_port_type, p_name);
+ undo_redo->add_undo_method(node.ptr(), "remove_output_port", p_port);
+ undo_redo->add_do_method(this, "_update_graph");
+ undo_redo->add_undo_method(this, "_update_graph");
+ undo_redo->add_do_method(this, "_rebuild");
+ undo_redo->add_undo_method(this, "_rebuild");
+ undo_redo->commit_action();
+}
+
+void VisualShaderEditor::_change_input_port_type(int p_type, int p_node, int p_port) {
+
+ VisualShader::Type type = VisualShader::Type(edit_type->get_selected());
+ Ref<VisualShaderNodeGroupBase> node = visual_shader->get_node(type, p_node);
+ if (node.is_null()) {
+ return;
+ }
+
+ undo_redo->create_action(TTR("Change input port type"));
+ undo_redo->add_do_method(node.ptr(), "set_input_port_type", p_port, p_type);
+ undo_redo->add_undo_method(node.ptr(), "set_input_port_type", p_port, node->get_input_port_type(p_port));
+ undo_redo->add_do_method(this, "_update_graph");
+ undo_redo->add_undo_method(this, "_update_graph");
+ undo_redo->add_do_method(this, "_rebuild");
+ undo_redo->add_undo_method(this, "_rebuild");
+ undo_redo->commit_action();
+}
+
+void VisualShaderEditor::_change_output_port_type(int p_type, int p_node, int p_port) {
+
+ VisualShader::Type type = VisualShader::Type(edit_type->get_selected());
+ Ref<VisualShaderNodeGroupBase> node = visual_shader->get_node(type, p_node);
+ if (node.is_null()) {
+ return;
+ }
+
+ undo_redo->create_action(TTR("Change output port type"));
+ undo_redo->add_do_method(node.ptr(), "set_output_port_type", p_port, p_type);
+ undo_redo->add_undo_method(node.ptr(), "set_output_port_type", p_port, node->get_output_port_type(p_port));
+ undo_redo->add_do_method(this, "_update_graph");
+ undo_redo->add_undo_method(this, "_update_graph");
+ undo_redo->add_do_method(this, "_rebuild");
+ undo_redo->add_undo_method(this, "_rebuild");
+ undo_redo->commit_action();
+}
+
+void VisualShaderEditor::_change_input_port_name(const String &p_text, Object *line_edit, int p_node_id, int p_port_id) {
+
+ VisualShader::Type type = VisualShader::Type(edit_type->get_selected());
+
+ Ref<VisualShaderNodeGroupBase> node = visual_shader->get_node(type, p_node_id);
+ ERR_FAIL_COND(!node.is_valid());
+
+ undo_redo->create_action(TTR("Change input port name"));
+ undo_redo->add_do_method(node.ptr(), "set_input_port_name", p_port_id, p_text);
+ undo_redo->add_undo_method(node.ptr(), "set_input_port_name", p_port_id, node->get_input_port_name(p_port_id));
+ undo_redo->add_do_method(this, "_rebuild");
+ undo_redo->add_undo_method(this, "_rebuild");
+ undo_redo->commit_action();
+}
+
+void VisualShaderEditor::_change_output_port_name(const String &p_text, Object *line_edit, int p_node_id, int p_port_id) {
+
+ VisualShader::Type type = VisualShader::Type(edit_type->get_selected());
+
+ Ref<VisualShaderNodeGroupBase> node = visual_shader->get_node(type, p_node_id);
+ ERR_FAIL_COND(!node.is_valid());
+
+ undo_redo->create_action(TTR("Change output port name"));
+ undo_redo->add_do_method(node.ptr(), "set_output_port_name", p_port_id, p_text);
+ undo_redo->add_undo_method(node.ptr(), "set_output_port_name", p_port_id, node->get_output_port_name(p_port_id));
+ undo_redo->add_do_method(this, "_rebuild");
+ undo_redo->add_undo_method(this, "_rebuild");
+ undo_redo->commit_action();
+}
+
+void VisualShaderEditor::_remove_input_port(int p_node, int p_port) {
+
+ VisualShader::Type type = VisualShader::Type(edit_type->get_selected());
+ Ref<VisualShaderNodeGroupBase> node = visual_shader->get_node(type, p_node);
+ if (node.is_null()) {
+ return;
+ }
+
+ undo_redo->create_action(TTR("Remove input port"));
+
+ List<VisualShader::Connection> conns;
+ visual_shader->get_node_connections(type, &conns);
+ for (List<VisualShader::Connection>::Element *E = conns.front(); E; E = E->next()) {
+
+ int from_node = E->get().from_node;
+ int from_port = E->get().from_port;
+ int to_node = E->get().to_node;
+ int to_port = E->get().to_port;
+
+ if (to_node == p_node) {
+ if (to_port == p_port) {
+ undo_redo->add_do_method(visual_shader.ptr(), "disconnect_nodes", type, from_node, from_port, to_node, to_port);
+ undo_redo->add_undo_method(visual_shader.ptr(), "connect_nodes_forced", type, from_node, from_port, to_node, to_port);
+ } else if (to_port > p_port) {
+ undo_redo->add_do_method(visual_shader.ptr(), "disconnect_nodes", type, from_node, from_port, to_node, to_port);
+ undo_redo->add_undo_method(visual_shader.ptr(), "connect_nodes_forced", type, from_node, from_port, to_node, to_port);
+
+ undo_redo->add_do_method(visual_shader.ptr(), "connect_nodes_forced", type, from_node, from_port, to_node, to_port - 1);
+ undo_redo->add_undo_method(visual_shader.ptr(), "disconnect_nodes", type, from_node, from_port, to_node, to_port - 1);
+ }
+ }
+ }
+
+ undo_redo->add_do_method(node.ptr(), "remove_input_port", p_port);
+ undo_redo->add_undo_method(node.ptr(), "add_input_port", p_port, (int)node->get_input_port_type(p_port), node->get_input_port_name(p_port));
+
+ undo_redo->add_do_method(this, "_update_graph");
+ undo_redo->add_undo_method(this, "_update_graph");
+
+ undo_redo->add_do_method(this, "_rebuild");
+ undo_redo->add_undo_method(this, "_rebuild");
+
+ undo_redo->commit_action();
+}
+
+void VisualShaderEditor::_remove_output_port(int p_node, int p_port) {
+
+ VisualShader::Type type = VisualShader::Type(edit_type->get_selected());
+ Ref<VisualShaderNodeGroupBase> node = visual_shader->get_node(type, p_node);
+ if (node.is_null()) {
+ return;
+ }
+
+ undo_redo->create_action(TTR("Remove output port"));
+
+ List<VisualShader::Connection> conns;
+ visual_shader->get_node_connections(type, &conns);
+ for (List<VisualShader::Connection>::Element *E = conns.front(); E; E = E->next()) {
+
+ int from_node = E->get().from_node;
+ int from_port = E->get().from_port;
+ int to_node = E->get().to_node;
+ int to_port = E->get().to_port;
+
+ if (from_node == p_node) {
+ if (from_port == p_port) {
+ undo_redo->add_do_method(visual_shader.ptr(), "disconnect_nodes", type, from_node, from_port, to_node, to_port);
+ undo_redo->add_undo_method(visual_shader.ptr(), "connect_nodes_forced", type, from_node, from_port, to_node, to_port);
+ } else if (from_port > p_port) {
+ undo_redo->add_do_method(visual_shader.ptr(), "disconnect_nodes", type, from_node, from_port, to_node, to_port);
+ undo_redo->add_undo_method(visual_shader.ptr(), "connect_nodes_forced", type, from_node, from_port, to_node, to_port);
+
+ undo_redo->add_do_method(visual_shader.ptr(), "connect_nodes_forced", type, from_node, from_port - 1, to_node, to_port);
+ undo_redo->add_undo_method(visual_shader.ptr(), "disconnect_nodes", type, from_node, from_port - 1, to_node, to_port);
+ }
+ }
+ }
+
+ undo_redo->add_do_method(node.ptr(), "remove_output_port", p_port);
+ undo_redo->add_undo_method(node.ptr(), "add_output_port", p_port, (int)node->get_output_port_type(p_port), node->get_output_port_name(p_port));
+
+ undo_redo->add_do_method(this, "_update_graph");
+ undo_redo->add_undo_method(this, "_update_graph");
+
+ undo_redo->add_do_method(this, "_rebuild");
+ undo_redo->add_undo_method(this, "_rebuild");
+
+ undo_redo->commit_action();
+}
+
+void VisualShaderEditor::_set_expression(int p_node) {
+
+ VisualShader::Type type = VisualShader::Type(edit_type->get_selected());
+ Ref<VisualShaderNodeExpression> node = visual_shader->get_node(type, p_node);
+ if (node.is_null()) {
+ return;
+ }
+
+ TextEdit *expression_box = Object::cast_to<TextEdit>(node->get_control(0));
+
+ undo_redo->create_action(TTR("Set expression"));
+ undo_redo->add_do_method(node.ptr(), "set_expression", expression_box->get_text());
+ undo_redo->add_undo_method(node.ptr(), "set_expression", node->get_expression());
+ undo_redo->add_do_method(this, "_start_rebuild_timer", 1.0);
+ undo_redo->add_undo_method(this, "_rebuild");
+ undo_redo->commit_action();
+}
+
+void VisualShaderEditor::_start_rebuild_timer(int p_delay) {
+ build_timer->start(p_delay);
+}
+
+void VisualShaderEditor::_rebuild_timeout() {
+ _rebuild();
+}
+
+void VisualShaderEditor::_rebuild() {
+ EditorNode::get_singleton()->get_log()->clear();
+ visual_shader->rebuild();
+}
+
+void VisualShaderEditor::_set_node_size(int p_type, int p_node, const Vector2 &p_size) {
+
+ VisualShader::Type type = VisualShader::Type(p_type);
+ Ref<VisualShaderNode> node = visual_shader->get_node(type, p_node);
+ if (node.is_null()) {
+ return;
+ }
+
+ Ref<VisualShaderNodeGroupBase> group_node = Object::cast_to<VisualShaderNodeGroupBase>(node.ptr());
+
+ if (group_node.is_null()) {
+ return;
+ }
+
+ Vector2 size = p_size;
+
+ group_node->set_size(size);
+
+ GraphNode *gn = NULL;
+ if (edit_type->get_selected() == p_type) { // check - otherwise the error will be emitted
+ Node *node2 = graph->get_node(itos(p_node));
+ gn = Object::cast_to<GraphNode>(node2);
+ if (!gn)
+ return;
+
+ gn->set_custom_minimum_size(size);
+ gn->set_size(Size2(1, 1));
+ }
+
+ Ref<VisualShaderNodeExpression> expression_node = Object::cast_to<VisualShaderNodeExpression>(node.ptr());
+ if (!expression_node.is_null()) {
+ Control *text_box = expression_node->get_control(0);
+ Size2 box_size = size;
+ if (gn != NULL) {
+ if (box_size.x < 150 * EDSCALE || box_size.y < 0) {
+ box_size.x = gn->get_size().x;
+ }
+ }
+ box_size.x -= text_box->get_margin(Margin::MARGIN_LEFT);
+ box_size.x -= 28 * EDSCALE;
+ box_size.y -= text_box->get_margin(Margin::MARGIN_TOP);
+ box_size.y -= 28 * EDSCALE;
+ text_box->set_custom_minimum_size(Size2(box_size.x, box_size.y));
+ text_box->set_size(Size2(1, 1));
+ }
+}
+
+void VisualShaderEditor::_node_resized(const Vector2 &p_new_size, int p_type, int p_node) {
+
+ VisualShader::Type type = VisualShader::Type(p_type);
+ Ref<VisualShaderNodeGroupBase> node = visual_shader->get_node(type, p_node);
+ if (node.is_null()) {
+ return;
+ }
+
+ undo_redo->create_action(TTR("Resize VisualShader node"), UndoRedo::MERGE_ENDS);
+ undo_redo->add_do_method(this, "_set_node_size", p_type, p_node, p_new_size / EDSCALE);
+ undo_redo->add_undo_method(this, "_set_node_size", p_type, p_node, node->get_size());
+ undo_redo->commit_action();
+}
+
void VisualShaderEditor::_preview_select_port(int p_node, int p_port) {
VisualShader::Type type = VisualShader::Type(edit_type->get_selected());
@@ -623,6 +1053,52 @@ void VisualShaderEditor::_line_edit_focus_out(Object *line_edit, int p_node_id)
_line_edit_changed(text, line_edit, p_node_id);
}
+void VisualShaderEditor::_port_name_focus_out(Object *line_edit, int p_node_id, int p_port_id, bool p_output) {
+
+ VisualShader::Type type = VisualShader::Type(edit_type->get_selected());
+
+ Ref<VisualShaderNodeGroupBase> node = visual_shader->get_node(type, p_node_id);
+ ERR_FAIL_COND(!node.is_valid());
+
+ String text = Object::cast_to<LineEdit>(line_edit)->get_text();
+
+ if (!p_output) {
+ if (node->get_input_port_name(p_port_id) == text)
+ return;
+ } else {
+ if (node->get_output_port_name(p_port_id) == text)
+ return;
+ }
+
+ List<String> input_names;
+ List<String> output_names;
+
+ for (int i = 0; i < node->get_input_port_count(); i++) {
+ if (!p_output && i == p_port_id) continue;
+ input_names.push_back(node->get_input_port_name(i));
+ }
+ for (int i = 0; i < node->get_output_port_count(); i++) {
+ if (p_output && i == p_port_id) continue;
+ output_names.push_back(node->get_output_port_name(i));
+ }
+
+ String validated_name = visual_shader->validate_port_name(text, input_names, output_names);
+ if (validated_name == "") {
+ if (!p_output) {
+ Object::cast_to<LineEdit>(line_edit)->set_text(node->get_input_port_name(p_port_id));
+ } else {
+ Object::cast_to<LineEdit>(line_edit)->set_text(node->get_output_port_name(p_port_id));
+ }
+ return;
+ }
+
+ if (!p_output) {
+ _change_input_port_name(validated_name, line_edit, p_node_id, p_port_id);
+ } else {
+ _change_output_port_name(validated_name, line_edit, p_node_id, p_port_id);
+ }
+}
+
void VisualShaderEditor::_port_edited() {
VisualShader::Type type = VisualShader::Type(edit_type->get_selected());
@@ -757,6 +1233,12 @@ void VisualShaderEditor::_add_node(int p_idx, int p_op_idx) {
undo_redo->create_action(TTR("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);
+
+ VisualShaderNodeExpression *expr = Object::cast_to<VisualShaderNodeExpression>(vsnode.ptr());
+ if (expr) {
+ undo_redo->add_do_method(expr, "set_size", Size2(250 * EDSCALE, 150 * EDSCALE));
+ }
+
undo_redo->add_do_method(this, "_update_graph");
undo_redo->add_undo_method(this, "_update_graph");
undo_redo->commit_action();
@@ -831,10 +1313,25 @@ void VisualShaderEditor::_connection_to_empty(const String &p_from, int p_from_s
void VisualShaderEditor::_delete_request(int which) {
VisualShader::Type type = VisualShader::Type(edit_type->get_selected());
+ Ref<VisualShaderNode> node = Ref<VisualShaderNode>(visual_shader->get_node(type, which));
undo_redo->create_action(TTR("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);
+ undo_redo->add_undo_method(visual_shader.ptr(), "add_node", type, node, visual_shader->get_node_position(type, which), which);
+
+ // restore size, inputs and outputs if node is group
+ VisualShaderNodeGroupBase *group = Object::cast_to<VisualShaderNodeGroupBase>(node.ptr());
+ if (group) {
+ undo_redo->add_undo_method(group, "set_size", group->get_size());
+ undo_redo->add_undo_method(group, "set_inputs", group->get_inputs());
+ undo_redo->add_undo_method(group, "set_outputs", group->get_outputs());
+ }
+
+ // restore expression text if node is expression
+ VisualShaderNodeExpression *expression = Object::cast_to<VisualShaderNodeExpression>(node.ptr());
+ if (expression) {
+ undo_redo->add_undo_method(expression, "set_expression", expression->get_expression());
+ }
List<VisualShader::Connection> conns;
visual_shader->get_node_connections(type, &conns);
@@ -1022,6 +1519,19 @@ void VisualShaderEditor::_duplicate_nodes() {
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);
+ // duplicate size, inputs and outputs if node is group
+ Ref<VisualShaderNodeGroupBase> group = Object::cast_to<VisualShaderNodeGroupBase>(node.ptr());
+ if (!group.is_null()) {
+ undo_redo->add_do_method(dupli.ptr(), "set_size", group->get_size());
+ undo_redo->add_do_method(dupli.ptr(), "set_inputs", group->get_inputs());
+ undo_redo->add_do_method(dupli.ptr(), "set_outputs", group->get_outputs());
+ }
+ // duplicate expression text if node is expression
+ Ref<VisualShaderNodeExpression> expression = Object::cast_to<VisualShaderNodeExpression>(node.ptr());
+ if (!expression.is_null()) {
+ undo_redo->add_do_method(dupli.ptr(), "set_expression", expression->get_expression());
+ }
+
id_from++;
}
@@ -1030,7 +1540,7 @@ void VisualShaderEditor::_duplicate_nodes() {
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(visual_shader.ptr(), "connect_nodes_forced", type, connection_remap[E->get().from_node], E->get().from_port, connection_remap[E->get().to_node], E->get().to_port);
}
}
@@ -1073,8 +1583,25 @@ void VisualShaderEditor::_on_nodes_delete() {
undo_redo->create_action(TTR("Delete Nodes"));
for (List<int>::Element *F = to_erase.front(); F; F = F->next()) {
+
+ Ref<VisualShaderNode> node = visual_shader->get_node(type, F->get());
+
undo_redo->add_do_method(visual_shader.ptr(), "remove_node", type, F->get());
- undo_redo->add_undo_method(visual_shader.ptr(), "add_node", type, visual_shader->get_node(type, F->get()), visual_shader->get_node_position(type, F->get()), F->get());
+ undo_redo->add_undo_method(visual_shader.ptr(), "add_node", type, node, visual_shader->get_node_position(type, F->get()), F->get());
+
+ // restore size, inputs and outputs if node is group
+ VisualShaderNodeGroupBase *group = Object::cast_to<VisualShaderNodeGroupBase>(node.ptr());
+ if (group) {
+ undo_redo->add_undo_method(group, "set_size", group->get_size());
+ undo_redo->add_undo_method(group, "set_inputs", group->get_inputs());
+ undo_redo->add_undo_method(group, "set_outputs", group->get_outputs());
+ }
+
+ // restore expression text if node is expression
+ VisualShaderNodeExpression *expression = Object::cast_to<VisualShaderNodeExpression>(node.ptr());
+ if (expression) {
+ undo_redo->add_undo_method(expression, "set_expression", expression->get_expression());
+ }
}
List<VisualShader::Connection> conns;
@@ -1267,8 +1794,12 @@ void VisualShaderEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da
}
void VisualShaderEditor::_bind_methods() {
+ ClassDB::bind_method("_rebuild", &VisualShaderEditor::_rebuild);
ClassDB::bind_method("_update_graph", &VisualShaderEditor::_update_graph);
ClassDB::bind_method("_update_options_menu", &VisualShaderEditor::_update_options_menu);
+ ClassDB::bind_method("_start_rebuild_timer", &VisualShaderEditor::_start_rebuild_timer);
+ ClassDB::bind_method("_set_expression", &VisualShaderEditor::_set_expression);
+ ClassDB::bind_method("_rebuild_timeout", &VisualShaderEditor::_rebuild_timeout);
ClassDB::bind_method("_add_node", &VisualShaderEditor::_add_node);
ClassDB::bind_method("_node_dragged", &VisualShaderEditor::_node_dragged);
ClassDB::bind_method("_connection_request", &VisualShaderEditor::_connection_request);
@@ -1283,11 +1814,22 @@ void VisualShaderEditor::_bind_methods() {
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("_port_name_focus_out", &VisualShaderEditor::_port_name_focus_out);
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);
ClassDB::bind_method("_graph_gui_input", &VisualShaderEditor::_graph_gui_input);
+ ClassDB::bind_method("_add_input_port", &VisualShaderEditor::_add_input_port);
+ ClassDB::bind_method("_change_input_port_type", &VisualShaderEditor::_change_input_port_type);
+ ClassDB::bind_method("_change_input_port_name", &VisualShaderEditor::_change_input_port_name);
+ ClassDB::bind_method("_remove_input_port", &VisualShaderEditor::_remove_input_port);
+ ClassDB::bind_method("_add_output_port", &VisualShaderEditor::_add_output_port);
+ ClassDB::bind_method("_change_output_port_type", &VisualShaderEditor::_change_output_port_type);
+ ClassDB::bind_method("_change_output_port_name", &VisualShaderEditor::_change_output_port_name);
+ ClassDB::bind_method("_remove_output_port", &VisualShaderEditor::_remove_output_port);
+ ClassDB::bind_method("_node_resized", &VisualShaderEditor::_node_resized);
+ ClassDB::bind_method("_set_node_size", &VisualShaderEditor::_set_node_size);
ClassDB::bind_method(D_METHOD("get_drag_data_fw"), &VisualShaderEditor::get_drag_data_fw);
ClassDB::bind_method(D_METHOD("can_drop_data_fw"), &VisualShaderEditor::can_drop_data_fw);
@@ -1311,6 +1853,7 @@ VisualShaderEditor::VisualShaderEditor() {
updating = false;
saved_node_pos_dirty = false;
saved_node_pos = Point2(0, 0);
+ ShaderLanguage::get_keyword_list(&keyword_list);
graph = memnew(GraphEdit);
add_child(graph);
@@ -1710,6 +2253,7 @@ VisualShaderEditor::VisualShaderEditor() {
// SPECIAL
+ add_options.push_back(AddOption("Expression", "Special", "", "VisualShaderNodeExpression", TTR("Custom Godot Shader Language expression, with custom amount of input and output ports. This is a direct injection of code into the vertex/fragment/light function, do not use it to write the function declarations inside.")));
add_options.push_back(AddOption("Fresnel", "Special", "", "VisualShaderNodeFresnel", TTR("Returns falloff based on the dot product of surface normal and view direction of camera (pass associated inputs to it)."), -1, VisualShaderNode::PORT_TYPE_SCALAR));
add_options.push_back(AddOption("ScalarDerivativeFunc", "Special", "Common", "VisualShaderNodeScalarDerivativeFunc", TTR("(GLES3 only) (Fragment/Light mode only) Scalar derivative function."), -1, VisualShaderNode::PORT_TYPE_SCALAR, VisualShader::TYPE_FRAGMENT | VisualShader::TYPE_LIGHT));
@@ -1743,6 +2287,13 @@ VisualShaderEditor::VisualShaderEditor() {
add_child(property_editor);
property_editor->connect("variant_changed", this, "_port_edited");
+
+ // BUILD TIMER FOR EXPRESSION NODES
+
+ build_timer = memnew(Timer);
+ add_child(build_timer);
+ build_timer->connect("timeout", this, "_rebuild_timeout");
+ build_timer->set_one_shot(true);
}
void VisualShaderEditorPlugin::edit(Object *p_object) {
diff --git a/editor/plugins/visual_shader_editor_plugin.h b/editor/plugins/visual_shader_editor_plugin.h
index eb0dee7594..b1cf2e7244 100644
--- a/editor/plugins/visual_shader_editor_plugin.h
+++ b/editor/plugins/visual_shader_editor_plugin.h
@@ -84,6 +84,8 @@ class VisualShaderEditor : public VBoxContainer {
LineEdit *node_filter;
RichTextLabel *node_desc;
+ Timer *build_timer;
+
void _tools_menu_option(int p_idx);
void _show_members_dialog(bool at_mouse_pos);
@@ -128,6 +130,7 @@ class VisualShaderEditor : public VBoxContainer {
};
Vector<AddOption> add_options;
+ List<String> keyword_list;
void _draw_color_over_button(Object *obj, Color p_color);
@@ -160,14 +163,35 @@ class VisualShaderEditor : public VBoxContainer {
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 _port_name_focus_out(Object *line_edit, int p_node_id, int p_port_id, bool p_output);
+
void _duplicate_nodes();
Vector<Ref<VisualShaderNodePlugin> > plugins;
void _mode_selected(int p_id);
+ void _rebuild();
void _input_select_item(Ref<VisualShaderNodeInput> input, String name);
+ void _add_input_port(int p_node, int p_port, int p_type, const String &p_name);
+ void _remove_input_port(int p_node, int p_port);
+ void _change_input_port_type(int p_type, int p_node, int p_port);
+ void _change_input_port_name(const String &p_text, Object *line_edit, int p_node, int p_port);
+
+ void _add_output_port(int p_node, int p_port, int p_type, const String &p_name);
+ void _remove_output_port(int p_node, int p_port);
+ void _change_output_port_type(int p_type, int p_node, int p_port);
+ void _change_output_port_name(const String &p_text, Object *line_edit, int p_node, int p_port);
+
+ void _start_rebuild_timer(int p_delay);
+
+ void _set_expression(int p_node);
+ void _rebuild_timeout();
+
+ void _set_node_size(int p_type, int p_node, const Size2 &p_size);
+ void _node_resized(const Vector2 &p_new_size, int p_type, int p_node);
+
void _preview_select_port(int p_node, int p_port);
void _graph_gui_input(const Ref<InputEvent> p_event);