Implement manual particle emission and particle sub emitters.

This commit is contained in:
reduz 2020-09-06 09:18:10 -03:00
parent ceed524936
commit d0bddf53c5
14 changed files with 871 additions and 317 deletions

View file

@ -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);
}

View file

@ -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

View file

@ -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);
}

View file

@ -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

View file

@ -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;

View file

@ -3119,52 +3119,47 @@ 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->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();
if (particles->particles_sort_buffer.is_valid()) {
RD::get_singleton()->free(particles->particles_sort_buffer);
particles->particles_sort_buffer = RID();
}
if (particles->amount == p_amount) {
return;
}
_particles_free_data(particles);
particles->amount = p_amount;
if (particles->amount > 0) {
particles->particle_buffer = RD::get_singleton()->storage_buffer_create(sizeof(ParticleData) * p_amount);
particles->frame_params_buffer = RD::get_singleton()->storage_buffer_create(sizeof(ParticlesFrameParams) * 1);
particles->particle_instance_buffer = RD::get_singleton()->storage_buffer_create(sizeof(float) * 4 * (3 + 1 + 1) * p_amount);
//needs to clear it
{
Vector<RD::Uniform> uniforms;
{
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;
@ -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.");
}

View file

@ -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);

View file

@ -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,71 +197,19 @@ void main() {
float mass = 1.0;
float restart_phase = float(index) / float(params.total_particles);
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);
}
restart_phase *= (1.0 - FRAME.explosiveness);
bool restart = false;
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
bool restart_position = false;
bool restart_rotation_scale = false;
bool restart_velocity = false;
bool restart_color = false;
bool restart_custom = false;
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;
}
}
} 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 (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;
}
#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;
}
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),
@ -190,6 +217,111 @@ void main() {
vec4(0.0, 0.0, 0.0, 1.0));
}
if (params.sub_emitter_mode) {
if (!PARTICLE.is_active) {
int src_index = atomicAdd(src_particles.particle_count, -1) - 1;
if (src_index >= 0) {
PARTICLE.is_active = true;
restart = true;
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 (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.emitting) {
float restart_phase = float(index) / float(params.total_particles);
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);
}
restart_phase *= (1.0 - FRAME.explosiveness);
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 (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;
}
}
} 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 (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;
}
}
if (PARTICLE.is_active) {
/* clang-format off */

View file

@ -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)

View file

@ -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)

View file

@ -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;
}

View file

@ -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;

View file

@ -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");

View file

@ -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,