Merge pull request #90534 from Geometror/vs-reroute-node
[VisualShader] Add reroute node and improve port drawing
This commit is contained in:
commit
dcd6db8680
|
@ -267,6 +267,9 @@
|
|||
</method>
|
||||
</methods>
|
||||
<members>
|
||||
<member name="ignore_invalid_connection_type" type="bool" setter="set_ignore_invalid_connection_type" getter="is_ignoring_valid_connection_type" default="false">
|
||||
If [code]true[/code], you can connect ports with different types, even if the connection was not explicitly allowed in the parent [GraphEdit].
|
||||
</member>
|
||||
<member name="mouse_filter" type="int" setter="set_mouse_filter" getter="get_mouse_filter" overrides="Control" enum="Control.MouseFilter" default="0" />
|
||||
<member name="title" type="String" setter="set_title" getter="get_title" default="""">
|
||||
The text displayed in the GraphNode's title bar.
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<class name="VisualShaderNodeReroute" inherits="VisualShaderNode" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
|
||||
<brief_description>
|
||||
A node that allows rerouting a connection within the visual shader graph.
|
||||
</brief_description>
|
||||
<description>
|
||||
Automatically adapts its port type to the type of the incoming connection and ensures valid connections.
|
||||
</description>
|
||||
<tutorials>
|
||||
</tutorials>
|
||||
<methods>
|
||||
<method name="get_port_type" qualifiers="const">
|
||||
<return type="int" enum="VisualShaderNode.PortType" />
|
||||
<description>
|
||||
Returns the port type of the reroute node.
|
||||
</description>
|
||||
</method>
|
||||
</methods>
|
||||
</class>
|
|
@ -45,6 +45,7 @@
|
|||
#include "editor/plugins/curve_editor_plugin.h"
|
||||
#include "editor/plugins/shader_editor_plugin.h"
|
||||
#include "editor/themes/editor_scale.h"
|
||||
#include "scene/animation/tween.h"
|
||||
#include "scene/gui/button.h"
|
||||
#include "scene/gui/check_box.h"
|
||||
#include "scene/gui/code_edit.h"
|
||||
|
@ -104,6 +105,83 @@ void VisualShaderNodePlugin::_bind_methods() {
|
|||
|
||||
///////////////////
|
||||
|
||||
void VSGraphNode::_draw_port(int p_slot_index, Point2i p_pos, bool p_left, const Color &p_color, const Color &p_rim_color) {
|
||||
Ref<Texture2D> port_icon = p_left ? get_slot_custom_icon_left(p_slot_index) : get_slot_custom_icon_right(p_slot_index);
|
||||
|
||||
Point2 icon_offset;
|
||||
if (!port_icon.is_valid()) {
|
||||
port_icon = get_theme_icon(SNAME("port"), SNAME("GraphNode"));
|
||||
}
|
||||
|
||||
icon_offset = -port_icon->get_size() * 0.5;
|
||||
|
||||
// Draw "shadow"/outline in the connection rim color.
|
||||
draw_texture_rect(port_icon, Rect2(p_pos + icon_offset - Size2(2, 2), port_icon->get_size() + Size2(4, 4)), false, p_rim_color);
|
||||
draw_texture(port_icon, p_pos + icon_offset, p_color);
|
||||
}
|
||||
|
||||
void VSGraphNode::draw_port(int p_slot_index, Point2i p_pos, bool p_left, const Color &p_color) {
|
||||
Color rim_color = get_theme_color(SNAME("connection_rim_color"), SNAME("GraphEdit"));
|
||||
_draw_port(p_slot_index, p_pos, p_left, p_color, rim_color);
|
||||
}
|
||||
|
||||
///////////////////
|
||||
|
||||
void VSRerouteNode::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_READY: {
|
||||
connect("mouse_entered", callable_mp(this, &VSRerouteNode::_on_mouse_entered));
|
||||
connect("mouse_exited", callable_mp(this, &VSRerouteNode::_on_mouse_exited));
|
||||
} break;
|
||||
case NOTIFICATION_DRAW: {
|
||||
Vector2 offset = Vector2(0, -16);
|
||||
Color drag_bg_color = get_theme_color(SNAME("drag_background"), SNAME("VSRerouteNode"));
|
||||
draw_circle(get_size() * 0.5 + offset, 16, Color(drag_bg_color, selected ? 1 : icon_opacity));
|
||||
|
||||
Ref<Texture2D> icon = get_theme_icon(SNAME("ToolMove"), SNAME("EditorIcons"));
|
||||
Point2 icon_offset = -icon->get_size() * 0.5 + get_size() * 0.5 + offset;
|
||||
draw_texture(icon, icon_offset, Color(1, 1, 1, selected ? 1 : icon_opacity));
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void VSRerouteNode::draw_port(int p_slot_index, Point2i p_pos, bool p_left, const Color &p_color) {
|
||||
Color rim_color = selected ? get_theme_color("selected_rim_color", "VSRerouteNode") : get_theme_color("connection_rim_color", "GraphEdit");
|
||||
_draw_port(p_slot_index, p_pos, p_left, p_color, rim_color);
|
||||
}
|
||||
|
||||
VSRerouteNode::VSRerouteNode() {
|
||||
Label *title_lbl = Object::cast_to<Label>(get_titlebar_hbox()->get_child(0));
|
||||
title_lbl->hide();
|
||||
|
||||
const Size2 size = Size2(32, 32) * EDSCALE;
|
||||
|
||||
Control *slot_area = memnew(Control);
|
||||
slot_area->set_custom_minimum_size(size);
|
||||
slot_area->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
|
||||
add_child(slot_area);
|
||||
|
||||
// Lay the input and output ports on top of each other to create the illusion of a single port.
|
||||
add_theme_constant_override("port_h_offset", size.width / 2);
|
||||
}
|
||||
|
||||
void VSRerouteNode::set_icon_opacity(float p_opacity) {
|
||||
icon_opacity = p_opacity;
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
void VSRerouteNode::_on_mouse_entered() {
|
||||
Ref<Tween> tween = create_tween();
|
||||
tween->tween_method(callable_mp(this, &VSRerouteNode::set_icon_opacity), 0.0, 1.0, FADE_ANIMATION_LENGTH_SEC);
|
||||
}
|
||||
|
||||
void VSRerouteNode::_on_mouse_exited() {
|
||||
Ref<Tween> tween = create_tween();
|
||||
tween->tween_method(callable_mp(this, &VSRerouteNode::set_icon_opacity), 1.0, 0.0, FADE_ANIMATION_LENGTH_SEC);
|
||||
}
|
||||
|
||||
///////////////////
|
||||
|
||||
VisualShaderGraphPlugin::VisualShaderGraphPlugin() {
|
||||
vs_msdf_fonts_theme.instantiate();
|
||||
}
|
||||
|
@ -376,6 +454,15 @@ void VisualShaderGraphPlugin::set_frame_autoshrink_enabled(VisualShader::Type p_
|
|||
frame->set_autoshrink_enabled(p_enable);
|
||||
}
|
||||
|
||||
void VisualShaderGraphPlugin::update_reroute_nodes() {
|
||||
for (const KeyValue<int, Link> &E : links) {
|
||||
Ref<VisualShaderNodeReroute> reroute_node = Object::cast_to<VisualShaderNodeReroute>(E.value.visual_node);
|
||||
if (reroute_node.is_valid()) {
|
||||
update_node(visual_shader->get_shader_type(), E.key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ref<Script> VisualShaderGraphPlugin::get_node_script(int p_node_id) const {
|
||||
if (!links.has(p_node_id)) {
|
||||
return Ref<Script>();
|
||||
|
@ -572,6 +659,9 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id, bool
|
|||
bool is_expression = expression_node.is_valid();
|
||||
String expression = "";
|
||||
|
||||
Ref<VisualShaderNodeReroute> reroute_node = vsnode;
|
||||
bool is_reroute = reroute_node.is_valid();
|
||||
|
||||
Ref<VisualShaderNodeCustom> custom_node = vsnode;
|
||||
if (custom_node.is_valid()) {
|
||||
custom_node->_set_initialized(true);
|
||||
|
@ -582,8 +672,12 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id, bool
|
|||
GraphFrame *frame = memnew(GraphFrame);
|
||||
frame->set_title(vsnode->get_caption());
|
||||
node = frame;
|
||||
} else if (is_reroute) {
|
||||
VSRerouteNode *reroute_gnode = memnew(VSRerouteNode);
|
||||
reroute_gnode->set_ignore_invalid_connection_type(true);
|
||||
node = reroute_gnode;
|
||||
} else {
|
||||
GraphNode *gnode = memnew(GraphNode);
|
||||
VSGraphNode *gnode = memnew(VSGraphNode);
|
||||
gnode->set_title(vsnode->get_caption());
|
||||
node = gnode;
|
||||
}
|
||||
|
@ -598,7 +692,7 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id, bool
|
|||
node->set_theme(vs_msdf_fonts_theme);
|
||||
|
||||
// Set the node's titlebar color based on its category.
|
||||
if (vsnode->get_category() != VisualShaderNode::CATEGORY_NONE) {
|
||||
if (vsnode->get_category() != VisualShaderNode::CATEGORY_NONE && !is_frame && !is_reroute) {
|
||||
Ref<StyleBoxFlat> sb_colored = editor->get_theme_stylebox("titlebar", "GraphNode")->duplicate();
|
||||
sb_colored->set_bg_color(category_color[vsnode->get_category()]);
|
||||
node->add_theme_style_override("titlebar", sb_colored);
|
||||
|
@ -685,9 +779,11 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id, bool
|
|||
return;
|
||||
}
|
||||
|
||||
Control *content_offset = memnew(Control);
|
||||
content_offset->set_custom_minimum_size(Size2(0, 5 * EDSCALE));
|
||||
node->add_child(content_offset);
|
||||
if (!is_reroute) {
|
||||
Control *content_offset = memnew(Control);
|
||||
content_offset->set_custom_minimum_size(Size2(0, 5 * EDSCALE));
|
||||
node->add_child(content_offset);
|
||||
}
|
||||
|
||||
if (is_group) {
|
||||
port_offset += 1;
|
||||
|
@ -696,7 +792,9 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id, bool
|
|||
// Set the minimum width of a node based on the preview size to avoid a resize when toggling the preview.
|
||||
Ref<StyleBoxFlat> graph_node_stylebox = graph->get_theme_stylebox("panel", "GraphNode");
|
||||
int port_preview_size = EDITOR_GET("editors/visual_editors/visual_shader/port_preview_size");
|
||||
node->set_custom_minimum_size(Size2((Math::ceil(graph_node_stylebox->get_minimum_size().width) + port_preview_size) * EDSCALE, 0));
|
||||
if (!is_frame && !is_reroute) {
|
||||
node->set_custom_minimum_size(Size2((Math::ceil(graph_node_stylebox->get_minimum_size().width) + port_preview_size) * EDSCALE, 0));
|
||||
}
|
||||
|
||||
Ref<VisualShaderNodeParticleEmit> emit = vsnode;
|
||||
if (emit.is_valid()) {
|
||||
|
@ -1129,7 +1227,7 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id, bool
|
|||
port_offset++;
|
||||
}
|
||||
|
||||
if (!is_first_hbox) {
|
||||
if (!is_first_hbox && !is_reroute) {
|
||||
node->add_child(hb);
|
||||
if (curve_xyz.is_valid()) {
|
||||
node->move_child(hb, 1 + expanded_port_counter);
|
||||
|
@ -1140,9 +1238,9 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id, bool
|
|||
continue;
|
||||
}
|
||||
|
||||
int idx = 1;
|
||||
if (!is_first_hbox) {
|
||||
idx = i + port_offset;
|
||||
int idx = is_first_hbox ? 1 : i + port_offset;
|
||||
if (is_reroute) {
|
||||
idx = 0;
|
||||
}
|
||||
if (!is_frame) {
|
||||
GraphNode *graph_node = Object::cast_to<GraphNode>(node);
|
||||
|
@ -1243,7 +1341,7 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id, bool
|
|||
if (vsnode->get_output_port_for_preview() >= 0) {
|
||||
has_relative_parameter_instances = is_node_has_parameter_instances_relatively(p_type, p_id);
|
||||
show_port_preview(p_type, p_id, vsnode->get_output_port_for_preview(), !has_relative_parameter_instances);
|
||||
} else {
|
||||
} else if (!is_reroute) {
|
||||
offset = memnew(Control);
|
||||
offset->set_custom_minimum_size(Size2(0, 4 * EDSCALE));
|
||||
node->add_child(offset);
|
||||
|
@ -1342,6 +1440,13 @@ void VisualShaderGraphPlugin::connect_nodes(VisualShader::Type p_type, int p_fro
|
|||
}
|
||||
|
||||
if (visual_shader.is_valid() && visual_shader->get_shader_type() == p_type) {
|
||||
// Update reroute nodes since their port type might have changed.
|
||||
Ref<VisualShaderNodeReroute> reroute_to = visual_shader->get_node(p_type, p_to_node);
|
||||
Ref<VisualShaderNodeReroute> reroute_from = visual_shader->get_node(p_type, p_from_node);
|
||||
if (reroute_to.is_valid() || reroute_from.is_valid()) {
|
||||
update_reroute_nodes();
|
||||
}
|
||||
|
||||
graph->connect_node(itos(p_from_node), p_from_port, itos(p_to_node), p_to_port);
|
||||
|
||||
connections.push_back({ p_from_node, p_from_port, p_to_node, p_to_port });
|
||||
|
@ -1954,6 +2059,7 @@ void VisualShaderEditor::_update_options_menu() {
|
|||
static Vector<String> type_filter_exceptions;
|
||||
if (type_filter_exceptions.is_empty()) {
|
||||
type_filter_exceptions.append("VisualShaderNodeExpression");
|
||||
type_filter_exceptions.append("VisualShaderNodeReroute");
|
||||
}
|
||||
|
||||
for (int i = 0; i < add_options.size(); i++) {
|
||||
|
@ -3478,6 +3584,8 @@ void VisualShaderEditor::_add_node(int p_idx, const Vector<Variant> &p_ops, cons
|
|||
frame->set_size(Size2(320 * EDSCALE, 180 * EDSCALE));
|
||||
}
|
||||
|
||||
Ref<VisualShaderNodeReroute> reroute = vsnode;
|
||||
|
||||
bool created_expression_port = false;
|
||||
|
||||
// A node is inserted in an already present connection.
|
||||
|
@ -3488,6 +3596,61 @@ void VisualShaderEditor::_add_node(int p_idx, const Vector<Variant> &p_ops, cons
|
|||
undo_redo->add_undo_method(graph_plugin.ptr(), "connect_nodes", type, from_node, from_slot, to_node, to_slot);
|
||||
}
|
||||
|
||||
// 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()) {
|
||||
expr->add_input_port(0, output_port_type, "input0");
|
||||
created_expression_port = true;
|
||||
}
|
||||
|
||||
if (vsnode->get_input_port_count() > 0 || created_expression_port) {
|
||||
int _to_node = id_to_use;
|
||||
|
||||
if (created_expression_port) {
|
||||
int _to_slot = 0;
|
||||
undo_redo->add_undo_method(visual_shader.ptr(), "disconnect_nodes", type, from_node, from_slot, _to_node, _to_slot);
|
||||
undo_redo->add_do_method(visual_shader.ptr(), "connect_nodes", type, from_node, from_slot, _to_node, _to_slot);
|
||||
undo_redo->add_undo_method(graph_plugin.ptr(), "disconnect_nodes", type, from_node, from_slot, _to_node, _to_slot);
|
||||
undo_redo->add_do_method(graph_plugin.ptr(), "connect_nodes", type, from_node, from_slot, _to_node, _to_slot);
|
||||
} else {
|
||||
int _to_slot = -1;
|
||||
|
||||
// Attempting to connect to the default input port or to the first correct port (if it's not found).
|
||||
for (int i = 0; i < vsnode->get_input_port_count(); i++) {
|
||||
if (visual_shader->is_port_types_compatible(output_port_type, vsnode->get_input_port_type(i)) || reroute.is_valid()) {
|
||||
if (i == vsnode->get_default_input_port(output_port_type)) {
|
||||
_to_slot = i;
|
||||
break;
|
||||
} else if (_to_slot == -1) {
|
||||
_to_slot = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_to_slot >= 0) {
|
||||
undo_redo->add_undo_method(visual_shader.ptr(), "disconnect_nodes", type, from_node, from_slot, _to_node, _to_slot);
|
||||
undo_redo->add_do_method(visual_shader.ptr(), "connect_nodes", type, from_node, from_slot, _to_node, _to_slot);
|
||||
undo_redo->add_undo_method(graph_plugin.ptr(), "disconnect_nodes", type, from_node, from_slot, _to_node, _to_slot);
|
||||
undo_redo->add_do_method(graph_plugin.ptr(), "connect_nodes", type, from_node, from_slot, _to_node, _to_slot);
|
||||
}
|
||||
}
|
||||
|
||||
if (output_port_type == VisualShaderNode::PORT_TYPE_SAMPLER) {
|
||||
if (is_texture2d) {
|
||||
undo_redo->add_do_method(vsnode.ptr(), "set_source", VisualShaderNodeTexture::SOURCE_PORT);
|
||||
}
|
||||
if (is_texture3d || is_texture2d_array) {
|
||||
undo_redo->add_do_method(vsnode.ptr(), "set_source", VisualShaderNodeSample3D::SOURCE_PORT);
|
||||
}
|
||||
if (is_cubemap) {
|
||||
undo_redo->add_do_method(vsnode.ptr(), "set_source", VisualShaderNodeCubemap::SOURCE_PORT);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
@ -3548,7 +3711,7 @@ void VisualShaderEditor::_add_node(int p_idx, const Vector<Variant> &p_ops, cons
|
|||
|
||||
// Attempting to connect to the first correct port.
|
||||
for (int i = 0; i < vsnode->get_output_port_count(); i++) {
|
||||
if (visual_shader->is_port_types_compatible(vsnode->get_output_port_type(i), input_port_type)) {
|
||||
if (visual_shader->is_port_types_compatible(vsnode->get_output_port_type(i), input_port_type) || reroute.is_valid()) {
|
||||
undo_redo->add_do_method(visual_shader.ptr(), "connect_nodes", type, _from_node, i, to_node, to_slot);
|
||||
undo_redo->add_undo_method(visual_shader.ptr(), "disconnect_nodes", type, _from_node, i, to_node, to_slot);
|
||||
undo_redo->add_do_method(graph_plugin.ptr(), "connect_nodes", type, _from_node, i, to_node, to_slot);
|
||||
|
@ -3560,60 +3723,6 @@ void VisualShaderEditor::_add_node(int p_idx, const Vector<Variant> &p_ops, cons
|
|||
}
|
||||
}
|
||||
|
||||
// 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()) {
|
||||
expr->add_input_port(0, output_port_type, "input0");
|
||||
created_expression_port = true;
|
||||
}
|
||||
|
||||
if (vsnode->get_input_port_count() > 0 || created_expression_port) {
|
||||
int _to_node = id_to_use;
|
||||
|
||||
if (created_expression_port) {
|
||||
int _to_slot = 0;
|
||||
undo_redo->add_undo_method(visual_shader.ptr(), "disconnect_nodes", type, from_node, from_slot, _to_node, _to_slot);
|
||||
undo_redo->add_do_method(visual_shader.ptr(), "connect_nodes", type, from_node, from_slot, _to_node, _to_slot);
|
||||
undo_redo->add_undo_method(graph_plugin.ptr(), "disconnect_nodes", type, from_node, from_slot, _to_node, _to_slot);
|
||||
undo_redo->add_do_method(graph_plugin.ptr(), "connect_nodes", type, from_node, from_slot, _to_node, _to_slot);
|
||||
} else {
|
||||
int _to_slot = -1;
|
||||
|
||||
// Attempting to connect to the default input port or to the first correct port (if it's not found).
|
||||
for (int i = 0; i < vsnode->get_input_port_count(); i++) {
|
||||
if (visual_shader->is_port_types_compatible(output_port_type, vsnode->get_input_port_type(i))) {
|
||||
if (i == vsnode->get_default_input_port(output_port_type)) {
|
||||
_to_slot = i;
|
||||
break;
|
||||
} else if (_to_slot == -1) {
|
||||
_to_slot = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_to_slot >= 0) {
|
||||
undo_redo->add_undo_method(visual_shader.ptr(), "disconnect_nodes", type, from_node, from_slot, _to_node, _to_slot);
|
||||
undo_redo->add_do_method(visual_shader.ptr(), "connect_nodes", type, from_node, from_slot, _to_node, _to_slot);
|
||||
undo_redo->add_undo_method(graph_plugin.ptr(), "disconnect_nodes", type, from_node, from_slot, _to_node, _to_slot);
|
||||
undo_redo->add_do_method(graph_plugin.ptr(), "connect_nodes", type, from_node, from_slot, _to_node, _to_slot);
|
||||
}
|
||||
}
|
||||
|
||||
if (output_port_type == VisualShaderNode::PORT_TYPE_SAMPLER) {
|
||||
if (is_texture2d) {
|
||||
undo_redo->add_do_method(vsnode.ptr(), "set_source", VisualShaderNodeTexture::SOURCE_PORT);
|
||||
}
|
||||
if (is_texture3d || is_texture2d_array) {
|
||||
undo_redo->add_do_method(vsnode.ptr(), "set_source", VisualShaderNodeSample3D::SOURCE_PORT);
|
||||
}
|
||||
if (is_cubemap) {
|
||||
undo_redo->add_do_method(vsnode.ptr(), "set_source", VisualShaderNodeCubemap::SOURCE_PORT);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_member_cancel();
|
||||
|
||||
if (is_parameter) {
|
||||
|
@ -3836,6 +3945,9 @@ void VisualShaderEditor::_connection_request(const String &p_from, int p_from_in
|
|||
undo_redo->add_undo_method(visual_shader.ptr(), "disconnect_nodes", type, from, p_from_index, to, p_to_index);
|
||||
undo_redo->add_do_method(graph_plugin.ptr(), "connect_nodes", type, from, p_from_index, to, p_to_index);
|
||||
undo_redo->add_undo_method(graph_plugin.ptr(), "disconnect_nodes", type, from, p_from_index, to, p_to_index);
|
||||
|
||||
undo_redo->add_do_method(graph_plugin.ptr(), "update_node", (int)type, from);
|
||||
undo_redo->add_undo_method(graph_plugin.ptr(), "update_node", (int)type, from);
|
||||
undo_redo->add_do_method(graph_plugin.ptr(), "update_node", (int)type, to);
|
||||
undo_redo->add_undo_method(graph_plugin.ptr(), "update_node", (int)type, to);
|
||||
undo_redo->commit_action();
|
||||
|
@ -3931,10 +4043,12 @@ bool VisualShaderEditor::_check_node_drop_on_connection(const Vector2 &p_positio
|
|||
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);
|
||||
|
||||
Ref<VisualShaderNodeReroute> reroute_node = selected_vsnode;
|
||||
|
||||
// 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 (visual_shader->is_port_types_compatible(original_port_type_from, selected_vsnode->get_input_port_type(i)) || reroute_node.is_valid()) {
|
||||
if (i == selected_vsnode->get_default_input_port(original_port_type_from)) {
|
||||
_to_port = i;
|
||||
break;
|
||||
|
@ -3947,7 +4061,7 @@ bool VisualShaderEditor::_check_node_drop_on_connection(const Vector2 &p_positio
|
|||
// 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)) {
|
||||
if (visual_shader->is_port_types_compatible(selected_vsnode->get_output_port_type(i), original_port_type_to) || reroute_node.is_valid()) {
|
||||
_from_port = i;
|
||||
break;
|
||||
}
|
||||
|
@ -4529,6 +4643,8 @@ void VisualShaderEditor::_graph_gui_input(const Ref<InputEvent> &p_event) {
|
|||
Ref<GraphEdit::Connection> closest_connection = graph->get_closest_connection_at_point(menu_point);
|
||||
if (closest_connection.is_valid()) {
|
||||
clicked_connection = closest_connection;
|
||||
saved_node_pos = graph->get_local_mouse_position();
|
||||
saved_node_pos_dirty = true;
|
||||
connection_popup_menu->set_position(gpos);
|
||||
connection_popup_menu->reset_size();
|
||||
connection_popup_menu->popup();
|
||||
|
@ -5628,6 +5744,25 @@ void VisualShaderEditor::_connection_menu_id_pressed(int p_idx) {
|
|||
connection_node_insert_requested = true;
|
||||
_show_members_dialog(true, input_port_type, output_port_type);
|
||||
} break;
|
||||
case ConnectionMenuOptions::INSERT_NEW_REROUTE: {
|
||||
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;
|
||||
|
||||
// Manual offset to place the port exactly at the mouse position.
|
||||
saved_node_pos -= Vector2(11 * EDSCALE * graph->get_zoom(), 50 * EDSCALE * graph->get_zoom());
|
||||
|
||||
// Find reroute addoptions.
|
||||
int idx = -1;
|
||||
for (int i = 0; i < add_options.size(); i++) {
|
||||
if (add_options[i].name == "Reroute") {
|
||||
idx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
_add_node(idx, add_options[idx].ops);
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -6123,6 +6258,7 @@ VisualShaderEditor::VisualShaderEditor() {
|
|||
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->add_item(TTR("Insert New Reroute"), ConnectionMenuOptions::INSERT_NEW_REROUTE);
|
||||
connection_popup_menu->connect("id_pressed", callable_mp(this, &VisualShaderEditor::_connection_menu_id_pressed));
|
||||
|
||||
///////////////////////////////////////
|
||||
|
@ -6962,6 +7098,7 @@ VisualShaderEditor::VisualShaderEditor() {
|
|||
add_options.push_back(AddOption("VaryingSetter", "Special", "VisualShaderNodeVaryingSetter", TTR("Set varying parameter."), {}, -1, TYPE_FLAGS_VERTEX | TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL));
|
||||
add_options.push_back(AddOption("VaryingGetter", "Special", "VisualShaderNodeVaryingGetter", TTR("Get varying parameter."), {}, -1, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM));
|
||||
add_options.push_back(AddOption("VaryingSetter", "Special", "VisualShaderNodeVaryingSetter", TTR("Set varying parameter."), {}, -1, TYPE_FLAGS_VERTEX | TYPE_FLAGS_FRAGMENT, Shader::MODE_CANVAS_ITEM));
|
||||
add_options.push_back(AddOption("Reroute", "Special", "VisualShaderNodeReroute", TTR("Reroute connections freely, can be used to connect multiple input ports to single output port.")));
|
||||
|
||||
custom_node_option_idx = add_options.size();
|
||||
|
||||
|
|
|
@ -66,6 +66,34 @@ public:
|
|||
virtual Control *create_editor(const Ref<Resource> &p_parent_resource, const Ref<VisualShaderNode> &p_node);
|
||||
};
|
||||
|
||||
class VSGraphNode : public GraphNode {
|
||||
GDCLASS(VSGraphNode, GraphNode);
|
||||
|
||||
protected:
|
||||
void _draw_port(int p_slot_index, Point2i p_pos, bool p_left, const Color &p_color, const Color &p_rim_color);
|
||||
virtual void draw_port(int p_slot_index, Point2i p_pos, bool p_left, const Color &p_color) override;
|
||||
};
|
||||
|
||||
class VSRerouteNode : public VSGraphNode {
|
||||
GDCLASS(VSRerouteNode, GraphNode);
|
||||
|
||||
const float FADE_ANIMATION_LENGTH_SEC = 0.3;
|
||||
|
||||
float icon_opacity = 0.0;
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
|
||||
virtual void draw_port(int p_slot_index, Point2i p_pos, bool p_left, const Color &p_color) override;
|
||||
|
||||
public:
|
||||
VSRerouteNode();
|
||||
void set_icon_opacity(float p_opacity);
|
||||
|
||||
void _on_mouse_entered();
|
||||
void _on_mouse_exited();
|
||||
};
|
||||
|
||||
class VisualShaderGraphPlugin : public RefCounted {
|
||||
GDCLASS(VisualShaderGraphPlugin, RefCounted);
|
||||
|
||||
|
@ -140,6 +168,7 @@ public:
|
|||
void set_frame_color_enabled(VisualShader::Type p_type, int p_node_id, bool p_enable);
|
||||
void set_frame_color(VisualShader::Type p_type, int p_node_id, const Color &p_color);
|
||||
void set_frame_autoshrink_enabled(VisualShader::Type p_type, int p_node_id, bool p_enable);
|
||||
void update_reroute_nodes();
|
||||
int get_constant_index(float p_constant) const;
|
||||
Ref<Script> get_node_script(int p_node_id) const;
|
||||
void update_theme();
|
||||
|
@ -297,6 +326,7 @@ class VisualShaderEditor : public VBoxContainer {
|
|||
|
||||
enum ConnectionMenuOptions {
|
||||
INSERT_NEW_NODE,
|
||||
INSERT_NEW_REROUTE,
|
||||
DISCONNECT,
|
||||
};
|
||||
|
||||
|
|
|
@ -1650,7 +1650,7 @@ void EditorThemeManager::_populate_standard_styles(const Ref<EditorTheme> &p_the
|
|||
p_theme->set_stylebox("titlebar_selected", "GraphFrame", make_empty_stylebox(4, 4, 4, 4));
|
||||
p_theme->set_color("resizer_color", "GraphFrame", gn_decoration_color);
|
||||
|
||||
// GraphFrame's title Label
|
||||
// GraphFrame's title Label.
|
||||
p_theme->set_type_variation("GraphFrameTitleLabel", "Label");
|
||||
p_theme->set_stylebox("normal", "GraphFrameTitleLabel", memnew(StyleBoxEmpty));
|
||||
p_theme->set_font_size("font_size", "GraphFrameTitleLabel", 22);
|
||||
|
@ -1663,6 +1663,21 @@ void EditorThemeManager::_populate_standard_styles(const Ref<EditorTheme> &p_the
|
|||
p_theme->set_constant("shadow_outline_size", "GraphFrameTitleLabel", 1 * EDSCALE);
|
||||
p_theme->set_constant("line_spacing", "GraphFrameTitleLabel", 3 * EDSCALE);
|
||||
}
|
||||
|
||||
// VisualShader reroute node.
|
||||
{
|
||||
Ref<StyleBox> vs_reroute_panel_style = make_empty_stylebox();
|
||||
Ref<StyleBox> vs_reroute_titlebar_style = vs_reroute_panel_style->duplicate();
|
||||
vs_reroute_titlebar_style->set_content_margin_all(16);
|
||||
p_theme->set_stylebox("panel", "VSRerouteNode", vs_reroute_panel_style);
|
||||
p_theme->set_stylebox("panel_selected", "VSRerouteNode", vs_reroute_panel_style);
|
||||
p_theme->set_stylebox("titlebar", "VSRerouteNode", vs_reroute_titlebar_style);
|
||||
p_theme->set_stylebox("titlebar_selected", "VSRerouteNode", vs_reroute_titlebar_style);
|
||||
p_theme->set_stylebox("slot", "VSRerouteNode", make_empty_stylebox());
|
||||
|
||||
p_theme->set_color("drag_background", "VSRerouteNode", p_config.dark_theme ? Color(0.19, 0.21, 0.24) : Color(0.8, 0.8, 0.8));
|
||||
p_theme->set_color("selected_rim_color", "VSRerouteNode", p_config.dark_theme ? Color(1, 1, 1) : Color(0, 0, 0));
|
||||
}
|
||||
}
|
||||
|
||||
// ColorPicker and related nodes.
|
||||
|
|
|
@ -1059,7 +1059,7 @@ void GraphEdit::_top_connection_layer_input(const Ref<InputEvent> &p_ev) {
|
|||
port_size.height = MAX(port_size.height, child ? child->get_size().y : 0);
|
||||
|
||||
int type = graph_node->get_output_port_type(j);
|
||||
if ((type == connecting_type ||
|
||||
if ((type == connecting_type || graph_node->is_ignoring_valid_connection_type() ||
|
||||
valid_connection_types.has(ConnectionType(type, connecting_type))) &&
|
||||
is_in_output_hotzone(graph_node, j, mpos, port_size)) {
|
||||
if (!is_node_hover_valid(graph_node->get_name(), j, connecting_from_node, connecting_from_port_index)) {
|
||||
|
@ -1084,7 +1084,7 @@ void GraphEdit::_top_connection_layer_input(const Ref<InputEvent> &p_ev) {
|
|||
port_size.height = MAX(port_size.height, child ? child->get_size().y : 0);
|
||||
|
||||
int type = graph_node->get_input_port_type(j);
|
||||
if ((type == connecting_type || valid_connection_types.has(ConnectionType(connecting_type, type))) &&
|
||||
if ((type == connecting_type || graph_node->is_ignoring_valid_connection_type() || valid_connection_types.has(ConnectionType(connecting_type, type))) &&
|
||||
is_in_input_hotzone(graph_node, j, mpos, port_size)) {
|
||||
if (!is_node_hover_valid(connecting_from_node, connecting_from_port_index, graph_node->get_name(), j)) {
|
||||
continue;
|
||||
|
@ -1117,6 +1117,8 @@ void GraphEdit::_top_connection_layer_input(const Ref<InputEvent> &p_ev) {
|
|||
emit_signal(SNAME("connection_from_empty"), connecting_from_node, connecting_from_port_index, mb->get_position());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
set_selected(get_node_or_null(NodePath(connecting_from_node)));
|
||||
}
|
||||
|
||||
if (connecting) {
|
||||
|
@ -1636,12 +1638,12 @@ void GraphEdit::_draw_grid() {
|
|||
|
||||
void GraphEdit::set_selected(Node *p_child) {
|
||||
for (int i = get_child_count() - 1; i >= 0; i--) {
|
||||
GraphNode *graph_node = Object::cast_to<GraphNode>(get_child(i));
|
||||
if (!graph_node) {
|
||||
GraphElement *graph_element = Object::cast_to<GraphElement>(get_child(i));
|
||||
if (!graph_element) {
|
||||
continue;
|
||||
}
|
||||
|
||||
graph_node->set_selected(graph_node == p_child);
|
||||
graph_element->set_selected(graph_element == p_child);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -604,6 +604,14 @@ void GraphNode::set_slot_draw_stylebox(int p_slot_index, bool p_enable) {
|
|||
emit_signal(SNAME("slot_updated"), p_slot_index);
|
||||
}
|
||||
|
||||
void GraphNode::set_ignore_invalid_connection_type(bool p_ignore) {
|
||||
ignore_invalid_connection_type = p_ignore;
|
||||
}
|
||||
|
||||
bool GraphNode::is_ignoring_valid_connection_type() const {
|
||||
return ignore_invalid_connection_type;
|
||||
}
|
||||
|
||||
Size2 GraphNode::get_minimum_size() const {
|
||||
Ref<StyleBox> sb_panel = theme_cache.panel;
|
||||
Ref<StyleBox> sb_titlebar = theme_cache.titlebar;
|
||||
|
@ -859,6 +867,9 @@ void GraphNode::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("is_slot_draw_stylebox", "slot_index"), &GraphNode::is_slot_draw_stylebox);
|
||||
ClassDB::bind_method(D_METHOD("set_slot_draw_stylebox", "slot_index", "enable"), &GraphNode::set_slot_draw_stylebox);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_ignore_invalid_connection_type", "ignore"), &GraphNode::set_ignore_invalid_connection_type);
|
||||
ClassDB::bind_method(D_METHOD("is_ignoring_valid_connection_type"), &GraphNode::is_ignoring_valid_connection_type);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_input_port_count"), &GraphNode::get_input_port_count);
|
||||
ClassDB::bind_method(D_METHOD("get_input_port_position", "port_idx"), &GraphNode::get_input_port_position);
|
||||
ClassDB::bind_method(D_METHOD("get_input_port_type", "port_idx"), &GraphNode::get_input_port_type);
|
||||
|
@ -874,6 +885,8 @@ void GraphNode::_bind_methods() {
|
|||
GDVIRTUAL_BIND(_draw_port, "slot_index", "position", "left", "color")
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::STRING, "title"), "set_title", "get_title");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ignore_invalid_connection_type"), "set_ignore_invalid_connection_type", "is_ignoring_valid_connection_type");
|
||||
|
||||
ADD_SIGNAL(MethodInfo("slot_updated", PropertyInfo(Variant::INT, "slot_index")));
|
||||
|
||||
BIND_THEME_ITEM(Theme::DATA_TYPE_STYLEBOX, GraphNode, panel);
|
||||
|
|
|
@ -95,6 +95,8 @@ class GraphNode : public GraphElement {
|
|||
|
||||
bool port_pos_dirty = true;
|
||||
|
||||
bool ignore_invalid_connection_type = false;
|
||||
|
||||
void _port_pos_update();
|
||||
|
||||
protected:
|
||||
|
@ -147,6 +149,9 @@ public:
|
|||
bool is_slot_draw_stylebox(int p_slot_index) const;
|
||||
void set_slot_draw_stylebox(int p_slot_index, bool p_enable);
|
||||
|
||||
void set_ignore_invalid_connection_type(bool p_ignore);
|
||||
bool is_ignoring_valid_connection_type() const;
|
||||
|
||||
int get_input_port_count();
|
||||
Vector2 get_input_port_position(int p_port_idx);
|
||||
int get_input_port_type(int p_port_idx);
|
||||
|
|
|
@ -744,6 +744,7 @@ void register_scene_types() {
|
|||
GDREGISTER_ABSTRACT_CLASS(VisualShaderNodeVarying);
|
||||
GDREGISTER_CLASS(VisualShaderNodeVaryingSetter);
|
||||
GDREGISTER_CLASS(VisualShaderNodeVaryingGetter);
|
||||
GDREGISTER_CLASS(VisualShaderNodeReroute);
|
||||
|
||||
GDREGISTER_CLASS(VisualShaderNodeSDFToScreenUV);
|
||||
GDREGISTER_CLASS(VisualShaderNodeScreenUVToSDF);
|
||||
|
|
|
@ -1101,6 +1101,42 @@ bool VisualShader::is_nodes_connected_relatively(const Graph *p_graph, int p_nod
|
|||
return result;
|
||||
}
|
||||
|
||||
bool VisualShader::_check_reroute_subgraph(Type p_type, int p_target_port_type, int p_reroute_node, List<int> *r_visited_reroute_nodes) const {
|
||||
const Graph *g = &graph[p_type];
|
||||
|
||||
// BFS to check whether connecting to the given subgraph (rooted at p_reroute_node) is valid.
|
||||
List<int> queue;
|
||||
queue.push_back(p_reroute_node);
|
||||
if (r_visited_reroute_nodes != nullptr) {
|
||||
r_visited_reroute_nodes->push_back(p_reroute_node);
|
||||
}
|
||||
while (!queue.is_empty()) {
|
||||
int current_node_id = queue.front()->get();
|
||||
VisualShader::Node current_node = g->nodes[current_node_id];
|
||||
queue.pop_front();
|
||||
for (const int &next_node_id : current_node.next_connected_nodes) {
|
||||
Ref<VisualShaderNodeReroute> next_vsnode = g->nodes[next_node_id].node;
|
||||
if (next_vsnode.is_valid()) {
|
||||
queue.push_back(next_node_id);
|
||||
if (r_visited_reroute_nodes != nullptr) {
|
||||
r_visited_reroute_nodes->push_back(next_node_id);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// Check whether all ports connected with the reroute node are compatible.
|
||||
for (const Connection &c : g->connections) {
|
||||
VisualShaderNode::PortType to_port_type = g->nodes[next_node_id].node->get_input_port_type(c.to_port);
|
||||
if (c.from_node == current_node_id &&
|
||||
c.to_node == next_node_id &&
|
||||
!is_port_types_compatible(p_target_port_type, to_port_type)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VisualShader::can_connect_nodes(Type p_type, int p_from_node, int p_from_port, int p_to_node, int p_to_port) const {
|
||||
ERR_FAIL_INDEX_V(p_type, TYPE_MAX, false);
|
||||
const Graph *g = &graph[p_type];
|
||||
|
@ -1128,7 +1164,12 @@ bool VisualShader::can_connect_nodes(Type p_type, int p_from_node, int p_from_po
|
|||
VisualShaderNode::PortType from_port_type = g->nodes[p_from_node].node->get_output_port_type(p_from_port);
|
||||
VisualShaderNode::PortType to_port_type = g->nodes[p_to_node].node->get_input_port_type(p_to_port);
|
||||
|
||||
if (!is_port_types_compatible(from_port_type, to_port_type)) {
|
||||
Ref<VisualShaderNodeReroute> to_node_reroute = g->nodes[p_to_node].node;
|
||||
if (to_node_reroute.is_valid()) {
|
||||
if (!_check_reroute_subgraph(p_type, from_port_type, p_to_node)) {
|
||||
return false;
|
||||
}
|
||||
} else if (!is_port_types_compatible(from_port_type, to_port_type)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1141,7 +1182,6 @@ bool VisualShader::can_connect_nodes(Type p_type, int p_from_node, int p_from_po
|
|||
if (is_nodes_connected_relatively(g, p_from_node, p_to_node)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1179,6 +1219,28 @@ void VisualShader::detach_node_from_frame(Type p_type, int p_node) {
|
|||
g->nodes[p_node].node->set_frame(-1);
|
||||
}
|
||||
|
||||
String VisualShader::get_reroute_parameter_name(Type p_type, int p_reroute_node) const {
|
||||
ERR_FAIL_INDEX_V(p_type, TYPE_MAX, "");
|
||||
const Graph *g = &graph[p_type];
|
||||
|
||||
ERR_FAIL_COND_V(!g->nodes.has(p_reroute_node), "");
|
||||
|
||||
const VisualShader::Node *node = &g->nodes[p_reroute_node];
|
||||
while (node->prev_connected_nodes.size() > 0) {
|
||||
int connected_node_id = node->prev_connected_nodes[0];
|
||||
node = &g->nodes[connected_node_id];
|
||||
Ref<VisualShaderNodeParameter> parameter_node = node->node;
|
||||
if (parameter_node.is_valid() && parameter_node->get_output_port_type(0) == VisualShaderNode::PORT_TYPE_SAMPLER) {
|
||||
return parameter_node->get_parameter_name();
|
||||
}
|
||||
Ref<VisualShaderNodeInput> input_node = node->node;
|
||||
if (input_node.is_valid() && input_node->get_output_port_type(0) == VisualShaderNode::PORT_TYPE_SAMPLER) {
|
||||
return input_node->get_input_real_name();
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
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];
|
||||
|
@ -1217,10 +1279,30 @@ Error VisualShader::connect_nodes(Type p_type, int p_from_node, int p_from_port,
|
|||
ERR_FAIL_COND_V(!g->nodes.has(p_to_node), ERR_INVALID_PARAMETER);
|
||||
ERR_FAIL_INDEX_V(p_to_port, g->nodes[p_to_node].node->get_input_port_count(), ERR_INVALID_PARAMETER);
|
||||
|
||||
Ref<VisualShaderNodeReroute> from_node_reroute = g->nodes[p_from_node].node;
|
||||
Ref<VisualShaderNodeReroute> to_node_reroute = g->nodes[p_to_node].node;
|
||||
|
||||
// Allow connection with incompatible port types only if the reroute node isn't connected to anything.
|
||||
VisualShaderNode::PortType from_port_type = g->nodes[p_from_node].node->get_output_port_type(p_from_port);
|
||||
VisualShaderNode::PortType to_port_type = g->nodes[p_to_node].node->get_input_port_type(p_to_port);
|
||||
bool port_types_are_compatible = is_port_types_compatible(from_port_type, to_port_type);
|
||||
|
||||
ERR_FAIL_COND_V_MSG(!is_port_types_compatible(from_port_type, to_port_type), ERR_INVALID_PARAMETER, "Incompatible port types (scalar/vec/bool) with transform.");
|
||||
if (to_node_reroute.is_valid()) {
|
||||
List<int> visited_reroute_nodes;
|
||||
port_types_are_compatible = _check_reroute_subgraph(p_type, from_port_type, p_to_node, &visited_reroute_nodes);
|
||||
if (port_types_are_compatible) {
|
||||
// Set the port type of all reroute nodes.
|
||||
for (const int &E : visited_reroute_nodes) {
|
||||
Ref<VisualShaderNodeReroute> reroute_node = g->nodes[E].node;
|
||||
reroute_node->_set_port_type(from_port_type);
|
||||
}
|
||||
}
|
||||
} else if (from_node_reroute.is_valid() && !from_node_reroute->is_input_port_connected(0)) {
|
||||
from_node_reroute->_set_port_type(to_port_type);
|
||||
port_types_are_compatible = true;
|
||||
}
|
||||
|
||||
ERR_FAIL_COND_V_MSG(!port_types_are_compatible, ERR_INVALID_PARAMETER, "Incompatible port types.");
|
||||
|
||||
for (const Connection &E : g->connections) {
|
||||
if (E.from_node == p_from_node && E.from_port == p_from_port && E.to_node == p_to_node && E.to_port == p_to_port) {
|
||||
|
@ -1904,12 +1986,18 @@ Error VisualShader::_write_node(Type type, StringBuilder *p_global_code, StringB
|
|||
|
||||
if (in_type == VisualShaderNode::PORT_TYPE_SAMPLER && out_type == VisualShaderNode::PORT_TYPE_SAMPLER) {
|
||||
VisualShaderNode *ptr = const_cast<VisualShaderNode *>(graph[type].nodes[from_node].node.ptr());
|
||||
// FIXME: This needs to be refactored at some point.
|
||||
if (ptr->has_method("get_input_real_name")) {
|
||||
inputs[i] = ptr->call("get_input_real_name");
|
||||
} else if (ptr->has_method("get_parameter_name")) {
|
||||
inputs[i] = ptr->call("get_parameter_name");
|
||||
} else {
|
||||
inputs[i] = "";
|
||||
Ref<VisualShaderNodeReroute> reroute = graph[type].nodes[from_node].node;
|
||||
if (reroute.is_valid()) {
|
||||
inputs[i] = get_reroute_parameter_name(type, from_node);
|
||||
} else {
|
||||
inputs[i] = "";
|
||||
}
|
||||
}
|
||||
} else if (in_type == out_type) {
|
||||
inputs[i] = src_var;
|
||||
|
|
|
@ -163,6 +163,8 @@ private:
|
|||
void _input_type_changed(Type p_type, int p_id);
|
||||
bool has_func_name(RenderingServer::ShaderMode p_mode, const String &p_func_name) const;
|
||||
|
||||
bool _check_reroute_subgraph(Type p_type, int p_target_port_type, int p_reroute_node, List<int> *r_visited_reroute_nodes = nullptr) const;
|
||||
|
||||
protected:
|
||||
virtual void _update_shader() const override;
|
||||
static void _bind_methods();
|
||||
|
@ -229,6 +231,8 @@ public: // internal methods
|
|||
void attach_node_to_frame(Type p_type, int p_node, int p_frame);
|
||||
void detach_node_from_frame(Type p_type, int p_node);
|
||||
|
||||
String get_reroute_parameter_name(Type p_type, int p_reroute_node) const;
|
||||
|
||||
void rebuild();
|
||||
void get_node_connections(Type p_type, List<Connection> *r_connections) const;
|
||||
|
||||
|
|
|
@ -8150,3 +8150,82 @@ VisualShaderNodeRotationByAxis::VisualShaderNodeRotationByAxis() {
|
|||
|
||||
simple_decl = false;
|
||||
}
|
||||
|
||||
String VisualShaderNodeReroute::get_caption() const {
|
||||
return "Reroute";
|
||||
}
|
||||
|
||||
int VisualShaderNodeReroute::get_input_port_count() const {
|
||||
return 1;
|
||||
}
|
||||
|
||||
VisualShaderNodeReroute::PortType VisualShaderNodeReroute::get_input_port_type(int p_port) const {
|
||||
return input_port_type;
|
||||
}
|
||||
|
||||
String VisualShaderNodeReroute::get_input_port_name(int p_port) const {
|
||||
return String();
|
||||
}
|
||||
|
||||
int VisualShaderNodeReroute::get_output_port_count() const {
|
||||
return 1;
|
||||
}
|
||||
|
||||
VisualShaderNodeReroute::PortType VisualShaderNodeReroute::get_output_port_type(int p_port) const {
|
||||
return input_port_type;
|
||||
}
|
||||
|
||||
String VisualShaderNodeReroute::get_output_port_name(int p_port) const {
|
||||
return String();
|
||||
}
|
||||
|
||||
String VisualShaderNodeReroute::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 code;
|
||||
for (int i = 0; i < get_output_port_count(); i++) {
|
||||
if (input_port_type == PORT_TYPE_SAMPLER) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String input = p_input_vars[0];
|
||||
if (input.is_empty()) {
|
||||
code += vformat(" %s;\n", p_output_vars[i]);
|
||||
continue;
|
||||
}
|
||||
code += vformat(" %s = %s;\n", p_output_vars[i], input);
|
||||
}
|
||||
return code;
|
||||
}
|
||||
|
||||
void VisualShaderNodeReroute::_set_port_type(PortType p_type) {
|
||||
input_port_type = p_type;
|
||||
switch (p_type) {
|
||||
case PORT_TYPE_SCALAR:
|
||||
set_input_port_default_value(0, 0.0);
|
||||
break;
|
||||
case PORT_TYPE_VECTOR_2D:
|
||||
set_input_port_default_value(0, Vector2());
|
||||
break;
|
||||
case PORT_TYPE_VECTOR_3D:
|
||||
set_input_port_default_value(0, Vector3());
|
||||
break;
|
||||
case PORT_TYPE_VECTOR_4D:
|
||||
set_input_port_default_value(0, Vector4());
|
||||
break;
|
||||
case PORT_TYPE_TRANSFORM:
|
||||
set_input_port_default_value(0, Transform3D());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void VisualShaderNodeReroute::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("_set_port_type", "port_type"), &VisualShaderNodeReroute::_set_port_type);
|
||||
ClassDB::bind_method(D_METHOD("get_port_type"), &VisualShaderNodeReroute::get_port_type);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "port_type", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_port_type", "get_port_type");
|
||||
}
|
||||
|
||||
VisualShaderNodeReroute::VisualShaderNodeReroute() {
|
||||
set_input_port_default_value(0, 0.0);
|
||||
}
|
||||
|
|
|
@ -3092,4 +3092,35 @@ public:
|
|||
VisualShaderNodeRotationByAxis();
|
||||
};
|
||||
|
||||
class VisualShaderNodeReroute : public VisualShaderNode {
|
||||
GDCLASS(VisualShaderNodeReroute, VisualShaderNode);
|
||||
|
||||
PortType input_port_type = PORT_TYPE_SCALAR;
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
virtual String get_caption() const override;
|
||||
|
||||
virtual int get_input_port_count() const override;
|
||||
virtual PortType get_input_port_type(int p_port) const override;
|
||||
virtual String get_input_port_name(int p_port) const override;
|
||||
|
||||
virtual int get_output_port_count() const override;
|
||||
virtual PortType get_output_port_type(int p_port) const override;
|
||||
virtual String get_output_port_name(int p_port) const override;
|
||||
virtual bool has_output_port_preview(int p_port) const override { return false; }
|
||||
virtual bool is_output_port_expandable(int p_port) const override { return false; }
|
||||
|
||||
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
|
||||
|
||||
virtual Category get_category() const override { return CATEGORY_SPECIAL; }
|
||||
|
||||
void _set_port_type(PortType p_type);
|
||||
PortType get_port_type() const { return input_port_type; }
|
||||
|
||||
VisualShaderNodeReroute();
|
||||
};
|
||||
|
||||
#endif // VISUAL_SHADER_NODES_H
|
||||
|
|
Loading…
Reference in New Issue