diff --git a/doc/classes/VisualShaderNode.xml b/doc/classes/VisualShaderNode.xml
index 6327ab534fd..9a74f2322e8 100644
--- a/doc/classes/VisualShaderNode.xml
+++ b/doc/classes/VisualShaderNode.xml
@@ -9,6 +9,13 @@
https://docs.godotengine.org/en/latest/tutorials/shading/visual_shaders.html
+
+
+
+
+ Clears the default input ports value.
+
+
@@ -25,6 +32,15 @@
Returns the default value of the input [code]port[/code].
+
+
+
+
+
+
+ Removes the default value of the input [code]port[/code].
+
+
diff --git a/doc/classes/VisualShaderNodeUVFunc.xml b/doc/classes/VisualShaderNodeUVFunc.xml
new file mode 100644
index 00000000000..042644feb02
--- /dev/null
+++ b/doc/classes/VisualShaderNodeUVFunc.xml
@@ -0,0 +1,28 @@
+
+
+
+ Contains functions to modify texture coordinates ([code]uv[/code]) to be used within the visual shader graph.
+
+
+
+
+
+
+
+
+
+ A function to be applied to the texture coordinates. See [enum Function] for options.
+
+
+
+
+ Translates [code]uv[/code] by using [code]scale[/code] and [code]offset[/code] values using the following formula: [code]uv = uv + offset * scale[/code]. [code]uv[/code] port is connected to [code]UV[/code] built-in by default.
+
+
+ Scales [code]uv[/uv] by using [code]scale[/code] and [code]pivot[/code] values using the following formula: [code]uv = (uv - pivot) * scale + pivot[/code]. [code]uv[/code] port is connected to [code]UV[/code] built-in by default.
+
+
+ Represents the size of the [enum Function] enum.
+
+
+
diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp
index 9e5d531e91b..7ef5e9e8cea 100644
--- a/editor/plugins/visual_shader_editor_plugin.cpp
+++ b/editor/plugins/visual_shader_editor_plugin.cpp
@@ -2081,6 +2081,16 @@ void VisualShaderEditor::_setup_node(VisualShaderNode *p_node, int p_op_idx) {
}
}
+ //UV_FUNC
+ {
+ VisualShaderNodeUVFunc *uvFunc = Object::cast_to(p_node);
+
+ if (uvFunc) {
+ uvFunc->set_function((VisualShaderNodeUVFunc::Function)p_op_idx);
+ return;
+ }
+ }
+
// IS
{
VisualShaderNodeIs *is = Object::cast_to(p_node);
@@ -4244,6 +4254,8 @@ VisualShaderEditor::VisualShaderEditor() {
// TEXTURES
+ add_options.push_back(AddOption("UVFunc", "Textures", "Common", "VisualShaderNodeUVFunc", TTR("Function to be applied on texture coordinates."), -1, VisualShaderNode::PORT_TYPE_VECTOR));
+
cubemap_node_option_idx = add_options.size();
add_options.push_back(AddOption("CubeMap", "Textures", "Functions", "VisualShaderNodeCubemap", TTR("Perform the cubic texture lookup."), -1, -1));
curve_node_option_idx = add_options.size();
@@ -4254,6 +4266,8 @@ VisualShaderEditor::VisualShaderEditor() {
add_options.push_back(AddOption("Texture2DArray", "Textures", "Functions", "VisualShaderNodeTexture2DArray", TTR("Perform the 2D-array texture lookup."), -1, -1, -1, -1, -1));
texture3d_node_option_idx = add_options.size();
add_options.push_back(AddOption("Texture3D", "Textures", "Functions", "VisualShaderNodeTexture3D", TTR("Perform the 3D texture lookup."), -1, -1));
+ add_options.push_back(AddOption("UVPanning", "Textures", "Functions", "VisualShaderNodeUVFunc", TTR("Apply panning function on texture coordinates."), VisualShaderNodeUVFunc::FUNC_PANNING, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("UVScaling", "Textures", "Functions", "VisualShaderNodeUVFunc", TTR("Apply scaling function on texture coordinates."), VisualShaderNodeUVFunc::FUNC_SCALING, VisualShaderNode::PORT_TYPE_VECTOR));
add_options.push_back(AddOption("CubeMapUniform", "Textures", "Variables", "VisualShaderNodeCubemapUniform", TTR("Cubic texture uniform lookup."), -1, -1));
add_options.push_back(AddOption("TextureUniform", "Textures", "Variables", "VisualShaderNodeTextureUniform", TTR("2D texture uniform lookup."), -1, -1));
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index d22aa662323..4faba0b9d27 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -560,6 +560,7 @@ void register_scene_types() {
ClassDB::register_class();
ClassDB::register_class();
ClassDB::register_class();
+ ClassDB::register_class();
ClassDB::register_class();
ClassDB::register_class();
ClassDB::register_class();
diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp
index b6caae1e30c..fd0b568f71a 100644
--- a/scene/resources/visual_shader.cpp
+++ b/scene/resources/visual_shader.cpp
@@ -60,6 +60,20 @@ Variant VisualShaderNode::get_input_port_default_value(int p_port) const {
return Variant();
}
+void VisualShaderNode::remove_input_port_default_value(int p_port) {
+ if (default_input_values.has(p_port)) {
+ default_input_values.erase(p_port);
+ emit_changed();
+ }
+}
+
+void VisualShaderNode::clear_default_input_values() {
+ if (!default_input_values.is_empty()) {
+ default_input_values.clear();
+ emit_changed();
+ }
+}
+
bool VisualShaderNode::is_port_separator(int p_index) const {
return false;
}
@@ -220,6 +234,9 @@ void VisualShaderNode::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_input_port_default_value", "port", "value"), &VisualShaderNode::set_input_port_default_value);
ClassDB::bind_method(D_METHOD("get_input_port_default_value", "port"), &VisualShaderNode::get_input_port_default_value);
+ ClassDB::bind_method(D_METHOD("remove_input_port_default_value", "port"), &VisualShaderNode::remove_input_port_default_value);
+ ClassDB::bind_method(D_METHOD("clear_default_input_values"), &VisualShaderNode::clear_default_input_values);
+
ClassDB::bind_method(D_METHOD("set_default_input_values", "values"), &VisualShaderNode::set_default_input_values);
ClassDB::bind_method(D_METHOD("get_default_input_values"), &VisualShaderNode::get_default_input_values);
@@ -373,6 +390,18 @@ void VisualShaderNodeCustom::set_default_input_values(const Array &p_values) {
}
}
+void VisualShaderNodeCustom::remove_input_port_default_value(int p_port) {
+ if (!is_initialized) {
+ VisualShaderNode::remove_input_port_default_value(p_port);
+ }
+}
+
+void VisualShaderNodeCustom::clear_default_input_values() {
+ if (!is_initialized) {
+ VisualShaderNode::clear_default_input_values();
+ }
+}
+
void VisualShaderNodeCustom::_set_input_port_default_value(int p_port, const Variant &p_value) {
VisualShaderNode::set_input_port_default_value(p_port, p_value);
}
diff --git a/scene/resources/visual_shader.h b/scene/resources/visual_shader.h
index aa7768751e7..21c4d238194 100644
--- a/scene/resources/visual_shader.h
+++ b/scene/resources/visual_shader.h
@@ -230,6 +230,8 @@ public:
Variant get_input_port_default_value(int p_port) const; // if NIL (default if node does not set anything) is returned, it means no default value is wanted if disconnected, thus no input var must be supplied (empty string will be supplied)
Array get_default_input_values() const;
virtual void set_default_input_values(const Array &p_values);
+ virtual void remove_input_port_default_value(int p_port);
+ virtual void clear_default_input_values();
virtual int get_output_port_count() const = 0;
virtual PortType get_output_port_type(int p_port) const = 0;
@@ -305,6 +307,8 @@ protected:
virtual void set_input_port_default_value(int p_port, const Variant &p_value) override;
virtual void set_default_input_values(const Array &p_values) override;
+ virtual void remove_input_port_default_value(int p_port) override;
+ virtual void clear_default_input_values() override;
protected:
void _set_input_port_default_value(int p_port, const Variant &p_value);
diff --git a/scene/resources/visual_shader_nodes.cpp b/scene/resources/visual_shader_nodes.cpp
index 998fae0ae97..5bcc6dda97a 100644
--- a/scene/resources/visual_shader_nodes.cpp
+++ b/scene/resources/visual_shader_nodes.cpp
@@ -2499,6 +2499,143 @@ VisualShaderNodeTransformFunc::VisualShaderNodeTransformFunc() {
set_input_port_default_value(0, Transform3D());
}
+////////////// UV Func
+
+String VisualShaderNodeUVFunc::get_caption() const {
+ return "UVFunc";
+}
+
+int VisualShaderNodeUVFunc::get_input_port_count() const {
+ return 3;
+}
+
+VisualShaderNodeUVFunc::PortType VisualShaderNodeUVFunc::get_input_port_type(int p_port) const {
+ switch (p_port) {
+ case 0:
+ [[fallthrough]]; // uv
+ case 1:
+ return PORT_TYPE_VECTOR; // scale
+ case 2:
+ return PORT_TYPE_VECTOR; // offset & pivot
+ default:
+ break;
+ }
+ return PORT_TYPE_SCALAR;
+}
+
+String VisualShaderNodeUVFunc::get_input_port_name(int p_port) const {
+ switch (p_port) {
+ case 0:
+ return "uv";
+ case 1:
+ return "scale";
+ case 2:
+ switch (func) {
+ case FUNC_PANNING:
+ return "offset";
+ case FUNC_SCALING:
+ return "pivot";
+ case FUNC_MAX:
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ return "";
+}
+
+String VisualShaderNodeUVFunc::get_input_port_default_hint(int p_port) const {
+ if (p_port == 0) {
+ return "UV";
+ }
+ return "";
+}
+
+int VisualShaderNodeUVFunc::get_output_port_count() const {
+ return 1;
+}
+
+VisualShaderNodeUVFunc::PortType VisualShaderNodeUVFunc::get_output_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+
+String VisualShaderNodeUVFunc::get_output_port_name(int p_port) const {
+ return "uv";
+}
+
+bool VisualShaderNodeUVFunc::is_show_prop_names() const {
+ return true;
+}
+
+String VisualShaderNodeUVFunc::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;
+
+ String uv;
+ if (p_input_vars[0].is_empty()) {
+ uv = "vec3(UV.xy, 0.0)";
+ } else {
+ uv = vformat("%s", p_input_vars[0]);
+ }
+ String scale = vformat("%s", p_input_vars[1]);
+ String offset_pivot = vformat("%s", p_input_vars[2]);
+
+ switch (func) {
+ case FUNC_PANNING: {
+ code += vformat("\t%s = fma(%s, %s, %s);\n", p_output_vars[0], offset_pivot, scale, uv);
+ } break;
+ case FUNC_SCALING: {
+ code += vformat("\t%s = fma((%s - %s), %s, %s);\n", p_output_vars[0], uv, offset_pivot, scale, offset_pivot);
+ } break;
+ case FUNC_MAX:
+ break;
+ }
+ return code;
+}
+
+void VisualShaderNodeUVFunc::set_function(VisualShaderNodeUVFunc::Function p_func) {
+ ERR_FAIL_INDEX(int(p_func), FUNC_MAX);
+ if (func == p_func) {
+ return;
+ }
+ func = p_func;
+
+ if (p_func == FUNC_PANNING) {
+ set_input_port_default_value(2, Vector3()); // offset
+ } else { // FUNC_SCALING
+ set_input_port_default_value(2, Vector3(0.5, 0.5, 0.0)); // pivot
+ }
+ emit_changed();
+}
+
+VisualShaderNodeUVFunc::Function VisualShaderNodeUVFunc::get_function() const {
+ return func;
+}
+
+Vector VisualShaderNodeUVFunc::get_editable_properties() const {
+ Vector props;
+ props.push_back("function");
+ return props;
+}
+
+void VisualShaderNodeUVFunc::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_function", "func"), &VisualShaderNodeUVFunc::set_function);
+ ClassDB::bind_method(D_METHOD("get_function"), &VisualShaderNodeUVFunc::get_function);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "Panning,Scaling"), "set_function", "get_function");
+
+ BIND_ENUM_CONSTANT(FUNC_PANNING);
+ BIND_ENUM_CONSTANT(FUNC_SCALING);
+ BIND_ENUM_CONSTANT(FUNC_MAX);
+}
+
+VisualShaderNodeUVFunc::VisualShaderNodeUVFunc() {
+ set_input_port_default_value(1, Vector3(1.0, 1.0, 0.0)); // scale
+ set_input_port_default_value(2, Vector3()); // offset
+}
+
////////////// Dot Product
String VisualShaderNodeDotProduct::get_caption() const {
diff --git a/scene/resources/visual_shader_nodes.h b/scene/resources/visual_shader_nodes.h
index 45237d28240..5b44e9f7760 100644
--- a/scene/resources/visual_shader_nodes.h
+++ b/scene/resources/visual_shader_nodes.h
@@ -1018,6 +1018,51 @@ public:
VARIANT_ENUM_CAST(VisualShaderNodeTransformFunc::Function)
+///////////////////////////////////////
+/// UV FUNC
+///////////////////////////////////////
+
+class VisualShaderNodeUVFunc : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeUVFunc, VisualShaderNode);
+
+public:
+ enum Function {
+ FUNC_PANNING,
+ FUNC_SCALING,
+ FUNC_MAX,
+ };
+
+protected:
+ Function func = FUNC_PANNING;
+
+ 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 String get_input_port_default_hint(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 is_show_prop_names() const override;
+
+ 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;
+
+ void set_function(Function p_op);
+ Function get_function() const;
+
+ virtual Vector get_editable_properties() const override;
+
+ VisualShaderNodeUVFunc();
+};
+
+VARIANT_ENUM_CAST(VisualShaderNodeUVFunc::Function)
+
///////////////////////////////////////
/// DOT
///////////////////////////////////////