Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add connection related functionality to VisualShader (deleting, dropping/inserting a node) #83510

Merged
merged 1 commit into from
Jan 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
227 changes: 222 additions & 5 deletions editor/plugins/visual_shader_editor_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3191,6 +3191,15 @@ void VisualShaderEditor::_add_node(int p_idx, const Vector<Variant> &p_ops, Stri

bool created_expression_port = false;

// A node is inserted in an already present connection.
if (from_node != -1 && from_slot != -1 && to_node != -1 && to_slot != -1) {
undo_redo->add_do_method(visual_shader.ptr(), "disconnect_nodes", type, from_node, from_slot, to_node, to_slot);
undo_redo->add_undo_method(visual_shader.ptr(), "connect_nodes", type, from_node, from_slot, to_node, to_slot);
undo_redo->add_do_method(graph_plugin.ptr(), "disconnect_nodes", type, from_node, from_slot, to_node, to_slot);
undo_redo->add_undo_method(graph_plugin.ptr(), "connect_nodes", type, from_node, from_slot, to_node, to_slot);
}

// Create a connection from the new node to an input port of an existing one.
if (to_node != -1 && to_slot != -1) {
VisualShaderNode::PortType input_port_type = visual_shader->get_node(type, to_node)->get_input_port_type(to_slot);

Expand Down Expand Up @@ -3260,7 +3269,10 @@ void VisualShaderEditor::_add_node(int p_idx, const Vector<Variant> &p_ops, Stri
}
}
}
} else if (from_node != -1 && from_slot != -1) {
}

// Create a connection from the output port of an existing node to the new one.
if (from_node != -1 && from_slot != -1) {
VisualShaderNode::PortType output_port_type = visual_shader->get_node(type, from_node)->get_output_port_type(from_slot);

if (expr && expr->is_editable()) {
Expand Down Expand Up @@ -3483,8 +3495,11 @@ void VisualShaderEditor::_nodes_dragged() {
undo_redo->add_undo_method(graph_plugin.ptr(), "set_node_position", E.type, E.node, E.from);
}

drag_buffer.clear();
undo_redo->commit_action();

_handle_node_drop_on_connection();

drag_buffer.clear();
}

void VisualShaderEditor::_connection_request(const String &p_from, int p_from_index, const String &p_to, int p_to_index) {
Expand Down Expand Up @@ -3564,6 +3579,132 @@ void VisualShaderEditor::_connection_from_empty(const String &p_to, int p_to_slo
_show_members_dialog(true, input_port_type, output_port_type);
}

bool VisualShaderEditor::_check_node_drop_on_connection(const Vector2 &p_position, Ref<GraphEdit::Connection> *r_closest_connection, int *r_from_port, int *r_to_port) {
VisualShader::Type shader_type = get_current_shader_type();

// Get selected graph node.
Ref<VisualShaderNode> selected_vsnode;
int selected_node_id = -1;
int selected_node_count = 0;
Rect2 selected_node_rect;

for (int i = 0; i < graph->get_child_count(); i++) {
GraphNode *graph_node = Object::cast_to<GraphNode>(graph->get_child(i));
if (graph_node && graph_node->is_selected()) {
selected_node_id = String(graph_node->get_name()).to_int();
Ref<VisualShaderNode> vsnode = visual_shader->get_node(shader_type, selected_node_id);
if (!vsnode->is_closable()) {
continue;
}

selected_node_count += 1;

Ref<VisualShaderNode> node = visual_shader->get_node(shader_type, selected_node_id);
selected_vsnode = node;
selected_node_rect = graph_node->get_rect();
}
}

// Only a single node - which has both input and output ports but is not connected yet - can be inserted.
if (selected_node_count != 1 || !selected_vsnode.is_valid()) {
return false;
}

// Check whether the dragged node was dropped over a connection.
List<Ref<GraphEdit::Connection>> intersecting_connections = graph->get_connections_intersecting_with_rect(selected_node_rect);

if (intersecting_connections.is_empty()) {
return false;
}

Ref<GraphEdit::Connection> intersecting_connection = intersecting_connections.front()->get();

if (selected_vsnode->is_any_port_connected() || selected_vsnode->get_input_port_count() == 0 || selected_vsnode->get_output_port_count() == 0) {
return false;
}

VisualShaderNode::PortType original_port_type_from = visual_shader->get_node(shader_type, String(intersecting_connection->from_node).to_int())->get_output_port_type(intersecting_connection->from_port);
VisualShaderNode::PortType original_port_type_to = visual_shader->get_node(shader_type, String(intersecting_connection->to_node).to_int())->get_input_port_type(intersecting_connection->to_port);

// Searching for the default port or the first compatible input port of the node to insert.
int _to_port = -1;
for (int i = 0; i < selected_vsnode->get_input_port_count(); i++) {
if (visual_shader->is_port_types_compatible(original_port_type_from, selected_vsnode->get_input_port_type(i))) {
if (i == selected_vsnode->get_default_input_port(original_port_type_from)) {
_to_port = i;
break;
} else if (_to_port == -1) {
_to_port = i;
}
}
}

// Searching for the first compatible output port of the node to insert.
int _from_port = -1;
for (int i = 0; i < selected_vsnode->get_output_port_count(); i++) {
if (visual_shader->is_port_types_compatible(selected_vsnode->get_output_port_type(i), original_port_type_to)) {
_from_port = i;
break;
}
}

if (_to_port == -1 || _from_port == -1) {
return false;
}

if (r_closest_connection != nullptr) {
*r_closest_connection = intersecting_connection;
}
if (r_from_port != nullptr) {
*r_from_port = _from_port;
}
if (r_to_port != nullptr) {
*r_to_port = _to_port;
}

return true;
}

void VisualShaderEditor::_handle_node_drop_on_connection() {
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Insert node"));

// Check whether the dragged node was dropped over a connection.
Ref<GraphEdit::Connection> closest_connection;
int _from_port = -1;
int _to_port = -1;

if (!_check_node_drop_on_connection(graph->get_local_mouse_position(), &closest_connection, &_from_port, &_to_port)) {
return;
}

int selected_node_id = drag_buffer[0].node;
VisualShader::Type shader_type = get_current_shader_type();
Ref<VisualShaderNode> selected_vsnode = visual_shader->get_node(shader_type, selected_node_id);

// Delete the old connection.
undo_redo->add_do_method(visual_shader.ptr(), "disconnect_nodes", shader_type, String(closest_connection->from_node).to_int(), closest_connection->from_port, String(closest_connection->to_node).to_int(), closest_connection->to_port);
undo_redo->add_undo_method(visual_shader.ptr(), "connect_nodes", shader_type, String(closest_connection->from_node).to_int(), closest_connection->from_port, String(closest_connection->to_node).to_int(), closest_connection->to_port);
undo_redo->add_do_method(graph_plugin.ptr(), "disconnect_nodes", shader_type, String(closest_connection->from_node).to_int(), closest_connection->from_port, String(closest_connection->to_node).to_int(), closest_connection->to_port);
undo_redo->add_undo_method(graph_plugin.ptr(), "connect_nodes", shader_type, String(closest_connection->from_node).to_int(), closest_connection->from_port, String(closest_connection->to_node).to_int(), closest_connection->to_port);

// Add the connection to the dropped node.
undo_redo->add_do_method(visual_shader.ptr(), "connect_nodes", shader_type, String(closest_connection->from_node).to_int(), closest_connection->from_port, selected_node_id, _to_port);
undo_redo->add_undo_method(visual_shader.ptr(), "disconnect_nodes", shader_type, String(closest_connection->from_node).to_int(), closest_connection->from_port, selected_node_id, _to_port);
undo_redo->add_do_method(graph_plugin.ptr(), "connect_nodes", shader_type, String(closest_connection->from_node).to_int(), closest_connection->from_port, selected_node_id, _to_port);
undo_redo->add_undo_method(graph_plugin.ptr(), "disconnect_nodes", shader_type, String(closest_connection->from_node).to_int(), closest_connection->from_port, selected_node_id, _to_port);

// Add the connection from the dropped node.
undo_redo->add_do_method(visual_shader.ptr(), "connect_nodes", shader_type, selected_node_id, _from_port, String(closest_connection->to_node).to_int(), closest_connection->to_port);
undo_redo->add_undo_method(visual_shader.ptr(), "disconnect_nodes", shader_type, selected_node_id, _from_port, String(closest_connection->to_node).to_int(), closest_connection->to_port);
undo_redo->add_do_method(graph_plugin.ptr(), "connect_nodes", shader_type, selected_node_id, _from_port, String(closest_connection->to_node).to_int(), closest_connection->to_port);
undo_redo->add_undo_method(graph_plugin.ptr(), "disconnect_nodes", shader_type, selected_node_id, _from_port, String(closest_connection->to_node).to_int(), closest_connection->to_port);

undo_redo->commit_action();

call_deferred(SNAME("_update_graph"));
}

void VisualShaderEditor::_delete_nodes(int p_type, const List<int> &p_nodes) {
VisualShader::Type type = VisualShader::Type(p_type);
List<VisualShader::Connection> conns;
Expand Down Expand Up @@ -3923,9 +4064,19 @@ void VisualShaderEditor::_node_selected(Object *p_node) {
}

void VisualShaderEditor::_graph_gui_input(const Ref<InputEvent> &p_event) {
Ref<InputEventMouseMotion> mm = p_event;
Ref<InputEventMouseButton> mb = p_event;
VisualShader::Type type = get_current_shader_type();

// Highlight valid connection on which a node can be dropped.
if (mm.is_valid() && mm->get_button_mask().has_flag(MouseButtonMask::LEFT)) {
Ref<GraphEdit::Connection> closest_connection;
graph->reset_all_connection_activity();
if (_check_node_drop_on_connection(graph->get_local_mouse_position(), &closest_connection)) {
graph->set_connection_activity(closest_connection->from_node, closest_connection->from_port, closest_connection->to_node, closest_connection->to_port, 1.0);
}
}

Ref<VisualShaderNode> selected_vsnode;
// Right click actions.
if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MouseButton::RIGHT) {
Expand Down Expand Up @@ -3981,7 +4132,16 @@ void VisualShaderEditor::_graph_gui_input(const Ref<InputEvent> &p_event) {
}
}

if (selected_closable_graph_elements.is_empty() && copy_buffer_empty) {
menu_point = graph->get_local_mouse_position();
Point2 gpos = get_screen_position() + get_local_mouse_position();

Ref<GraphEdit::Connection> closest_connection = graph->get_closest_connection_at_point(menu_point);
if (closest_connection.is_valid()) {
clicked_connection = closest_connection;
connection_popup_menu->set_position(gpos);
connection_popup_menu->reset_size();
connection_popup_menu->popup();
} else if (selected_closable_graph_elements.is_empty() && copy_buffer_empty) {
_show_members_dialog(true);
} else {
popup_menu->set_item_disabled(NodeMenuOptions::CUT, selected_closable_graph_elements.is_empty());
Expand Down Expand Up @@ -4053,8 +4213,6 @@ void VisualShaderEditor::_graph_gui_input(const Ref<InputEvent> &p_event) {
popup_menu->add_item(TTR("Set Comment Description"), NodeMenuOptions::SET_COMMENT_DESCRIPTION);
}

menu_point = graph->get_local_mouse_position();
Point2 gpos = get_screen_position() + get_local_mouse_position();
popup_menu->set_position(gpos);
popup_menu->reset_size();
popup_menu->popup();
Expand Down Expand Up @@ -4757,6 +4915,27 @@ void VisualShaderEditor::_member_create() {
TreeItem *item = members->get_selected();
if (item != nullptr && item->has_meta("id")) {
int idx = members->get_selected()->get_meta("id");
if (connection_node_insert_requested) {
from_node = String(clicked_connection->from_node).to_int();
from_slot = clicked_connection->from_port;
to_node = String(clicked_connection->to_node).to_int();
to_slot = clicked_connection->to_port;

connection_node_insert_requested = false;

saved_node_pos_dirty = true;

// Find both graph nodes and get their positions.
GraphNode *from_graph_element = Object::cast_to<GraphNode>(graph->get_node(itos(from_node)));
GraphNode *to_graph_element = Object::cast_to<GraphNode>(graph->get_node(itos(to_node)));

ERR_FAIL_NULL(from_graph_element);
ERR_FAIL_NULL(to_graph_element);

// Since the size of the node to add is not known yet, it's not possible to center it exactly.
float zoom = graph->get_zoom();
saved_node_pos = 0.5 * (from_graph_element->get_position() + zoom * from_graph_element->get_output_port_position(from_slot) + to_graph_element->get_position() + zoom * to_graph_element->get_input_port_position(to_slot));
}
_add_node(idx, add_options[idx].ops);
members_dialog->hide();
}
Expand All @@ -4767,6 +4946,7 @@ void VisualShaderEditor::_member_cancel() {
to_slot = -1;
from_node = -1;
from_slot = -1;
connection_node_insert_requested = false;
}

void VisualShaderEditor::_update_varying_tree() {
Expand Down Expand Up @@ -4938,6 +5118,37 @@ void VisualShaderEditor::_node_menu_id_pressed(int p_idx) {
}
}

void VisualShaderEditor::_connection_menu_id_pressed(int p_idx) {
switch (p_idx) {
case ConnectionMenuOptions::DISCONNECT: {
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Disconnect"));
undo_redo->add_do_method(visual_shader.ptr(), "disconnect_nodes", get_current_shader_type(), String(clicked_connection->from_node).to_int(), clicked_connection->from_port, String(clicked_connection->to_node).to_int(), clicked_connection->to_port);
undo_redo->add_undo_method(visual_shader.ptr(), "connect_nodes", get_current_shader_type(), String(clicked_connection->from_node).to_int(), clicked_connection->from_port, String(clicked_connection->to_node).to_int(), clicked_connection->to_port);
undo_redo->add_do_method(graph_plugin.ptr(), "disconnect_nodes", get_current_shader_type(), String(clicked_connection->from_node).to_int(), clicked_connection->from_port, String(clicked_connection->to_node).to_int(), clicked_connection->to_port);
undo_redo->add_undo_method(graph_plugin.ptr(), "connect_nodes", get_current_shader_type(), String(clicked_connection->from_node).to_int(), clicked_connection->from_port, String(clicked_connection->to_node).to_int(), clicked_connection->to_port);
undo_redo->commit_action();
} break;
case ConnectionMenuOptions::INSERT_NEW_NODE: {
VisualShaderNode::PortType input_port_type = VisualShaderNode::PORT_TYPE_MAX;
VisualShaderNode::PortType output_port_type = VisualShaderNode::PORT_TYPE_MAX;
Ref<VisualShaderNode> node1 = visual_shader->get_node(get_current_shader_type(), String(clicked_connection->from_node).to_int());
if (node1.is_valid()) {
output_port_type = node1->get_output_port_type(from_slot);
}
Ref<VisualShaderNode> node2 = visual_shader->get_node(get_current_shader_type(), String(clicked_connection->to_node).to_int());
if (node2.is_valid()) {
input_port_type = node2->get_input_port_type(to_slot);
}

connection_node_insert_requested = true;
_show_members_dialog(true, input_port_type, output_port_type);
} break;
default:
break;
}
}

Variant VisualShaderEditor::get_drag_data_fw(const Point2 &p_point, Control *p_from) {
if (p_from == members) {
TreeItem *it = members->get_item_at_position(p_point);
Expand Down Expand Up @@ -5417,6 +5628,12 @@ VisualShaderEditor::VisualShaderEditor() {
popup_menu->add_item(TTR("Clear Copy Buffer"), NodeMenuOptions::CLEAR_COPY_BUFFER);
popup_menu->connect("id_pressed", callable_mp(this, &VisualShaderEditor::_node_menu_id_pressed));

connection_popup_menu = memnew(PopupMenu);
add_child(connection_popup_menu);
connection_popup_menu->add_item(TTR("Disconnect"), ConnectionMenuOptions::DISCONNECT);
connection_popup_menu->add_item(TTR("Insert New Node"), ConnectionMenuOptions::INSERT_NEW_NODE);
connection_popup_menu->connect("id_pressed", callable_mp(this, &VisualShaderEditor::_connection_menu_id_pressed));

///////////////////////////////////////
// SHADER NODES TREE
///////////////////////////////////////
Expand Down
14 changes: 13 additions & 1 deletion editor/plugins/visual_shader_editor_plugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,13 @@
#include "editor/editor_plugin.h"
#include "editor/editor_properties.h"
#include "editor/plugins/editor_resource_conversion_plugin.h"
#include "scene/gui/graph_edit.h"
#include "scene/resources/syntax_highlighter.h"
#include "scene/resources/visual_shader.h"

class CodeEdit;
class ColorPicker;
class CurveEditor;
class GraphEdit;
class GraphElement;
class MenuButton;
class PopupPanel;
Expand Down Expand Up @@ -203,6 +203,7 @@ class VisualShaderEditor : public VBoxContainer {
VisualShaderNode::PortType members_input_port_type = VisualShaderNode::PORT_TYPE_MAX;
VisualShaderNode::PortType members_output_port_type = VisualShaderNode::PORT_TYPE_MAX;
PopupMenu *popup_menu = nullptr;
PopupMenu *connection_popup_menu = nullptr;
PopupMenu *constants_submenu = nullptr;
MenuButton *tools = nullptr;

Expand Down Expand Up @@ -282,6 +283,11 @@ class VisualShaderEditor : public VBoxContainer {
SET_COMMENT_DESCRIPTION,
};

enum ConnectionMenuOptions {
INSERT_NEW_NODE,
DISCONNECT,
};

enum class VaryingMenuOptions {
ADD,
REMOVE,
Expand Down Expand Up @@ -397,6 +403,9 @@ class VisualShaderEditor : public VBoxContainer {
int from_node = -1;
int from_slot = -1;

Ref<GraphEdit::Connection> clicked_connection;
bool connection_node_insert_requested = false;

HashSet<int> selected_constants;
HashSet<int> selected_parameters;
int selected_comment = -1;
Expand All @@ -409,6 +418,8 @@ class VisualShaderEditor : public VBoxContainer {

void _connection_to_empty(const String &p_from, int p_from_slot, const Vector2 &p_release_position);
void _connection_from_empty(const String &p_to, int p_to_slot, const Vector2 &p_release_position);
bool _check_node_drop_on_connection(const Vector2 &p_position, Ref<GraphEdit::Connection> *r_closest_connection, int *r_node_id = nullptr, int *r_to_port = nullptr);
void _handle_node_drop_on_connection();

void _comment_title_popup_show(const Point2 &p_position, int p_node_id);
void _comment_title_popup_hide();
Expand Down Expand Up @@ -501,6 +512,7 @@ class VisualShaderEditor : public VBoxContainer {

Vector2 menu_point;
void _node_menu_id_pressed(int p_idx);
void _connection_menu_id_pressed(int p_idx);

Variant get_drag_data_fw(const Point2 &p_point, Control *p_from);
bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const;
Expand Down
2 changes: 1 addition & 1 deletion editor/themes/editor_theme_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1365,7 +1365,7 @@ void EditorThemeManager::_populate_standard_styles(const Ref<Theme> &p_theme, Th
}
p_theme->set_color("selection_fill", "GraphEdit", p_theme->get_color(SNAME("box_selection_fill_color"), EditorStringName(Editor)));
p_theme->set_color("selection_stroke", "GraphEdit", p_theme->get_color(SNAME("box_selection_stroke_color"), EditorStringName(Editor)));
p_theme->set_color("activity", "GraphEdit", p_config.accent_color);
p_theme->set_color("activity", "GraphEdit", p_config.dark_theme ? Color(1, 1, 1) : Color(0, 0, 0));

p_theme->set_color("connection_hover_tint_color", "GraphEdit", p_config.dark_theme ? Color(0, 0, 0, 0.3) : Color(1, 1, 1, 0.3));
p_theme->set_color("connection_valid_target_tint_color", "GraphEdit", p_config.dark_theme ? Color(1, 1, 1, 0.4) : Color(0, 0, 0, 0.4));
Expand Down
14 changes: 14 additions & 0 deletions scene/resources/visual_shader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,20 @@ void VisualShaderNode::set_input_port_connected(int p_port, bool p_connected) {
connected_input_ports[p_port] = p_connected;
}

bool VisualShaderNode::is_any_port_connected() const {
for (const KeyValue<int, bool> &E : connected_input_ports) {
if (E.value) {
return true;
}
}
for (const KeyValue<int, int> &E : connected_output_ports) {
if (E.value > 0) {
return true;
}
}
return false;
}

bool VisualShaderNode::is_generate_input_var(int p_port) const {
return true;
}
Expand Down
Loading
Loading