Skip to content

Commit

Permalink
Add Particles Turbulence Nodes and Posterize ColorFunc
Browse files Browse the repository at this point in the history
Adds Posterize ColorFunc Mode to CanvasItem and Spatial Visual Shader Nodes
Adds Particle Turbulence to Particle Visual Shader Nodes
  • Loading branch information
paddy-exe committed Apr 12, 2024
1 parent 29b3d9e commit c900dec
Show file tree
Hide file tree
Showing 8 changed files with 289 additions and 3 deletions.
5 changes: 4 additions & 1 deletion doc/classes/VisualShaderNodeColorFunc.xml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,10 @@
return vec3(r, g, b);
[/codeblock]
</constant>
<constant name="FUNC_MAX" value="4" enum="Function">
<constant name="FUNC_POSTERIZE" value="4" enum="Function">
Applies an effect which divides the input colors into a number of colors of the given steps input.
</constant>
<constant name="FUNC_MAX" value="5" enum="Function">
Represents the size of the [enum Function] enum.
</constant>
</constants>
Expand Down
12 changes: 12 additions & 0 deletions doc/classes/VisualShaderNodeParticleTurbulence.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="VisualShaderNodeParticleTurbulence" inherits="VisualShaderNode" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
A visual shader node for randomized particle movement.
</brief_description>
<description>
The Turbulence Node generates a noise direction and turbulence influence value based on the given noise parameters.
These can be used to add a turbulent motion to the particle's velocity, which allows for creating organic and natural-looking particle effects.
</description>
<tutorials>
</tutorials>
</class>
3 changes: 3 additions & 0 deletions editor/plugins/visual_shader_editor_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6135,6 +6135,7 @@ VisualShaderEditor::VisualShaderEditor() {
add_options.push_back(AddOption("EmitParticle", "Particles", "VisualShaderNodeParticleEmit", "", {}, -1, TYPE_FLAGS_PROCESS | TYPE_FLAGS_PROCESS_CUSTOM | TYPE_FLAGS_COLLIDE, Shader::MODE_PARTICLES));
add_options.push_back(AddOption("ParticleAccelerator", "Particles", "VisualShaderNodeParticleAccelerator", "", {}, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_PROCESS, Shader::MODE_PARTICLES));
add_options.push_back(AddOption("ParticleRandomness", "Particles", "VisualShaderNodeParticleRandomness", "", {}, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_EMIT | TYPE_FLAGS_PROCESS | TYPE_FLAGS_COLLIDE, Shader::MODE_PARTICLES));
add_options.push_back(AddOption("ParticleTurbulence", "Particles", "VisualShaderNodeParticleTurbulence", "", {}, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_PROCESS, Shader::MODE_PARTICLES));
add_options.push_back(AddOption("MultiplyByAxisAngle (*)", "Particles/Transform", "VisualShaderNodeParticleMultiplyByAxisAngle", TTR("A node for help to multiply a position input vector by rotation using specific axis. Intended to work with emitters."), {}, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_EMIT | TYPE_FLAGS_PROCESS | TYPE_FLAGS_COLLIDE, Shader::MODE_PARTICLES));

add_options.push_back(AddOption("BoxEmitter", "Particles/Emitters", "VisualShaderNodeParticleBoxEmitter", "", {}, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_EMIT, Shader::MODE_PARTICLES));
Expand Down Expand Up @@ -6495,6 +6496,8 @@ VisualShaderEditor::VisualShaderEditor() {
add_options.push_back(AddOption("Trunc", "Vector/Functions", "VisualShaderNodeVectorFunc", TTR("Finds the truncated value of the parameter."), { VisualShaderNodeVectorFunc::FUNC_TRUNC, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("Trunc", "Vector/Functions", "VisualShaderNodeVectorFunc", TTR("Finds the truncated value of the parameter."), { VisualShaderNodeVectorFunc::FUNC_TRUNC, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D));

add_options.push_back(AddOption("Posterize", "Color", "VisualShaderNodeColorFunc", TTR("Reduces a color input to a color gradient of the given step number."), { VisualShaderNodeColorFunc::FUNC_POSTERIZE }, VisualShaderNode::PORT_TYPE_VECTOR_3D));

add_options.push_back(AddOption("Add (+)", "Vector/Operators", "VisualShaderNodeVectorOp", TTR("Adds 2D vector to 2D vector."), { VisualShaderNodeVectorOp::OP_ADD, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
add_options.push_back(AddOption("Add (+)", "Vector/Operators", "VisualShaderNodeVectorOp", TTR("Adds 3D vector to 3D vector."), { VisualShaderNodeVectorOp::OP_ADD, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("Add (+)", "Vector/Operators", "VisualShaderNodeVectorOp", TTR("Adds 4D vector to 4D vector."), { VisualShaderNodeVectorOp::OP_ADD, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D));
Expand Down
1 change: 1 addition & 0 deletions scene/register_scene_types.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -750,6 +750,7 @@ void register_scene_types() {
GDREGISTER_CLASS(VisualShaderNodeParticleConeVelocity);
GDREGISTER_CLASS(VisualShaderNodeParticleRandomness);
GDREGISTER_CLASS(VisualShaderNodeParticleAccelerator);
GDREGISTER_CLASS(VisualShaderNodeParticleTurbulence);
GDREGISTER_CLASS(VisualShaderNodeParticleEmit);

GDREGISTER_VIRTUAL_CLASS(Material);
Expand Down
46 changes: 44 additions & 2 deletions scene/resources/visual_shader_nodes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3179,14 +3179,47 @@ String VisualShaderNodeColorFunc::get_caption() const {
}

int VisualShaderNodeColorFunc::get_input_port_count() const {
return 1;
switch (func) {
case FUNC_POSTERIZE:
return 2;
default:
return 1;
}
}

VisualShaderNodeColorFunc::PortType VisualShaderNodeColorFunc::get_input_port_type(int p_port) const {
switch (func) {
case FUNC_POSTERIZE:
switch (p_port) {
case 0:
return PORT_TYPE_VECTOR_3D;
case 1:
return PORT_TYPE_SCALAR_INT;
default:
break;
}
break;
default:
break;
}
return PORT_TYPE_VECTOR_3D;
}

String VisualShaderNodeColorFunc::get_input_port_name(int p_port) const {
switch (func) {
case FUNC_POSTERIZE:
switch (p_port) {
case 0:
return "";
case 1:
return "Steps";
default:
break;
}
break;
default:
break;
}
return "";
}

Expand Down Expand Up @@ -3242,6 +3275,13 @@ String VisualShaderNodeColorFunc::generate_code(Shader::Mode p_mode, VisualShade
code += " " + p_output_vars[0] + " = vec3(r, g, b);\n";
code += " }\n";
break;
case FUNC_POSTERIZE:
code += " {\n";
code += " vec3 c = " + p_input_vars[0] + ";\n";
code += " int s = " + p_input_vars[1] + ";\n";
code += " " + p_output_vars[0] + " = (floor(c * float(s)) / float(s - 1));\n";
code += " }\n";
break;
default:
break;
}
Expand Down Expand Up @@ -3272,18 +3312,20 @@ void VisualShaderNodeColorFunc::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_function", "func"), &VisualShaderNodeColorFunc::set_function);
ClassDB::bind_method(D_METHOD("get_function"), &VisualShaderNodeColorFunc::get_function);

ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "Grayscale,HSV2RGB,RGB2HSV,Sepia"), "set_function", "get_function");
ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "Grayscale,HSV2RGB,RGB2HSV,Sepia, Posterize"), "set_function", "get_function");

BIND_ENUM_CONSTANT(FUNC_GRAYSCALE);
BIND_ENUM_CONSTANT(FUNC_HSV2RGB);
BIND_ENUM_CONSTANT(FUNC_RGB2HSV);
BIND_ENUM_CONSTANT(FUNC_SEPIA);
BIND_ENUM_CONSTANT(FUNC_POSTERIZE);
BIND_ENUM_CONSTANT(FUNC_MAX);
}

VisualShaderNodeColorFunc::VisualShaderNodeColorFunc() {
simple_decl = false;
set_input_port_default_value(0, Vector3());
set_input_port_default_value(1, 2);
}

////////////// Transform Func
Expand Down
1 change: 1 addition & 0 deletions scene/resources/visual_shader_nodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -1340,6 +1340,7 @@ class VisualShaderNodeColorFunc : public VisualShaderNode {
FUNC_HSV2RGB,
FUNC_RGB2HSV,
FUNC_SEPIA,
FUNC_POSTERIZE,
FUNC_MAX,
};

Expand Down
200 changes: 200 additions & 0 deletions scene/resources/visual_shader_particle_nodes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1162,6 +1162,206 @@ VisualShaderNodeParticleAccelerator::VisualShaderNodeParticleAccelerator() {
simple_decl = false;
}

// VisualShaderNodeParticleTurbulence

String VisualShaderNodeParticleTurbulence::get_caption() const {
return "ParticleTurbulence";
}

int VisualShaderNodeParticleTurbulence::get_output_port_count() const {
return 2;
}

VisualShaderNodeParticleTurbulence::PortType VisualShaderNodeParticleTurbulence::get_output_port_type(int p_port) const {
switch (p_port) {
case 0:
return PORT_TYPE_SCALAR;
case 1:
return PORT_TYPE_VECTOR_3D;
}
return PORT_TYPE_SCALAR;
}

String VisualShaderNodeParticleTurbulence::get_output_port_name(int p_port) const {
switch (p_port) {
case 0:
return "turbulence influence";
case 1:
return "noise direction";
}
return String();
}

bool VisualShaderNodeParticleTurbulence::is_input_port_default(int p_port, Shader::Mode p_mode) const {
return p_port == 1; // seed
}

int VisualShaderNodeParticleTurbulence::get_input_port_count() const {
return 9;
}

VisualShaderNodeParticleTurbulence::PortType VisualShaderNodeParticleTurbulence::get_input_port_type(int p_port) const {
switch (p_port) {
case 0:
return PORT_TYPE_VECTOR_3D;
case 1:
return PORT_TYPE_SCALAR_UINT;
case 2:
return PORT_TYPE_SCALAR;
case 3:
return PORT_TYPE_SCALAR;
case 4:
return PORT_TYPE_VECTOR_3D;
case 5:
return PORT_TYPE_SCALAR;
case 6:
return PORT_TYPE_SCALAR;
case 7:
return PORT_TYPE_SCALAR;
case 8:
return PORT_TYPE_SCALAR;
}
return PORT_TYPE_SCALAR;
}

String VisualShaderNodeParticleTurbulence::get_input_port_name(int p_port) const {
switch (p_port) {
case 0:
return "particle position";
case 1:
return "noise seed";
case 2:
return "noise strength";
case 3:
return "noise scale";
case 4:
return "noise speed";
case 5:
return "noise speed random";
case 6:
return "influence min";
case 7:
return "influence max";
case 8:
return "influence over life";
}
return String();
}

String VisualShaderNodeParticleTurbulence::generate_global_per_node(Shader::Mode p_mode, int p_id) const {
String code;

code = vformat(R"(
vec4 turb_grad(vec4 p) {
p = fract(vec4(
dot(p, vec4(0.143081, 0.001724, 0.280166, 0.262771)),
dot(p, vec4(0.645401, -0.047791, -0.146698, 0.595016)),
dot(p, vec4(-0.499665, -0.095734, 0.425674, -0.207367)),
dot(p, vec4(-0.013596, -0.848588, 0.423736, 0.17044))));
return fract((p.xyzw * p.yzwx) * 2365.952041) * 2.0 - 1.0;
}
float turb_noise(vec4 coord) {
// Domain rotation to improve the look of XYZ slices + animation patterns.
coord = vec4(
coord.xyz + dot(coord, vec4(vec3(-0.1666667), -0.5)),
dot(coord, vec4(0.5)));
vec4 base = floor(coord), delta = coord - base;
vec4 grad_0000 = turb_grad(base + vec4(0.0, 0.0, 0.0, 0.0)), grad_1000 = turb_grad(base + vec4(1.0, 0.0, 0.0, 0.0));
vec4 grad_0100 = turb_grad(base + vec4(0.0, 1.0, 0.0, 0.0)), grad_1100 = turb_grad(base + vec4(1.0, 1.0, 0.0, 0.0));
vec4 grad_0010 = turb_grad(base + vec4(0.0, 0.0, 1.0, 0.0)), grad_1010 = turb_grad(base + vec4(1.0, 0.0, 1.0, 0.0));
vec4 grad_0110 = turb_grad(base + vec4(0.0, 1.0, 1.0, 0.0)), grad_1110 = turb_grad(base + vec4(1.0, 1.0, 1.0, 0.0));
vec4 grad_0001 = turb_grad(base + vec4(0.0, 0.0, 0.0, 1.0)), grad_1001 = turb_grad(base + vec4(1.0, 0.0, 0.0, 1.0));
vec4 grad_0101 = turb_grad(base + vec4(0.0, 1.0, 0.0, 1.0)), grad_1101 = turb_grad(base + vec4(1.0, 1.0, 0.0, 1.0));
vec4 grad_0011 = turb_grad(base + vec4(0.0, 0.0, 1.0, 1.0)), grad_1011 = turb_grad(base + vec4(1.0, 0.0, 1.0, 1.0));
vec4 grad_0111 = turb_grad(base + vec4(0.0, 1.0, 1.0, 1.0)), grad_1111 = turb_grad(base + vec4(1.0, 1.0, 1.0, 1.0));
vec4 result_0123 = vec4(
dot(delta - vec4(0.0, 0.0, 0.0, 0.0), grad_0000), dot(delta - vec4(1.0, 0.0, 0.0, 0.0), grad_1000),
dot(delta - vec4(0.0, 1.0, 0.0, 0.0), grad_0100), dot(delta - vec4(1.0, 1.0, 0.0, 0.0), grad_1100));
vec4 result_4567 = vec4(
dot(delta - vec4(0.0, 0.0, 1.0, 0.0), grad_0010), dot(delta - vec4(1.0, 0.0, 1.0, 0.0), grad_1010),
dot(delta - vec4(0.0, 1.0, 1.0, 0.0), grad_0110), dot(delta - vec4(1.0, 1.0, 1.0, 0.0), grad_1110));
vec4 result_89AB = vec4(
dot(delta - vec4(0.0, 0.0, 0.0, 1.0), grad_0001), dot(delta - vec4(1.0, 0.0, 0.0, 1.0), grad_1001),
dot(delta - vec4(0.0, 1.0, 0.0, 1.0), grad_0101), dot(delta - vec4(1.0, 1.0, 0.0, 1.0), grad_1101));
vec4 result_CDEF = vec4(
dot(delta - vec4(0.0, 0.0, 1.0, 1.0), grad_0011), dot(delta - vec4(1.0, 0.0, 1.0, 1.0), grad_1011),
dot(delta - vec4(0.0, 1.0, 1.0, 1.0), grad_0111), dot(delta - vec4(1.0, 1.0, 1.0, 1.0), grad_1111));
vec4 fade = delta * delta * delta * (10.0 + delta * (-15.0 + delta * 6.0));
vec4 result_W0 = mix(result_0123, result_89AB, fade.w), result_W1 = mix(result_4567, result_CDEF, fade.w);
vec4 result_WZ = mix(result_W0, result_W1, fade.z);
vec2 result_WZY = mix(result_WZ.xy, result_WZ.zw, fade.y);
return mix(result_WZY.x, result_WZY.y, fade.x);
}
// Curl 3D and three-noise function with friendly permission by Isaac Cohen.
// Modified to accept 4D noise.
vec3 turb_noise_3x(vec4 p) {
float s = turb_noise(p);
float s1 = turb_noise(p + vec4(vec3(0.0), 1.7320508 * 2048.333333));
float s2 = turb_noise(p - vec4(vec3(0.0), 1.7320508 * 2048.333333));
vec3 c = vec3(s, s1, s2);
return c;
}
vec3 turb_curl_3d(vec4 p, float c) {
float epsilon = 0.001 + c;
vec4 dx = vec4(epsilon, 0.0, 0.0, 0.0);
vec4 dy = vec4(0.0, epsilon, 0.0, 0.0);
vec4 dz = vec4(0.0, 0.0, epsilon, 0.0);
vec3 x0 = turb_noise_3x(p - dx).xyz;
vec3 x1 = turb_noise_3x(p + dx).xyz;
vec3 y0 = turb_noise_3x(p - dy).xyz;
vec3 y1 = turb_noise_3x(p + dy).xyz;
vec3 z0 = turb_noise_3x(p - dz).xyz;
vec3 z1 = turb_noise_3x(p + dz).xyz;
float x = (y1.z - y0.z) - (z1.y - z0.y);
float y = (z1.x - z0.x) - (x1.z - x0.z);
float z = (x1.y - x0.y) - (y1.x - y0.x);
return normalize(vec3(x, y, z));
}
vec3 get_turb_noise_direction(vec3 __pos, float __turb_n_strength, float __turb_n_scale, vec3 __turb_n_speed, float __turb_n_speed_random) {
float __adj_contrast = max((__turb_n_strength - 1.0), 0.0) * 70.0;
vec4 __noise_time = TIME * vec4(__turb_n_speed, __turb_n_speed_random);
vec4 __noise_pos = vec4(__pos * __turb_n_scale, 0.0);
vec3 __noise_direction = turb_curl_3d(__noise_pos + __noise_time, __adj_contrast);
__noise_direction = mix(0.9 * __noise_direction, __noise_direction, __turb_n_strength - 9.0);
return __noise_direction;
}
float get_turb_influence(uint __noise_seed, float __turb_influence_min, float __turb_influence_max, float __turb_influence_over_life) {
float __turb_influence_range = mix(__turb_influence_min, __turb_influence_max, __rand_from_seed(__noise_seed));
float __turb_influence = clamp(__turb_influence_range * __turb_influence_over_life, 0.0, 1.0);
return __turb_influence;
}
)");

return code;
}

String VisualShaderNodeParticleTurbulence::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;
code += vformat(" %s = get_turb_influence(%s, %s, %s, %s);\n", p_output_vars[0], p_input_vars[1].is_empty() ? "__seed" : p_input_vars[1], p_input_vars[6], p_input_vars[7], p_input_vars[8]);
code += vformat(" %s = get_turb_noise_direction(%s, %s, %s, %s, %s);\n", p_output_vars[1], p_input_vars[0], p_input_vars[2], p_input_vars[3], p_input_vars[4], p_input_vars[5]);

return code;
}

bool VisualShaderNodeParticleTurbulence::has_output_port_preview(int p_port) const {
return false;
}

VisualShaderNodeParticleTurbulence::VisualShaderNodeParticleTurbulence() {
set_input_port_default_value(2, 1.0); // noise strength
set_input_port_default_value(3, 9.0); // noise scale
set_input_port_default_value(4, Vector3(0.0, 0.0, 0.0)); // noise speed
set_input_port_default_value(5, 0.2); // noise speed random
set_input_port_default_value(6, 0.1); // influence min
set_input_port_default_value(7, 0.1); // influence max
set_input_port_default_value(8, 1.0); // influence over life
}

// VisualShaderNodeParticleOutput

String VisualShaderNodeParticleOutput::get_caption() const {
Expand Down
24 changes: 24 additions & 0 deletions scene/resources/visual_shader_particle_nodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,30 @@ class VisualShaderNodeParticleAccelerator : public VisualShaderNode {

VARIANT_ENUM_CAST(VisualShaderNodeParticleAccelerator::Mode)

class VisualShaderNodeParticleTurbulence : public VisualShaderNode {
GDCLASS(VisualShaderNodeParticleTurbulence, VisualShaderNode);

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 bool is_input_port_default(int p_port, Shader::Mode p_mode) 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;

virtual String generate_global_per_node(Shader::Mode p_mode, int p_id) 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;

virtual Category get_category() const override { return CATEGORY_PARTICLE; }

VisualShaderNodeParticleTurbulence();
};

// Common nodes

class VisualShaderNodeParticleOutput : public VisualShaderNodeOutput {
Expand Down

0 comments on commit c900dec

Please sign in to comment.