From ddf82697d7e47a3a8b950731e02b5d05278a140c Mon Sep 17 00:00:00 2001 From: Raffaele Picca Date: Tue, 23 Nov 2021 13:50:35 +0100 Subject: [PATCH] Random initial color parameter for ParticleMaterial Works with 2D and 3D GPU Particles --- doc/classes/CPUParticles2D.xml | 3 +++ doc/classes/CPUParticles3D.xml | 3 +++ doc/classes/ParticlesMaterial.xml | 3 +++ scene/2d/cpu_particles_2d.cpp | 25 ++++++++++++++++++- scene/2d/cpu_particles_2d.h | 5 ++++ scene/3d/cpu_particles_3d.cpp | 25 ++++++++++++++++++- scene/3d/cpu_particles_3d.h | 5 ++++ scene/resources/particles_material.cpp | 34 ++++++++++++++++++++++++++ scene/resources/particles_material.h | 5 ++++ 9 files changed, 106 insertions(+), 2 deletions(-) diff --git a/doc/classes/CPUParticles2D.xml b/doc/classes/CPUParticles2D.xml index 0505d8ad3694..a4415e89dbbe 100644 --- a/doc/classes/CPUParticles2D.xml +++ b/doc/classes/CPUParticles2D.xml @@ -116,6 +116,9 @@ Each particle's initial color. If [member texture] is defined, it will be multiplied by this color. + + Each particle's initial color will vary along this [GradientTexture1D] (multiplied with [member color]). + Each particle's color will vary along this [Gradient] (multiplied with [member color]). diff --git a/doc/classes/CPUParticles3D.xml b/doc/classes/CPUParticles3D.xml index ad491465f23c..83728a807be6 100644 --- a/doc/classes/CPUParticles3D.xml +++ b/doc/classes/CPUParticles3D.xml @@ -125,6 +125,9 @@ Each particle's initial color. To have particle display color in a [BaseMaterial3D] make sure to set [member BaseMaterial3D.vertex_color_use_as_albedo] to [code]true[/code]. + + Each particle's initial color will vary along this [GradientTexture1D] (multiplied with [member color]). + Each particle's color will vary along this [GradientTexture1D] over its lifetime (multiplied with [member color]). diff --git a/doc/classes/ParticlesMaterial.xml b/doc/classes/ParticlesMaterial.xml index 2cc0d8f2b014..6ff57bd58a79 100644 --- a/doc/classes/ParticlesMaterial.xml +++ b/doc/classes/ParticlesMaterial.xml @@ -127,6 +127,9 @@ Each particle's initial color. If the [GPUParticles2D]'s [code]texture[/code] is defined, it will be multiplied by this color. To have particle display color in a [BaseMaterial3D] make sure to set [member BaseMaterial3D.vertex_color_use_as_albedo] to [code]true[/code]. + + Each particle's initial color will vary along this [GradientTexture1D] (multiplied with [member color]). + Each particle's color will vary along this [GradientTexture1D] over its lifetime (multiplied with [member color]). diff --git a/scene/2d/cpu_particles_2d.cpp b/scene/2d/cpu_particles_2d.cpp index 80c17b6e88be..6b1750f9e91c 100644 --- a/scene/2d/cpu_particles_2d.cpp +++ b/scene/2d/cpu_particles_2d.cpp @@ -398,6 +398,14 @@ Ref CPUParticles2D::get_color_ramp() const { return color_ramp; } +void CPUParticles2D::set_color_initial_ramp(const Ref &p_ramp) { + color_initial_ramp = p_ramp; +} + +Ref CPUParticles2D::get_color_initial_ramp() const { + return color_initial_ramp; +} + void CPUParticles2D::set_particle_flag(ParticleFlags p_particle_flag, bool p_enable) { ERR_FAIL_INDEX(p_particle_flag, PARTICLE_FLAG_MAX); particle_flags[p_particle_flag] = p_enable; @@ -727,6 +735,12 @@ void CPUParticles2D::_particles_process(double p_delta) { p.hue_rot_rand = Math::randf(); p.anim_offset_rand = Math::randf(); + if (color_initial_ramp.is_valid()) { + p.start_color_rand = color_initial_ramp->get_color_at_offset(Math::randf()); + } else { + p.start_color_rand = Color(1, 1, 1, 1); + } + real_t angle1_rad = direction.angle() + Math::deg2rad((Math::randf() * 2.0 - 1.0) * spread); Vector2 rot = Vector2(Math::cos(angle1_rad), Math::sin(angle1_rad)); p.velocity = rot * Math::lerp(parameters_min[PARAM_INITIAL_LINEAR_VELOCITY], parameters_min[PARAM_INITIAL_LINEAR_VELOCITY], Math::randf()); @@ -946,7 +960,7 @@ void CPUParticles2D::_particles_process(double p_delta) { p.color.g = color_rgb.y; p.color.b = color_rgb.z; - p.color *= p.base_color; + p.color *= p.base_color * p.start_color_rand; if (particle_flags[PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY]) { if (p.velocity.length() > 0.0) { @@ -1173,6 +1187,11 @@ void CPUParticles2D::convert_from_particles(Node *p_particles) { set_color_ramp(gt->get_gradient()); } + Ref gti = material->get_color_initial_ramp(); + if (gti.is_valid()) { + set_color_initial_ramp(gti->get_gradient()); + } + set_particle_flag(PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY, material->get_particle_flag(ParticlesMaterial::PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY)); set_emission_shape(EmissionShape(material->get_emission_shape())); @@ -1295,6 +1314,9 @@ void CPUParticles2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_color_ramp", "ramp"), &CPUParticles2D::set_color_ramp); ClassDB::bind_method(D_METHOD("get_color_ramp"), &CPUParticles2D::get_color_ramp); + ClassDB::bind_method(D_METHOD("set_color_initial_ramp", "ramp"), &CPUParticles2D::set_color_initial_ramp); + ClassDB::bind_method(D_METHOD("get_color_initial_ramp"), &CPUParticles2D::get_color_initial_ramp); + ClassDB::bind_method(D_METHOD("set_particle_flag", "particle_flag", "enable"), &CPUParticles2D::set_particle_flag); ClassDB::bind_method(D_METHOD("get_particle_flag", "particle_flag"), &CPUParticles2D::get_particle_flag); @@ -1386,6 +1408,7 @@ void CPUParticles2D::_bind_methods() { ADD_GROUP("Color", ""); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_color", "get_color"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "color_ramp", PROPERTY_HINT_RESOURCE_TYPE, "Gradient"), "set_color_ramp", "get_color_ramp"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "color_initial_ramp", PROPERTY_HINT_RESOURCE_TYPE, "Gradient"), "set_color_initial_ramp", "get_color_initial_ramp"); ADD_GROUP("Hue Variation", "hue_"); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "hue_variation_min", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_param_min", "get_param_min", PARAM_HUE_VARIATION); diff --git a/scene/2d/cpu_particles_2d.h b/scene/2d/cpu_particles_2d.h index 391f51224e4e..3cb3d64855aa 100644 --- a/scene/2d/cpu_particles_2d.h +++ b/scene/2d/cpu_particles_2d.h @@ -89,6 +89,7 @@ class CPUParticles2D : public Node2D { real_t scale_rand = 0.0; real_t hue_rot_rand = 0.0; real_t anim_offset_rand = 0.0; + Color start_color_rand; double time = 0.0; double lifetime = 0.0; Color base_color; @@ -156,6 +157,7 @@ class CPUParticles2D : public Node2D { Ref curve_parameters[PARAM_MAX]; Color color; Ref color_ramp; + Ref color_initial_ramp; bool particle_flags[PARTICLE_FLAG_MAX]; @@ -250,6 +252,9 @@ class CPUParticles2D : public Node2D { void set_color_ramp(const Ref &p_ramp); Ref get_color_ramp() const; + void set_color_initial_ramp(const Ref &p_ramp); + Ref get_color_initial_ramp() const; + void set_particle_flag(ParticleFlags p_particle_flag, bool p_enable); bool get_particle_flag(ParticleFlags p_particle_flag) const; diff --git a/scene/3d/cpu_particles_3d.cpp b/scene/3d/cpu_particles_3d.cpp index d347d24c2c86..cfe538398d65 100644 --- a/scene/3d/cpu_particles_3d.cpp +++ b/scene/3d/cpu_particles_3d.cpp @@ -368,6 +368,14 @@ Ref CPUParticles3D::get_color_ramp() const { return color_ramp; } +void CPUParticles3D::set_color_initial_ramp(const Ref &p_ramp) { + color_initial_ramp = p_ramp; +} + +Ref CPUParticles3D::get_color_initial_ramp() const { + return color_initial_ramp; +} + void CPUParticles3D::set_particle_flag(ParticleFlags p_particle_flag, bool p_enable) { ERR_FAIL_INDEX(p_particle_flag, PARTICLE_FLAG_MAX); particle_flags[p_particle_flag] = p_enable; @@ -748,6 +756,12 @@ void CPUParticles3D::_particles_process(double p_delta) { p.hue_rot_rand = Math::randf(); p.anim_offset_rand = Math::randf(); + if (color_initial_ramp.is_valid()) { + p.start_color_rand = color_initial_ramp->get_color_at_offset(Math::randf()); + } else { + p.start_color_rand = Color(1, 1, 1, 1); + } + if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) { real_t angle1_rad = Math::atan2(direction.y, direction.x) + Math::deg2rad((Math::randf() * 2.0 - 1.0) * spread); Vector3 rot = Vector3(Math::cos(angle1_rad), Math::sin(angle1_rad), 0.0); @@ -1046,7 +1060,7 @@ void CPUParticles3D::_particles_process(double p_delta) { p.color.g = color_rgb.y; p.color.b = color_rgb.z; - p.color *= p.base_color; + p.color *= p.base_color * p.start_color_rand; if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) { if (particle_flags[PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY]) { @@ -1333,6 +1347,11 @@ void CPUParticles3D::convert_from_particles(Node *p_particles) { set_color_ramp(gt->get_gradient()); } + Ref gti = material->get_color_initial_ramp(); + if (gti.is_valid()) { + set_color_initial_ramp(gti->get_gradient()); + } + set_particle_flag(PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY, material->get_particle_flag(ParticlesMaterial::PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY)); set_particle_flag(PARTICLE_FLAG_ROTATE_Y, material->get_particle_flag(ParticlesMaterial::PARTICLE_FLAG_ROTATE_Y)); set_particle_flag(PARTICLE_FLAG_DISABLE_Z, material->get_particle_flag(ParticlesMaterial::PARTICLE_FLAG_DISABLE_Z)); @@ -1459,6 +1478,9 @@ void CPUParticles3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_color_ramp", "ramp"), &CPUParticles3D::set_color_ramp); ClassDB::bind_method(D_METHOD("get_color_ramp"), &CPUParticles3D::get_color_ramp); + ClassDB::bind_method(D_METHOD("set_color_initial_ramp", "ramp"), &CPUParticles3D::set_color_initial_ramp); + ClassDB::bind_method(D_METHOD("get_color_initial_ramp"), &CPUParticles3D::get_color_initial_ramp); + ClassDB::bind_method(D_METHOD("set_particle_flag", "particle_flag", "enable"), &CPUParticles3D::set_particle_flag); ClassDB::bind_method(D_METHOD("get_particle_flag", "particle_flag"), &CPUParticles3D::get_particle_flag); @@ -1572,6 +1594,7 @@ void CPUParticles3D::_bind_methods() { ADD_GROUP("Color", ""); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_color", "get_color"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "color_ramp", PROPERTY_HINT_RESOURCE_TYPE, "Gradient"), "set_color_ramp", "get_color_ramp"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "color_initial_ramp", PROPERTY_HINT_RESOURCE_TYPE, "Gradient"), "set_color_initial_ramp", "get_color_initial_ramp"); ADD_GROUP("Hue Variation", "hue_"); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "hue_variation_min", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_param_min", "get_param_min", PARAM_HUE_VARIATION); diff --git a/scene/3d/cpu_particles_3d.h b/scene/3d/cpu_particles_3d.h index aca7328a279d..6addeab1a679 100644 --- a/scene/3d/cpu_particles_3d.h +++ b/scene/3d/cpu_particles_3d.h @@ -91,6 +91,7 @@ class CPUParticles3D : public GeometryInstance3D { real_t scale_rand = 0.0; real_t hue_rot_rand = 0.0; real_t anim_offset_rand = 0.0; + Color start_color_rand; double time = 0.0; double lifetime = 0.0; Color base_color; @@ -160,6 +161,7 @@ class CPUParticles3D : public GeometryInstance3D { Ref curve_parameters[PARAM_MAX]; Color color = Color(1, 1, 1, 1); Ref color_ramp; + Ref color_initial_ramp; bool particle_flags[PARTICLE_FLAG_MAX] = {}; @@ -261,6 +263,9 @@ class CPUParticles3D : public GeometryInstance3D { void set_color_ramp(const Ref &p_ramp); Ref get_color_ramp() const; + void set_color_initial_ramp(const Ref &p_ramp); + Ref get_color_initial_ramp() const; + void set_particle_flag(ParticleFlags p_particle_flag, bool p_enable); bool get_particle_flag(ParticleFlags p_particle_flag) const; diff --git a/scene/resources/particles_material.cpp b/scene/resources/particles_material.cpp index e77f5a0be734..6b7eef3712a2 100644 --- a/scene/resources/particles_material.cpp +++ b/scene/resources/particles_material.cpp @@ -85,6 +85,7 @@ void ParticlesMaterial::init_shaders() { shader_names->color = "color_value"; shader_names->color_ramp = "color_ramp"; + shader_names->color_initial_ramp = "color_initial_ramp"; shader_names->emission_sphere_radius = "emission_sphere_radius"; shader_names->emission_box_extents = "emission_box_extents"; @@ -232,6 +233,10 @@ void ParticlesMaterial::_update_shader() { code += "uniform sampler2D color_ramp;\n"; } + if (color_initial_ramp.is_valid()) { + code += "uniform sampler2D color_initial_ramp;\n"; + } + if (tex_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) { code += "uniform sampler2D linear_velocity_texture;\n"; } @@ -311,6 +316,9 @@ void ParticlesMaterial::_update_shader() { code += " float scale_rand = rand_from_seed(alt_seed);\n"; code += " float hue_rot_rand = rand_from_seed(alt_seed);\n"; code += " float anim_offset_rand = rand_from_seed(alt_seed);\n"; + if (color_initial_ramp.is_valid()) { + code += " float color_initial_rand = rand_from_seed(alt_seed);\n"; + } code += " float pi = 3.14159;\n"; code += " float degree_to_rad = pi / 180.0;\n"; code += "\n"; @@ -462,6 +470,10 @@ void ParticlesMaterial::_update_shader() { code += " float scale_rand = rand_from_seed(alt_seed);\n"; code += " float hue_rot_rand = rand_from_seed(alt_seed);\n"; code += " float anim_offset_rand = rand_from_seed(alt_seed);\n"; + if (color_initial_ramp.is_valid()) { + code += " float color_initial_rand = rand_from_seed(alt_seed);\n"; + } + code += " float pi = 3.14159;\n"; code += " float degree_to_rad = pi / 180.0;\n"; code += "\n"; @@ -620,6 +632,12 @@ void ParticlesMaterial::_update_shader() { } else { code += " COLOR = hue_rot_mat * color_value;\n"; } + + if (color_initial_ramp.is_valid()) { + code += " vec4 start_color = textureLod(color_initial_ramp, vec2(color_initial_rand, 0.0), 0.0);\n"; + code += " COLOR *= start_color;\n"; + } + 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"; } @@ -988,6 +1006,18 @@ Ref ParticlesMaterial::get_color_ramp() const { return color_ramp; } +void ParticlesMaterial::set_color_initial_ramp(const Ref &p_texture) { + color_initial_ramp = p_texture; + RID tex_rid = p_texture.is_valid() ? p_texture->get_rid() : RID(); + RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->color_initial_ramp, tex_rid); + _queue_shader_change(); + notify_property_list_changed(); +} + +Ref ParticlesMaterial::get_color_initial_ramp() const { + return color_initial_ramp; +} + void ParticlesMaterial::set_particle_flag(ParticleFlags p_particle_flag, bool p_enable) { ERR_FAIL_INDEX(p_particle_flag, PARTICLE_FLAG_MAX); particle_flags[p_particle_flag] = p_enable; @@ -1282,6 +1312,9 @@ void ParticlesMaterial::_bind_methods() { ClassDB::bind_method(D_METHOD("set_color_ramp", "ramp"), &ParticlesMaterial::set_color_ramp); ClassDB::bind_method(D_METHOD("get_color_ramp"), &ParticlesMaterial::get_color_ramp); + ClassDB::bind_method(D_METHOD("set_color_initial_ramp", "ramp"), &ParticlesMaterial::set_color_initial_ramp); + ClassDB::bind_method(D_METHOD("get_color_initial_ramp"), &ParticlesMaterial::get_color_initial_ramp); + ClassDB::bind_method(D_METHOD("set_particle_flag", "particle_flag", "enable"), &ParticlesMaterial::set_particle_flag); ClassDB::bind_method(D_METHOD("get_particle_flag", "particle_flag"), &ParticlesMaterial::get_particle_flag); @@ -1414,6 +1447,7 @@ void ParticlesMaterial::_bind_methods() { ADD_GROUP("Color", ""); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_color", "get_color"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "color_ramp", PROPERTY_HINT_RESOURCE_TYPE, "GradientTexture1D"), "set_color_ramp", "get_color_ramp"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "color_initial_ramp", PROPERTY_HINT_RESOURCE_TYPE, "GradientTexture1D"), "set_color_initial_ramp", "get_color_initial_ramp"); ADD_GROUP("Hue Variation", "hue_"); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "hue_variation_min", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_param_min", "get_param_min", PARAM_HUE_VARIATION); diff --git a/scene/resources/particles_material.h b/scene/resources/particles_material.h index 36bc4569786c..9c0bbe3b5d9c 100644 --- a/scene/resources/particles_material.h +++ b/scene/resources/particles_material.h @@ -194,6 +194,7 @@ class ParticlesMaterial : public Material { StringName color; StringName color_ramp; + StringName color_initial_ramp; StringName emission_sphere_radius; StringName emission_box_extents; @@ -237,6 +238,7 @@ class ParticlesMaterial : public Material { Ref tex_parameters[PARAM_MAX]; Color color; Ref color_ramp; + Ref color_initial_ramp; bool particle_flags[PARTICLE_FLAG_MAX]; @@ -299,6 +301,9 @@ class ParticlesMaterial : public Material { void set_color_ramp(const Ref &p_texture); Ref get_color_ramp() const; + void set_color_initial_ramp(const Ref &p_texture); + Ref get_color_initial_ramp() const; + void set_particle_flag(ParticleFlags p_particle_flag, bool p_enable); bool get_particle_flag(ParticleFlags p_particle_flag) const;