summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJuan Linietsky <reduzio@gmail.com>2020-09-06 11:35:36 -0300
committerGitHub <noreply@github.com>2020-09-06 11:35:36 -0300
commit39f58c74120f093375b15ea1cdd02407027cd644 (patch)
treece3afb87522e23f36e235090cf8e5d658c3dd03a
parent859322343ca02d1fac9c2f29b58fcfa8d3b27b0c (diff)
parentd0bddf53c5415c1f127d51dba5f94dd485def0e3 (diff)
Merge pull request #41810 from reduz/particle-improvements
Implement manual particle emission and particle sub emitters.
-rw-r--r--scene/3d/gpu_particles_3d.cpp52
-rw-r--r--scene/3d/gpu_particles_3d.h17
-rw-r--r--scene/resources/particles_material.cpp277
-rw-r--r--scene/resources/particles_material.h64
-rw-r--r--servers/rendering/rasterizer.h2
-rw-r--r--servers/rendering/rasterizer_rd/rasterizer_storage_rd.cpp235
-rw-r--r--servers/rendering/rasterizer_rd/rasterizer_storage_rd.h38
-rw-r--r--servers/rendering/rasterizer_rd/shaders/particles.glsl238
-rw-r--r--servers/rendering/rendering_server_raster.h2
-rw-r--r--servers/rendering/rendering_server_wrap_mt.h3
-rw-r--r--servers/rendering/shader_language.cpp180
-rw-r--r--servers/rendering/shader_language.h31
-rw-r--r--servers/rendering/shader_types.cpp21
-rw-r--r--servers/rendering_server.h14
14 files changed, 864 insertions, 310 deletions
diff --git a/scene/3d/gpu_particles_3d.cpp b/scene/3d/gpu_particles_3d.cpp
index c4480e3ed2..6fa0fc6ecb 100644
--- a/scene/3d/gpu_particles_3d.cpp
+++ b/scene/3d/gpu_particles_3d.cpp
@@ -301,6 +301,36 @@ void GPUParticles3D::_validate_property(PropertyInfo &property) const {
}
}
+void GPUParticles3D::emit_particle(const Transform &p_transform, const Vector3 &p_velocity, const Color &p_color, const Color &p_custom, uint32_t p_emit_flags) {
+ RS::get_singleton()->particles_emit(particles, p_transform, p_velocity, p_color, p_custom, p_emit_flags);
+}
+
+void GPUParticles3D::_attach_sub_emitter() {
+ Node *n = get_node_or_null(sub_emitter);
+ if (n) {
+ GPUParticles3D *sen = Object::cast_to<GPUParticles3D>(n);
+ if (sen && sen != this) {
+ RS::get_singleton()->particles_set_subemitter(particles, sen->particles);
+ }
+ }
+}
+
+void GPUParticles3D::set_sub_emitter(const NodePath &p_path) {
+ if (is_inside_tree()) {
+ RS::get_singleton()->particles_set_subemitter(particles, RID());
+ }
+
+ sub_emitter = p_path;
+
+ if (is_inside_tree() && sub_emitter != NodePath()) {
+ _attach_sub_emitter();
+ }
+}
+
+NodePath GPUParticles3D::get_sub_emitter() const {
+ return sub_emitter;
+}
+
void GPUParticles3D::_notification(int p_what) {
if (p_what == NOTIFICATION_PAUSED || p_what == NOTIFICATION_UNPAUSED) {
if (can_process()) {
@@ -319,6 +349,16 @@ void GPUParticles3D::_notification(int p_what) {
}
}
+ if (p_what == NOTIFICATION_ENTER_TREE) {
+ if (sub_emitter != NodePath()) {
+ _attach_sub_emitter();
+ }
+ }
+
+ if (p_what == NOTIFICATION_EXIT_TREE) {
+ RS::get_singleton()->particles_set_subemitter(particles, RID());
+ }
+
if (p_what == NOTIFICATION_VISIBILITY_CHANGED) {
// make sure particles are updated before rendering occurs if they were active before
if (is_visible_in_tree() && !RS::get_singleton()->particles_is_inactive(particles)) {
@@ -369,8 +409,14 @@ void GPUParticles3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("restart"), &GPUParticles3D::restart);
ClassDB::bind_method(D_METHOD("capture_aabb"), &GPUParticles3D::capture_aabb);
+ ClassDB::bind_method(D_METHOD("set_sub_emitter", "path"), &GPUParticles3D::set_sub_emitter);
+ ClassDB::bind_method(D_METHOD("get_sub_emitter"), &GPUParticles3D::get_sub_emitter);
+
+ ClassDB::bind_method(D_METHOD("emit_particle", "xform", "velocity", "color", "custom", "flags"), &GPUParticles3D::emit_particle);
+
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emitting"), "set_emitting", "is_emitting");
ADD_PROPERTY(PropertyInfo(Variant::INT, "amount", PROPERTY_HINT_EXP_RANGE, "1,1000000,1"), "set_amount", "get_amount");
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "sub_emitter", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "GPUParticles3D"), "set_sub_emitter", "get_sub_emitter");
ADD_GROUP("Time", "");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lifetime", PROPERTY_HINT_EXP_RANGE, "0.01,600.0,0.01,or_greater"), "set_lifetime", "get_lifetime");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "one_shot"), "set_one_shot", "get_one_shot");
@@ -396,6 +442,12 @@ void GPUParticles3D::_bind_methods() {
BIND_ENUM_CONSTANT(DRAW_ORDER_LIFETIME);
BIND_ENUM_CONSTANT(DRAW_ORDER_VIEW_DEPTH);
+ BIND_ENUM_CONSTANT(EMIT_FLAG_POSITION);
+ BIND_ENUM_CONSTANT(EMIT_FLAG_ROTATION_SCALE);
+ BIND_ENUM_CONSTANT(EMIT_FLAG_VELOCITY);
+ BIND_ENUM_CONSTANT(EMIT_FLAG_COLOR);
+ BIND_ENUM_CONSTANT(EMIT_FLAG_CUSTOM);
+
BIND_CONSTANT(MAX_DRAW_PASSES);
}
diff --git a/scene/3d/gpu_particles_3d.h b/scene/3d/gpu_particles_3d.h
index e04473727d..0d8dadd31d 100644
--- a/scene/3d/gpu_particles_3d.h
+++ b/scene/3d/gpu_particles_3d.h
@@ -64,6 +64,7 @@ private:
bool local_coords;
int fixed_fps;
bool fractional_delta;
+ NodePath sub_emitter;
Ref<Material> process_material;
@@ -71,6 +72,8 @@ private:
Vector<Ref<Mesh>> draw_passes;
+ void _attach_sub_emitter();
+
protected:
static void _bind_methods();
void _notification(int p_what);
@@ -121,13 +124,27 @@ public:
virtual String get_configuration_warning() const override;
+ void set_sub_emitter(const NodePath &p_path);
+ NodePath get_sub_emitter() const;
+
void restart();
+ enum EmitFlags {
+ EMIT_FLAG_POSITION = RS::PARTICLES_EMIT_FLAG_POSITION,
+ EMIT_FLAG_ROTATION_SCALE = RS::PARTICLES_EMIT_FLAG_ROTATION_SCALE,
+ EMIT_FLAG_VELOCITY = RS::PARTICLES_EMIT_FLAG_VELOCITY,
+ EMIT_FLAG_COLOR = RS::PARTICLES_EMIT_FLAG_COLOR,
+ EMIT_FLAG_CUSTOM = RS::PARTICLES_EMIT_FLAG_CUSTOM
+ };
+
+ void emit_particle(const Transform &p_transform, const Vector3 &p_velocity, const Color &p_color, const Color &p_custom, uint32_t p_emit_flags);
+
AABB capture_aabb() const;
GPUParticles3D();
~GPUParticles3D();
};
VARIANT_ENUM_CAST(GPUParticles3D::DrawOrder)
+VARIANT_ENUM_CAST(GPUParticles3D::EmitFlags)
#endif // PARTICLES_H
diff --git a/scene/resources/particles_material.cpp b/scene/resources/particles_material.cpp
index a0095ed952..49b8cbe7e0 100644
--- a/scene/resources/particles_material.cpp
+++ b/scene/resources/particles_material.cpp
@@ -91,13 +91,13 @@ void ParticlesMaterial::init_shaders() {
shader_names->emission_texture_normal = "emission_texture_normal";
shader_names->emission_texture_color = "emission_texture_color";
- shader_names->trail_divisor = "trail_divisor";
- shader_names->trail_size_modifier = "trail_size_modifier";
- shader_names->trail_color_modifier = "trail_color_modifier";
-
shader_names->gravity = "gravity";
shader_names->lifetime_randomness = "lifetime_randomness";
+
+ shader_names->sub_emitter_frequency = "sub_emitter_frequency";
+ shader_names->sub_emitter_amount_at_end = "sub_emitter_amount_at_end";
+ shader_names->sub_emitter_keep_velocity = "sub_emitter_keep_velocity";
}
void ParticlesMaterial::finish_shaders() {
@@ -192,9 +192,17 @@ void ParticlesMaterial::_update_shader() {
}
}
- code += "uniform vec4 color_value : hint_color;\n";
+ if (sub_emitter_mode != SUB_EMITTER_DISABLED) {
+ if (sub_emitter_mode == SUB_EMITTER_CONSTANT) {
+ code += "uniform float sub_emitter_frequency;\n";
+ }
+ if (sub_emitter_mode == SUB_EMITTER_AT_END) {
+ code += "uniform int sub_emitter_amount_at_end;\n";
+ }
+ code += "uniform bool sub_emitter_keep_velocity;\n";
+ }
- code += "uniform int trail_divisor;\n";
+ code += "uniform vec4 color_value : hint_color;\n";
code += "uniform vec3 gravity;\n";
@@ -239,14 +247,6 @@ void ParticlesMaterial::_update_shader() {
code += "uniform sampler2D anim_offset_texture;\n";
}
- if (trail_size_modifier.is_valid()) {
- code += "uniform sampler2D trail_size_modifier;\n";
- }
-
- if (trail_color_modifier.is_valid()) {
- code += "uniform sampler2D trail_color_modifier;\n";
- }
-
//need a random function
code += "\n\n";
code += "float rand_from_seed(inout uint seed) {\n";
@@ -278,7 +278,7 @@ void ParticlesMaterial::_update_shader() {
code += "\n";
code += "void compute() {\n";
- code += " uint base_number = NUMBER / uint(trail_divisor);\n";
+ code += " uint base_number = NUMBER;\n";
code += " uint alt_seed = hash(base_number + uint(1) + RANDOM_SEED);\n";
code += " float angle_rand = rand_from_seed(alt_seed);\n";
code += " float scale_rand = rand_from_seed(alt_seed);\n";
@@ -293,17 +293,7 @@ void ParticlesMaterial::_update_shader() {
code += " ivec2 emission_tex_size = textureSize(emission_texture_points, 0);\n";
code += " ivec2 emission_tex_ofs = ivec2(point % emission_tex_size.x, point / emission_tex_size.x);\n";
}
- code += " bool restart = false;\n";
- code += " if (CUSTOM.y > CUSTOM.w) {\n";
- code += " restart = true;\n";
- code += " }\n\n";
- code += " if (RESTART || restart) {\n";
-
- if (tex_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) {
- code += " float tex_linear_velocity = textureLod(linear_velocity_texture, vec2(0.0, 0.0), 0.0).r;\n";
- } else {
- code += " float tex_linear_velocity = 0.0;\n";
- }
+ code += " if (RESTART) {\n";
if (tex_parameters[PARAM_ANGLE].is_valid()) {
code += " float tex_angle = textureLod(angle_texture, vec2(0.0, 0.0), 0.0).r;\n";
@@ -319,25 +309,34 @@ void ParticlesMaterial::_update_shader() {
code += " float spread_rad = spread * degree_to_rad;\n";
+ code += " if (RESTART_VELOCITY) {\n";
+
+ if (tex_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) {
+ code += " float tex_linear_velocity = textureLod(linear_velocity_texture, vec2(0.0, 0.0), 0.0).r;\n";
+ } else {
+ code += " float tex_linear_velocity = 0.0;\n";
+ }
+
if (flags[FLAG_DISABLE_Z]) {
- code += " float angle1_rad = rand_from_seed_m1_p1(alt_seed) * spread_rad;\n";
- code += " angle1_rad += direction.x != 0.0 ? atan(direction.y, direction.x) : sign(direction.y) * (pi / 2.0);\n";
- code += " vec3 rot = vec3(cos(angle1_rad), sin(angle1_rad), 0.0);\n";
- code += " VELOCITY = rot * initial_linear_velocity * mix(1.0, rand_from_seed(alt_seed), initial_linear_velocity_random);\n";
+ code += " float angle1_rad = rand_from_seed_m1_p1(alt_seed) * spread_rad;\n";
+ code += " angle1_rad += direction.x != 0.0 ? atan(direction.y, direction.x) : sign(direction.y) * (pi / 2.0);\n";
+ code += " vec3 rot = vec3(cos(angle1_rad), sin(angle1_rad), 0.0);\n";
+ code += " VELOCITY = rot * initial_linear_velocity * mix(1.0, rand_from_seed(alt_seed), initial_linear_velocity_random);\n";
} else {
//initiate velocity spread in 3D
- code += " float angle1_rad = rand_from_seed_m1_p1(alt_seed) * spread_rad;\n";
- code += " float angle2_rad = rand_from_seed_m1_p1(alt_seed) * spread_rad * (1.0 - flatness);\n";
- code += " angle1_rad += direction.z != 0.0 ? atan(direction.x, direction.z) : sign(direction.x) * (pi / 2.0);\n";
- code += " angle2_rad += direction.z != 0.0 ? atan(direction.y, abs(direction.z)) : (direction.x != 0.0 ? atan(direction.y, abs(direction.x)) : sign(direction.y) * (pi / 2.0));\n";
- code += " vec3 direction_xz = vec3(sin(angle1_rad), 0.0, cos(angle1_rad));\n";
- code += " vec3 direction_yz = vec3(0.0, sin(angle2_rad), cos(angle2_rad));\n";
- code += " direction_yz.z = direction_yz.z / max(0.0001,sqrt(abs(direction_yz.z))); // better uniform distribution\n";
- code += " vec3 vec_direction = vec3(direction_xz.x * direction_yz.z, direction_yz.y, direction_xz.z * direction_yz.z);\n";
- code += " vec_direction = normalize(vec_direction);\n";
- code += " VELOCITY = vec_direction * initial_linear_velocity * mix(1.0, rand_from_seed(alt_seed), initial_linear_velocity_random);\n";
+ code += " float angle1_rad = rand_from_seed_m1_p1(alt_seed) * spread_rad;\n";
+ code += " float angle2_rad = rand_from_seed_m1_p1(alt_seed) * spread_rad * (1.0 - flatness);\n";
+ code += " angle1_rad += direction.z != 0.0 ? atan(direction.x, direction.z) : sign(direction.x) * (pi / 2.0);\n";
+ code += " angle2_rad += direction.z != 0.0 ? atan(direction.y, abs(direction.z)) : (direction.x != 0.0 ? atan(direction.y, abs(direction.x)) : sign(direction.y) * (pi / 2.0));\n";
+ code += " vec3 direction_xz = vec3(sin(angle1_rad), 0.0, cos(angle1_rad));\n";
+ code += " vec3 direction_yz = vec3(0.0, sin(angle2_rad), cos(angle2_rad));\n";
+ code += " direction_yz.z = direction_yz.z / max(0.0001,sqrt(abs(direction_yz.z))); // better uniform distribution\n";
+ code += " vec3 vec_direction = vec3(direction_xz.x * direction_yz.z, direction_yz.y, direction_xz.z * direction_yz.z);\n";
+ code += " vec_direction = normalize(vec_direction);\n";
+ code += " VELOCITY = vec_direction * initial_linear_velocity * mix(1.0, rand_from_seed(alt_seed), initial_linear_velocity_random);\n";
}
+ code += " }\n";
code += " float base_angle = (initial_angle + tex_angle) * mix(1.0, angle_rand, initial_angle_random);\n";
code += " CUSTOM.x = base_angle * degree_to_rad;\n"; // angle
@@ -345,35 +344,38 @@ void ParticlesMaterial::_update_shader() {
code += " CUSTOM.w = (1.0 - lifetime_randomness * rand_from_seed(alt_seed));\n";
code += " CUSTOM.z = (anim_offset + tex_anim_offset) * mix(1.0, anim_offset_rand, anim_offset_random);\n"; // animation offset (0-1)
+ code += " if (RESTART_POSITION) {\n";
+
switch (emission_shape) {
case EMISSION_SHAPE_POINT: {
- //do none
+ //do none, identity (will later be multiplied by emission transform)
+ code += " TRANSFORM = mat4(vec4(1,0,0,0),vec4(0,1,0,0),vec4(0,0,1,0),vec4(0,0,0,1));\n";
} break;
case EMISSION_SHAPE_SPHERE: {
- code += " float s = rand_from_seed(alt_seed) * 2.0 - 1.0;\n";
- code += " float t = rand_from_seed(alt_seed) * 2.0 * pi;\n";
- code += " float radius = emission_sphere_radius * sqrt(1.0 - s * s);\n";
- code += " TRANSFORM[3].xyz = vec3(radius * cos(t), radius * sin(t), emission_sphere_radius * s);\n";
+ code += " float s = rand_from_seed(alt_seed) * 2.0 - 1.0;\n";
+ code += " float t = rand_from_seed(alt_seed) * 2.0 * pi;\n";
+ code += " float radius = emission_sphere_radius * sqrt(1.0 - s * s);\n";
+ code += " TRANSFORM[3].xyz = vec3(radius * cos(t), radius * sin(t), emission_sphere_radius * s);\n";
} break;
case EMISSION_SHAPE_BOX: {
- code += " TRANSFORM[3].xyz = vec3(rand_from_seed(alt_seed) * 2.0 - 1.0, rand_from_seed(alt_seed) * 2.0 - 1.0, rand_from_seed(alt_seed) * 2.0 - 1.0) * emission_box_extents;\n";
+ code += " TRANSFORM[3].xyz = vec3(rand_from_seed(alt_seed) * 2.0 - 1.0, rand_from_seed(alt_seed) * 2.0 - 1.0, rand_from_seed(alt_seed) * 2.0 - 1.0) * emission_box_extents;\n";
} break;
case EMISSION_SHAPE_POINTS:
case EMISSION_SHAPE_DIRECTED_POINTS: {
- code += " TRANSFORM[3].xyz = texelFetch(emission_texture_points, emission_tex_ofs, 0).xyz;\n";
+ code += " TRANSFORM[3].xyz = texelFetch(emission_texture_points, emission_tex_ofs, 0).xyz;\n";
if (emission_shape == EMISSION_SHAPE_DIRECTED_POINTS) {
if (flags[FLAG_DISABLE_Z]) {
- code += " mat2 rotm;";
- code += " rotm[0] = texelFetch(emission_texture_normal, emission_tex_ofs, 0).xy;\n";
- code += " rotm[1] = rotm[0].yx * vec2(1.0, -1.0);\n";
- code += " VELOCITY.xy = rotm * VELOCITY.xy;\n";
+ code += " mat2 rotm;";
+ code += " rotm[0] = texelFetch(emission_texture_normal, emission_tex_ofs, 0).xy;\n";
+ code += " rotm[1] = rotm[0].yx * vec2(1.0, -1.0);\n";
+ code += " if (RESTART_VELOCITY) VELOCITY.xy = rotm * VELOCITY.xy;\n";
} else {
- code += " vec3 normal = texelFetch(emission_texture_normal, emission_tex_ofs, 0).xyz;\n";
- code += " vec3 v0 = abs(normal.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(0.0, 1.0, 0.0);\n";
- code += " vec3 tangent = normalize(cross(v0, normal));\n";
- code += " vec3 bitangent = normalize(cross(tangent, normal));\n";
- code += " VELOCITY = mat3(tangent, bitangent, normal) * VELOCITY;\n";
+ code += " vec3 normal = texelFetch(emission_texture_normal, emission_tex_ofs, 0).xyz;\n";
+ code += " vec3 v0 = abs(normal.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(0.0, 1.0, 0.0);\n";
+ code += " vec3 tangent = normalize(cross(v0, normal));\n";
+ code += " vec3 bitangent = normalize(cross(tangent, normal));\n";
+ code += " if (RESTART_VELOCITY) VELOCITY = mat3(tangent, bitangent, normal) * VELOCITY;\n";
}
}
} break;
@@ -381,12 +383,14 @@ void ParticlesMaterial::_update_shader() {
break;
}
}
- code += " VELOCITY = (EMISSION_TRANSFORM * vec4(VELOCITY, 0.0)).xyz;\n";
- code += " TRANSFORM = EMISSION_TRANSFORM * TRANSFORM;\n";
+
+ code += " if (RESTART_VELOCITY) VELOCITY = (EMISSION_TRANSFORM * vec4(VELOCITY, 0.0)).xyz;\n";
+ code += " TRANSFORM = EMISSION_TRANSFORM * TRANSFORM;\n";
if (flags[FLAG_DISABLE_Z]) {
- code += " VELOCITY.z = 0.0;\n";
- code += " TRANSFORM[3].z = 0.0;\n";
+ code += " VELOCITY.z = 0.0;\n";
+ code += " TRANSFORM[3].z = 0.0;\n";
}
+ code += " }\n";
code += " } else {\n";
@@ -540,11 +544,6 @@ void ParticlesMaterial::_update_shader() {
if (emission_color_texture.is_valid() && (emission_shape == EMISSION_SHAPE_POINTS || emission_shape == EMISSION_SHAPE_DIRECTED_POINTS)) {
code += " COLOR *= texelFetch(emission_texture_color, emission_tex_ofs, 0);\n";
}
- if (trail_color_modifier.is_valid()) {
- code += " if (trail_divisor > 1) {\n";
- code += " COLOR *= textureLod(trail_color_modifier, vec2(float(int(NUMBER) % trail_divisor) / float(trail_divisor - 1), 0.0), 0.0);\n";
- code += " }\n";
- }
code += "\n";
if (flags[FLAG_DISABLE_Z]) {
@@ -592,11 +591,6 @@ void ParticlesMaterial::_update_shader() {
code += " if (base_scale < 0.000001) {\n";
code += " base_scale = 0.000001;\n";
code += " }\n";
- if (trail_size_modifier.is_valid()) {
- code += " if (trail_divisor > 1) {\n";
- code += " base_scale *= textureLod(trail_size_modifier, vec2(float(int(NUMBER) % trail_divisor) / float(trail_divisor - 1), 0.0), 0.0).r;\n";
- code += " }\n";
- }
code += " TRANSFORM[0].xyz *= base_scale;\n";
code += " TRANSFORM[1].xyz *= base_scale;\n";
@@ -605,6 +599,33 @@ void ParticlesMaterial::_update_shader() {
code += " VELOCITY.z = 0.0;\n";
code += " TRANSFORM[3].z = 0.0;\n";
}
+ if (sub_emitter_mode != SUB_EMITTER_DISABLED) {
+ code += " int emit_count = 0;\n";
+ switch (sub_emitter_mode) {
+ case SUB_EMITTER_CONSTANT: {
+ code += " float interval_from = CUSTOM.y * LIFETIME - DELTA;\n";
+ code += " float interval_rem = sub_emitter_frequency - mod(interval_from,sub_emitter_frequency);\n";
+ code += " if (DELTA >= interval_rem) emit_count = 1;\n";
+ } break;
+ case SUB_EMITTER_AT_COLLISION: {
+ //not implemented yet
+ } break;
+ case SUB_EMITTER_AT_END: {
+ //not implemented yet
+ code += " float unit_delta = DELTA/LIFETIME;\n";
+ code += " float end_time = CUSTOM.w * 0.95;\n"; // if we do at the end we might miss it, as it can just get deactivated by emitter
+ code += " if (CUSTOM.y < end_time && (CUSTOM.y + unit_delta) >= end_time) emit_count = sub_emitter_amount_at_end;\n";
+ } break;
+ default: {
+ }
+ }
+ code += " for(int i=0;i<emit_count;i++) {\n";
+ code += " uint flags = FLAG_EMIT_POSITION|FLAG_EMIT_ROT_SCALE;\n";
+ code += " if (sub_emitter_keep_velocity) flags|=FLAG_EMIT_VELOCITY;\n";
+ code += " emit_particle(TRANSFORM,VELOCITY,vec4(0.0),vec4(0.0),flags);\n";
+ code += " }";
+ }
+
code += " if (CUSTOM.y > CUSTOM.w) {";
code += " ACTIVE = false;\n";
code += " }\n";
@@ -951,41 +972,6 @@ int ParticlesMaterial::get_emission_point_count() const {
return emission_point_count;
}
-void ParticlesMaterial::set_trail_divisor(int p_divisor) {
- trail_divisor = p_divisor;
- RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->trail_divisor, p_divisor);
-}
-
-int ParticlesMaterial::get_trail_divisor() const {
- return trail_divisor;
-}
-
-void ParticlesMaterial::set_trail_size_modifier(const Ref<CurveTexture> &p_trail_size_modifier) {
- trail_size_modifier = p_trail_size_modifier;
-
- Ref<CurveTexture> curve = trail_size_modifier;
- if (curve.is_valid()) {
- curve->ensure_default_setup();
- }
-
- RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->trail_size_modifier, curve);
- _queue_shader_change();
-}
-
-Ref<CurveTexture> ParticlesMaterial::get_trail_size_modifier() const {
- return trail_size_modifier;
-}
-
-void ParticlesMaterial::set_trail_color_modifier(const Ref<GradientTexture> &p_trail_color_modifier) {
- trail_color_modifier = p_trail_color_modifier;
- RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->trail_color_modifier, p_trail_color_modifier);
- _queue_shader_change();
-}
-
-Ref<GradientTexture> ParticlesMaterial::get_trail_color_modifier() const {
- return trail_color_modifier;
-}
-
void ParticlesMaterial::set_gravity(const Vector3 &p_gravity) {
gravity = p_gravity;
Vector3 gset = gravity;
@@ -1038,11 +1024,54 @@ void ParticlesMaterial::_validate_property(PropertyInfo &property) const {
property.usage = 0;
}
+ if (property.name == "sub_emitter_frequency" && sub_emitter_mode != SUB_EMITTER_CONSTANT) {
+ property.usage = 0;
+ }
+
+ if (property.name == "sub_emitter_amount_at_end" && sub_emitter_mode != SUB_EMITTER_AT_END) {
+ property.usage = 0;
+ }
+
if (property.name.begins_with("orbit_") && !flags[FLAG_DISABLE_Z]) {
property.usage = 0;
}
}
+void ParticlesMaterial::set_sub_emitter_mode(SubEmitterMode p_sub_emitter_mode) {
+ sub_emitter_mode = p_sub_emitter_mode;
+ _queue_shader_change();
+ _change_notify();
+}
+
+ParticlesMaterial::SubEmitterMode ParticlesMaterial::get_sub_emitter_mode() const {
+ return sub_emitter_mode;
+}
+
+void ParticlesMaterial::set_sub_emitter_frequency(float p_frequency) {
+ sub_emitter_frequency = p_frequency;
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->sub_emitter_frequency, 1.0 / p_frequency); //pas delta instead of frequency, since its easier to compute
+}
+float ParticlesMaterial::get_sub_emitter_frequency() const {
+ return sub_emitter_frequency;
+}
+
+void ParticlesMaterial::set_sub_emitter_amount_at_end(int p_amount) {
+ sub_emitter_amount_at_end = p_amount;
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->sub_emitter_amount_at_end, p_amount);
+}
+
+int ParticlesMaterial::get_sub_emitter_amount_at_end() const {
+ return sub_emitter_amount_at_end;
+}
+
+void ParticlesMaterial::set_sub_emitter_keep_velocity(bool p_enable) {
+ sub_emitter_keep_velocity = p_enable;
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->sub_emitter_keep_velocity, p_enable);
+}
+bool ParticlesMaterial::get_sub_emitter_keep_velocity() const {
+ return sub_emitter_keep_velocity;
+}
+
Shader::Mode ParticlesMaterial::get_shader_mode() const {
return Shader::MODE_PARTICLES;
}
@@ -1096,27 +1125,27 @@ void ParticlesMaterial::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_emission_point_count", "point_count"), &ParticlesMaterial::set_emission_point_count);
ClassDB::bind_method(D_METHOD("get_emission_point_count"), &ParticlesMaterial::get_emission_point_count);
- ClassDB::bind_method(D_METHOD("set_trail_divisor", "divisor"), &ParticlesMaterial::set_trail_divisor);
- ClassDB::bind_method(D_METHOD("get_trail_divisor"), &ParticlesMaterial::get_trail_divisor);
-
- ClassDB::bind_method(D_METHOD("set_trail_size_modifier", "texture"), &ParticlesMaterial::set_trail_size_modifier);
- ClassDB::bind_method(D_METHOD("get_trail_size_modifier"), &ParticlesMaterial::get_trail_size_modifier);
-
- ClassDB::bind_method(D_METHOD("set_trail_color_modifier", "texture"), &ParticlesMaterial::set_trail_color_modifier);
- ClassDB::bind_method(D_METHOD("get_trail_color_modifier"), &ParticlesMaterial::get_trail_color_modifier);
-
ClassDB::bind_method(D_METHOD("get_gravity"), &ParticlesMaterial::get_gravity);
ClassDB::bind_method(D_METHOD("set_gravity", "accel_vec"), &ParticlesMaterial::set_gravity);
ClassDB::bind_method(D_METHOD("set_lifetime_randomness", "randomness"), &ParticlesMaterial::set_lifetime_randomness);
ClassDB::bind_method(D_METHOD("get_lifetime_randomness"), &ParticlesMaterial::get_lifetime_randomness);
+ ClassDB::bind_method(D_METHOD("get_sub_emitter_mode"), &ParticlesMaterial::get_sub_emitter_mode);
+ ClassDB::bind_method(D_METHOD("set_sub_emitter_mode", "mode"), &ParticlesMaterial::set_sub_emitter_mode);
+
+ ClassDB::bind_method(D_METHOD("get_sub_emitter_frequency"), &ParticlesMaterial::get_sub_emitter_frequency);
+ ClassDB::bind_method(D_METHOD("set_sub_emitter_frequency", "hz"), &ParticlesMaterial::set_sub_emitter_frequency);
+
+ ClassDB::bind_method(D_METHOD("get_sub_emitter_amount_at_end"), &ParticlesMaterial::get_sub_emitter_amount_at_end);
+ ClassDB::bind_method(D_METHOD("set_sub_emitter_amount_at_end", "amount"), &ParticlesMaterial::set_sub_emitter_amount_at_end);
+
+ ClassDB::bind_method(D_METHOD("get_sub_emitter_keep_velocity"), &ParticlesMaterial::get_sub_emitter_keep_velocity);
+ ClassDB::bind_method(D_METHOD("set_sub_emitter_keep_velocity", "enable"), &ParticlesMaterial::set_sub_emitter_keep_velocity);
+
ADD_GROUP("Time", "");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lifetime_randomness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_lifetime_randomness", "get_lifetime_randomness");
- ADD_GROUP("Trail", "trail_");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "trail_divisor", PROPERTY_HINT_RANGE, "1,1000000,1"), "set_trail_divisor", "get_trail_divisor");
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "trail_size_modifier", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_trail_size_modifier", "get_trail_size_modifier");
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "trail_color_modifier", PROPERTY_HINT_RESOURCE_TYPE, "GradientTexture"), "set_trail_color_modifier", "get_trail_color_modifier");
+
ADD_GROUP("Emission Shape", "emission_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "emission_shape", PROPERTY_HINT_ENUM, "Point,Sphere,Box,Points,Directed Points"), "set_emission_shape", "get_emission_shape");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_sphere_radius", PROPERTY_HINT_RANGE, "0.01,128,0.01,or_greater"), "set_emission_sphere_radius", "get_emission_sphere_radius");
@@ -1186,6 +1215,12 @@ void ParticlesMaterial::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_offset_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ANIM_OFFSET);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "anim_offset_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_ANIM_OFFSET);
+ ADD_GROUP("Sub Emitter", "sub_emitter_");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "sub_emitter_mode", PROPERTY_HINT_ENUM, "Disabled,Constant,AtEnd,AtCollision"), "set_sub_emitter_mode", "get_sub_emitter_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sub_emitter_frequency", PROPERTY_HINT_RANGE, "0.01,100,0.01"), "set_sub_emitter_frequency", "get_sub_emitter_frequency");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "sub_emitter_amount_at_end", PROPERTY_HINT_RANGE, "1,32,1"), "set_sub_emitter_amount_at_end", "get_sub_emitter_amount_at_end");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sub_emitter_keep_velocity"), "set_sub_emitter_keep_velocity", "get_sub_emitter_keep_velocity");
+
BIND_ENUM_CONSTANT(PARAM_INITIAL_LINEAR_VELOCITY);
BIND_ENUM_CONSTANT(PARAM_ANGULAR_VELOCITY);
BIND_ENUM_CONSTANT(PARAM_ORBIT_VELOCITY);
@@ -1233,11 +1268,15 @@ ParticlesMaterial::ParticlesMaterial() :
set_emission_shape(EMISSION_SHAPE_POINT);
set_emission_sphere_radius(1);
set_emission_box_extents(Vector3(1, 1, 1));
- set_trail_divisor(1);
set_gravity(Vector3(0, -9.8, 0));
set_lifetime_randomness(0);
emission_point_count = 1;
+ set_sub_emitter_mode(SUB_EMITTER_DISABLED);
+ set_sub_emitter_frequency(4);
+ set_sub_emitter_amount_at_end(1);
+ set_sub_emitter_keep_velocity(false);
+
for (int i = 0; i < PARAM_MAX; i++) {
set_param_randomness(Parameter(i), 0);
}
diff --git a/scene/resources/particles_material.h b/scene/resources/particles_material.h
index 1c1b6c92f9..9d41c91937 100644
--- a/scene/resources/particles_material.h
+++ b/scene/resources/particles_material.h
@@ -34,6 +34,17 @@
#ifndef PARTICLES_MATERIAL_H
#define PARTICLES_MATERIAL_H
+/*
+ TODO:
+-Path following
+*Manual emission
+-Sub Emitters
+-Attractors
+-Emitter positions deformable by bones
+-Collision
+-Proper trails
+*/
+
class ParticlesMaterial : public Material {
GDCLASS(ParticlesMaterial, Material);
@@ -71,6 +82,14 @@ public:
EMISSION_SHAPE_MAX
};
+ enum SubEmitterMode {
+ SUB_EMITTER_DISABLED,
+ SUB_EMITTER_CONSTANT,
+ SUB_EMITTER_AT_END,
+ SUB_EMITTER_AT_COLLISION,
+ SUB_EMITTER_MAX
+ };
+
private:
union MaterialKey {
struct {
@@ -78,10 +97,9 @@ private:
uint32_t texture_color : 1;
uint32_t flags : 4;
uint32_t emission_shape : 2;
- uint32_t trail_size_texture : 1;
- uint32_t trail_color_texture : 1;
uint32_t invalid_key : 1;
uint32_t has_emission_color : 1;
+ uint32_t sub_emitter : 2;
};
uint32_t key;
@@ -116,9 +134,8 @@ private:
mk.texture_color = color_ramp.is_valid() ? 1 : 0;
mk.emission_shape = emission_shape;
- mk.trail_color_texture = trail_color_modifier.is_valid() ? 1 : 0;
- mk.trail_size_texture = trail_size_modifier.is_valid() ? 1 : 0;
mk.has_emission_color = emission_shape >= EMISSION_SHAPE_POINTS && emission_color_texture.is_valid();
+ mk.sub_emitter = sub_emitter_mode;
return mk;
}
@@ -178,13 +195,13 @@ private:
StringName emission_texture_normal;
StringName emission_texture_color;
- StringName trail_divisor;
- StringName trail_size_modifier;
- StringName trail_color_modifier;
-
StringName gravity;
StringName lifetime_randomness;
+
+ StringName sub_emitter_frequency;
+ StringName sub_emitter_amount_at_end;
+ StringName sub_emitter_keep_velocity;
};
static ShaderNames *shader_names;
@@ -218,15 +235,14 @@ private:
bool anim_loop;
- int trail_divisor;
-
- Ref<CurveTexture> trail_size_modifier;
- Ref<GradientTexture> trail_color_modifier;
-
Vector3 gravity;
float lifetime_randomness;
+ SubEmitterMode sub_emitter_mode;
+ float sub_emitter_frequency;
+ int sub_emitter_amount_at_end;
+ bool sub_emitter_keep_velocity;
//do not save emission points here
protected:
@@ -277,15 +293,6 @@ public:
Ref<Texture2D> get_emission_color_texture() const;
int get_emission_point_count() const;
- void set_trail_divisor(int p_divisor);
- int get_trail_divisor() const;
-
- void set_trail_size_modifier(const Ref<CurveTexture> &p_trail_size_modifier);
- Ref<CurveTexture> get_trail_size_modifier() const;
-
- void set_trail_color_modifier(const Ref<GradientTexture> &p_trail_color_modifier);
- Ref<GradientTexture> get_trail_color_modifier() const;
-
void set_gravity(const Vector3 &p_gravity);
Vector3 get_gravity() const;
@@ -296,6 +303,18 @@ public:
static void finish_shaders();
static void flush_changes();
+ void set_sub_emitter_mode(SubEmitterMode p_sub_emitter_mode);
+ SubEmitterMode get_sub_emitter_mode() const;
+
+ void set_sub_emitter_frequency(float p_frequency);
+ float get_sub_emitter_frequency() const;
+
+ void set_sub_emitter_amount_at_end(int p_amount);
+ int get_sub_emitter_amount_at_end() const;
+
+ void set_sub_emitter_keep_velocity(bool p_enable);
+ bool get_sub_emitter_keep_velocity() const;
+
RID get_shader_rid() const;
virtual Shader::Mode get_shader_mode() const override;
@@ -307,5 +326,6 @@ public:
VARIANT_ENUM_CAST(ParticlesMaterial::Parameter)
VARIANT_ENUM_CAST(ParticlesMaterial::Flags)
VARIANT_ENUM_CAST(ParticlesMaterial::EmissionShape)
+VARIANT_ENUM_CAST(ParticlesMaterial::SubEmitterMode)
#endif // PARTICLES_MATERIAL_H
diff --git a/servers/rendering/rasterizer.h b/servers/rendering/rasterizer.h
index 41dcd387de..989c9e2834 100644
--- a/servers/rendering/rasterizer.h
+++ b/servers/rendering/rasterizer.h
@@ -661,6 +661,8 @@ public:
virtual void particles_set_fixed_fps(RID p_particles, int p_fps) = 0;
virtual void particles_set_fractional_delta(RID p_particles, bool p_enable) = 0;
virtual void particles_restart(RID p_particles) = 0;
+ virtual void particles_emit(RID p_particles, const Transform &p_transform, const Vector3 &p_velocity, const Color &p_color, const Color &p_custom, uint32_t p_emit_flags) = 0;
+ virtual void particles_set_subemitter(RID p_particles, RID p_subemitter_particles) = 0;
virtual bool particles_is_inactive(RID p_particles) const = 0;
diff --git a/servers/rendering/rasterizer_rd/rasterizer_storage_rd.cpp b/servers/rendering/rasterizer_rd/rasterizer_storage_rd.cpp
index e09926c97e..d751f474cd 100644
--- a/servers/rendering/rasterizer_rd/rasterizer_storage_rd.cpp
+++ b/servers/rendering/rasterizer_rd/rasterizer_storage_rd.cpp
@@ -3119,24 +3119,40 @@ bool RasterizerStorageRD::particles_get_emitting(RID p_particles) {
return particles->emitting;
}
+void RasterizerStorageRD::_particles_free_data(Particles *particles) {
+ if (!particles->particle_buffer.is_valid()) {
+ return;
+ }
+ RD::get_singleton()->free(particles->particle_buffer);
+ RD::get_singleton()->free(particles->frame_params_buffer);
+ RD::get_singleton()->free(particles->particle_instance_buffer);
+ particles->particles_transforms_buffer_uniform_set = RID();
+ particles->particle_buffer = RID();
+
+ if (particles->particles_sort_buffer.is_valid()) {
+ RD::get_singleton()->free(particles->particles_sort_buffer);
+ particles->particles_sort_buffer = RID();
+ }
+
+ if (particles->emission_buffer != nullptr) {
+ particles->emission_buffer = nullptr;
+ particles->emission_buffer_data.clear();
+ RD::get_singleton()->free(particles->emission_storage_buffer);
+ particles->emission_storage_buffer = RID();
+ }
+}
+
void RasterizerStorageRD::particles_set_amount(RID p_particles, int p_amount) {
Particles *particles = particles_owner.getornull(p_particles);
ERR_FAIL_COND(!particles);
- particles->amount = p_amount;
+ if (particles->amount == p_amount) {
+ return;
+ }
- if (particles->particle_buffer.is_valid()) {
- RD::get_singleton()->free(particles->particle_buffer);
- RD::get_singleton()->free(particles->frame_params_buffer);
- RD::get_singleton()->free(particles->particle_instance_buffer);
- particles->particles_transforms_buffer_uniform_set = RID();
- particles->particle_buffer = RID();
+ _particles_free_data(particles);
- if (particles->particles_sort_buffer.is_valid()) {
- RD::get_singleton()->free(particles->particles_sort_buffer);
- particles->particles_sort_buffer = RID();
- }
- }
+ particles->amount = p_amount;
if (particles->amount > 0) {
particles->particle_buffer = RD::get_singleton()->storage_buffer_create(sizeof(ParticleData) * p_amount);
@@ -3150,27 +3166,6 @@ void RasterizerStorageRD::particles_set_amount(RID p_particles, int p_amount) {
{
RD::Uniform u;
u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
- u.binding = 0;
- u.ids.push_back(particles->frame_params_buffer);
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
- u.binding = 1;
- u.ids.push_back(particles->particle_buffer);
- uniforms.push_back(u);
- }
-
- particles->particles_material_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, particles_shader.default_shader_rd, 1);
- }
-
- {
- Vector<RD::Uniform> uniforms;
-
- {
- RD::Uniform u;
- u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
u.binding = 1;
u.ids.push_back(particles->particle_buffer);
uniforms.push_back(u);
@@ -3290,6 +3285,79 @@ void RasterizerStorageRD::particles_restart(RID p_particles) {
particles->restart_request = true;
}
+void RasterizerStorageRD::_particles_allocate_emission_buffer(Particles *particles) {
+ ERR_FAIL_COND(particles->emission_buffer != nullptr);
+
+ particles->emission_buffer_data.resize(sizeof(ParticleEmissionBuffer::Data) * particles->amount + sizeof(uint32_t) * 4);
+ zeromem(particles->emission_buffer_data.ptrw(), particles->emission_buffer_data.size());
+ particles->emission_buffer = (ParticleEmissionBuffer *)particles->emission_buffer_data.ptrw();
+ particles->emission_buffer->particle_max = particles->amount;
+
+ particles->emission_storage_buffer = RD::get_singleton()->storage_buffer_create(particles->emission_buffer_data.size(), particles->emission_buffer_data);
+
+ if (RD::get_singleton()->uniform_set_is_valid(particles->particles_material_uniform_set)) {
+ //will need to be re-created
+ RD::get_singleton()->free(particles->particles_material_uniform_set);
+ particles->particles_material_uniform_set = RID();
+ }
+}
+
+void RasterizerStorageRD::particles_set_subemitter(RID p_particles, RID p_subemitter_particles) {
+ Particles *particles = particles_owner.getornull(p_particles);
+ ERR_FAIL_COND(!particles);
+ ERR_FAIL_COND(p_particles == p_subemitter_particles);
+
+ particles->sub_emitter = p_subemitter_particles;
+
+ if (RD::get_singleton()->uniform_set_is_valid(particles->particles_material_uniform_set)) {
+ RD::get_singleton()->free(particles->particles_material_uniform_set);
+ particles->particles_material_uniform_set = RID(); //clear and force to re create sub emitting
+ }
+}
+
+void RasterizerStorageRD::particles_emit(RID p_particles, const Transform &p_transform, const Vector3 &p_velocity, const Color &p_color, const Color &p_custom, uint32_t p_emit_flags) {
+ Particles *particles = particles_owner.getornull(p_particles);
+ ERR_FAIL_COND(!particles);
+ ERR_FAIL_COND(particles->amount == 0);
+
+ if (particles->emitting) {
+ particles->clear = true;
+ particles->emitting = false;
+ }
+
+ if (particles->emission_buffer == nullptr) {
+ _particles_allocate_emission_buffer(particles);
+ }
+
+ if (particles->inactive) {
+ //in case it was inactive, make active again
+ particles->inactive = false;
+ particles->inactive_time = 0;
+ }
+
+ int32_t idx = particles->emission_buffer->particle_count;
+ if (idx < particles->emission_buffer->particle_max) {
+ store_transform(p_transform, particles->emission_buffer->data[idx].xform);
+
+ particles->emission_buffer->data[idx].velocity[0] = p_velocity.x;
+ particles->emission_buffer->data[idx].velocity[1] = p_velocity.y;
+ particles->emission_buffer->data[idx].velocity[2] = p_velocity.z;
+
+ particles->emission_buffer->data[idx].custom[0] = p_custom.r;
+ particles->emission_buffer->data[idx].custom[1] = p_custom.g;
+ particles->emission_buffer->data[idx].custom[2] = p_custom.b;
+ particles->emission_buffer->data[idx].custom[3] = p_custom.a;
+
+ particles->emission_buffer->data[idx].color[0] = p_color.r;
+ particles->emission_buffer->data[idx].color[1] = p_color.g;
+ particles->emission_buffer->data[idx].color[2] = p_color.b;
+ particles->emission_buffer->data[idx].color[3] = p_color.a;
+
+ particles->emission_buffer->data[idx].flags = p_emit_flags;
+ particles->emission_buffer->particle_count++;
+ }
+}
+
void RasterizerStorageRD::particles_request_process(RID p_particles) {
Particles *particles = particles_owner.getornull(p_particles);
ERR_FAIL_COND(!particles);
@@ -3375,6 +3443,54 @@ RID RasterizerStorageRD::particles_get_draw_pass_mesh(RID p_particles, int p_pas
}
void RasterizerStorageRD::_particles_process(Particles *p_particles, float p_delta) {
+ if (p_particles->particles_material_uniform_set.is_null() || !RD::get_singleton()->uniform_set_is_valid(p_particles->particles_material_uniform_set)) {
+ Vector<RD::Uniform> uniforms;
+
+ {
+ RD::Uniform u;
+ u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
+ u.binding = 0;
+ u.ids.push_back(p_particles->frame_params_buffer);
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
+ u.binding = 1;
+ u.ids.push_back(p_particles->particle_buffer);
+ uniforms.push_back(u);
+ }
+
+ {
+ RD::Uniform u;
+ u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
+ u.binding = 2;
+ if (p_particles->emission_storage_buffer.is_valid()) {
+ u.ids.push_back(p_particles->emission_storage_buffer);
+ } else {
+ u.ids.push_back(default_rd_storage_buffer);
+ }
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
+ u.binding = 3;
+ Particles *sub_emitter = particles_owner.getornull(p_particles->sub_emitter);
+ if (sub_emitter) {
+ if (sub_emitter->emission_buffer == nullptr) { //no emission buffer, allocate emission buffer
+ _particles_allocate_emission_buffer(sub_emitter);
+ }
+ u.ids.push_back(sub_emitter->emission_storage_buffer);
+ } else {
+ u.ids.push_back(default_rd_storage_buffer);
+ }
+ uniforms.push_back(u);
+ }
+
+ p_particles->particles_material_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, particles_shader.default_shader_rd, 1);
+ }
+
float new_phase = Math::fmod((float)p_particles->phase + (p_delta / p_particles->lifetime) * p_particles->speed_scale, (float)1.0);
ParticlesFrameParams &frame_params = p_particles->frame_params;
@@ -3416,6 +3532,36 @@ void RasterizerStorageRD::_particles_process(Particles *p_particles, float p_del
push_constant.lifetime = p_particles->lifetime;
push_constant.trail_size = 1;
push_constant.use_fractional_delta = p_particles->fractional_delta;
+ push_constant.sub_emitter_mode = !p_particles->emitting && p_particles->emission_buffer && (p_particles->emission_buffer->particle_count > 0 || p_particles->force_sub_emit);
+
+ p_particles->force_sub_emit = false; //reset
+
+ Particles *sub_emitter = particles_owner.getornull(p_particles->sub_emitter);
+
+ if (sub_emitter && sub_emitter->emission_storage_buffer.is_valid()) {
+ // print_line("updating subemitter buffer");
+ int32_t zero[4] = { 0, sub_emitter->amount, 0, 0 };
+ RD::get_singleton()->buffer_update(sub_emitter->emission_storage_buffer, 0, sizeof(uint32_t) * 4, zero, true);
+ push_constant.can_emit = true;
+
+ if (sub_emitter->emitting) {
+ sub_emitter->emitting = false;
+ sub_emitter->clear = true; //will need to clear if it was emitting, sorry
+ }
+ //make sure the sub emitter processes particles too
+ sub_emitter->inactive = false;
+ sub_emitter->inactive_time = 0;
+
+ sub_emitter->force_sub_emit = true;
+
+ } else {
+ push_constant.can_emit = false;
+ }
+
+ if (p_particles->emission_buffer && p_particles->emission_buffer->particle_count) {
+ RD::get_singleton()->buffer_update(p_particles->emission_storage_buffer, 0, sizeof(uint32_t) * 4 + sizeof(ParticleEmissionBuffer::Data) * p_particles->emission_buffer->particle_count, p_particles->emission_buffer, true);
+ p_particles->emission_buffer->particle_count = 0;
+ }
p_particles->clear = false;
@@ -3617,9 +3763,6 @@ void RasterizerStorageRD::update_particles() {
RD::get_singleton()->compute_list_end();
}
- particle_update_list = particles->update_list;
- particles->update_list = nullptr;
-
particles->instance_dependency.instance_notify_changed(true, false); //make sure shadows are updated
}
}
@@ -6519,6 +6662,11 @@ bool RasterizerStorageRD::free(RID p_rid) {
light->instance_dependency.instance_notify_deleted(p_rid);
light_owner.free(p_rid);
+ } else if (particles_owner.owns(p_rid)) {
+ Particles *particles = particles_owner.getornull(p_rid);
+ _particles_free_data(particles);
+ particles->instance_dependency.instance_notify_deleted(p_rid);
+ particles_owner.free(p_rid);
} else if (render_target_owner.owns(p_rid)) {
RenderTarget *rt = render_target_owner.getornull(p_rid);
@@ -7013,6 +7161,17 @@ RasterizerStorageRD::RasterizerStorageRD() {
//actions.renames["GRAVITY"] = "current_gravity";
actions.renames["EMISSION_TRANSFORM"] = "FRAME.emission_transform";
actions.renames["RANDOM_SEED"] = "FRAME.random_seed";
+ actions.renames["FLAG_EMIT_POSITION"] = "EMISSION_FLAG_HAS_POSITION";
+ actions.renames["FLAG_EMIT_ROT_SCALE"] = "EMISSION_FLAG_HAS_ROTATION_SCALE";
+ actions.renames["FLAG_EMIT_VELOCITY"] = "EMISSION_FLAG_HAS_VELOCITY";
+ actions.renames["FLAG_EMIT_COLOR"] = "EMISSION_FLAG_HAS_COLOR";
+ actions.renames["FLAG_EMIT_CUSTOM"] = "EMISSION_FLAG_HAS_CUSTOM";
+ actions.renames["RESTART_POSITION"] = "restart_position";
+ actions.renames["RESTART_ROT_SCALE"] = "restart_rotation_scale";
+ actions.renames["RESTART_VELOCITY"] = "restart_velocity";
+ actions.renames["RESTART_COLOR"] = "restart_color";
+ actions.renames["RESTART_CUSTOM"] = "restart_custom";
+ actions.renames["emit_particle"] = "emit_particle";
actions.render_mode_defines["disable_force"] = "#define DISABLE_FORCE\n";
actions.render_mode_defines["disable_velocity"] = "#define DISABLE_VELOCITY\n";
@@ -7075,6 +7234,8 @@ RasterizerStorageRD::RasterizerStorageRD() {
particles_shader.base_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, particles_shader.default_shader_rd, 0);
}
+ default_rd_storage_buffer = RD::get_singleton()->storage_buffer_create(sizeof(uint32_t) * 4);
+
{
Vector<String> copy_modes;
copy_modes.push_back("\n#define MODE_FILL_INSTANCES\n");
@@ -7115,6 +7276,8 @@ RasterizerStorageRD::~RasterizerStorageRD() {
}
giprobe_sdf_shader.version_free(giprobe_sdf_shader_version);
+ RD::get_singleton()->free(default_rd_storage_buffer);
+
if (decal_atlas.textures.size()) {
ERR_PRINT("Decal Atlas: " + itos(decal_atlas.textures.size()) + " textures were not removed from the atlas.");
}
diff --git a/servers/rendering/rasterizer_rd/rasterizer_storage_rd.h b/servers/rendering/rasterizer_rd/rasterizer_storage_rd.h
index e09b4a52eb..cecae6bbb2 100644
--- a/servers/rendering/rasterizer_rd/rasterizer_storage_rd.h
+++ b/servers/rendering/rasterizer_rd/rasterizer_storage_rd.h
@@ -249,6 +249,7 @@ private:
RID default_rd_textures[DEFAULT_RD_TEXTURE_MAX];
RID default_rd_samplers[RS::CANVAS_ITEM_TEXTURE_FILTER_MAX][RS::CANVAS_ITEM_TEXTURE_REPEAT_MAX];
+ RID default_rd_storage_buffer;
/* DECAL ATLAS */
@@ -482,6 +483,25 @@ private:
float emission_transform[16];
};
+ struct ParticleEmissionBufferData {
+ };
+
+ struct ParticleEmissionBuffer {
+ struct Data {
+ float xform[16];
+ float velocity[3];
+ uint32_t flags;
+ float color[4];
+ float custom[4];
+ };
+
+ int32_t particle_count;
+ int32_t particle_max;
+ uint32_t pad1;
+ uint32_t pad2;
+ Data data[1]; //its 2020 and empty arrays are still non standard in C++
+ };
+
struct Particles {
bool inactive;
float inactive_time;
@@ -515,6 +535,8 @@ private:
bool dirty = false;
Particles *update_list = nullptr;
+ RID sub_emitter;
+
float phase;
float prev_phase;
uint64_t prev_ticks;
@@ -530,8 +552,15 @@ private:
bool clear;
+ bool force_sub_emit = false;
+
Transform emission_transform;
+ Vector<uint8_t> emission_buffer_data;
+
+ ParticleEmissionBuffer *emission_buffer = nullptr;
+ RID emission_storage_buffer;
+
Particles() :
inactive(true),
inactive_time(0.0),
@@ -562,6 +591,8 @@ private:
};
void _particles_process(Particles *p_particles, float p_delta);
+ void _particles_allocate_emission_buffer(Particles *particles);
+ void _particles_free_data(Particles *particles);
struct ParticlesShader {
struct PushConstant {
@@ -569,8 +600,11 @@ private:
uint32_t clear;
uint32_t total_particles;
uint32_t trail_size;
+
uint32_t use_fractional_delta;
- uint32_t pad[3];
+ uint32_t sub_emitter_mode;
+ uint32_t can_emit;
+ uint32_t pad;
};
ParticlesShaderRD shader;
@@ -1650,6 +1684,8 @@ public:
void particles_set_fixed_fps(RID p_particles, int p_fps);
void particles_set_fractional_delta(RID p_particles, bool p_enable);
void particles_restart(RID p_particles);
+ void particles_emit(RID p_particles, const Transform &p_transform, const Vector3 &p_velocity, const Color &p_color, const Color &p_custom, uint32_t p_emit_flags);
+ void particles_set_subemitter(RID p_particles, RID p_subemitter_particles);
void particles_set_draw_order(RID p_particles, RS::ParticlesDrawOrder p_order);
diff --git a/servers/rendering/rasterizer_rd/shaders/particles.glsl b/servers/rendering/rasterizer_rd/shaders/particles.glsl
index 7cdedfcbfe..3de807b57c 100644
--- a/servers/rendering/rasterizer_rd/shaders/particles.glsl
+++ b/servers/rendering/rasterizer_rd/shaders/particles.glsl
@@ -66,6 +66,38 @@ layout(set = 1, binding = 1, std430) restrict buffer Particles {
}
particles;
+#define EMISSION_FLAG_HAS_POSITION 1
+#define EMISSION_FLAG_HAS_ROTATION_SCALE 2
+#define EMISSION_FLAG_HAS_VELOCITY 4
+#define EMISSION_FLAG_HAS_COLOR 8
+#define EMISSION_FLAG_HAS_CUSTOM 16
+
+struct ParticleEmission {
+ mat4 xform;
+ vec3 velocity;
+ uint flags;
+ vec4 color;
+ vec4 custom;
+};
+
+layout(set = 1, binding = 2, std430) restrict volatile coherent buffer SourceEmission {
+ int particle_count;
+ uint pad0;
+ uint pad1;
+ uint pad2;
+ ParticleEmission data[];
+}
+src_particles;
+
+layout(set = 1, binding = 3, std430) restrict volatile coherent buffer DestEmission {
+ int particle_count;
+ int particle_max;
+ uint pad1;
+ uint pad2;
+ ParticleEmission data[];
+}
+dst_particles;
+
/* SET 2: MATERIAL */
#ifdef USE_MATERIAL_UNIFORMS
@@ -82,7 +114,9 @@ layout(push_constant, binding = 0, std430) uniform Params {
uint total_particles;
uint trail_size;
bool use_fractional_delta;
- uint pad[3];
+ bool sub_emitter_mode;
+ bool can_emit;
+ uint pad;
}
params;
@@ -93,6 +127,51 @@ uint hash(uint x) {
return x;
}
+bool emit_particle(mat4 p_xform, vec3 p_velocity, vec4 p_color, vec4 p_custom, uint p_flags) {
+ if (!params.can_emit) {
+ return false;
+ }
+
+ bool valid = false;
+
+ int dst_index = atomicAdd(dst_particles.particle_count, 1);
+
+ if (dst_index >= dst_particles.particle_max) {
+ atomicAdd(dst_particles.particle_count, -1);
+ return false;
+ }
+ /*
+ valid = true;
+
+ int attempts = 256; // never trust compute
+ while(attempts-- > 0) {
+ dst_index = dst_particles.particle_count;
+ if (dst_index == dst_particles.particle_max) {
+ return false; //cant emit anymore
+ }
+
+ if (atomicCompSwap(dst_particles.particle_count, dst_index, dst_index +1 ) != dst_index) {
+ continue;
+ }
+ valid=true;
+ break;
+ }
+
+ barrier();
+
+ if (!valid) {
+ return false; //gave up (attempts exhausted)
+ }
+*/
+ dst_particles.data[dst_index].xform = p_xform;
+ dst_particles.data[dst_index].velocity = p_velocity;
+ dst_particles.data[dst_index].color = p_color;
+ dst_particles.data[dst_index].custom = p_custom;
+ dst_particles.data[dst_index].flags = p_flags;
+
+ return true;
+}
+
/* clang-format off */
COMPUTE_SHADER_GLOBALS
@@ -118,76 +197,129 @@ void main() {
float mass = 1.0;
- float restart_phase = float(index) / float(params.total_particles);
+ bool restart = false;
- if (FRAME.randomness > 0.0) {
- uint seed = FRAME.cycle;
- if (restart_phase >= FRAME.system_phase) {
- seed -= uint(1);
- }
- seed *= uint(params.total_particles);
- seed += uint(index);
- float random = float(hash(seed) % uint(65536)) / 65536.0;
- restart_phase += FRAME.randomness * random * 1.0 / float(params.total_particles);
+ bool restart_position = false;
+ bool restart_rotation_scale = false;
+ bool restart_velocity = false;
+ bool restart_color = false;
+ bool restart_custom = false;
+
+ if (params.clear) {
+ PARTICLE.color = vec4(1.0);
+ PARTICLE.custom = vec4(0.0);
+ PARTICLE.velocity = vec3(0.0);
+ PARTICLE.is_active = false;
+ PARTICLE.xform = 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));
}
- restart_phase *= (1.0 - FRAME.explosiveness);
+ if (params.sub_emitter_mode) {
+ if (!PARTICLE.is_active) {
+ int src_index = atomicAdd(src_particles.particle_count, -1) - 1;
- bool restart = false;
+ if (src_index >= 0) {
+ PARTICLE.is_active = true;
+ restart = true;
- if (FRAME.system_phase > FRAME.prev_system_phase) {
- // restart_phase >= prev_system_phase is used so particles emit in the first frame they are processed
+ if (bool(src_particles.data[src_index].flags & EMISSION_FLAG_HAS_POSITION)) {
+ PARTICLE.xform[3] = src_particles.data[src_index].xform[3];
+ } else {
+ PARTICLE.xform[3] = vec4(0, 0, 0, 1);
+ restart_position = true;
+ }
+ if (bool(src_particles.data[src_index].flags & EMISSION_FLAG_HAS_ROTATION_SCALE)) {
+ PARTICLE.xform[0] = src_particles.data[src_index].xform[0];
+ PARTICLE.xform[1] = src_particles.data[src_index].xform[1];
+ PARTICLE.xform[2] = src_particles.data[src_index].xform[2];
+ } else {
+ PARTICLE.xform[0] = vec4(1, 0, 0, 0);
+ PARTICLE.xform[1] = vec4(0, 1, 0, 0);
+ PARTICLE.xform[2] = vec4(0, 0, 1, 0);
+ restart_rotation_scale = true;
+ }
+ if (bool(src_particles.data[src_index].flags & EMISSION_FLAG_HAS_VELOCITY)) {
+ PARTICLE.velocity = src_particles.data[src_index].velocity;
+ } else {
+ PARTICLE.velocity = vec3(0);
+ restart_velocity = true;
+ }
+ if (bool(src_particles.data[src_index].flags & EMISSION_FLAG_HAS_COLOR)) {
+ PARTICLE.color = src_particles.data[src_index].color;
+ } else {
+ PARTICLE.color = vec4(1);
+ restart_color = true;
+ }
- if (restart_phase >= FRAME.prev_system_phase && restart_phase < FRAME.system_phase) {
- restart = true;
- if (params.use_fractional_delta) {
- local_delta = (FRAME.system_phase - restart_phase) * params.lifetime;
+ if (bool(src_particles.data[src_index].flags & EMISSION_FLAG_HAS_CUSTOM)) {
+ PARTICLE.custom = src_particles.data[src_index].custom;
+ } else {
+ PARTICLE.custom = vec4(0);
+ restart_custom = true;
+ }
}
}
- } else if (FRAME.delta > 0.0) {
- if (restart_phase >= FRAME.prev_system_phase) {
- restart = true;
- if (params.use_fractional_delta) {
- local_delta = (1.0 - restart_phase + FRAME.system_phase) * params.lifetime;
- }
+ } else if (FRAME.emitting) {
+ float restart_phase = float(index) / float(params.total_particles);
- } else if (restart_phase < FRAME.system_phase) {
- restart = true;
- if (params.use_fractional_delta) {
- local_delta = (FRAME.system_phase - restart_phase) * params.lifetime;
+ if (FRAME.randomness > 0.0) {
+ uint seed = FRAME.cycle;
+ if (restart_phase >= FRAME.system_phase) {
+ seed -= uint(1);
}
+ seed *= uint(params.total_particles);
+ seed += uint(index);
+ float random = float(hash(seed) % uint(65536)) / 65536.0;
+ restart_phase += FRAME.randomness * random * 1.0 / float(params.total_particles);
}
- }
- uint current_cycle = FRAME.cycle;
+ restart_phase *= (1.0 - FRAME.explosiveness);
- if (FRAME.system_phase < restart_phase) {
- current_cycle -= uint(1);
- }
+ if (FRAME.system_phase > FRAME.prev_system_phase) {
+ // restart_phase >= prev_system_phase is used so particles emit in the first frame they are processed
- uint particle_number = current_cycle * uint(params.total_particles) + particle;
+ if (restart_phase >= FRAME.prev_system_phase && restart_phase < FRAME.system_phase) {
+ restart = true;
+ if (params.use_fractional_delta) {
+ local_delta = (FRAME.system_phase - restart_phase) * params.lifetime;
+ }
+ }
- if (restart) {
- PARTICLE.is_active = FRAME.emitting;
- }
+ } else if (FRAME.delta > 0.0) {
+ if (restart_phase >= FRAME.prev_system_phase) {
+ restart = true;
+ if (params.use_fractional_delta) {
+ local_delta = (1.0 - restart_phase + FRAME.system_phase) * params.lifetime;
+ }
-#ifdef ENABLE_KEEP_DATA
- if (params.clear) {
-#else
- if (params.clear || restart) {
-#endif
- PARTICLE.color = vec4(1.0);
- PARTICLE.custom = vec4(0.0);
- PARTICLE.velocity = vec3(0.0);
- if (!restart) {
- PARTICLE.is_active = false;
+ } else if (restart_phase < FRAME.system_phase) {
+ restart = true;
+ if (params.use_fractional_delta) {
+ local_delta = (FRAME.system_phase - restart_phase) * params.lifetime;
+ }
+ }
+ }
+
+ uint current_cycle = FRAME.cycle;
+
+ if (FRAME.system_phase < restart_phase) {
+ current_cycle -= uint(1);
+ }
+
+ uint particle_number = current_cycle * uint(params.total_particles) + particle;
+
+ if (restart) {
+ PARTICLE.is_active = FRAME.emitting;
+ restart_position = true;
+ restart_rotation_scale = true;
+ restart_velocity = true;
+ restart_color = true;
+ restart_custom = true;
}
- PARTICLE.xform = 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));
}
if (PARTICLE.is_active) {
diff --git a/servers/rendering/rendering_server_raster.h b/servers/rendering/rendering_server_raster.h
index 547595f9b0..ceefcfa1fc 100644
--- a/servers/rendering/rendering_server_raster.h
+++ b/servers/rendering/rendering_server_raster.h
@@ -442,6 +442,8 @@ public:
BIND1R(bool, particles_is_inactive, RID)
BIND1(particles_request_process, RID)
BIND1(particles_restart, RID)
+ BIND6(particles_emit, RID, const Transform &, const Vector3 &, const Color &, const Color &, uint32_t)
+ BIND2(particles_set_subemitter, RID, RID)
BIND2(particles_set_draw_order, RID, RS::ParticlesDrawOrder)
diff --git a/servers/rendering/rendering_server_wrap_mt.h b/servers/rendering/rendering_server_wrap_mt.h
index 40aff46a3c..a8a56e7d56 100644
--- a/servers/rendering/rendering_server_wrap_mt.h
+++ b/servers/rendering/rendering_server_wrap_mt.h
@@ -360,11 +360,14 @@ public:
FUNC1(particles_request_process, RID)
FUNC1(particles_restart, RID)
+ FUNC6(particles_emit, RID, const Transform &, const Vector3 &, const Color &, const Color &, uint32_t)
+
FUNC2(particles_set_draw_order, RID, RS::ParticlesDrawOrder)
FUNC2(particles_set_draw_passes, RID, int)
FUNC3(particles_set_draw_pass_mesh, RID, int, RID)
FUNC2(particles_set_emission_transform, RID, const Transform &)
+ FUNC2(particles_set_subemitter, RID, RID)
FUNC1R(AABB, particles_get_current_aabb, RID)
diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp
index e9b1cb366d..28c41fb2dc 100644
--- a/servers/rendering/shader_language.cpp
+++ b/servers/rendering/shader_language.cpp
@@ -920,13 +920,13 @@ void ShaderLanguage::clear() {
}
}
-bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_reassign, const Map<StringName, BuiltInInfo> &p_builtin_types, const StringName &p_identifier, DataType *r_data_type, IdentifierType *r_type, bool *r_is_const, int *r_array_size, StringName *r_struct_name) {
- if (p_builtin_types.has(p_identifier)) {
+bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_reassign, const FunctionInfo &p_function_info, const StringName &p_identifier, DataType *r_data_type, IdentifierType *r_type, bool *r_is_const, int *r_array_size, StringName *r_struct_name) {
+ if (p_function_info.built_ins.has(p_identifier)) {
if (r_data_type) {
- *r_data_type = p_builtin_types[p_identifier].type;
+ *r_data_type = p_function_info.built_ins[p_identifier].type;
}
if (r_is_const) {
- *r_is_const = p_builtin_types[p_identifier].constant;
+ *r_is_const = p_function_info.built_ins[p_identifier].constant;
}
if (r_type) {
*r_type = IDENTIFIER_BUILTIN_VAR;
@@ -935,6 +935,20 @@ bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_rea
return true;
}
+ if (p_function_info.stage_functions.has(p_identifier)) {
+ if (r_data_type) {
+ *r_data_type = p_function_info.stage_functions[p_identifier].return_type;
+ }
+ if (r_is_const) {
+ *r_is_const = true;
+ }
+ if (r_type) {
+ *r_type = IDENTIFIER_FUNCTION;
+ }
+
+ return true;
+ }
+
FunctionNode *function = nullptr;
while (p_block) {
@@ -2152,7 +2166,7 @@ const ShaderLanguage::BuiltinFuncOutArgs ShaderLanguage::builtin_func_out_args[]
{ nullptr, 0 }
};
-bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const Map<StringName, BuiltInInfo> &p_builtin_types, OperatorNode *p_func, DataType *r_ret_type, StringName *r_ret_type_str) {
+bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionInfo &p_function_info, OperatorNode *p_func, DataType *r_ret_type, StringName *r_ret_type_str) {
ERR_FAIL_COND_V(p_func->op != OP_CALL && p_func->op != OP_CONSTRUCT, false);
Vector<DataType> args;
@@ -2169,6 +2183,30 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const Map<Strin
int argcount = args.size();
+ if (p_function_info.stage_functions.has(name)) {
+ //stage based function
+ const StageFunctionInfo &sf = p_function_info.stage_functions[name];
+ if (argcount != sf.arguments.size()) {
+ _set_error(vformat("Invalid number of arguments when calling stage function '%s', which expects %d arguments.", String(name), sf.arguments.size()));
+ return false;
+ }
+ //validate arguments
+ for (int i = 0; i < argcount; i++) {
+ if (args[i] != sf.arguments[i].type) {
+ _set_error(vformat("Invalid argument type when calling stage function '%s', type expected is '%s'.", String(name), String(get_datatype_name(sf.arguments[i].type))));
+ return false;
+ }
+ }
+
+ if (r_ret_type) {
+ *r_ret_type = sf.return_type;
+ }
+ if (r_ret_type_str) {
+ *r_ret_type_str = "";
+ }
+ return true;
+ }
+
bool failed_builtin = false;
bool unsupported_builtin = false;
int builtin_idx = 0;
@@ -2241,8 +2279,8 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const Map<Strin
if (shader->uniforms.has(varname)) {
fail = true;
} else {
- if (p_builtin_types.has(varname)) {
- BuiltInInfo info = p_builtin_types[varname];
+ if (p_function_info.built_ins.has(varname)) {
+ BuiltInInfo info = p_function_info.built_ins[varname];
if (info.constant) {
fail = true;
}
@@ -2278,7 +2316,7 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const Map<Strin
const BlockNode *b = p_block;
bool valid = false;
while (b) {
- if (b->variables.has(var_name) || p_builtin_types.has(var_name)) {
+ if (b->variables.has(var_name) || p_function_info.built_ins.has(var_name)) {
valid = true;
break;
}
@@ -2492,7 +2530,7 @@ bool ShaderLanguage::_compare_datatypes_in_nodes(Node *a, Node *b) const {
return true;
}
-bool ShaderLanguage::_parse_function_arguments(BlockNode *p_block, const Map<StringName, BuiltInInfo> &p_builtin_types, OperatorNode *p_func, int *r_complete_arg) {
+bool ShaderLanguage::_parse_function_arguments(BlockNode *p_block, const FunctionInfo &p_function_info, OperatorNode *p_func, int *r_complete_arg) {
TkPos pos = _get_tkpos();
Token tk = _get_token();
@@ -2514,7 +2552,7 @@ bool ShaderLanguage::_parse_function_arguments(BlockNode *p_block, const Map<Str
}
}
- Node *arg = _parse_and_reduce_expression(p_block, p_builtin_types);
+ Node *arg = _parse_and_reduce_expression(p_block, p_function_info);
if (!arg) {
return false;
@@ -3053,16 +3091,16 @@ bool ShaderLanguage::_is_operator_assign(Operator p_op) const {
return false;
}
-bool ShaderLanguage::_validate_assign(Node *p_node, const Map<StringName, BuiltInInfo> &p_builtin_types, String *r_message) {
+bool ShaderLanguage::_validate_assign(Node *p_node, const FunctionInfo &p_function_info, String *r_message) {
if (p_node->type == Node::TYPE_OPERATOR) {
OperatorNode *op = static_cast<OperatorNode *>(p_node);
if (op->op == OP_INDEX) {
- return _validate_assign(op->arguments[0], p_builtin_types, r_message);
+ return _validate_assign(op->arguments[0], p_function_info, r_message);
} else if (_is_operator_assign(op->op)) {
//chained assignment
- return _validate_assign(op->arguments[1], p_builtin_types, r_message);
+ return _validate_assign(op->arguments[1], p_function_info, r_message);
} else if (op->op == OP_CALL) {
if (r_message) {
@@ -3081,7 +3119,7 @@ bool ShaderLanguage::_validate_assign(Node *p_node, const Map<StringName, BuiltI
return false;
}
- return _validate_assign(member->owner, p_builtin_types, r_message);
+ return _validate_assign(member->owner, p_function_info, r_message);
} else if (p_node->type == Node::TYPE_VARIABLE) {
VariableNode *var = static_cast<VariableNode *>(p_node);
@@ -3107,7 +3145,7 @@ bool ShaderLanguage::_validate_assign(Node *p_node, const Map<StringName, BuiltI
return false;
}
- if (!(p_builtin_types.has(var->name) && p_builtin_types[var->name].constant)) {
+ if (!(p_function_info.built_ins.has(var->name) && p_function_info.built_ins[var->name].constant)) {
return true;
}
} else if (p_node->type == Node::TYPE_ARRAY) {
@@ -3204,7 +3242,7 @@ bool ShaderLanguage::_propagate_function_call_sampler_builtin_reference(StringNa
ERR_FAIL_V(false); //bug? function not found
}
-ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, const Map<StringName, BuiltInInfo> &p_builtin_types) {
+ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, const FunctionInfo &p_function_info) {
Vector<Expression> expression;
//Vector<TokenType> operators;
@@ -3220,7 +3258,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
if (tk.type == TK_PARENTHESIS_OPEN) {
//handle subexpression
- expr = _parse_and_reduce_expression(p_block, p_builtin_types);
+ expr = _parse_and_reduce_expression(p_block, p_function_info);
if (!expr) {
return nullptr;
}
@@ -3293,7 +3331,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
int carg = -1;
- bool ok = _parse_function_arguments(p_block, p_builtin_types, func, &carg);
+ bool ok = _parse_function_arguments(p_block, p_function_info, func, &carg);
if (carg >= 0) {
completion_type = COMPLETION_CALL_ARGUMENTS;
@@ -3307,7 +3345,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
return nullptr;
}
- if (!_validate_function_call(p_block, p_builtin_types, func, &func->return_cache, &func->struct_name)) {
+ if (!_validate_function_call(p_block, p_function_info, func, &func->return_cache, &func->struct_name)) {
_set_error("No matching constructor found for: '" + String(funcname->name) + "'");
return nullptr;
}
@@ -3383,7 +3421,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
} else {
_set_tkpos(pos2);
- Node *n = _parse_and_reduce_expression(p_block, p_builtin_types);
+ Node *n = _parse_and_reduce_expression(p_block, p_function_info);
if (!n || n->type != Node::TYPE_CONSTANT || n->get_datatype() != TYPE_INT) {
_set_error("Expected single integer constant > 0");
return nullptr;
@@ -3444,7 +3482,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
if (tk.type == TK_PARENTHESIS_OPEN || auto_size) { // initialization
while (true) {
- Node *n = _parse_and_reduce_expression(p_block, p_builtin_types);
+ Node *n = _parse_and_reduce_expression(p_block, p_function_info);
if (!n) {
return nullptr;
}
@@ -3484,7 +3522,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
nexpr = an;
} else {
- nexpr = _parse_and_reduce_expression(p_block, p_builtin_types);
+ nexpr = _parse_and_reduce_expression(p_block, p_function_info);
if (!nexpr) {
return nullptr;
}
@@ -3526,7 +3564,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
int carg = -1;
- bool ok = _parse_function_arguments(p_block, p_builtin_types, func, &carg);
+ bool ok = _parse_function_arguments(p_block, p_function_info, func, &carg);
// Check if block has a variable with the same name as function to prevent shader crash.
ShaderLanguage::BlockNode *bnode = p_block;
@@ -3568,7 +3606,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
return nullptr;
}
- if (!_validate_function_call(p_block, p_builtin_types, func, &func->return_cache, &func->struct_name)) {
+ if (!_validate_function_call(p_block, p_function_info, func, &func->return_cache, &func->struct_name)) {
_set_error("No matching function found for: '" + String(funcname->name) + "'");
return nullptr;
}
@@ -3620,8 +3658,8 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
} else if (shader->uniforms.has(varname)) {
error = true;
} else {
- if (p_builtin_types.has(varname)) {
- BuiltInInfo info = p_builtin_types[varname];
+ if (p_function_info.built_ins.has(varname)) {
+ BuiltInInfo info = p_function_info.built_ins[varname];
if (info.constant) {
error = true;
}
@@ -3653,7 +3691,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
if (!_propagate_function_call_sampler_uniform_settings(name, i, u->filter, u->repeat)) {
return nullptr;
}
- } else if (p_builtin_types.has(varname)) {
+ } else if (p_function_info.built_ins.has(varname)) {
//a built-in
if (!_propagate_function_call_sampler_builtin_reference(name, i, varname)) {
return nullptr;
@@ -3708,7 +3746,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
return nullptr;
}
} else {
- if (!_find_identifier(p_block, false, p_builtin_types, identifier, &data_type, &ident_type, &is_const, &array_size, &struct_name)) {
+ if (!_find_identifier(p_block, false, p_function_info, identifier, &data_type, &ident_type, &is_const, &array_size, &struct_name)) {
_set_error("Unknown identifier in expression: " + String(identifier));
return nullptr;
}
@@ -3733,7 +3771,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
if (tk.type == TK_PERIOD) {
completion_class = TAG_ARRAY;
p_block->block_tag = SubClassTag::TAG_ARRAY;
- call_expression = _parse_and_reduce_expression(p_block, p_builtin_types);
+ call_expression = _parse_and_reduce_expression(p_block, p_function_info);
p_block->block_tag = SubClassTag::TAG_GLOBAL;
if (!call_expression) {
return nullptr;
@@ -3741,7 +3779,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
data_type = call_expression->get_datatype();
} else { // indexing
- index_expression = _parse_and_reduce_expression(p_block, p_builtin_types);
+ index_expression = _parse_and_reduce_expression(p_block, p_function_info);
if (!index_expression) {
return nullptr;
}
@@ -4121,7 +4159,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
_set_error("Nested array length() is not yet implemented");
return nullptr;
} else if (tk.type == TK_BRACKET_OPEN) {
- Node *index_expression = _parse_and_reduce_expression(p_block, p_builtin_types);
+ Node *index_expression = _parse_and_reduce_expression(p_block, p_function_info);
if (!index_expression) {
return nullptr;
}
@@ -4170,7 +4208,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
*/
} else if (tk.type == TK_BRACKET_OPEN) {
- Node *index = _parse_and_reduce_expression(p_block, p_builtin_types);
+ Node *index = _parse_and_reduce_expression(p_block, p_function_info);
if (!index) {
return nullptr;
}
@@ -4312,7 +4350,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
return nullptr;
}
- if (!_validate_assign(expr, p_builtin_types)) {
+ if (!_validate_assign(expr, p_function_info)) {
_set_error("Invalid use of increment/decrement operator in constant expression.");
return nullptr;
}
@@ -4610,7 +4648,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
for (int i = expr_pos - 1; i >= next_op; i--) {
OperatorNode *op = alloc_node<OperatorNode>();
op->op = expression[i].op;
- if ((op->op == OP_INCREMENT || op->op == OP_DECREMENT) && !_validate_assign(expression[i + 1].node, p_builtin_types)) {
+ if ((op->op == OP_INCREMENT || op->op == OP_DECREMENT) && !_validate_assign(expression[i + 1].node, p_function_info)) {
_set_error("Can't use increment/decrement operator in constant expression.");
return nullptr;
}
@@ -4684,7 +4722,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
if (_is_operator_assign(op->op)) {
String assign_message;
- if (!_validate_assign(expression[next_op - 1].node, p_builtin_types, &assign_message)) {
+ if (!_validate_assign(expression[next_op - 1].node, p_function_info, &assign_message)) {
_set_error(assign_message);
return nullptr;
}
@@ -4838,8 +4876,8 @@ ShaderLanguage::Node *ShaderLanguage::_reduce_expression(BlockNode *p_block, Sha
return p_node;
}
-ShaderLanguage::Node *ShaderLanguage::_parse_and_reduce_expression(BlockNode *p_block, const Map<StringName, BuiltInInfo> &p_builtin_types) {
- ShaderLanguage::Node *expr = _parse_expression(p_block, p_builtin_types);
+ShaderLanguage::Node *ShaderLanguage::_parse_and_reduce_expression(BlockNode *p_block, const FunctionInfo &p_function_info) {
+ ShaderLanguage::Node *expr = _parse_expression(p_block, p_function_info);
if (!expr) { //errored
return nullptr;
}
@@ -4849,7 +4887,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_and_reduce_expression(BlockNode *p_
return expr;
}
-Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, BuiltInInfo> &p_builtin_types, bool p_just_one, bool p_can_break, bool p_can_continue) {
+Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_function_info, bool p_just_one, bool p_can_break, bool p_can_continue) {
while (true) {
TkPos pos = _get_tkpos();
@@ -4933,7 +4971,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui
StringName name = tk.text;
ShaderLanguage::IdentifierType itype;
- if (_find_identifier(p_block, true, p_builtin_types, name, (ShaderLanguage::DataType *)nullptr, &itype)) {
+ if (_find_identifier(p_block, true, p_function_info, name, (ShaderLanguage::DataType *)nullptr, &itype)) {
if (itype != IDENTIFIER_FUNCTION) {
_set_error("Redefinition of '" + String(name) + "'");
return ERR_PARSE_ERROR;
@@ -5052,7 +5090,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui
} else {
_set_tkpos(pos2);
- Node *n = _parse_and_reduce_expression(p_block, p_builtin_types);
+ Node *n = _parse_and_reduce_expression(p_block, p_function_info);
if (!n || n->type != Node::TYPE_CONSTANT || n->get_datatype() != TYPE_INT) {
_set_error("Expected single integer constant > 0");
return ERR_PARSE_ERROR;
@@ -5133,7 +5171,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui
if (tk.type == TK_PARENTHESIS_OPEN || curly) { // initialization
while (true) {
- Node *n = _parse_and_reduce_expression(p_block, p_builtin_types);
+ Node *n = _parse_and_reduce_expression(p_block, p_function_info);
if (!n) {
return ERR_PARSE_ERROR;
}
@@ -5205,7 +5243,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui
decl.initializer = nullptr;
//variable created with assignment! must parse an expression
- Node *n = _parse_and_reduce_expression(p_block, p_builtin_types);
+ Node *n = _parse_and_reduce_expression(p_block, p_function_info);
if (!n) {
return ERR_PARSE_ERROR;
}
@@ -5265,7 +5303,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui
//a sub block, just because..
BlockNode *block = alloc_node<BlockNode>();
block->parent_block = p_block;
- if (_parse_block(block, p_builtin_types, false, p_can_break, p_can_continue) != OK) {
+ if (_parse_block(block, p_function_info, false, p_can_break, p_can_continue) != OK) {
return ERR_PARSE_ERROR;
}
p_block->statements.push_back(block);
@@ -5279,7 +5317,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui
ControlFlowNode *cf = alloc_node<ControlFlowNode>();
cf->flow_op = FLOW_OP_IF;
- Node *n = _parse_and_reduce_expression(p_block, p_builtin_types);
+ Node *n = _parse_and_reduce_expression(p_block, p_function_info);
if (!n) {
return ERR_PARSE_ERROR;
}
@@ -5301,7 +5339,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui
cf->blocks.push_back(block);
p_block->statements.push_back(cf);
- Error err = _parse_block(block, p_builtin_types, true, p_can_break, p_can_continue);
+ Error err = _parse_block(block, p_function_info, true, p_can_break, p_can_continue);
if (err) {
return err;
}
@@ -5312,7 +5350,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui
block = alloc_node<BlockNode>();
block->parent_block = p_block;
cf->blocks.push_back(block);
- err = _parse_block(block, p_builtin_types, true, p_can_break, p_can_continue);
+ err = _parse_block(block, p_function_info, true, p_can_break, p_can_continue);
} else {
_set_tkpos(pos); //rollback
@@ -5331,7 +5369,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui
}
ControlFlowNode *cf = alloc_node<ControlFlowNode>();
cf->flow_op = FLOW_OP_SWITCH;
- Node *n = _parse_and_reduce_expression(p_block, p_builtin_types);
+ Node *n = _parse_and_reduce_expression(p_block, p_function_info);
if (!n) {
return ERR_PARSE_ERROR;
}
@@ -5359,7 +5397,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui
int prev_type = TK_CF_CASE;
while (true) { // Go-through multiple cases.
- if (_parse_block(switch_block, p_builtin_types, true, true, false) != OK) {
+ if (_parse_block(switch_block, p_function_info, true, true, false) != OK) {
return ERR_PARSE_ERROR;
}
pos = _get_tkpos();
@@ -5460,7 +5498,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui
cf->blocks.push_back(case_block);
p_block->statements.push_back(cf);
- Error err = _parse_block(case_block, p_builtin_types, false, true, false);
+ Error err = _parse_block(case_block, p_function_info, false, true, false);
if (err) {
return err;
}
@@ -5494,7 +5532,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui
cf->blocks.push_back(default_block);
p_block->statements.push_back(cf);
- Error err = _parse_block(default_block, p_builtin_types, false, true, false);
+ Error err = _parse_block(default_block, p_function_info, false, true, false);
if (err) {
return err;
}
@@ -5511,7 +5549,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui
do_block = alloc_node<BlockNode>();
do_block->parent_block = p_block;
- Error err = _parse_block(do_block, p_builtin_types, true, true, true);
+ Error err = _parse_block(do_block, p_function_info, true, true, true);
if (err) {
return err;
}
@@ -5535,7 +5573,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui
} else {
cf->flow_op = FLOW_OP_WHILE;
}
- Node *n = _parse_and_reduce_expression(p_block, p_builtin_types);
+ Node *n = _parse_and_reduce_expression(p_block, p_function_info);
if (!n) {
return ERR_PARSE_ERROR;
}
@@ -5552,7 +5590,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui
cf->blocks.push_back(block);
p_block->statements.push_back(cf);
- Error err = _parse_block(block, p_builtin_types, true, true, true);
+ Error err = _parse_block(block, p_function_info, true, true, true);
if (err) {
return err;
}
@@ -5583,11 +5621,11 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui
init_block->parent_block = p_block;
init_block->single_statement = true;
cf->blocks.push_back(init_block);
- if (_parse_block(init_block, p_builtin_types, true, false, false) != OK) {
+ if (_parse_block(init_block, p_function_info, true, false, false) != OK) {
return ERR_PARSE_ERROR;
}
- Node *n = _parse_and_reduce_expression(init_block, p_builtin_types);
+ Node *n = _parse_and_reduce_expression(init_block, p_function_info);
if (!n) {
return ERR_PARSE_ERROR;
}
@@ -5605,7 +5643,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui
cf->expressions.push_back(n);
- n = _parse_and_reduce_expression(init_block, p_builtin_types);
+ n = _parse_and_reduce_expression(init_block, p_function_info);
if (!n) {
return ERR_PARSE_ERROR;
}
@@ -5623,7 +5661,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui
cf->blocks.push_back(block);
p_block->statements.push_back(cf);
- Error err = _parse_block(block, p_builtin_types, true, true, true);
+ Error err = _parse_block(block, p_function_info, true, true, true);
if (err) {
return err;
}
@@ -5659,7 +5697,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui
}
} else {
_set_tkpos(pos); //rollback, wants expression
- Node *expr = _parse_and_reduce_expression(p_block, p_builtin_types);
+ Node *expr = _parse_and_reduce_expression(p_block, p_function_info);
if (!expr) {
return ERR_PARSE_ERROR;
}
@@ -5761,7 +5799,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui
} else {
//nothing else, so expression
_set_tkpos(pos); //rollback
- Node *expr = _parse_and_reduce_expression(p_block, p_builtin_types);
+ Node *expr = _parse_and_reduce_expression(p_block, p_function_info);
if (!expr) {
return ERR_PARSE_ERROR;
}
@@ -6102,7 +6140,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
name = tk.text;
- if (_find_identifier(nullptr, false, Map<StringName, BuiltInInfo>(), name)) {
+ if (_find_identifier(nullptr, false, FunctionInfo(), name)) {
_set_error("Redefinition of '" + String(name) + "'");
return ERR_PARSE_ERROR;
}
@@ -6351,7 +6389,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
//reset scope for next uniform
if (tk.type == TK_OP_ASSIGN) {
- Node *expr = _parse_and_reduce_expression(nullptr, Map<StringName, BuiltInInfo>());
+ Node *expr = _parse_and_reduce_expression(nullptr, FunctionInfo());
if (!expr) {
return ERR_PARSE_ERROR;
}
@@ -6476,7 +6514,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
return ERR_PARSE_ERROR;
}
- if (_find_identifier(nullptr, false, Map<StringName, BuiltInInfo>(), name)) {
+ if (_find_identifier(nullptr, false, FunctionInfo(), name)) {
_set_error("Redefinition of '" + String(name) + "'");
return ERR_PARSE_ERROR;
}
@@ -6589,7 +6627,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
} else {
_set_tkpos(pos2);
- Node *n = _parse_and_reduce_expression(NULL, Map<StringName, BuiltInInfo>());
+ Node *n = _parse_and_reduce_expression(NULL, FunctionInfo());
if (!n || n->type != Node::TYPE_CONSTANT || n->get_datatype() != TYPE_INT) {
_set_error("Expected single integer constant > 0");
return ERR_PARSE_ERROR;
@@ -6670,7 +6708,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
if (tk.type == TK_PARENTHESIS_OPEN || curly) { // initialization
while (true) {
- Node *n = _parse_and_reduce_expression(NULL, Map<StringName, BuiltInInfo>());
+ Node *n = _parse_and_reduce_expression(NULL, FunctionInfo());
if (!n) {
return ERR_PARSE_ERROR;
}
@@ -6725,7 +6763,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
constant.initializer = static_cast<ConstantNode *>(expr);
} else {
//variable created with assignment! must parse an expression
- Node *expr = _parse_and_reduce_expression(NULL, Map<StringName, BuiltInInfo>());
+ Node *expr = _parse_and_reduce_expression(NULL, FunctionInfo());
if (!expr)
return ERR_PARSE_ERROR;
if (expr->type == Node::TYPE_OPERATOR && ((OperatorNode *)expr)->op == OP_CALL) {
@@ -6762,7 +6800,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
}
name = tk.text;
- if (_find_identifier(nullptr, false, Map<StringName, BuiltInInfo>(), name)) {
+ if (_find_identifier(nullptr, false, FunctionInfo(), name)) {
_set_error("Redefinition of '" + String(name) + "'");
return ERR_PARSE_ERROR;
}
@@ -6785,14 +6823,14 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
break;
}
- Map<StringName, BuiltInInfo> builtin_types;
+ FunctionInfo builtins;
if (p_functions.has(name)) {
- builtin_types = p_functions[name].built_ins;
+ builtins = p_functions[name];
}
if (p_functions.has("global")) { // Adds global variables: 'TIME'
for (Map<StringName, BuiltInInfo>::Element *E = p_functions["global"].built_ins.front(); E; E = E->next()) {
- builtin_types.insert(E->key(), E->value());
+ builtins.built_ins.insert(E->key(), E->value());
}
}
@@ -6915,7 +6953,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
pname = tk.text;
ShaderLanguage::IdentifierType itype;
- if (_find_identifier(func_node->body, false, builtin_types, pname, (ShaderLanguage::DataType *)nullptr, &itype)) {
+ if (_find_identifier(func_node->body, false, builtins, pname, (ShaderLanguage::DataType *)nullptr, &itype)) {
if (itype != IDENTIFIER_FUNCTION) {
_set_error("Redefinition of '" + String(pname) + "'");
return ERR_PARSE_ERROR;
@@ -6977,7 +7015,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
current_function = name;
- Error err = _parse_block(func_node->body, builtin_types);
+ Error err = _parse_block(func_node->body, builtins);
if (err) {
return err;
}
diff --git a/servers/rendering/shader_language.h b/servers/rendering/shader_language.h
index faf06a9a85..0d044a21c7 100644
--- a/servers/rendering/shader_language.h
+++ b/servers/rendering/shader_language.h
@@ -730,8 +730,25 @@ public:
constant(p_constant) {}
};
+ struct StageFunctionInfo {
+ struct Argument {
+ StringName name;
+ DataType type;
+
+ Argument(const StringName &p_name = StringName(), DataType p_type = TYPE_VOID) {
+ name = p_name;
+ type = p_type;
+ }
+ };
+
+ Vector<Argument> arguments;
+ DataType return_type = TYPE_VOID;
+ };
+
struct FunctionInfo {
Map<StringName, BuiltInInfo> built_ins;
+ Map<StringName, StageFunctionInfo> stage_functions;
+
bool can_discard;
};
static bool has_builtin(const Map<StringName, ShaderLanguage::FunctionInfo> &p_functions, const StringName &p_name);
@@ -802,9 +819,9 @@ private:
IDENTIFIER_CONSTANT,
};
- bool _find_identifier(const BlockNode *p_block, bool p_allow_reassign, const Map<StringName, BuiltInInfo> &p_builtin_types, const StringName &p_identifier, DataType *r_data_type = nullptr, IdentifierType *r_type = nullptr, bool *r_is_const = nullptr, int *r_array_size = nullptr, StringName *r_struct_name = nullptr);
+ bool _find_identifier(const BlockNode *p_block, bool p_allow_reassign, const FunctionInfo &p_function_info, const StringName &p_identifier, DataType *r_data_type = nullptr, IdentifierType *r_type = nullptr, bool *r_is_const = nullptr, int *r_array_size = nullptr, StringName *r_struct_name = nullptr);
bool _is_operator_assign(Operator p_op) const;
- bool _validate_assign(Node *p_node, const Map<StringName, BuiltInInfo> &p_builtin_types, String *r_message = nullptr);
+ bool _validate_assign(Node *p_node, const FunctionInfo &p_function_info, String *r_message = nullptr);
bool _validate_operator(OperatorNode *p_op, DataType *r_ret_type = nullptr);
struct BuiltinFuncDef {
@@ -837,16 +854,16 @@ private:
Error _validate_datatype(DataType p_type);
bool _compare_datatypes_in_nodes(Node *a, Node *b) const;
- bool _validate_function_call(BlockNode *p_block, const Map<StringName, BuiltInInfo> &p_builtin_types, OperatorNode *p_func, DataType *r_ret_type, StringName *r_ret_type_str);
- bool _parse_function_arguments(BlockNode *p_block, const Map<StringName, BuiltInInfo> &p_builtin_types, OperatorNode *p_func, int *r_complete_arg = nullptr);
+ bool _validate_function_call(BlockNode *p_block, const FunctionInfo &p_function_info, OperatorNode *p_func, DataType *r_ret_type, StringName *r_ret_type_str);
+ bool _parse_function_arguments(BlockNode *p_block, const FunctionInfo &p_function_info, OperatorNode *p_func, int *r_complete_arg = nullptr);
bool _propagate_function_call_sampler_uniform_settings(StringName p_name, int p_argument, TextureFilter p_filter, TextureRepeat p_repeat);
bool _propagate_function_call_sampler_builtin_reference(StringName p_name, int p_argument, const StringName &p_builtin);
- Node *_parse_expression(BlockNode *p_block, const Map<StringName, BuiltInInfo> &p_builtin_types);
+ Node *_parse_expression(BlockNode *p_block, const FunctionInfo &p_function_info);
ShaderLanguage::Node *_reduce_expression(BlockNode *p_block, ShaderLanguage::Node *p_node);
- Node *_parse_and_reduce_expression(BlockNode *p_block, const Map<StringName, BuiltInInfo> &p_builtin_types);
- Error _parse_block(BlockNode *p_block, const Map<StringName, BuiltInInfo> &p_builtin_types, bool p_just_one = false, bool p_can_break = false, bool p_can_continue = false);
+ Node *_parse_and_reduce_expression(BlockNode *p_block, const FunctionInfo &p_function_info);
+ Error _parse_block(BlockNode *p_block, const FunctionInfo &p_function_info, bool p_just_one = false, bool p_can_break = false, bool p_can_continue = false);
String _get_shader_type_list(const Set<String> &p_shader_types) const;
String _get_qualifier_str(ArgumentQualifier p_qualifier) const;
diff --git a/servers/rendering/shader_types.cpp b/servers/rendering/shader_types.cpp
index 9c4f44b928..f3ef322ad7 100644
--- a/servers/rendering/shader_types.cpp
+++ b/servers/rendering/shader_types.cpp
@@ -283,8 +283,29 @@ ShaderTypes::ShaderTypes() {
shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["INDEX"] = constt(ShaderLanguage::TYPE_INT);
shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["EMISSION_TRANSFORM"] = constt(ShaderLanguage::TYPE_MAT4);
shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["RANDOM_SEED"] = constt(ShaderLanguage::TYPE_UINT);
+ shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["FLAG_EMIT_POSITION"] = constt(ShaderLanguage::TYPE_UINT);
+ shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["FLAG_EMIT_ROT_SCALE"] = constt(ShaderLanguage::TYPE_UINT);
+ shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["FLAG_EMIT_VELOCITY"] = constt(ShaderLanguage::TYPE_UINT);
+ shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["FLAG_EMIT_COLOR"] = constt(ShaderLanguage::TYPE_UINT);
+ shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["FLAG_EMIT_CUSTOM"] = constt(ShaderLanguage::TYPE_UINT);
+ shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["RESTART_POSITION"] = constt(ShaderLanguage::TYPE_BOOL);
+ shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["RESTART_ROT_SCALE"] = constt(ShaderLanguage::TYPE_BOOL);
+ shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["RESTART_VELOCITY"] = constt(ShaderLanguage::TYPE_BOOL);
+ shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["RESTART_COLOR"] = constt(ShaderLanguage::TYPE_BOOL);
+ shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["RESTART_CUSTOM"] = constt(ShaderLanguage::TYPE_BOOL);
shader_modes[RS::SHADER_PARTICLES].functions["compute"].can_discard = false;
+ {
+ ShaderLanguage::StageFunctionInfo emit_vertex_func;
+ emit_vertex_func.arguments.push_back(ShaderLanguage::StageFunctionInfo::Argument("xform", ShaderLanguage::TYPE_MAT4));
+ emit_vertex_func.arguments.push_back(ShaderLanguage::StageFunctionInfo::Argument("velocity", ShaderLanguage::TYPE_VEC3));
+ emit_vertex_func.arguments.push_back(ShaderLanguage::StageFunctionInfo::Argument("color", ShaderLanguage::TYPE_VEC4));
+ emit_vertex_func.arguments.push_back(ShaderLanguage::StageFunctionInfo::Argument("custom", ShaderLanguage::TYPE_VEC4));
+ emit_vertex_func.arguments.push_back(ShaderLanguage::StageFunctionInfo::Argument("flags", ShaderLanguage::TYPE_UINT));
+ emit_vertex_func.return_type = ShaderLanguage::TYPE_BOOL; //whether it could emit
+ shader_modes[RS::SHADER_PARTICLES].functions["compute"].stage_functions["emit_particle"] = emit_vertex_func;
+ }
+
shader_modes[RS::SHADER_PARTICLES].modes.push_back("disable_force");
shader_modes[RS::SHADER_PARTICLES].modes.push_back("disable_velocity");
shader_modes[RS::SHADER_PARTICLES].modes.push_back("keep_data");
diff --git a/servers/rendering_server.h b/servers/rendering_server.h
index 38b4dec658..6f844c67d8 100644
--- a/servers/rendering_server.h
+++ b/servers/rendering_server.h
@@ -563,7 +563,7 @@ public:
virtual RID particles_create() = 0;
- virtual void particles_set_emitting(RID p_particles, bool p_emitting) = 0;
+ virtual void particles_set_emitting(RID p_particles, bool p_enable) = 0;
virtual bool particles_get_emitting(RID p_particles) = 0;
virtual void particles_set_amount(RID p_particles, int p_amount) = 0;
virtual void particles_set_lifetime(RID p_particles, float p_lifetime) = 0;
@@ -581,6 +581,18 @@ public:
virtual void particles_request_process(RID p_particles) = 0;
virtual void particles_restart(RID p_particles) = 0;
+ virtual void particles_set_subemitter(RID p_particles, RID p_subemitter_particles) = 0;
+
+ enum ParticlesEmitFlags {
+ PARTICLES_EMIT_FLAG_POSITION = 1,
+ PARTICLES_EMIT_FLAG_ROTATION_SCALE = 2,
+ PARTICLES_EMIT_FLAG_VELOCITY = 4,
+ PARTICLES_EMIT_FLAG_COLOR = 8,
+ PARTICLES_EMIT_FLAG_CUSTOM = 16
+ };
+
+ virtual void particles_emit(RID p_particles, const Transform &p_transform, const Vector3 &p_velocity, const Color &p_color, const Color &p_custom, uint32_t p_emit_flags) = 0;
+
enum ParticlesDrawOrder {
PARTICLES_DRAW_ORDER_INDEX,
PARTICLES_DRAW_ORDER_LIFETIME,