summaryrefslogtreecommitdiff
path: root/scene/resources
diff options
context:
space:
mode:
authorYuri Roubinsky <chaosus89@gmail.com>2020-09-15 10:57:40 +0300
committerYuri Roubinsky <chaosus89@gmail.com>2021-06-07 20:33:17 +0300
commitf632e36ae5fbc8499eb0bdac753bfd864759cbbb (patch)
tree2043999161236429a17021312c1289d598818a4e /scene/resources
parent3041becc644e5feaef6133afeaf801659ec886c4 (diff)
Continuation of work on visual particles system
Diffstat (limited to 'scene/resources')
-rw-r--r--scene/resources/visual_shader.cpp334
-rw-r--r--scene/resources/visual_shader.h6
-rw-r--r--scene/resources/visual_shader_nodes.cpp2
-rw-r--r--scene/resources/visual_shader_particle_nodes.cpp1025
-rw-r--r--scene/resources/visual_shader_particle_nodes.h285
5 files changed, 1566 insertions, 86 deletions
diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp
index fd0b568f71..5759948fe6 100644
--- a/scene/resources/visual_shader.cpp
+++ b/scene/resources/visual_shader.cpp
@@ -33,6 +33,7 @@
#include "core/templates/vmap.h"
#include "servers/rendering/shader_types.h"
#include "visual_shader_nodes.h"
+#include "visual_shader_particle_nodes.h"
#include "visual_shader_sdf_nodes.h"
bool VisualShaderNode::is_simple_decl() const {
@@ -1079,9 +1080,11 @@ static const char *type_string[VisualShader::TYPE_MAX] = {
"vertex",
"fragment",
"light",
- "emit",
+ "start",
"process",
- "end",
+ "collide",
+ "start_custom",
+ "process_custom",
"sky",
};
@@ -1357,7 +1360,8 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui
return OK;
}
- code += "// " + vsnode->get_caption() + ":" + itos(node) + "\n";
+ String node_name = "// " + vsnode->get_caption() + ":" + itos(node) + "\n";
+ String node_code;
Vector<String> input_vars;
input_vars.resize(vsnode->get_input_port_count());
@@ -1428,19 +1432,19 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui
if (defval.get_type() == Variant::FLOAT) {
float val = defval;
inputs[i] = "n_in" + itos(node) + "p" + itos(i);
- code += "\tfloat " + inputs[i] + " = " + vformat("%.5f", val) + ";\n";
+ node_code += "\tfloat " + inputs[i] + " = " + vformat("%.5f", val) + ";\n";
} else if (defval.get_type() == Variant::INT) {
int val = defval;
inputs[i] = "n_in" + itos(node) + "p" + itos(i);
- code += "\tint " + inputs[i] + " = " + itos(val) + ";\n";
+ node_code += "\tint " + inputs[i] + " = " + itos(val) + ";\n";
} else if (defval.get_type() == Variant::BOOL) {
bool val = defval;
inputs[i] = "n_in" + itos(node) + "p" + itos(i);
- code += "\tbool " + inputs[i] + " = " + (val ? "true" : "false") + ";\n";
+ node_code += "\tbool " + inputs[i] + " = " + (val ? "true" : "false") + ";\n";
} else if (defval.get_type() == Variant::VECTOR3) {
Vector3 val = defval;
inputs[i] = "n_in" + itos(node) + "p" + itos(i);
- code += "\tvec3 " + inputs[i] + " = " + vformat("vec3(%.5f, %.5f, %.5f);\n", val.x, val.y, val.z);
+ node_code += "\tvec3 " + inputs[i] + " = " + vformat("vec3(%.5f, %.5f, %.5f);\n", val.x, val.y, val.z);
} else if (defval.get_type() == Variant::TRANSFORM3D) {
Transform3D val = defval;
val.basis.transpose();
@@ -1455,7 +1459,7 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui
values.push_back(val.origin.y);
values.push_back(val.origin.z);
bool err = false;
- code += "\tmat4 " + inputs[i] + " = " + String("mat4(vec4(%.5f, %.5f, %.5f, 0.0), vec4(%.5f, %.5f, %.5f, 0.0), vec4(%.5f, %.5f, %.5f, 0.0), vec4(%.5f, %.5f, %.5f, 1.0));\n").sprintf(values, &err);
+ node_code += "\tmat4 " + inputs[i] + " = " + String("mat4(vec4(%.5f, %.5f, %.5f, 0.0), vec4(%.5f, %.5f, %.5f, 0.0), vec4(%.5f, %.5f, %.5f, 0.0), vec4(%.5f, %.5f, %.5f, 1.0));\n").sprintf(values, &err);
} else {
//will go empty, node is expected to know what it is doing at this point and handle it
}
@@ -1543,7 +1547,12 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui
}
}
- code += vsnode->generate_code(get_mode(), type, node, inputs, outputs, for_preview);
+ node_code += vsnode->generate_code(get_mode(), type, node, inputs, outputs, for_preview);
+ if (node_code != String()) {
+ code += node_name;
+ code += node_code;
+ code += "\n";
+ }
for (int i = 0; i < output_count; i++) {
bool new_line_inserted = false;
@@ -1593,7 +1602,7 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui
bool VisualShader::has_func_name(RenderingServer::ShaderMode p_mode, const String &p_func_name) const {
if (!ShaderTypes::get_singleton()->get_functions(p_mode).has(p_func_name)) {
if (p_mode == RenderingServer::ShaderMode::SHADER_PARTICLES) {
- if (p_func_name == "emit" || p_func_name == "process" || p_func_name == "end") {
+ if (p_func_name == "start_custom" || p_func_name == "process_custom" || p_func_name == "collide") {
return true;
}
}
@@ -1674,11 +1683,12 @@ void VisualShader::_update_shader() const {
global_code += "render_mode " + render_mode + ";\n\n";
}
- static const char *func_name[TYPE_MAX] = { "vertex", "fragment", "light", "emit", "process", "end", "sky" };
+ static const char *func_name[TYPE_MAX] = { "vertex", "fragment", "light", "start", "process", "collide", "start_custom", "process_custom", "sky" };
String global_expressions;
Set<String> used_uniform_names;
List<VisualShaderNodeUniform *> uniforms;
+ Map<int, List<int>> emitters;
for (int i = 0, index = 0; i < TYPE_MAX; i++) {
if (!has_func_name(RenderingServer::ShaderMode(shader_mode), func_name[i])) {
@@ -1703,6 +1713,19 @@ void VisualShader::_update_shader() const {
if (uniform.is_valid()) {
uniforms.push_back(uniform.ptr());
}
+ Ref<VisualShaderNodeParticleEmit> emit_particle = Object::cast_to<VisualShaderNodeParticleEmit>(E->get().node.ptr());
+ if (emit_particle.is_valid()) {
+ if (!emitters.has(i)) {
+ emitters.insert(i, List<int>());
+ }
+
+ for (Map<int, Node>::Element *M = graph[i].nodes.front(); M; M = M->next()) {
+ if (M->get().node == emit_particle.ptr()) {
+ emitters[i].push_back(M->key());
+ break;
+ }
+ }
+ }
}
}
@@ -1751,6 +1774,13 @@ void VisualShader::_update_shader() const {
Error err = _write_node(Type(i), global_code, global_code_per_node, global_code_per_func, func_code, default_tex_params, input_connections, output_connections, NODE_ID_OUTPUT, processed, false, classes);
ERR_FAIL_COND(err != OK);
+ if (emitters.has(i)) {
+ for (List<int>::Element *E = emitters[i].front(); E; E = E->next()) {
+ err = _write_node(Type(i), global_code, global_code_per_node, global_code_per_func, func_code, default_tex_params, input_connections, output_connections, E->get(), processed, false, classes);
+ ERR_FAIL_COND(err != OK);
+ }
+ }
+
if (shader_mode == Shader::MODE_PARTICLES) {
code_map.insert(i, func_code);
} else {
@@ -1759,19 +1789,130 @@ void VisualShader::_update_shader() const {
}
}
+ String global_compute_code;
+
if (shader_mode == Shader::MODE_PARTICLES) {
- code += "\nvoid compute() {\n";
- code += "\tif (RESTART) {\n";
- code += code_map[TYPE_EMIT];
- code += "\t} else {\n";
- code += code_map[TYPE_PROCESS];
+ bool has_start = !code_map[TYPE_START].is_empty();
+ bool has_start_custom = !code_map[TYPE_START_CUSTOM].is_empty();
+ bool has_process = !code_map[TYPE_PROCESS].is_empty();
+ bool has_process_custom = !code_map[TYPE_PROCESS_CUSTOM].is_empty();
+ bool has_collide = !code_map[TYPE_COLLIDE].is_empty();
+
+ code += "void start() {\n";
+ if (has_start || has_start_custom) {
+ code += "\tuint __seed = __hash(NUMBER + uint(1) + RANDOM_SEED);\n";
+ code += "\tvec3 __diff = TRANSFORM[3].xyz - EMISSION_TRANSFORM[3].xyz;\n";
+ code += "\tfloat __radians;\n";
+ code += "\tvec3 __vec3_buff1;\n";
+ code += "\tvec3 __vec3_buff2;\n";
+ code += "\tfloat __scalar_buff1;\n";
+ code += "\tfloat __scalar_buff2;\n";
+ code += "\tvec3 __ndiff = normalize(__diff);\n\n";
+ }
+ if (has_start) {
+ code += "\t{\n";
+ code += code_map[TYPE_START].replace("\n\t", "\n\t\t");
+ code += "\t}\n";
+ if (has_start_custom) {
+ code += "\t\n";
+ }
+ }
+ if (has_start_custom) {
+ code += "\t{\n";
+ code += code_map[TYPE_START_CUSTOM].replace("\n\t", "\n\t\t");
+ code += "\t}\n";
+ }
+ code += "}\n\n";
+ code += "void process() {\n";
+ if (has_process || has_process_custom || has_collide) {
+ code += "\tuint __seed = __hash(NUMBER + uint(1) + RANDOM_SEED);\n";
+ code += "\tvec3 __vec3_buff1;\n";
+ code += "\tvec3 __diff = TRANSFORM[3].xyz - EMISSION_TRANSFORM[3].xyz;\n";
+ code += "\tvec3 __ndiff = normalize(__diff);\n\n";
+ }
+ code += "\t{\n";
+ String tab = "\t";
+ if (has_collide) {
+ code += "\t\tif (COLLIDED) {\n\n";
+ code += code_map[TYPE_COLLIDE].replace("\n\t", "\n\t\t\t");
+ if (has_process) {
+ code += "\t\t} else {\n\n";
+ tab += "\t";
+ }
+ }
+ if (has_process) {
+ code += code_map[TYPE_PROCESS].replace("\n\t", "\n\t" + tab);
+ }
+ if (has_collide) {
+ code += "\t\t}\n";
+ }
code += "\t}\n";
- code += "}\n";
+
+ if (has_process_custom) {
+ code += "\t{\n\n";
+ code += code_map[TYPE_PROCESS_CUSTOM].replace("\n\t", "\n\t\t");
+ code += "\t}\n";
+ }
+
+ code += "}\n\n";
+
+ global_compute_code += "float __rand_from_seed(inout uint seed) {\n";
+ global_compute_code += "\tint k;\n";
+ global_compute_code += "\tint s = int(seed);\n";
+ global_compute_code += "\tif (s == 0)\n";
+ global_compute_code += "\ts = 305420679;\n";
+ global_compute_code += "\tk = s / 127773;\n";
+ global_compute_code += "\ts = 16807 * (s - k * 127773) - 2836 * k;\n";
+ global_compute_code += "\tif (s < 0)\n";
+ global_compute_code += "\t\ts += 2147483647;\n";
+ global_compute_code += "\tseed = uint(s);\n";
+ global_compute_code += "\treturn float(seed % uint(65536)) / 65535.0;\n";
+ global_compute_code += "}\n\n";
+
+ global_compute_code += "float __rand_from_seed_m1_p1(inout uint seed) {\n";
+ global_compute_code += "\treturn __rand_from_seed(seed) * 2.0 - 1.0;\n";
+ global_compute_code += "}\n\n";
+
+ global_compute_code += "float __randf_range(inout uint seed, float from, float to) {\n";
+ global_compute_code += "\treturn __rand_from_seed(seed) * (to - from) + from;\n";
+ global_compute_code += "}\n\n";
+
+ global_compute_code += "vec3 __randv_range(inout uint seed, vec3 from, vec3 to) {\n";
+ global_compute_code += "\treturn vec3(__randf_range(seed, from.x, to.x), __randf_range(seed, from.y, to.y), __randf_range(seed, from.z, to.z));\n";
+ global_compute_code += "}\n\n";
+
+ global_compute_code += "uint __hash(uint x) {\n";
+ global_compute_code += "\tx = ((x >> uint(16)) ^ x) * uint(73244475);\n";
+ global_compute_code += "\tx = ((x >> uint(16)) ^ x) * uint(73244475);\n";
+ global_compute_code += "\tx = (x >> uint(16)) ^ x;\n";
+ global_compute_code += "\treturn x;\n";
+ global_compute_code += "}\n\n";
+
+ global_compute_code += "mat3 __build_rotation_mat3(vec3 axis, float angle) {\n";
+ global_compute_code += "\taxis = normalize(axis);\n";
+ global_compute_code += "\tfloat s = sin(angle);\n";
+ global_compute_code += "\tfloat c = cos(angle);\n";
+ global_compute_code += "\tfloat oc = 1.0 - c;\n";
+ global_compute_code += "\treturn mat3(vec3(oc * axis.x * axis.x + c, oc * axis.x * axis.y - axis.z * s, oc * axis.z * axis.x + axis.y * s), vec3(oc * axis.x * axis.y + axis.z * s, oc * axis.y * axis.y + c, oc * axis.y * axis.z - axis.x * s), vec3(oc * axis.z * axis.x - axis.y * s, oc * axis.y * axis.z + axis.x * s, oc * axis.z * axis.z + c));\n";
+ global_compute_code += "}\n\n";
+
+ global_compute_code += "mat4 __build_rotation_mat4(vec3 axis, float angle) {\n";
+ global_compute_code += "\taxis = normalize(axis);\n";
+ global_compute_code += "\tfloat s = sin(angle);\n";
+ global_compute_code += "\tfloat c = cos(angle);\n";
+ global_compute_code += "\tfloat oc = 1.0 - c;\n";
+ global_compute_code += "\treturn mat4(vec4(oc * axis.x * axis.x + c, oc * axis.x * axis.y - axis.z * s, oc * axis.z * axis.x + axis.y * s, 0), vec4(oc * axis.x * axis.y + axis.z * s, oc * axis.y * axis.y + c, oc * axis.y * axis.z - axis.x * s, 0), vec4(oc * axis.z * axis.x - axis.y * s, oc * axis.y * axis.z + axis.x * s, oc * axis.z * axis.z + c, 0), vec4(0, 0, 0, 1));\n";
+ global_compute_code += "}\n\n";
+
+ global_compute_code += "vec3 __get_random_unit_vec3(inout uint seed) {\n";
+ global_compute_code += "\treturn normalize(vec3(__rand_from_seed_m1_p1(seed), __rand_from_seed_m1_p1(seed), __rand_from_seed_m1_p1(seed)));\n";
+ global_compute_code += "}\n\n";
}
//set code secretly
global_code += "\n\n";
String final_code = global_code;
+ final_code += global_compute_code;
final_code += global_code_per_node;
final_code += global_expressions;
String tcode = code;
@@ -1862,9 +2003,11 @@ void VisualShader::_bind_methods() {
BIND_ENUM_CONSTANT(TYPE_VERTEX);
BIND_ENUM_CONSTANT(TYPE_FRAGMENT);
BIND_ENUM_CONSTANT(TYPE_LIGHT);
- BIND_ENUM_CONSTANT(TYPE_EMIT);
+ BIND_ENUM_CONSTANT(TYPE_START);
BIND_ENUM_CONSTANT(TYPE_PROCESS);
- BIND_ENUM_CONSTANT(TYPE_END);
+ BIND_ENUM_CONSTANT(TYPE_COLLIDE);
+ BIND_ENUM_CONSTANT(TYPE_START_CUSTOM);
+ BIND_ENUM_CONSTANT(TYPE_PROCESS_CUSTOM);
BIND_ENUM_CONSTANT(TYPE_SKY);
BIND_ENUM_CONSTANT(TYPE_MAX);
@@ -1875,11 +2018,20 @@ void VisualShader::_bind_methods() {
VisualShader::VisualShader() {
dirty.set();
for (int i = 0; i < TYPE_MAX; i++) {
- Ref<VisualShaderNodeOutput> output;
- output.instance();
- output->shader_type = Type(i);
- output->shader_mode = shader_mode;
- graph[i].nodes[NODE_ID_OUTPUT].node = output;
+ if (i > (int)TYPE_LIGHT && i < (int)TYPE_SKY) {
+ Ref<VisualShaderNodeParticleOutput> output;
+ output.instance();
+ output->shader_type = Type(i);
+ output->shader_mode = shader_mode;
+ graph[i].nodes[NODE_ID_OUTPUT].node = output;
+ } else {
+ Ref<VisualShaderNodeOutput> output;
+ output.instance();
+ output->shader_type = Type(i);
+ output->shader_mode = shader_mode;
+ graph[i].nodes[NODE_ID_OUTPUT].node = output;
+ }
+
graph[i].nodes[NODE_ID_OUTPUT].position = Vector2(400, 150);
}
}
@@ -2008,27 +2160,45 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = {
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "specular_shininess", "SPECULAR_SHININESS.rgb" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "specular_shininess_alpha", "SPECULAR_SHININESS.a" },
- // Particles, Emit
- { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_VECTOR, "velocity", "VELOCITY" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_SCALAR, "restart", "float(RESTART ? 1.0 : 0.0)" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_SCALAR, "active", "float(ACTIVE ? 1.0 : 0.0)" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_VECTOR, "custom", "CUSTOM.rgb" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_SCALAR, "custom_alpha", "CUSTOM.a" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_TRANSFORM, "transform", "TRANSFORM" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_SCALAR, "delta", "DELTA" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_SCALAR, "lifetime", "LIFETIME" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_SCALAR_INT, "index", "INDEX" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_TRANSFORM, "emission_transform", "EMISSION_TRANSFORM" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+ // Particles, Start
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_VECTOR, "attractor_force", "ATTRACTOR_FORCE" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_VECTOR, "velocity", "VELOCITY" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_BOOLEAN, "restart", "RESTART" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_BOOLEAN, "active", "ACTIVE" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_VECTOR, "custom", "CUSTOM.rgb" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_SCALAR, "custom_alpha", "CUSTOM.a" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_TRANSFORM, "transform", "TRANSFORM" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_SCALAR, "delta", "DELTA" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_SCALAR, "lifetime", "LIFETIME" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_SCALAR_INT, "index", "INDEX" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_TRANSFORM, "emission_transform", "EMISSION_TRANSFORM" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+
+ // Particles, Start (Custom)
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_VECTOR, "attractor_force", "ATTRACTOR_FORCE" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_VECTOR, "velocity", "VELOCITY" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_BOOLEAN, "restart", "RESTART" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_BOOLEAN, "active", "ACTIVE" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_VECTOR, "custom", "CUSTOM.rgb" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR, "custom_alpha", "CUSTOM.a" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_TRANSFORM, "transform", "TRANSFORM" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR, "delta", "DELTA" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR, "lifetime", "LIFETIME" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR_INT, "index", "INDEX" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_TRANSFORM, "emission_transform", "EMISSION_TRANSFORM" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
// Particles, Process
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_VECTOR, "attractor_force", "ATTRACTOR_FORCE" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_VECTOR, "velocity", "VELOCITY" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_SCALAR, "restart", "float(RESTART ? 1.0 : 0.0)" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_SCALAR, "active", "float(ACTIVE ? 1.0 : 0.0)" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_BOOLEAN, "restart", "RESTART" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_BOOLEAN, "active", "ACTIVE" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_VECTOR, "custom", "CUSTOM.rgb" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_SCALAR, "custom_alpha", "CUSTOM.a" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_TRANSFORM, "transform", "TRANSFORM" },
@@ -2038,20 +2208,39 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = {
{ Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_TRANSFORM, "emission_transform", "EMISSION_TRANSFORM" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
- // Particles, End
- { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_VECTOR, "velocity", "VELOCITY" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_SCALAR, "restart", "float(RESTART ? 1.0 : 0.0)" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_SCALAR, "active", "float(ACTIVE ? 1.0 : 0.0)" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_VECTOR, "custom", "CUSTOM.rgb" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_SCALAR, "custom_alpha", "CUSTOM.a" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_TRANSFORM, "transform", "TRANSFORM" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_SCALAR, "delta", "DELTA" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_SCALAR, "lifetime", "LIFETIME" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_SCALAR_INT, "index", "INDEX" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_TRANSFORM, "emission_transform", "EMISSION_TRANSFORM" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+ // Particles, Process (Custom)
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_VECTOR, "attractor_force", "ATTRACTOR_FORCE" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_VECTOR, "velocity", "VELOCITY" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_BOOLEAN, "restart", "RESTART" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_BOOLEAN, "active", "ACTIVE" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_VECTOR, "custom", "CUSTOM.rgb" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR, "custom_alpha", "CUSTOM.a" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_TRANSFORM, "transform", "TRANSFORM" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR, "delta", "DELTA" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR, "lifetime", "LIFETIME" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR_INT, "index", "INDEX" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_TRANSFORM, "emission_transform", "EMISSION_TRANSFORM" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+
+ // Particles, Collide
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_VECTOR, "attractor_force", "ATTRACTOR_FORCE" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_SCALAR, "collision_depth", "COLLISION_DEPTH" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_VECTOR, "collision_normal", "COLLISION_NORMAL" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_VECTOR, "velocity", "VELOCITY" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_BOOLEAN, "restart", "RESTART" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_BOOLEAN, "active", "ACTIVE" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_VECTOR, "custom", "CUSTOM.rgb" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_SCALAR, "custom_alpha", "CUSTOM.a" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_TRANSFORM, "transform", "TRANSFORM" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_SCALAR, "delta", "DELTA" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_SCALAR, "lifetime", "LIFETIME" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_SCALAR_INT, "index", "INDEX" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_TRANSFORM, "emission_transform", "EMISSION_TRANSFORM" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
// Sky, Sky
{ Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_BOOLEAN, "at_cubemap_pass", "AT_CUBEMAP_PASS" },
@@ -2127,11 +2316,13 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::preview_ports[] = {
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(SCREEN_UV, 0.0)" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
- // Particles, Vertex
- { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "vec3(1.0)" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "1.0" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "velocity", "vec3(0.0, 0.0, 1.0)" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+
+ // Particles
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
{ Shader::MODE_MAX, VisualShader::TYPE_MAX, VisualShaderNode::PORT_TYPE_TRANSFORM, nullptr, nullptr },
};
@@ -2624,30 +2815,7 @@ const VisualShaderNodeOutput::Port VisualShaderNodeOutput::ports[] = {
// Canvas Item, Light
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light", "LIGHT.rgb" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "light_alpha", "LIGHT.a" },
- // Particles, Emit
- { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_VECTOR, "velocity", "VELOCITY" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_VECTOR, "custom", "CUSTOM.rgb" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_SCALAR, "custom_alpha", "CUSTOM.a" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_TRANSFORM, "transform", "TRANSFORM" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_BOOLEAN, "active", "ACTIVE" },
- // Particles, Process
- { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_VECTOR, "velocity", "VELOCITY" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_VECTOR, "custom", "CUSTOM.rgb" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_SCALAR, "custom_alpha", "CUSTOM.a" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_TRANSFORM, "transform", "TRANSFORM" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_BOOLEAN, "active", "ACTIVE" },
- // Particles, End
- { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_VECTOR, "velocity", "VELOCITY" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_VECTOR, "custom", "CUSTOM.rgb" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_SCALAR, "custom_alpha", "CUSTOM.a" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_TRANSFORM, "transform", "TRANSFORM" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_BOOLEAN, "active", "ACTIVE" },
+
// Sky, Sky
{ Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR" },
{ Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "ALPHA" },
diff --git a/scene/resources/visual_shader.h b/scene/resources/visual_shader.h
index 21c4d23819..53b165fe0f 100644
--- a/scene/resources/visual_shader.h
+++ b/scene/resources/visual_shader.h
@@ -51,9 +51,11 @@ public:
TYPE_VERTEX,
TYPE_FRAGMENT,
TYPE_LIGHT,
- TYPE_EMIT,
+ TYPE_START,
TYPE_PROCESS,
- TYPE_END,
+ TYPE_COLLIDE,
+ TYPE_START_CUSTOM,
+ TYPE_PROCESS_CUSTOM,
TYPE_SKY,
TYPE_MAX
};
diff --git a/scene/resources/visual_shader_nodes.cpp b/scene/resources/visual_shader_nodes.cpp
index 5bcc6dda97..d3b094de31 100644
--- a/scene/resources/visual_shader_nodes.cpp
+++ b/scene/resources/visual_shader_nodes.cpp
@@ -860,7 +860,7 @@ String VisualShaderNodeCurveTexture::generate_code(Shader::Mode p_mode, VisualSh
}
String id = make_unique_id(p_type, p_id, "curve");
String code;
- code += "\t" + p_output_vars[0] + " = texture(" + id + ", vec2(" + p_input_vars[0] + ", 0.0)).r;\n";
+ code += "\t" + p_output_vars[0] + " = texture(" + id + ", vec2(" + p_input_vars[0] + ")).r;\n";
return code;
}
diff --git a/scene/resources/visual_shader_particle_nodes.cpp b/scene/resources/visual_shader_particle_nodes.cpp
new file mode 100644
index 0000000000..29d583a82a
--- /dev/null
+++ b/scene/resources/visual_shader_particle_nodes.cpp
@@ -0,0 +1,1025 @@
+/*************************************************************************/
+/* visual_shader_particle_nodes.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "visual_shader_particle_nodes.h"
+
+// VisualShaderNodeParticleEmitter
+
+int VisualShaderNodeParticleEmitter::get_output_port_count() const {
+ return 1;
+}
+
+VisualShaderNodeParticleEmitter::PortType VisualShaderNodeParticleEmitter::get_output_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+
+String VisualShaderNodeParticleEmitter::get_output_port_name(int p_port) const {
+ if (p_port == 0) {
+ return "position";
+ }
+ return String();
+}
+
+VisualShaderNodeParticleEmitter::VisualShaderNodeParticleEmitter() {
+}
+
+// VisualShaderNodeParticleSphereEmitter
+
+String VisualShaderNodeParticleSphereEmitter::get_caption() const {
+ return "SphereEmitter";
+}
+
+int VisualShaderNodeParticleSphereEmitter::get_input_port_count() const {
+ return 2;
+}
+
+VisualShaderNodeParticleSphereEmitter::PortType VisualShaderNodeParticleSphereEmitter::get_input_port_type(int p_port) const {
+ return PORT_TYPE_SCALAR;
+}
+
+String VisualShaderNodeParticleSphereEmitter::get_input_port_name(int p_port) const {
+ if (p_port == 0) {
+ return "radius";
+ } else if (p_port == 1) {
+ return "inner_radius";
+ }
+ return String();
+}
+
+String VisualShaderNodeParticleSphereEmitter::generate_global_per_node(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const {
+ String code;
+ code += "vec3 __get_random_point_in_sphere(inout uint seed, float radius, float inner_radius) {\n";
+ code += "\treturn __get_random_unit_vec3(seed) * __randf_range(seed, inner_radius, radius);\n";
+ code += "}\n\n";
+ return code;
+}
+
+String VisualShaderNodeParticleSphereEmitter::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 += "\t" + p_output_vars[0] + " = __get_random_point_in_sphere(__seed, " + (p_input_vars[0].is_empty() ? (String)get_input_port_default_value(0) : p_input_vars[0]) + ", " + (p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]) + ");\n";
+ return code;
+}
+
+VisualShaderNodeParticleSphereEmitter::VisualShaderNodeParticleSphereEmitter() {
+ set_input_port_default_value(0, 10.0);
+ set_input_port_default_value(1, 0.0);
+}
+
+// VisualShaderNodeParticleBoxEmitter
+
+String VisualShaderNodeParticleBoxEmitter::get_caption() const {
+ return "BoxEmitter";
+}
+
+int VisualShaderNodeParticleBoxEmitter::get_input_port_count() const {
+ return 1;
+}
+
+VisualShaderNodeParticleBoxEmitter::PortType VisualShaderNodeParticleBoxEmitter::get_input_port_type(int p_port) const {
+ if (p_port == 0) {
+ return PORT_TYPE_VECTOR;
+ }
+ return PORT_TYPE_SCALAR;
+}
+
+String VisualShaderNodeParticleBoxEmitter::get_input_port_name(int p_port) const {
+ if (p_port == 0) {
+ return "extents";
+ }
+ return String();
+}
+
+String VisualShaderNodeParticleBoxEmitter::generate_global_per_node(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const {
+ String code;
+ code += "vec3 __get_random_point_in_box(inout uint seed, vec3 extents) {\n";
+ code += "\tvec3 half_extents = extents / 2.0;\n";
+ code += "\treturn vec3(__randf_range(seed, -half_extents.x, half_extents.x), __randf_range(seed, -half_extents.y, half_extents.y), __randf_range(seed, -half_extents.z, half_extents.z));\n";
+ code += "}\n\n";
+ return code;
+}
+
+String VisualShaderNodeParticleBoxEmitter::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 += "\t" + p_output_vars[0] + " = __get_random_point_in_box(__seed, " + (p_input_vars[0].is_empty() ? (String)get_input_port_default_value(0) : p_input_vars[0]) + ");\n";
+ return code;
+}
+
+VisualShaderNodeParticleBoxEmitter::VisualShaderNodeParticleBoxEmitter() {
+ set_input_port_default_value(0, Vector3(1.0, 1.0, 1.0));
+}
+
+// VisualShaderNodeParticleRingEmitter
+
+String VisualShaderNodeParticleRingEmitter::get_caption() const {
+ return "RingEmitter";
+}
+
+int VisualShaderNodeParticleRingEmitter::get_input_port_count() const {
+ return 3;
+}
+
+VisualShaderNodeParticleRingEmitter::PortType VisualShaderNodeParticleRingEmitter::get_input_port_type(int p_port) const {
+ return PORT_TYPE_SCALAR;
+}
+
+String VisualShaderNodeParticleRingEmitter::get_input_port_name(int p_port) const {
+ if (p_port == 0) {
+ return "radius";
+ } else if (p_port == 1) {
+ return "inner_radius";
+ } else if (p_port == 2) {
+ return "height";
+ }
+ return String();
+}
+
+String VisualShaderNodeParticleRingEmitter::generate_global_per_node(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const {
+ String code;
+ code += "vec3 __get_random_point_on_ring(inout uint seed, float radius, float inner_radius, float height) {\n";
+ code += "\tfloat angle = __rand_from_seed(seed) * PI * 2.0;\n";
+ code += "\tvec2 ring = vec2(sin(angle), cos(angle)) * __randf_range(seed, inner_radius, radius);\n";
+ code += "\treturn vec3(ring.x, __randf_range(seed, min(0.0, height), max(0.0, height)), ring.y);\n";
+ code += "}\n\n";
+ return code;
+}
+
+String VisualShaderNodeParticleRingEmitter::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 = "\t" + p_output_vars[0] + " = __get_random_point_on_ring(__seed, " + (p_input_vars[0].is_empty() ? (String)get_input_port_default_value(0) : p_input_vars[0]) + ", " + (p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]) + ", " + (p_input_vars[2].is_empty() ? (String)get_input_port_default_value(2) : p_input_vars[2]) + ");\n";
+ return code;
+}
+
+VisualShaderNodeParticleRingEmitter::VisualShaderNodeParticleRingEmitter() {
+ set_input_port_default_value(0, 10.0);
+ set_input_port_default_value(1, 0.0);
+ set_input_port_default_value(2, 0.0);
+}
+
+// VisualShaderNodeParticleMultiplyByAxisAngle
+
+void VisualShaderNodeParticleMultiplyByAxisAngle::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_degrees_mode", "enabled"), &VisualShaderNodeParticleMultiplyByAxisAngle::set_degrees_mode);
+ ClassDB::bind_method(D_METHOD("is_degrees_mode"), &VisualShaderNodeParticleMultiplyByAxisAngle::is_degrees_mode);
+
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "degrees_mode"), "set_degrees_mode", "is_degrees_mode");
+}
+
+String VisualShaderNodeParticleMultiplyByAxisAngle::get_caption() const {
+ return "MultiplyByAxisAngle";
+}
+
+int VisualShaderNodeParticleMultiplyByAxisAngle::get_input_port_count() const {
+ return 3;
+}
+
+VisualShaderNodeParticleMultiplyByAxisAngle::PortType VisualShaderNodeParticleMultiplyByAxisAngle::get_input_port_type(int p_port) const {
+ if (p_port == 0 || p_port == 1) { // position, rotation_axis
+ return PORT_TYPE_VECTOR;
+ }
+ return PORT_TYPE_SCALAR; // angle (degrees/radians)
+}
+
+String VisualShaderNodeParticleMultiplyByAxisAngle::get_input_port_name(int p_port) const {
+ if (p_port == 0) {
+ return "position";
+ }
+ if (p_port == 1) {
+ return "axis";
+ }
+ if (p_port == 2) {
+ if (degrees_mode) {
+ return "angle (degrees)";
+ } else {
+ return "angle (radians)";
+ }
+ }
+ return String();
+}
+
+bool VisualShaderNodeParticleMultiplyByAxisAngle::is_show_prop_names() const {
+ return true;
+}
+
+int VisualShaderNodeParticleMultiplyByAxisAngle::get_output_port_count() const {
+ return 1;
+}
+
+VisualShaderNodeParticleMultiplyByAxisAngle::PortType VisualShaderNodeParticleMultiplyByAxisAngle::get_output_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+
+String VisualShaderNodeParticleMultiplyByAxisAngle::get_output_port_name(int p_port) const {
+ return "position";
+}
+
+String VisualShaderNodeParticleMultiplyByAxisAngle::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;
+ if (degrees_mode) {
+ code += "\t" + p_output_vars[0] + " = __build_rotation_mat3(" + (p_input_vars[1].is_empty() ? ("vec3" + (String)get_input_port_default_value(1)) : p_input_vars[1]) + ", radians(" + (p_input_vars[2].is_empty() ? (String)get_input_port_default_value(2) : p_input_vars[2]) + ")) * " + (p_input_vars[0].is_empty() ? "vec3(0.0)" : p_input_vars[0]) + ";\n";
+ } else {
+ code += "\t" + p_output_vars[0] + " = __build_rotation_mat3(" + (p_input_vars[1].is_empty() ? ("vec3" + (String)get_input_port_default_value(1)) : p_input_vars[1]) + ", " + (p_input_vars[2].is_empty() ? (String)get_input_port_default_value(2) : p_input_vars[2]) + ") * " + (p_input_vars[0].is_empty() ? "vec3(0.0)" : p_input_vars[0]) + ";\n";
+ }
+ return code;
+}
+
+void VisualShaderNodeParticleMultiplyByAxisAngle::set_degrees_mode(bool p_enabled) {
+ degrees_mode = p_enabled;
+ emit_changed();
+}
+
+bool VisualShaderNodeParticleMultiplyByAxisAngle::is_degrees_mode() const {
+ return degrees_mode;
+}
+
+Vector<StringName> VisualShaderNodeParticleMultiplyByAxisAngle::get_editable_properties() const {
+ Vector<StringName> props;
+ props.push_back("degrees_mode");
+ props.push_back("axis_amount");
+ return props;
+}
+
+VisualShaderNodeParticleMultiplyByAxisAngle::VisualShaderNodeParticleMultiplyByAxisAngle() {
+ set_input_port_default_value(1, Vector3(1, 0, 0));
+ set_input_port_default_value(2, 0.0);
+}
+
+// VisualShaderNodeParticleConeVelocity
+
+String VisualShaderNodeParticleConeVelocity::get_caption() const {
+ return "ConeVelocity";
+}
+
+int VisualShaderNodeParticleConeVelocity::get_input_port_count() const {
+ return 2;
+}
+
+VisualShaderNodeParticleConeVelocity::PortType VisualShaderNodeParticleConeVelocity::get_input_port_type(int p_port) const {
+ if (p_port == 0) {
+ return PORT_TYPE_VECTOR;
+ } else if (p_port == 1) {
+ return PORT_TYPE_SCALAR;
+ }
+ return PORT_TYPE_SCALAR;
+}
+
+String VisualShaderNodeParticleConeVelocity::get_input_port_name(int p_port) const {
+ if (p_port == 0) {
+ return "direction";
+ } else if (p_port == 1) {
+ return "spread(degrees)";
+ }
+ return String();
+}
+
+int VisualShaderNodeParticleConeVelocity::get_output_port_count() const {
+ return 1;
+}
+
+VisualShaderNodeParticleConeVelocity::PortType VisualShaderNodeParticleConeVelocity::get_output_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+
+String VisualShaderNodeParticleConeVelocity::get_output_port_name(int p_port) const {
+ if (p_port == 0) {
+ return "velocity";
+ }
+ return String();
+}
+
+String VisualShaderNodeParticleConeVelocity::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 += "\t__radians = radians(" + (p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]) + ");\n";
+ code += "\t__scalar_buff1 = __rand_from_seed_m1_p1(__seed) * __radians;\n";
+ code += "\t__scalar_buff2 = __rand_from_seed_m1_p1(__seed) * __radians;\n";
+ code += "\t__vec3_buff1 = " + (p_input_vars[0].is_empty() ? "vec3" + (String)get_input_port_default_value(0) : p_input_vars[0]) + ";\n";
+ code += "\t__scalar_buff1 += __vec3_buff1.z != 0.0 ? atan(__vec3_buff1.x, __vec3_buff1.z) : sign(__vec3_buff1.x) * (PI / 2.0);\n";
+ code += "\t__scalar_buff2 += __vec3_buff1.z != 0.0 ? atan(__vec3_buff1.y, abs(__vec3_buff1.z)) : (__vec3_buff1.x != 0.0 ? atan(__vec3_buff1.y, abs(__vec3_buff1.x)) : sign(__vec3_buff1.y) * (PI / 2.0));\n";
+ code += "\t__vec3_buff1 = vec3(sin(__scalar_buff1), 0.0, cos(__scalar_buff1));\n";
+ code += "\t__vec3_buff2 = vec3(0.0, sin(__scalar_buff2), cos(__scalar_buff2));\n";
+ code += "\t__vec3_buff2.z = __vec3_buff2.z / max(0.0001, sqrt(abs(__vec3_buff2.z)));\n";
+ code += "\t" + p_output_vars[0] + " = normalize(vec3(__vec3_buff1.x * __vec3_buff2.z, __vec3_buff2.y, __vec3_buff1.z * __vec3_buff2.z));\n";
+ return code;
+}
+
+VisualShaderNodeParticleConeVelocity::VisualShaderNodeParticleConeVelocity() {
+ set_input_port_default_value(0, Vector3(1, 0, 0));
+ set_input_port_default_value(1, 45.0);
+}
+
+// VisualShaderNodeParticleRandomness
+
+void VisualShaderNodeParticleRandomness::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_op_type", "type"), &VisualShaderNodeParticleRandomness::set_op_type);
+ ClassDB::bind_method(D_METHOD("get_op_type"), &VisualShaderNodeParticleRandomness::get_op_type);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Scalar,Vector"), "set_op_type", "get_op_type");
+
+ BIND_ENUM_CONSTANT(OP_TYPE_SCALAR);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR);
+ BIND_ENUM_CONSTANT(OP_TYPE_MAX);
+}
+
+Vector<StringName> VisualShaderNodeParticleRandomness::get_editable_properties() const {
+ Vector<StringName> props;
+ props.push_back("op_type");
+ return props;
+}
+
+String VisualShaderNodeParticleRandomness::get_caption() const {
+ return "ParticleRandomness";
+}
+
+int VisualShaderNodeParticleRandomness::get_output_port_count() const {
+ return 1;
+}
+
+VisualShaderNodeParticleRandomness::PortType VisualShaderNodeParticleRandomness::get_output_port_type(int p_port) const {
+ if (op_type == OP_TYPE_VECTOR) {
+ return PORT_TYPE_VECTOR;
+ }
+ return PORT_TYPE_SCALAR;
+}
+
+String VisualShaderNodeParticleRandomness::get_output_port_name(int p_port) const {
+ return "random";
+}
+
+int VisualShaderNodeParticleRandomness::get_input_port_count() const {
+ return 2;
+}
+
+VisualShaderNodeParticleRandomness::PortType VisualShaderNodeParticleRandomness::get_input_port_type(int p_port) const {
+ if (op_type == OP_TYPE_VECTOR) {
+ return PORT_TYPE_VECTOR;
+ }
+ return PORT_TYPE_SCALAR;
+}
+
+String VisualShaderNodeParticleRandomness::get_input_port_name(int p_port) const {
+ if (p_port == 0) {
+ return "min";
+ } else if (p_port == 1) {
+ return "max";
+ }
+ return String();
+}
+
+String VisualShaderNodeParticleRandomness::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;
+ if (op_type == OP_TYPE_SCALAR) {
+ code += vformat("\t%s = __randf_range(__seed, %s, %s);\n", p_output_vars[0], p_input_vars[0].is_empty() ? (String)get_input_port_default_value(0) : p_input_vars[0], p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]);
+ } else if (op_type == OP_TYPE_VECTOR) {
+ code += vformat("\t%s = __randv_range(__seed, %s, %s);\n", p_output_vars[0], p_input_vars[0].is_empty() ? (String)get_input_port_default_value(0) : p_input_vars[0], p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]);
+ }
+ return code;
+}
+
+void VisualShaderNodeParticleRandomness::set_op_type(OpType p_op_type) {
+ ERR_FAIL_INDEX((int)p_op_type, OP_TYPE_MAX);
+ if (p_op_type != op_type) {
+ if (p_op_type == OP_TYPE_SCALAR) {
+ set_input_port_default_value(0, 0.0);
+ set_input_port_default_value(1, 1.0);
+ } else {
+ set_input_port_default_value(0, Vector3(-1.0, -1.0, -1.0));
+ set_input_port_default_value(1, Vector3(1.0, 1.0, 1.0));
+ }
+ }
+ op_type = p_op_type;
+ emit_changed();
+}
+
+VisualShaderNodeParticleRandomness::OpType VisualShaderNodeParticleRandomness::get_op_type() const {
+ return op_type;
+}
+
+VisualShaderNodeParticleRandomness::VisualShaderNodeParticleRandomness() {
+ set_input_port_default_value(0, 0.0);
+ set_input_port_default_value(1, 1.0);
+}
+
+// VisualShaderNodeParticleAccelerator
+
+void VisualShaderNodeParticleAccelerator::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_mode", "mode"), &VisualShaderNodeParticleAccelerator::set_mode);
+ ClassDB::bind_method(D_METHOD("get_mode"), &VisualShaderNodeParticleAccelerator::get_mode);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Linear,Radial,Tangential"), "set_mode", "get_mode");
+
+ BIND_ENUM_CONSTANT(MODE_LINEAR);
+ BIND_ENUM_CONSTANT(MODE_RADIAL)
+ BIND_ENUM_CONSTANT(MODE_TANGENTIAL);
+ BIND_ENUM_CONSTANT(MODE_MAX);
+}
+
+Vector<StringName> VisualShaderNodeParticleAccelerator::get_editable_properties() const {
+ Vector<StringName> props;
+ props.push_back("mode");
+ return props;
+}
+
+String VisualShaderNodeParticleAccelerator::get_caption() const {
+ return "ParticleAccelerator";
+}
+
+int VisualShaderNodeParticleAccelerator::get_output_port_count() const {
+ return 1;
+}
+
+VisualShaderNodeParticleAccelerator::PortType VisualShaderNodeParticleAccelerator::get_output_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+
+String VisualShaderNodeParticleAccelerator::get_output_port_name(int p_port) const {
+ return String();
+}
+
+int VisualShaderNodeParticleAccelerator::get_input_port_count() const {
+ return 3;
+}
+
+VisualShaderNodeParticleAccelerator::PortType VisualShaderNodeParticleAccelerator::get_input_port_type(int p_port) const {
+ if (p_port == 0) {
+ return PORT_TYPE_VECTOR;
+ } else if (p_port == 1) {
+ return PORT_TYPE_SCALAR;
+ } else if (p_port == 2) {
+ return PORT_TYPE_VECTOR;
+ }
+ return PORT_TYPE_SCALAR;
+}
+
+String VisualShaderNodeParticleAccelerator::get_input_port_name(int p_port) const {
+ if (p_port == 0) {
+ return "amount";
+ } else if (p_port == 1) {
+ return "randomness";
+ } else if (p_port == 2) {
+ return "axis";
+ }
+ return String();
+}
+
+String VisualShaderNodeParticleAccelerator::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;
+ switch (mode) {
+ case MODE_LINEAR:
+ code += "\t" + p_output_vars[0] + " = length(VELOCITY) > 0.0 ? " + "normalize(VELOCITY) * " + (p_input_vars[0].is_empty() ? "vec3" + (String)get_input_port_default_value(0) : p_input_vars[0]) + " * mix(1.0, __rand_from_seed(__seed), " + (p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]) + ") : vec3(0.0);\n";
+ break;
+ case MODE_RADIAL:
+ code += "\t" + p_output_vars[0] + " = length(__diff) > 0.0 ? __ndiff * " + (p_input_vars[0].is_empty() ? "vec3" + (String)get_input_port_default_value(0) : p_input_vars[0]) + " * mix(1.0, __rand_from_seed(__seed), " + (p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]) + ") : vec3(0.0);\n";
+ break;
+ case MODE_TANGENTIAL:
+ code += "\t__vec3_buff1 = cross(__ndiff, normalize(" + (p_input_vars[2].is_empty() ? "vec3" + (String)get_input_port_default_value(2) : p_input_vars[2]) + "));\n";
+ code += "\t" + p_output_vars[0] + " = length(__vec3_buff1) > 0.0 ? normalize(__vec3_buff1) * (" + (p_input_vars[0].is_empty() ? "vec3" + (String)get_input_port_default_value(0) : p_input_vars[0]) + " * mix(1.0, __rand_from_seed(__seed), " + (p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]) + ")) : vec3(0.0);\n";
+ break;
+ case MODE_MAX:
+ break;
+ default:
+ break;
+ }
+
+ return code;
+}
+
+void VisualShaderNodeParticleAccelerator::set_mode(Mode p_mode) {
+ mode = p_mode;
+ emit_changed();
+}
+
+VisualShaderNodeParticleAccelerator::Mode VisualShaderNodeParticleAccelerator::get_mode() const {
+ return mode;
+}
+
+VisualShaderNodeParticleAccelerator::VisualShaderNodeParticleAccelerator() {
+ set_input_port_default_value(0, Vector3(1, 1, 1));
+ set_input_port_default_value(1, 0.0);
+ set_input_port_default_value(2, Vector3(0, -9.8, 0));
+}
+
+// VisualShaderNodeParticleOutput
+
+String VisualShaderNodeParticleOutput::get_caption() const {
+ if (shader_type == VisualShader::TYPE_START) {
+ return "StartOutput";
+ } else if (shader_type == VisualShader::TYPE_PROCESS) {
+ return "ProcessOutput";
+ } else if (shader_type == VisualShader::TYPE_COLLIDE) {
+ return "CollideOutput";
+ } else if (shader_type == VisualShader::TYPE_START_CUSTOM) {
+ return "CustomStartOutput";
+ } else if (shader_type == VisualShader::TYPE_PROCESS_CUSTOM) {
+ return "CustomProcessOutput";
+ }
+ return String();
+}
+
+int VisualShaderNodeParticleOutput::get_input_port_count() const {
+ if (shader_type == VisualShader::TYPE_START) {
+ return 8;
+ } else if (shader_type == VisualShader::TYPE_COLLIDE) {
+ return 5;
+ } else if (shader_type == VisualShader::TYPE_START_CUSTOM || shader_type == VisualShader::TYPE_PROCESS_CUSTOM) {
+ return 6;
+ } else { // TYPE_PROCESS
+ return 7;
+ }
+ return 0;
+}
+
+VisualShaderNodeParticleOutput::PortType VisualShaderNodeParticleOutput::get_input_port_type(int p_port) const {
+ switch (p_port) {
+ case 0:
+ if (shader_type == VisualShader::TYPE_START_CUSTOM || shader_type == VisualShader::TYPE_PROCESS_CUSTOM) {
+ return PORT_TYPE_VECTOR; // custom.rgb
+ }
+ return PORT_TYPE_BOOLEAN; // active
+ case 1:
+ if (shader_type == VisualShader::TYPE_START_CUSTOM || shader_type == VisualShader::TYPE_PROCESS_CUSTOM) {
+ break; // custom.a (scalar)
+ }
+ return PORT_TYPE_VECTOR; // velocity
+ case 2:
+ return PORT_TYPE_VECTOR; // color & velocity
+ case 3:
+ if (shader_type == VisualShader::TYPE_START_CUSTOM || shader_type == VisualShader::TYPE_PROCESS_CUSTOM) {
+ return PORT_TYPE_VECTOR; // color
+ }
+ break; // alpha (scalar)
+ case 4:
+ if (shader_type == VisualShader::TYPE_START_CUSTOM || shader_type == VisualShader::TYPE_PROCESS_CUSTOM) {
+ break; // alpha
+ }
+ if (shader_type == VisualShader::TYPE_PROCESS) {
+ break; // scale
+ }
+ if (shader_type == VisualShader::TYPE_COLLIDE) {
+ return PORT_TYPE_TRANSFORM; // transform
+ }
+ return PORT_TYPE_VECTOR; // position
+ case 5:
+ if (shader_type == VisualShader::TYPE_START_CUSTOM || shader_type == VisualShader::TYPE_PROCESS_CUSTOM) {
+ return PORT_TYPE_TRANSFORM; // transform
+ }
+ if (shader_type == VisualShader::TYPE_PROCESS) {
+ return PORT_TYPE_VECTOR; // rotation_axis
+ }
+ break; // scale (scalar)
+ case 6:
+ if (shader_type == VisualShader::TYPE_START) {
+ return PORT_TYPE_VECTOR; // rotation_axis
+ }
+ break;
+ case 7:
+ break; // angle (scalar)
+ }
+ return PORT_TYPE_SCALAR;
+}
+
+String VisualShaderNodeParticleOutput::get_input_port_name(int p_port) const {
+ String port_name;
+ switch (p_port) {
+ case 0:
+ if (shader_type == VisualShader::TYPE_START_CUSTOM || shader_type == VisualShader::TYPE_PROCESS_CUSTOM) {
+ port_name = "custom";
+ break;
+ }
+ port_name = "active";
+ break;
+ case 1:
+ if (shader_type == VisualShader::TYPE_START_CUSTOM || shader_type == VisualShader::TYPE_PROCESS_CUSTOM) {
+ port_name = "custom_alpha";
+ break;
+ }
+ port_name = "velocity";
+ break;
+ case 2:
+ if (shader_type == VisualShader::TYPE_START_CUSTOM || shader_type == VisualShader::TYPE_PROCESS_CUSTOM) {
+ port_name = "velocity";
+ break;
+ }
+ port_name = "color";
+ break;
+ case 3:
+ if (shader_type == VisualShader::TYPE_START_CUSTOM || shader_type == VisualShader::TYPE_PROCESS_CUSTOM) {
+ port_name = "color";
+ break;
+ }
+ port_name = "alpha";
+ break;
+ case 4:
+ if (shader_type == VisualShader::TYPE_START_CUSTOM || shader_type == VisualShader::TYPE_PROCESS_CUSTOM) {
+ port_name = "alpha";
+ break;
+ }
+ if (shader_type == VisualShader::TYPE_PROCESS) {
+ port_name = "scale";
+ break;
+ }
+ if (shader_type == VisualShader::TYPE_COLLIDE) {
+ port_name = "transform";
+ break;
+ }
+ port_name = "position";
+ break;
+ case 5:
+ if (shader_type == VisualShader::TYPE_START_CUSTOM || shader_type == VisualShader::TYPE_PROCESS_CUSTOM) {
+ port_name = "transform";
+ break;
+ }
+ if (shader_type == VisualShader::TYPE_PROCESS) {
+ port_name = "rotation_axis";
+ break;
+ }
+ port_name = "scale";
+ break;
+ case 6:
+ if (shader_type == VisualShader::TYPE_PROCESS) {
+ port_name = "angle_in_radians";
+ break;
+ }
+ port_name = "rotation_axis";
+ break;
+ case 7:
+ port_name = "angle_in_radians";
+ break;
+ default:
+ break;
+ }
+ if (!port_name.is_empty()) {
+ return port_name.capitalize();
+ }
+ return String();
+}
+
+bool VisualShaderNodeParticleOutput::is_port_separator(int p_index) const {
+ if (shader_type == VisualShader::TYPE_START || shader_type == VisualShader::TYPE_PROCESS) {
+ String name = get_input_port_name(p_index);
+ return bool(name == "Scale");
+ }
+ if (shader_type == VisualShader::TYPE_START_CUSTOM || shader_type == VisualShader::TYPE_PROCESS_CUSTOM) {
+ String name = get_input_port_name(p_index);
+ return bool(name == "Velocity");
+ }
+ return false;
+}
+
+String VisualShaderNodeParticleOutput::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 tab = "\t";
+
+ if (shader_type == VisualShader::TYPE_START_CUSTOM || shader_type == VisualShader::TYPE_PROCESS_CUSTOM) {
+ if (!p_input_vars[0].is_empty()) { // custom.rgb
+ code += tab + "CUSTOM.rgb = " + p_input_vars[0] + ";\n";
+ }
+ if (!p_input_vars[1].is_empty()) { // custom.a
+ code += tab + "CUSTOM.a = " + p_input_vars[1] + ";\n";
+ }
+ if (!p_input_vars[2].is_empty()) { // velocity
+ code += tab + "VELOCITY = " + p_input_vars[2] + ";\n";
+ }
+ if (!p_input_vars[3].is_empty()) { // color.rgb
+ code += tab + "COLOR.rgb = " + p_input_vars[3] + ";\n";
+ }
+ if (!p_input_vars[4].is_empty()) { // color.a
+ code += tab + "COLOR.a = " + p_input_vars[4] + ";\n";
+ }
+ if (!p_input_vars[5].is_empty()) { // transform
+ code += tab + "TRANSFORM = " + p_input_vars[5] + ";\n";
+ }
+ } else {
+ if (!p_input_vars[0].is_empty()) { // active (begin)
+ code += tab + "ACTIVE = " + p_input_vars[0] + ";\n";
+ code += tab + "if(ACTIVE) {\n";
+ tab += "\t";
+ }
+ if (!p_input_vars[1].is_empty()) { // velocity
+ code += tab + "VELOCITY = " + p_input_vars[1] + ";\n";
+ }
+ if (!p_input_vars[2].is_empty()) { // color
+ code += tab + "COLOR.rgb = " + p_input_vars[2] + ";\n";
+ }
+ if (!p_input_vars[3].is_empty()) { // alpha
+ code += tab + "COLOR.a = " + p_input_vars[3] + ";\n";
+ }
+
+ // position
+ if (shader_type == VisualShader::TYPE_START) {
+ code += tab + "if (RESTART_POSITION) {\n";
+ if (!p_input_vars[4].is_empty()) {
+ code += tab + "\tTRANSFORM = mat4(vec4(1.0, 0.0, 0.0, 0.0), vec4(0.0, 1.0, 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0), vec4(" + p_input_vars[4] + ", 1.0));\n";
+ } else {
+ code += tab + "\tTRANSFORM = mat4(vec4(1.0, 0.0, 0.0, 0.0), vec4(0.0, 1.0, 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0));\n";
+ }
+ code += tab + "\tif (RESTART_VELOCITY) {\n";
+ code += tab + "\t\tVELOCITY = (EMISSION_TRANSFORM * vec4(VELOCITY, 0.0)).xyz;\n";
+ code += tab + "\t}\n";
+ code += tab + "\tTRANSFORM = EMISSION_TRANSFORM * TRANSFORM;\n";
+ code += tab + "}\n";
+ } else if (shader_type == VisualShader::TYPE_COLLIDE) { // position
+ if (!p_input_vars[4].is_empty()) {
+ code += tab + "TRANSFORM = " + p_input_vars[4] + ";\n";
+ }
+ }
+
+ if (shader_type == VisualShader::TYPE_START || shader_type == VisualShader::TYPE_PROCESS) {
+ int scale = 5;
+ int rotation_axis = 6;
+ int rotation = 7;
+ if (shader_type == VisualShader::TYPE_PROCESS) {
+ scale = 4;
+ rotation_axis = 5;
+ rotation = 6;
+ }
+ String op;
+ if (shader_type == VisualShader::TYPE_START) {
+ op = "*=";
+ } else {
+ op = "=";
+ }
+
+ if (!p_input_vars[rotation].is_empty()) { // rotation_axis & angle_in_radians
+ String axis;
+ if (p_input_vars[rotation_axis].is_empty()) {
+ axis = "vec3(0, 1, 0)";
+ } else {
+ axis = p_input_vars[rotation_axis];
+ }
+ code += tab + "TRANSFORM " + op + " __build_rotation_mat4(" + axis + ", " + p_input_vars[rotation] + ");\n";
+ }
+ if (!p_input_vars[scale].is_empty()) { // scale
+ code += tab + "TRANSFORM " + op + " mat4(vec4(" + p_input_vars[scale] + ", 0, 0, 0), vec4(0, " + p_input_vars[scale] + ", 0, 0), vec4(0, 0, " + p_input_vars[scale] + ", 0), vec4(0, 0, 0, 1));\n";
+ }
+ }
+ if (!p_input_vars[0].is_empty()) { // active (end)
+ code += "\t}\n";
+ }
+ }
+ return code;
+}
+
+VisualShaderNodeParticleOutput::VisualShaderNodeParticleOutput() {
+}
+
+// EmitParticle
+
+Vector<StringName> VisualShaderNodeParticleEmit::get_editable_properties() const {
+ Vector<StringName> props;
+ props.push_back("flags");
+ return props;
+}
+
+void VisualShaderNodeParticleEmit::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_flags", "flags"), &VisualShaderNodeParticleEmit::set_flags);
+ ClassDB::bind_method(D_METHOD("get_flags"), &VisualShaderNodeParticleEmit::get_flags);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "flags", PROPERTY_HINT_FLAGS, "Position,RotScale,Velocity,Color,Custom"), "set_flags", "get_flags");
+
+ BIND_ENUM_CONSTANT(EMIT_FLAG_POSITION);
+ BIND_ENUM_CONSTANT(EMIT_FLAG_ROT_SCALE);
+ BIND_ENUM_CONSTANT(EMIT_FLAG_VELOCITY);
+ BIND_ENUM_CONSTANT(EMIT_FLAG_COLOR);
+ BIND_ENUM_CONSTANT(EMIT_FLAG_CUSTOM);
+}
+
+String VisualShaderNodeParticleEmit::get_caption() const {
+ return "EmitParticle";
+}
+
+int VisualShaderNodeParticleEmit::get_input_port_count() const {
+ return 7;
+}
+
+VisualShaderNodeParticleEmit::PortType VisualShaderNodeParticleEmit::get_input_port_type(int p_port) const {
+ switch (p_port) {
+ case 0:
+ return PORT_TYPE_BOOLEAN;
+ case 1:
+ return PORT_TYPE_TRANSFORM;
+ case 2:
+ return PORT_TYPE_VECTOR;
+ case 3:
+ return PORT_TYPE_VECTOR;
+ case 4:
+ return PORT_TYPE_SCALAR;
+ case 5:
+ return PORT_TYPE_VECTOR;
+ case 6:
+ return PORT_TYPE_SCALAR;
+ }
+ return PORT_TYPE_SCALAR;
+}
+
+String VisualShaderNodeParticleEmit::get_input_port_name(int p_port) const {
+ switch (p_port) {
+ case 0:
+ return "condition";
+ case 1:
+ return "transform";
+ case 2:
+ return "velocity";
+ case 3:
+ return "color";
+ case 4:
+ return "alpha";
+ case 5:
+ return "custom";
+ case 6:
+ return "custom_alpha";
+ }
+ return String();
+}
+
+int VisualShaderNodeParticleEmit::get_output_port_count() const {
+ return 0;
+}
+
+VisualShaderNodeParticleEmit::PortType VisualShaderNodeParticleEmit::get_output_port_type(int p_port) const {
+ return PORT_TYPE_SCALAR;
+}
+
+String VisualShaderNodeParticleEmit::get_output_port_name(int p_port) const {
+ return String();
+}
+
+void VisualShaderNodeParticleEmit::add_flag(EmitFlags p_flag) {
+ flags |= p_flag;
+ emit_changed();
+}
+
+bool VisualShaderNodeParticleEmit::has_flag(EmitFlags p_flag) const {
+ return flags & p_flag;
+}
+
+void VisualShaderNodeParticleEmit::set_flags(EmitFlags p_flags) {
+ flags = (int)p_flags;
+ emit_changed();
+}
+
+VisualShaderNodeParticleEmit::EmitFlags VisualShaderNodeParticleEmit::get_flags() const {
+ return EmitFlags(flags);
+}
+
+bool VisualShaderNodeParticleEmit::is_show_prop_names() const {
+ return true;
+}
+
+bool VisualShaderNodeParticleEmit::is_generate_input_var(int p_port) const {
+ if (p_port == 0) {
+ if (!is_input_port_connected(0)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+String VisualShaderNodeParticleEmit::get_input_port_default_hint(int p_port) const {
+ switch (p_port) {
+ case 1:
+ return "default";
+ case 2:
+ return "default";
+ case 3:
+ return "default";
+ case 4:
+ return "default";
+ case 5:
+ return "default";
+ case 6:
+ return "default";
+ }
+ return String();
+}
+
+String VisualShaderNodeParticleEmit::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 tab;
+ bool default_condition = false;
+
+ if (!is_input_port_connected(0)) {
+ default_condition = true;
+ if (get_input_port_default_value(0)) {
+ tab = "\t";
+ } else {
+ return code;
+ }
+ } else {
+ tab = "\t\t";
+ }
+
+ String transform;
+ if (p_input_vars[1].is_empty()) {
+ transform = "TRANSFORM";
+ } else {
+ transform = p_input_vars[1];
+ }
+
+ String velocity;
+ if (p_input_vars[2].is_empty()) {
+ velocity = "VELOCITY";
+ } else {
+ velocity = p_input_vars[2];
+ }
+
+ String color;
+ if (p_input_vars[3].is_empty()) {
+ color = "COLOR.rgb";
+ } else {
+ color = p_input_vars[3];
+ }
+
+ String alpha;
+ if (p_input_vars[4].is_empty()) {
+ alpha = "COLOR.a";
+ } else {
+ alpha = p_input_vars[4];
+ }
+
+ String custom;
+ if (p_input_vars[5].is_empty()) {
+ custom = "CUSTOM.rgb";
+ } else {
+ custom = p_input_vars[5];
+ }
+
+ String custom_alpha;
+ if (p_input_vars[6].is_empty()) {
+ custom_alpha = "CUSTOM.a";
+ } else {
+ custom_alpha = p_input_vars[6];
+ }
+
+ List<String> flags_arr;
+
+ if (has_flag(EmitFlags::EMIT_FLAG_POSITION)) {
+ flags_arr.push_back("FLAG_EMIT_POSITION");
+ }
+ if (has_flag(EmitFlags::EMIT_FLAG_ROT_SCALE)) {
+ flags_arr.push_back("FLAG_EMIT_ROT_SCALE");
+ }
+ if (has_flag(EmitFlags::EMIT_FLAG_VELOCITY)) {
+ flags_arr.push_back("FLAG_EMIT_VELOCITY");
+ }
+ if (has_flag(EmitFlags::EMIT_FLAG_COLOR)) {
+ flags_arr.push_back("FLAG_EMIT_COLOR");
+ }
+ if (has_flag(EmitFlags::EMIT_FLAG_CUSTOM)) {
+ flags_arr.push_back("FLAG_EMIT_CUSTOM");
+ }
+
+ String flags;
+
+ for (int i = 0; i < flags_arr.size(); i++) {
+ if (i > 0) {
+ flags += "|";
+ }
+ flags += flags_arr[i];
+ }
+
+ if (flags.is_empty()) {
+ flags = "uint(0)";
+ }
+
+ if (!default_condition) {
+ code += "\tif (" + p_input_vars[0] + ") {\n";
+ }
+
+ code += tab + "emit_subparticle(" + transform + ", " + velocity + ", vec4(" + color + ", " + alpha + "), vec4(" + custom + ", " + custom_alpha + "), " + flags + ");\n";
+
+ if (!default_condition) {
+ code += "\t}\n";
+ }
+
+ return code;
+}
+
+VisualShaderNodeParticleEmit::VisualShaderNodeParticleEmit() {
+ set_input_port_default_value(0, true);
+}
diff --git a/scene/resources/visual_shader_particle_nodes.h b/scene/resources/visual_shader_particle_nodes.h
new file mode 100644
index 0000000000..ecd187a885
--- /dev/null
+++ b/scene/resources/visual_shader_particle_nodes.h
@@ -0,0 +1,285 @@
+/*************************************************************************/
+/* visual_shader_particle_nodes.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef VISUAL_SHADER_PARTICLE_NODES_H
+#define VISUAL_SHADER_PARTICLE_NODES_H
+
+#include "scene/resources/visual_shader.h"
+
+// Emit nodes
+
+class VisualShaderNodeParticleEmitter : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeParticleEmitter, VisualShaderNode);
+
+public:
+ 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;
+
+ VisualShaderNodeParticleEmitter();
+};
+
+class VisualShaderNodeParticleSphereEmitter : public VisualShaderNodeParticleEmitter {
+ GDCLASS(VisualShaderNodeParticleSphereEmitter, VisualShaderNodeParticleEmitter);
+
+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 generate_global_per_node(Shader::Mode p_mode, VisualShader::Type p_type, 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;
+
+ VisualShaderNodeParticleSphereEmitter();
+};
+
+class VisualShaderNodeParticleBoxEmitter : public VisualShaderNodeParticleEmitter {
+ GDCLASS(VisualShaderNodeParticleBoxEmitter, VisualShaderNodeParticleEmitter);
+
+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 generate_global_per_node(Shader::Mode p_mode, VisualShader::Type p_type, 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;
+
+ VisualShaderNodeParticleBoxEmitter();
+};
+
+class VisualShaderNodeParticleRingEmitter : public VisualShaderNodeParticleEmitter {
+ GDCLASS(VisualShaderNodeParticleRingEmitter, VisualShaderNodeParticleEmitter);
+
+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 generate_global_per_node(Shader::Mode p_mode, VisualShader::Type p_type, 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;
+
+ VisualShaderNodeParticleRingEmitter();
+};
+
+class VisualShaderNodeParticleMultiplyByAxisAngle : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeParticleMultiplyByAxisAngle, VisualShaderNode);
+ bool degrees_mode = true;
+
+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 bool is_show_prop_names() 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 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_degrees_mode(bool p_enabled);
+ bool is_degrees_mode() const;
+ Vector<StringName> get_editable_properties() const override;
+
+ VisualShaderNodeParticleMultiplyByAxisAngle();
+};
+
+class VisualShaderNodeParticleConeVelocity : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeParticleConeVelocity, 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 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 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;
+
+ VisualShaderNodeParticleConeVelocity();
+};
+
+class VisualShaderNodeParticleRandomness : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeParticleRandomness, VisualShaderNode);
+
+public:
+ enum OpType {
+ OP_TYPE_SCALAR,
+ OP_TYPE_VECTOR,
+ OP_TYPE_MAX,
+ };
+
+private:
+ OpType op_type = OP_TYPE_SCALAR;
+
+protected:
+ static void _bind_methods();
+
+public:
+ Vector<StringName> get_editable_properties() const override;
+ 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 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_op_type(OpType p_type);
+ OpType get_op_type() const;
+
+ VisualShaderNodeParticleRandomness();
+};
+
+VARIANT_ENUM_CAST(VisualShaderNodeParticleRandomness::OpType)
+
+// Process nodes
+
+class VisualShaderNodeParticleAccelerator : public VisualShaderNodeOutput {
+ GDCLASS(VisualShaderNodeParticleAccelerator, VisualShaderNodeOutput);
+
+public:
+ enum Mode {
+ MODE_LINEAR,
+ MODE_RADIAL,
+ MODE_TANGENTIAL,
+ MODE_MAX,
+ };
+
+private:
+ Mode mode = MODE_LINEAR;
+
+protected:
+ static void _bind_methods();
+
+public:
+ Vector<StringName> get_editable_properties() const override;
+ 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 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_mode(Mode p_mode);
+ Mode get_mode() const;
+
+ VisualShaderNodeParticleAccelerator();
+};
+
+VARIANT_ENUM_CAST(VisualShaderNodeParticleAccelerator::Mode)
+
+// Common nodes
+
+class VisualShaderNodeParticleOutput : public VisualShaderNodeOutput {
+ GDCLASS(VisualShaderNodeParticleOutput, VisualShaderNodeOutput);
+
+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_port_separator(int p_index) 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;
+
+ VisualShaderNodeParticleOutput();
+};
+
+class VisualShaderNodeParticleEmit : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeParticleEmit, VisualShaderNode);
+
+public:
+ enum EmitFlags {
+ EMIT_FLAG_POSITION = 1,
+ EMIT_FLAG_ROT_SCALE = 2,
+ EMIT_FLAG_VELOCITY = 4,
+ EMIT_FLAG_COLOR = 8,
+ EMIT_FLAG_CUSTOM = 16,
+ };
+
+protected:
+ int flags = EMIT_FLAG_POSITION | EMIT_FLAG_ROT_SCALE | EMIT_FLAG_VELOCITY | EMIT_FLAG_COLOR | EMIT_FLAG_CUSTOM;
+ static void _bind_methods();
+
+public:
+ Vector<StringName> get_editable_properties() const override;
+ 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;
+
+ void add_flag(EmitFlags p_flag);
+ bool has_flag(EmitFlags p_flag) const;
+
+ void set_flags(EmitFlags p_flags);
+ EmitFlags get_flags() const;
+
+ virtual bool is_show_prop_names() const override;
+ virtual bool is_generate_input_var(int p_port) const override;
+ virtual String get_input_port_default_hint(int p_port) 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;
+
+ VisualShaderNodeParticleEmit();
+};
+
+VARIANT_ENUM_CAST(VisualShaderNodeParticleEmit::EmitFlags)
+
+#endif