summaryrefslogtreecommitdiff
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
parent3041becc644e5feaef6133afeaf801659ec886c4 (diff)
Continuation of work on visual particles system
-rw-r--r--doc/classes/VisualShader.xml12
-rw-r--r--doc/classes/VisualShaderNodeParticleAccelerator.xml25
-rw-r--r--doc/classes/VisualShaderNodeParticleBoxEmitter.xml13
-rw-r--r--doc/classes/VisualShaderNodeParticleConeVelocity.xml13
-rw-r--r--doc/classes/VisualShaderNodeParticleEmit.xml27
-rw-r--r--doc/classes/VisualShaderNodeParticleEmitter.xml13
-rw-r--r--doc/classes/VisualShaderNodeParticleMultiplyByAxisAngle.xml17
-rw-r--r--doc/classes/VisualShaderNodeParticleOutput.xml13
-rw-r--r--doc/classes/VisualShaderNodeParticleRandomness.xml23
-rw-r--r--doc/classes/VisualShaderNodeParticleRingEmitter.xml13
-rw-r--r--doc/classes/VisualShaderNodeParticleSphereEmitter.xml13
-rw-r--r--editor/plugins/visual_shader_editor_plugin.cpp177
-rw-r--r--editor/plugins/visual_shader_editor_plugin.h9
-rw-r--r--scene/register_scene_types.cpp12
-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
19 files changed, 1870 insertions, 162 deletions
diff --git a/doc/classes/VisualShader.xml b/doc/classes/VisualShader.xml
index ff00a848b9..775ac88eff 100644
--- a/doc/classes/VisualShader.xml
+++ b/doc/classes/VisualShader.xml
@@ -222,15 +222,19 @@
<constant name="TYPE_LIGHT" value="2" enum="Type">
A shader for light calculations.
</constant>
- <constant name="TYPE_EMIT" value="3" enum="Type">
+ <constant name="TYPE_START" value="3" enum="Type">
</constant>
<constant name="TYPE_PROCESS" value="4" enum="Type">
</constant>
- <constant name="TYPE_END" value="5" enum="Type">
+ <constant name="TYPE_COLLIDE" value="5" enum="Type">
</constant>
- <constant name="TYPE_SKY" value="6" enum="Type">
+ <constant name="TYPE_START_CUSTOM" value="6" enum="Type">
</constant>
- <constant name="TYPE_MAX" value="7" enum="Type">
+ <constant name="TYPE_PROCESS_CUSTOM" value="7" enum="Type">
+ </constant>
+ <constant name="TYPE_SKY" value="8" enum="Type">
+ </constant>
+ <constant name="TYPE_MAX" value="9" enum="Type">
Represents the size of the [enum Type] enum.
</constant>
<constant name="NODE_ID_INVALID" value="-1">
diff --git a/doc/classes/VisualShaderNodeParticleAccelerator.xml b/doc/classes/VisualShaderNodeParticleAccelerator.xml
new file mode 100644
index 0000000000..82aae1df63
--- /dev/null
+++ b/doc/classes/VisualShaderNodeParticleAccelerator.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="VisualShaderNodeParticleAccelerator" inherits="VisualShaderNodeOutput" version="4.0">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ </methods>
+ <members>
+ <member name="mode" type="int" setter="set_mode" getter="get_mode" enum="VisualShaderNodeParticleAccelerator.Mode" default="0">
+ </member>
+ </members>
+ <constants>
+ <constant name="MODE_LINEAR" value="0" enum="Mode">
+ </constant>
+ <constant name="MODE_RADIAL" value="1" enum="Mode">
+ </constant>
+ <constant name="MODE_TANGENTIAL" value="2" enum="Mode">
+ </constant>
+ <constant name="MODE_MAX" value="3" enum="Mode">
+ </constant>
+ </constants>
+</class>
diff --git a/doc/classes/VisualShaderNodeParticleBoxEmitter.xml b/doc/classes/VisualShaderNodeParticleBoxEmitter.xml
new file mode 100644
index 0000000000..af33b285d2
--- /dev/null
+++ b/doc/classes/VisualShaderNodeParticleBoxEmitter.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="VisualShaderNodeParticleBoxEmitter" inherits="VisualShaderNodeParticleEmitter" version="4.0">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ </methods>
+ <constants>
+ </constants>
+</class>
diff --git a/doc/classes/VisualShaderNodeParticleConeVelocity.xml b/doc/classes/VisualShaderNodeParticleConeVelocity.xml
new file mode 100644
index 0000000000..7a40c2a7d0
--- /dev/null
+++ b/doc/classes/VisualShaderNodeParticleConeVelocity.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="VisualShaderNodeParticleConeVelocity" inherits="VisualShaderNode" version="4.0">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ </methods>
+ <constants>
+ </constants>
+</class>
diff --git a/doc/classes/VisualShaderNodeParticleEmit.xml b/doc/classes/VisualShaderNodeParticleEmit.xml
new file mode 100644
index 0000000000..120b12d643
--- /dev/null
+++ b/doc/classes/VisualShaderNodeParticleEmit.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="VisualShaderNodeParticleEmit" inherits="VisualShaderNode" version="4.0">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ </methods>
+ <members>
+ <member name="flags" type="int" setter="set_flags" getter="get_flags" enum="VisualShaderNodeParticleEmit.EmitFlags" default="31">
+ </member>
+ </members>
+ <constants>
+ <constant name="EMIT_FLAG_POSITION" value="1" enum="EmitFlags">
+ </constant>
+ <constant name="EMIT_FLAG_ROT_SCALE" value="2" enum="EmitFlags">
+ </constant>
+ <constant name="EMIT_FLAG_VELOCITY" value="4" enum="EmitFlags">
+ </constant>
+ <constant name="EMIT_FLAG_COLOR" value="8" enum="EmitFlags">
+ </constant>
+ <constant name="EMIT_FLAG_CUSTOM" value="16" enum="EmitFlags">
+ </constant>
+ </constants>
+</class>
diff --git a/doc/classes/VisualShaderNodeParticleEmitter.xml b/doc/classes/VisualShaderNodeParticleEmitter.xml
new file mode 100644
index 0000000000..3a25fc1c7f
--- /dev/null
+++ b/doc/classes/VisualShaderNodeParticleEmitter.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="VisualShaderNodeParticleEmitter" inherits="VisualShaderNode" version="4.0">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ </methods>
+ <constants>
+ </constants>
+</class>
diff --git a/doc/classes/VisualShaderNodeParticleMultiplyByAxisAngle.xml b/doc/classes/VisualShaderNodeParticleMultiplyByAxisAngle.xml
new file mode 100644
index 0000000000..89a53699c9
--- /dev/null
+++ b/doc/classes/VisualShaderNodeParticleMultiplyByAxisAngle.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="VisualShaderNodeParticleMultiplyByAxisAngle" inherits="VisualShaderNode" version="4.0">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ </methods>
+ <members>
+ <member name="degrees_mode" type="bool" setter="set_degrees_mode" getter="is_degrees_mode" default="true">
+ </member>
+ </members>
+ <constants>
+ </constants>
+</class>
diff --git a/doc/classes/VisualShaderNodeParticleOutput.xml b/doc/classes/VisualShaderNodeParticleOutput.xml
new file mode 100644
index 0000000000..c8fc66f2ff
--- /dev/null
+++ b/doc/classes/VisualShaderNodeParticleOutput.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="VisualShaderNodeParticleOutput" inherits="VisualShaderNodeOutput" version="4.0">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ </methods>
+ <constants>
+ </constants>
+</class>
diff --git a/doc/classes/VisualShaderNodeParticleRandomness.xml b/doc/classes/VisualShaderNodeParticleRandomness.xml
new file mode 100644
index 0000000000..f19723a663
--- /dev/null
+++ b/doc/classes/VisualShaderNodeParticleRandomness.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="VisualShaderNodeParticleRandomness" inherits="VisualShaderNode" version="4.0">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ </methods>
+ <members>
+ <member name="op_type" type="int" setter="set_op_type" getter="get_op_type" enum="VisualShaderNodeParticleRandomness.OpType" default="0">
+ </member>
+ </members>
+ <constants>
+ <constant name="OP_TYPE_SCALAR" value="0" enum="OpType">
+ </constant>
+ <constant name="OP_TYPE_VECTOR" value="1" enum="OpType">
+ </constant>
+ <constant name="OP_TYPE_MAX" value="2" enum="OpType">
+ </constant>
+ </constants>
+</class>
diff --git a/doc/classes/VisualShaderNodeParticleRingEmitter.xml b/doc/classes/VisualShaderNodeParticleRingEmitter.xml
new file mode 100644
index 0000000000..ee3fbe7faf
--- /dev/null
+++ b/doc/classes/VisualShaderNodeParticleRingEmitter.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="VisualShaderNodeParticleRingEmitter" inherits="VisualShaderNodeParticleEmitter" version="4.0">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ </methods>
+ <constants>
+ </constants>
+</class>
diff --git a/doc/classes/VisualShaderNodeParticleSphereEmitter.xml b/doc/classes/VisualShaderNodeParticleSphereEmitter.xml
new file mode 100644
index 0000000000..d43ac518cf
--- /dev/null
+++ b/doc/classes/VisualShaderNodeParticleSphereEmitter.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="VisualShaderNodeParticleSphereEmitter" inherits="VisualShaderNodeParticleEmitter" version="4.0">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ </methods>
+ <constants>
+ </constants>
+</class>
diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp
index 7ef5e9e8ce..2bb9e64262 100644
--- a/editor/plugins/visual_shader_editor_plugin.cpp
+++ b/editor/plugins/visual_shader_editor_plugin.cpp
@@ -44,6 +44,7 @@
#include "scene/gui/panel.h"
#include "scene/main/window.h"
#include "scene/resources/visual_shader_nodes.h"
+#include "scene/resources/visual_shader_particle_nodes.h"
#include "scene/resources/visual_shader_sdf_nodes.h"
#include "servers/display_server.h"
#include "servers/rendering/shader_types.h"
@@ -417,6 +418,11 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id) {
}
}
+ Ref<VisualShaderNodeParticleEmit> emit = vsnode;
+ if (emit.is_valid()) {
+ node->set_custom_minimum_size(Size2(200 * EDSCALE, 0));
+ }
+
Ref<VisualShaderNodeUniform> uniform = vsnode;
if (uniform.is_valid()) {
VisualShaderEditor::get_singleton()->graph->add_child(node);
@@ -1013,13 +1019,13 @@ bool VisualShaderEditor::_is_available(int p_mode) {
if (p_mode != -1) {
switch (current_mode) {
- case 0: // Vertex or Emit
+ case 0: // Vertex / Emit
current_mode = 1;
break;
- case 1: // Fragment or Process
+ case 1: // Fragment / Process
current_mode = 2;
break;
- case 2: // Light or End
+ case 2: // Light / Collide
current_mode = 4;
break;
default:
@@ -1235,22 +1241,29 @@ void VisualShaderEditor::_update_options_menu() {
void VisualShaderEditor::_set_mode(int p_which) {
if (p_which == VisualShader::MODE_SKY) {
- edit_type_standart->set_visible(false);
+ edit_type_standard->set_visible(false);
edit_type_particles->set_visible(false);
edit_type_sky->set_visible(true);
edit_type = edit_type_sky;
+ custom_mode_box->set_visible(false);
mode = MODE_FLAGS_SKY;
} else if (p_which == VisualShader::MODE_PARTICLES) {
- edit_type_standart->set_visible(false);
+ edit_type_standard->set_visible(false);
edit_type_particles->set_visible(true);
edit_type_sky->set_visible(false);
edit_type = edit_type_particles;
+ if ((edit_type->get_selected() + 3) > VisualShader::TYPE_PROCESS) {
+ custom_mode_box->set_visible(false);
+ } else {
+ custom_mode_box->set_visible(true);
+ }
mode = MODE_FLAGS_PARTICLES;
} else {
edit_type_particles->set_visible(false);
- edit_type_standart->set_visible(true);
+ edit_type_standard->set_visible(true);
edit_type_sky->set_visible(false);
- edit_type = edit_type_standart;
+ edit_type = edit_type_standard;
+ custom_mode_box->set_visible(false);
mode = MODE_FLAGS_SPATIAL_CANVASITEM;
}
visual_shader->set_shader_type(get_current_shader_type());
@@ -1403,9 +1416,9 @@ void VisualShaderEditor::_update_graph() {
VisualShader::Type VisualShaderEditor::get_current_shader_type() const {
VisualShader::Type type;
if (mode & MODE_FLAGS_PARTICLES) {
- type = VisualShader::Type(edit_type->get_selected() + 3);
+ type = VisualShader::Type(edit_type->get_selected() + 3 + (custom_mode_enabled ? 3 : 0));
} else if (mode & MODE_FLAGS_SKY) {
- type = VisualShader::Type(edit_type->get_selected() + 6);
+ type = VisualShader::Type(edit_type->get_selected() + 8);
} else {
type = VisualShader::Type(edit_type->get_selected());
}
@@ -3224,8 +3237,18 @@ void VisualShaderEditor::_mode_selected(int p_id) {
int offset = 0;
if (mode & MODE_FLAGS_PARTICLES) {
offset = 3;
+ if (p_id + offset > VisualShader::TYPE_PROCESS) {
+ custom_mode_box->set_visible(false);
+ custom_mode_enabled = false;
+ } else {
+ custom_mode_box->set_visible(true);
+ if (custom_mode_box->is_pressed()) {
+ custom_mode_enabled = true;
+ offset += 3;
+ }
+ }
} else if (mode & MODE_FLAGS_SKY) {
- offset = 6;
+ offset = 8;
}
visual_shader->set_shader_type(VisualShader::Type(p_id + offset));
@@ -3233,6 +3256,21 @@ void VisualShaderEditor::_mode_selected(int p_id) {
_update_graph();
}
+void VisualShaderEditor::_custom_mode_toggled(bool p_enabled) {
+ if (!(mode & MODE_FLAGS_PARTICLES)) {
+ return;
+ }
+ custom_mode_enabled = p_enabled;
+ int id = edit_type->get_selected() + 3;
+ if (p_enabled) {
+ visual_shader->set_shader_type(VisualShader::Type(id + 3));
+ } else {
+ visual_shader->set_shader_type(VisualShader::Type(id));
+ }
+ _update_options_menu();
+ _update_graph();
+}
+
void VisualShaderEditor::_input_select_item(Ref<VisualShaderNodeInput> p_input, String p_name) {
String prev_name = p_input->get_input_name();
@@ -3731,17 +3769,23 @@ VisualShaderEditor::VisualShaderEditor() {
graph->get_zoom_hbox()->add_child(vs);
graph->get_zoom_hbox()->move_child(vs, 0);
- edit_type_standart = memnew(OptionButton);
- edit_type_standart->add_item(TTR("Vertex"));
- edit_type_standart->add_item(TTR("Fragment"));
- edit_type_standart->add_item(TTR("Light"));
- edit_type_standart->select(1);
- edit_type_standart->connect("item_selected", callable_mp(this, &VisualShaderEditor::_mode_selected));
+ custom_mode_box = memnew(CheckBox);
+ custom_mode_box->set_text(TTR("Custom"));
+ custom_mode_box->set_pressed(false);
+ custom_mode_box->set_visible(false);
+ custom_mode_box->connect("toggled", callable_mp(this, &VisualShaderEditor::_custom_mode_toggled));
+
+ edit_type_standard = memnew(OptionButton);
+ edit_type_standard->add_item(TTR("Vertex"));
+ edit_type_standard->add_item(TTR("Fragment"));
+ edit_type_standard->add_item(TTR("Light"));
+ edit_type_standard->select(1);
+ edit_type_standard->connect("item_selected", callable_mp(this, &VisualShaderEditor::_mode_selected));
edit_type_particles = memnew(OptionButton);
- edit_type_particles->add_item(TTR("Emit"));
+ edit_type_particles->add_item(TTR("Start"));
edit_type_particles->add_item(TTR("Process"));
- edit_type_particles->add_item(TTR("End"));
+ edit_type_particles->add_item(TTR("Collide"));
edit_type_particles->select(0);
edit_type_particles->connect("item_selected", callable_mp(this, &VisualShaderEditor::_mode_selected));
@@ -3750,14 +3794,16 @@ VisualShaderEditor::VisualShaderEditor() {
edit_type_sky->select(0);
edit_type_sky->connect("item_selected", callable_mp(this, &VisualShaderEditor::_mode_selected));
- edit_type = edit_type_standart;
+ edit_type = edit_type_standard;
+ graph->get_zoom_hbox()->add_child(custom_mode_box);
+ graph->get_zoom_hbox()->move_child(custom_mode_box, 0);
+ graph->get_zoom_hbox()->add_child(edit_type_standard);
+ graph->get_zoom_hbox()->move_child(edit_type_standard, 0);
graph->get_zoom_hbox()->add_child(edit_type_particles);
graph->get_zoom_hbox()->move_child(edit_type_particles, 0);
graph->get_zoom_hbox()->add_child(edit_type_sky);
graph->get_zoom_hbox()->move_child(edit_type_sky, 0);
- graph->get_zoom_hbox()->add_child(edit_type_standart);
- graph->get_zoom_hbox()->move_child(edit_type_standart, 0);
add_node = memnew(Button);
add_node->set_flat(true);
@@ -3977,9 +4023,10 @@ VisualShaderEditor::VisualShaderEditor() {
// INPUT
+ const String input_param_shader_modes = TTR("'%s' input parameter for all shader modes.");
+
// SPATIAL-FOR-ALL
- const String input_param_shader_modes = TTR("'%s' input parameter for all shader modes.");
add_options.push_back(AddOption("Camera", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "camera"), "camera", VisualShaderNode::PORT_TYPE_TRANSFORM, -1, Shader::MODE_SPATIAL));
add_options.push_back(AddOption("InvCamera", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "inv_camera"), "inv_camera", VisualShaderNode::PORT_TYPE_TRANSFORM, -1, Shader::MODE_SPATIAL));
add_options.push_back(AddOption("InvProjection", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "inv_projection"), "inv_projection", VisualShaderNode::PORT_TYPE_TRANSFORM, -1, Shader::MODE_SPATIAL));
@@ -3998,6 +4045,23 @@ VisualShaderEditor::VisualShaderEditor() {
add_options.push_back(AddOption("Time", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "time"), "time", VisualShaderNode::PORT_TYPE_SCALAR, -1, Shader::MODE_CANVAS_ITEM));
add_options.push_back(AddOption("UV", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "uv"), "uv", VisualShaderNode::PORT_TYPE_VECTOR, -1, Shader::MODE_CANVAS_ITEM));
+ // PARTICLES-FOR-ALL
+
+ add_options.push_back(AddOption("Active", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "active"), "active", VisualShaderNode::PORT_TYPE_BOOLEAN, -1, Shader::MODE_PARTICLES));
+ add_options.push_back(AddOption("Alpha", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "alpha"), "alpha", VisualShaderNode::PORT_TYPE_SCALAR, -1, Shader::MODE_PARTICLES));
+ add_options.push_back(AddOption("AttractorForce", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "attractor_force"), "attractor_force", VisualShaderNode::PORT_TYPE_VECTOR, -1, Shader::MODE_PARTICLES));
+ add_options.push_back(AddOption("Color", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "color"), "color", VisualShaderNode::PORT_TYPE_VECTOR, -1, Shader::MODE_PARTICLES));
+ add_options.push_back(AddOption("Custom", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "custom"), "custom", VisualShaderNode::PORT_TYPE_VECTOR, -1, Shader::MODE_PARTICLES));
+ add_options.push_back(AddOption("CustomAlpha", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "custom_alpha"), "custom_alpha", VisualShaderNode::PORT_TYPE_SCALAR, -1, Shader::MODE_PARTICLES));
+ add_options.push_back(AddOption("Delta", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "delta"), "delta", VisualShaderNode::PORT_TYPE_SCALAR, -1, Shader::MODE_PARTICLES));
+ add_options.push_back(AddOption("EmissionTransform", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "emission_transform"), "emission_transform", VisualShaderNode::PORT_TYPE_TRANSFORM, -1, Shader::MODE_PARTICLES));
+ add_options.push_back(AddOption("Index", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "index"), "index", VisualShaderNode::PORT_TYPE_SCALAR_INT, -1, Shader::MODE_PARTICLES));
+ add_options.push_back(AddOption("LifeTime", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "lifetime"), "lifetime", VisualShaderNode::PORT_TYPE_SCALAR, -1, Shader::MODE_PARTICLES));
+ add_options.push_back(AddOption("Restart", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "restart"), "restart", VisualShaderNode::PORT_TYPE_BOOLEAN, -1, Shader::MODE_PARTICLES));
+ add_options.push_back(AddOption("Time", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "time"), "time", VisualShaderNode::PORT_TYPE_SCALAR, -1, Shader::MODE_PARTICLES));
+ add_options.push_back(AddOption("Transform", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "transform"), "transform", VisualShaderNode::PORT_TYPE_TRANSFORM, -1, Shader::MODE_PARTICLES));
+ add_options.push_back(AddOption("Velocity", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "velocity"), "velocity", VisualShaderNode::PORT_TYPE_VECTOR, -1, Shader::MODE_PARTICLES));
+
/////////////////
add_options.push_back(AddOption("Input", "Input", "Common", "VisualShaderNodeInput", TTR("Input parameter.")));
@@ -4010,11 +4074,12 @@ VisualShaderEditor::VisualShaderEditor() {
const String input_param_for_sky_shader_mode = TTR("'%s' input parameter for sky shader mode.");
const String input_param_for_light_shader_mode = TTR("'%s' input parameter for light shader mode.");
const String input_param_for_vertex_shader_mode = TTR("'%s' input parameter for vertex shader mode.");
- const String input_param_for_emit_shader_mode = TTR("'%s' input parameter for emit shader mode.");
+ const String input_param_for_start_shader_mode = TTR("'%s' input parameter for start shader mode.");
const String input_param_for_process_shader_mode = TTR("'%s' input parameter for process shader mode.");
- const String input_param_for_end_shader_mode = TTR("'%s' input parameter for end shader mode.");
- const String input_param_for_emit_and_process_shader_mode = TTR("'%s' input parameter for emit and process shader mode.");
- const String input_param_for_vertex_and_fragment_shader_mode = TTR("'%s' input parameter for vertex and fragment shader mode.");
+ const String input_param_for_collide_shader_mode = TTR("'%s' input parameter for collide shader mode.");
+ const String input_param_for_start_and_process_shader_mode = TTR("'%s' input parameter for start and process shader modes.");
+ const String input_param_for_process_and_collide_shader_mode = TTR("'%s' input parameter for process and collide shader modes.");
+ const String input_param_for_vertex_and_fragment_shader_mode = TTR("'%s' input parameter for vertex and fragment shader modes.");
add_options.push_back(AddOption("Alpha", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "alpha"), "alpha", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL));
add_options.push_back(AddOption("Binormal", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "binormal"), "binormal", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL));
@@ -4091,50 +4156,6 @@ VisualShaderEditor::VisualShaderEditor() {
add_options.push_back(AddOption("Vertex", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "vertex"), "vertex", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_VERTEX, Shader::MODE_CANVAS_ITEM));
add_options.push_back(AddOption("World", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "world"), "world", VisualShaderNode::PORT_TYPE_TRANSFORM, TYPE_FLAGS_VERTEX, Shader::MODE_CANVAS_ITEM));
- // PARTICLES INPUTS
-
- add_options.push_back(AddOption("Active", "Input", "Emit", "VisualShaderNodeInput", vformat(input_param_shader_modes, "active"), "active", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_EMIT, Shader::MODE_PARTICLES));
- add_options.push_back(AddOption("Alpha", "Input", "Emit", "VisualShaderNodeInput", vformat(input_param_shader_modes, "alpha"), "alpha", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_EMIT, Shader::MODE_PARTICLES));
- add_options.push_back(AddOption("Color", "Input", "Emit", "VisualShaderNodeInput", vformat(input_param_shader_modes, "color"), "color", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_EMIT, Shader::MODE_PARTICLES));
- add_options.push_back(AddOption("Custom", "Input", "Emit", "VisualShaderNodeInput", vformat(input_param_shader_modes, "custom"), "custom", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_EMIT, Shader::MODE_PARTICLES));
- add_options.push_back(AddOption("CustomAlpha", "Input", "Emit", "VisualShaderNodeInput", vformat(input_param_shader_modes, "custom_alpha"), "custom_alpha", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_EMIT, Shader::MODE_PARTICLES));
- add_options.push_back(AddOption("Delta", "Input", "Emit", "VisualShaderNodeInput", vformat(input_param_shader_modes, "delta"), "delta", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_EMIT, Shader::MODE_PARTICLES));
- add_options.push_back(AddOption("EmissionTransform", "Input", "Emit", "VisualShaderNodeInput", vformat(input_param_shader_modes, "emission_transform"), "emission_transform", VisualShaderNode::PORT_TYPE_TRANSFORM, TYPE_FLAGS_EMIT, Shader::MODE_PARTICLES));
- add_options.push_back(AddOption("Index", "Input", "Emit", "VisualShaderNodeInput", vformat(input_param_shader_modes, "index"), "index", VisualShaderNode::PORT_TYPE_SCALAR_INT, TYPE_FLAGS_EMIT, Shader::MODE_PARTICLES));
- add_options.push_back(AddOption("LifeTime", "Input", "Emit", "VisualShaderNodeInput", vformat(input_param_shader_modes, "lifetime"), "lifetime", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_EMIT, Shader::MODE_PARTICLES));
- add_options.push_back(AddOption("Restart", "Input", "Emit", "VisualShaderNodeInput", vformat(input_param_shader_modes, "restart"), "restart", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_EMIT, Shader::MODE_PARTICLES));
- add_options.push_back(AddOption("Time", "Input", "Emit", "VisualShaderNodeInput", vformat(input_param_shader_modes, "time"), "time", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_EMIT, Shader::MODE_PARTICLES));
- add_options.push_back(AddOption("Transform", "Input", "Emit", "VisualShaderNodeInput", vformat(input_param_shader_modes, "transform"), "transform", VisualShaderNode::PORT_TYPE_TRANSFORM, TYPE_FLAGS_EMIT, Shader::MODE_PARTICLES));
- add_options.push_back(AddOption("Velocity", "Input", "Emit", "VisualShaderNodeInput", vformat(input_param_shader_modes, "velocity"), "velocity", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_EMIT, Shader::MODE_PARTICLES));
-
- add_options.push_back(AddOption("Active", "Input", "Process", "VisualShaderNodeInput", vformat(input_param_shader_modes, "active"), "active", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_PROCESS, Shader::MODE_PARTICLES));
- add_options.push_back(AddOption("Alpha", "Input", "Process", "VisualShaderNodeInput", vformat(input_param_shader_modes, "alpha"), "alpha", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_PROCESS, Shader::MODE_PARTICLES));
- add_options.push_back(AddOption("Color", "Input", "Process", "VisualShaderNodeInput", vformat(input_param_shader_modes, "color"), "color", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_PROCESS, Shader::MODE_PARTICLES));
- add_options.push_back(AddOption("Custom", "Input", "Process", "VisualShaderNodeInput", vformat(input_param_shader_modes, "custom"), "custom", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_PROCESS, Shader::MODE_PARTICLES));
- add_options.push_back(AddOption("CustomAlpha", "Input", "Process", "VisualShaderNodeInput", vformat(input_param_shader_modes, "custom_alpha"), "custom_alpha", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_PROCESS, Shader::MODE_PARTICLES));
- add_options.push_back(AddOption("Delta", "Input", "Process", "VisualShaderNodeInput", vformat(input_param_shader_modes, "delta"), "delta", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_PROCESS, Shader::MODE_PARTICLES));
- add_options.push_back(AddOption("EmissionTransform", "Input", "Process", "VisualShaderNodeInput", vformat(input_param_shader_modes, "emission_transform"), "emission_transform", VisualShaderNode::PORT_TYPE_TRANSFORM, TYPE_FLAGS_PROCESS, Shader::MODE_PARTICLES));
- add_options.push_back(AddOption("Index", "Input", "Process", "VisualShaderNodeInput", vformat(input_param_shader_modes, "index"), "index", VisualShaderNode::PORT_TYPE_SCALAR_INT, TYPE_FLAGS_PROCESS, Shader::MODE_PARTICLES));
- add_options.push_back(AddOption("LifeTime", "Input", "Process", "VisualShaderNodeInput", vformat(input_param_shader_modes, "lifetime"), "lifetime", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_PROCESS, Shader::MODE_PARTICLES));
- add_options.push_back(AddOption("Restart", "Input", "Process", "VisualShaderNodeInput", vformat(input_param_shader_modes, "restart"), "restart", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_PROCESS, Shader::MODE_PARTICLES));
- add_options.push_back(AddOption("Time", "Input", "Process", "VisualShaderNodeInput", vformat(input_param_shader_modes, "time"), "time", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_PROCESS, Shader::MODE_PARTICLES));
- add_options.push_back(AddOption("Transform", "Input", "Process", "VisualShaderNodeInput", vformat(input_param_shader_modes, "transform"), "transform", VisualShaderNode::PORT_TYPE_TRANSFORM, TYPE_FLAGS_PROCESS, Shader::MODE_PARTICLES));
- add_options.push_back(AddOption("Velocity", "Input", "Process", "VisualShaderNodeInput", vformat(input_param_shader_modes, "velocity"), "velocity", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_PROCESS, Shader::MODE_PARTICLES));
-
- add_options.push_back(AddOption("Active", "Input", "End", "VisualShaderNodeInput", vformat(input_param_shader_modes, "active"), "active", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_END, Shader::MODE_PARTICLES));
- add_options.push_back(AddOption("Alpha", "Input", "End", "VisualShaderNodeInput", vformat(input_param_shader_modes, "alpha"), "alpha", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_END, Shader::MODE_PARTICLES));
- add_options.push_back(AddOption("Color", "Input", "End", "VisualShaderNodeInput", vformat(input_param_shader_modes, "color"), "color", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_END, Shader::MODE_PARTICLES));
- add_options.push_back(AddOption("Custom", "Input", "End", "VisualShaderNodeInput", vformat(input_param_shader_modes, "custom"), "custom", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_END, Shader::MODE_PARTICLES));
- add_options.push_back(AddOption("CustomAlpha", "Input", "End", "VisualShaderNodeInput", vformat(input_param_shader_modes, "custom_alpha"), "custom_alpha", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_END, Shader::MODE_PARTICLES));
- add_options.push_back(AddOption("Delta", "Input", "End", "VisualShaderNodeInput", vformat(input_param_shader_modes, "delta"), "delta", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_END, Shader::MODE_PARTICLES));
- add_options.push_back(AddOption("EmissionTransform", "Input", "End", "VisualShaderNodeInput", vformat(input_param_shader_modes, "emission_transform"), "emission_transform", VisualShaderNode::PORT_TYPE_TRANSFORM, TYPE_FLAGS_END, Shader::MODE_PARTICLES));
- add_options.push_back(AddOption("Index", "Input", "End", "VisualShaderNodeInput", vformat(input_param_shader_modes, "index"), "index", VisualShaderNode::PORT_TYPE_SCALAR_INT, TYPE_FLAGS_END, Shader::MODE_PARTICLES));
- add_options.push_back(AddOption("LifeTime", "Input", "End", "VisualShaderNodeInput", vformat(input_param_shader_modes, "lifetime"), "lifetime", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_END, Shader::MODE_PARTICLES));
- add_options.push_back(AddOption("Restart", "Input", "End", "VisualShaderNodeInput", vformat(input_param_shader_modes, "restart"), "restart", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_END, Shader::MODE_PARTICLES));
- add_options.push_back(AddOption("Time", "Input", "End", "VisualShaderNodeInput", vformat(input_param_shader_modes, "time"), "time", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_END, Shader::MODE_PARTICLES));
- add_options.push_back(AddOption("Transform", "Input", "End", "VisualShaderNodeInput", vformat(input_param_shader_modes, "transform"), "transform", VisualShaderNode::PORT_TYPE_TRANSFORM, TYPE_FLAGS_END, Shader::MODE_PARTICLES));
- add_options.push_back(AddOption("Velocity", "Input", "End", "VisualShaderNodeInput", vformat(input_param_shader_modes, "velocity"), "velocity", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_END, Shader::MODE_PARTICLES));
-
// SKY INPUTS
add_options.push_back(AddOption("AtCubeMapPass", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "at_cubemap_pass"), "at_cubemap_pass", VisualShaderNode::PORT_TYPE_BOOLEAN, TYPE_FLAGS_SKY, Shader::MODE_SKY));
@@ -4167,6 +4188,22 @@ VisualShaderEditor::VisualShaderEditor() {
add_options.push_back(AddOption("SkyCoords", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "sky_coords"), "sky_coords", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
add_options.push_back(AddOption("Time", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "time"), "time", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
+ // PARTICLES
+
+ add_options.push_back(AddOption("CollisionDepth", "Input", "Collide", "VisualShaderNodeInput", vformat(input_param_for_collide_shader_mode, "collision_depth"), "collision_depth", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_COLLIDE, Shader::MODE_PARTICLES));
+ add_options.push_back(AddOption("CollisionNormal", "Input", "Collide", "VisualShaderNodeInput", vformat(input_param_for_collide_shader_mode, "collision_normal"), "collision_normal", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_COLLIDE, Shader::MODE_PARTICLES));
+
+ add_options.push_back(AddOption("EmitParticle", "Particles", "", "VisualShaderNodeParticleEmit", "", -1, -1, -1, Shader::MODE_PARTICLES));
+ add_options.push_back(AddOption("ParticleAccelerator", "Particles", "", "VisualShaderNodeParticleAccelerator", "", -1, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_PROCESS, Shader::MODE_PARTICLES));
+ add_options.push_back(AddOption("ParticleRandomness", "Particles", "", "VisualShaderNodeParticleRandomness", "", -1, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_EMIT | TYPE_FLAGS_PROCESS | TYPE_FLAGS_COLLIDE, Shader::MODE_PARTICLES));
+ add_options.push_back(AddOption("MultiplyByAxisAngle", "Particles", "Transform", "VisualShaderNodeParticleMultiplyByAxisAngle", "A node for help to multiply a position input vector by rotation using specific axis. Intended to work with emitters.", -1, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_EMIT | TYPE_FLAGS_PROCESS | TYPE_FLAGS_COLLIDE, Shader::MODE_PARTICLES));
+
+ add_options.push_back(AddOption("BoxEmitter", "Particles", "Emitters", "VisualShaderNodeParticleBoxEmitter", "", -1, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_EMIT, Shader::MODE_PARTICLES));
+ add_options.push_back(AddOption("RingEmitter", "Particles", "Emitters", "VisualShaderNodeParticleRingEmitter", "", -1, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_EMIT, Shader::MODE_PARTICLES));
+ add_options.push_back(AddOption("SphereEmitter", "Particles", "Emitters", "VisualShaderNodeParticleSphereEmitter", "", -1, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_EMIT, Shader::MODE_PARTICLES));
+
+ add_options.push_back(AddOption("ConeVelocity", "Particles", "Velocity", "VisualShaderNodeParticleConeVelocity", "", -1, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_EMIT, Shader::MODE_PARTICLES));
+
// SCALAR
add_options.push_back(AddOption("FloatFunc", "Scalar", "Common", "VisualShaderNodeFloatFunc", TTR("Float function."), -1, VisualShaderNode::PORT_TYPE_SCALAR));
diff --git a/editor/plugins/visual_shader_editor_plugin.h b/editor/plugins/visual_shader_editor_plugin.h
index f12d05f7ed..ad57850e9d 100644
--- a/editor/plugins/visual_shader_editor_plugin.h
+++ b/editor/plugins/visual_shader_editor_plugin.h
@@ -142,9 +142,11 @@ class VisualShaderEditor : public VBoxContainer {
Button *preview_shader;
OptionButton *edit_type = nullptr;
- OptionButton *edit_type_standart;
+ OptionButton *edit_type_standard;
OptionButton *edit_type_particles;
OptionButton *edit_type_sky;
+ CheckBox *custom_mode_box;
+ bool custom_mode_enabled = false;
bool pending_update_preview;
bool shader_error;
@@ -189,7 +191,9 @@ class VisualShaderEditor : public VBoxContainer {
enum ParticlesTypeFlags {
TYPE_FLAGS_EMIT = 1,
TYPE_FLAGS_PROCESS = 2,
- TYPE_FLAGS_END = 4
+ TYPE_FLAGS_COLLIDE = 4,
+ TYPE_FLAGS_EMIT_CUSTOM = 8,
+ TYPE_FLAGS_PROCESS_CUSTOM = 16,
};
enum SkyTypeFlags {
@@ -385,6 +389,7 @@ class VisualShaderEditor : public VBoxContainer {
Ref<VisualShaderGraphPlugin> graph_plugin;
void _mode_selected(int p_id);
+ void _custom_mode_toggled(bool p_enabled);
void _input_select_item(Ref<VisualShaderNodeInput> input, String name);
void _uniform_select_item(Ref<VisualShaderNodeUniformRef> p_uniform, String p_name);
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index 4faba0b9d2..332976a18d 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -183,6 +183,7 @@
#include "scene/resources/video_stream.h"
#include "scene/resources/visual_shader.h"
#include "scene/resources/visual_shader_nodes.h"
+#include "scene/resources/visual_shader_particle_nodes.h"
#include "scene/resources/visual_shader_sdf_nodes.h"
#include "scene/resources/world_2d.h"
#include "scene/resources/world_3d.h"
@@ -613,6 +614,17 @@ void register_scene_types() {
ClassDB::register_class<VisualShaderNodeTextureSDFNormal>();
ClassDB::register_class<VisualShaderNodeSDFRaymarch>();
+ ClassDB::register_class<VisualShaderNodeParticleOutput>();
+ ClassDB::register_virtual_class<VisualShaderNodeParticleEmitter>();
+ ClassDB::register_class<VisualShaderNodeParticleSphereEmitter>();
+ ClassDB::register_class<VisualShaderNodeParticleBoxEmitter>();
+ ClassDB::register_class<VisualShaderNodeParticleRingEmitter>();
+ ClassDB::register_class<VisualShaderNodeParticleMultiplyByAxisAngle>();
+ ClassDB::register_class<VisualShaderNodeParticleConeVelocity>();
+ ClassDB::register_class<VisualShaderNodeParticleRandomness>();
+ ClassDB::register_class<VisualShaderNodeParticleAccelerator>();
+ ClassDB::register_class<VisualShaderNodeParticleEmit>();
+
ClassDB::register_class<ShaderMaterial>();
ClassDB::register_virtual_class<CanvasItem>();
ClassDB::register_class<CanvasTexture>();
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