Add cone angle control to particle emission ring shape

This commit is contained in:
Kasper Arnklit Frandsen 2024-09-02 14:35:51 +02:00
parent 61598c5c88
commit 40b9516724
6 changed files with 66 additions and 10 deletions

View file

@ -168,6 +168,10 @@
<member name="emission_ring_axis" type="Vector3" setter="set_emission_ring_axis" getter="get_emission_ring_axis">
The axis of the ring when using the emitter [constant EMISSION_SHAPE_RING].
</member>
<member name="emission_ring_cone_angle" type="float" setter="set_emission_ring_cone_angle" getter="get_emission_ring_cone_angle">
The angle of the cone when using the emitter [constant EMISSION_SHAPE_RING]. The default angle of 90 degrees results in a ring, while an angle of 0 degrees results in a cone. Intermediate values will result in a ring where one end is larger than the other.
[b]Note:[/b] Depending on [member emission_ring_height], the angle may be clamped if the ring's end is reached to form a perfect cone.
</member>
<member name="emission_ring_height" type="float" setter="set_emission_ring_height" getter="get_emission_ring_height">
The height of the ring when using the emitter [constant EMISSION_SHAPE_RING].
</member>

View file

@ -207,6 +207,10 @@
<member name="emission_ring_axis" type="Vector3" setter="set_emission_ring_axis" getter="get_emission_ring_axis">
The axis of the ring when using the emitter [constant EMISSION_SHAPE_RING].
</member>
<member name="emission_ring_cone_angle" type="float" setter="set_emission_ring_cone_angle" getter="get_emission_ring_cone_angle">
The angle of the cone when using the emitter [constant EMISSION_SHAPE_RING]. The default angle of 90 degrees results in a ring, while an angle of 0 degrees results in a cone. Intermediate values will result in a ring where one end is larger than the other.
[b]Note:[/b] Depending on [member emission_ring_height], the angle may be clamped if the ring's end is reached to form a perfect cone.
</member>
<member name="emission_ring_height" type="float" setter="set_emission_ring_height" getter="get_emission_ring_height">
The height of the ring when using the emitter [constant EMISSION_SHAPE_RING].
</member>

View file

@ -448,6 +448,10 @@ void CPUParticles3D::set_emission_ring_inner_radius(real_t p_radius) {
emission_ring_inner_radius = p_radius;
}
void CPUParticles3D::set_emission_ring_cone_angle(real_t p_angle) {
emission_ring_cone_angle = p_angle;
}
void CPUParticles3D::set_scale_curve_x(Ref<Curve> p_scale_curve) {
scale_curve_x = p_scale_curve;
}
@ -501,6 +505,10 @@ real_t CPUParticles3D::get_emission_ring_inner_radius() const {
return emission_ring_inner_radius;
}
real_t CPUParticles3D::get_emission_ring_cone_angle() const {
return emission_ring_cone_angle;
}
CPUParticles3D::EmissionShape CPUParticles3D::get_emission_shape() const {
return emission_shape;
}
@ -878,8 +886,14 @@ void CPUParticles3D::_particles_process(double p_delta) {
}
} break;
case EMISSION_SHAPE_RING: {
real_t radius_clamped = MAX(0.001, emission_ring_radius);
real_t top_radius = MAX(radius_clamped - Math::tan(Math::deg_to_rad(90.0 - emission_ring_cone_angle)) * emission_ring_height, 0.0);
real_t y_pos = Math::randf();
real_t skew = MAX(MIN(radius_clamped, top_radius) / MAX(radius_clamped, top_radius), 0.5);
y_pos = radius_clamped < top_radius ? Math::pow(y_pos, skew) : 1.0 - Math::pow(y_pos, skew);
real_t ring_random_angle = Math::randf() * Math_TAU;
real_t ring_random_radius = Math::sqrt(Math::randf() * (emission_ring_radius * emission_ring_radius - emission_ring_inner_radius * emission_ring_inner_radius) + emission_ring_inner_radius * emission_ring_inner_radius);
real_t ring_random_radius = Math::sqrt(Math::randf() * (radius_clamped * radius_clamped - emission_ring_inner_radius * emission_ring_inner_radius) + emission_ring_inner_radius * emission_ring_inner_radius);
ring_random_radius = Math::lerp(ring_random_radius, ring_random_radius * (top_radius / radius_clamped), y_pos);
Vector3 axis = emission_ring_axis == Vector3(0.0, 0.0, 0.0) ? Vector3(0.0, 0.0, 1.0) : emission_ring_axis.normalized();
Vector3 ortho_axis;
if (axis.abs() == Vector3(1.0, 0.0, 0.0)) {
@ -890,7 +904,7 @@ void CPUParticles3D::_particles_process(double p_delta) {
ortho_axis = ortho_axis.normalized();
ortho_axis.rotate(axis, ring_random_angle);
ortho_axis = ortho_axis.normalized();
p.transform.origin = ortho_axis * ring_random_radius + (Math::randf() * emission_ring_height - emission_ring_height / 2.0) * axis;
p.transform.origin = ortho_axis * ring_random_radius + (y_pos * emission_ring_height - emission_ring_height / 2.0) * axis;
} break;
case EMISSION_SHAPE_MAX: { // Max value for validity check.
break;
@ -1550,6 +1564,9 @@ void CPUParticles3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_emission_ring_inner_radius", "inner_radius"), &CPUParticles3D::set_emission_ring_inner_radius);
ClassDB::bind_method(D_METHOD("get_emission_ring_inner_radius"), &CPUParticles3D::get_emission_ring_inner_radius);
ClassDB::bind_method(D_METHOD("set_emission_ring_cone_angle", "cone_angle"), &CPUParticles3D::set_emission_ring_cone_angle);
ClassDB::bind_method(D_METHOD("get_emission_ring_cone_angle"), &CPUParticles3D::get_emission_ring_cone_angle);
ClassDB::bind_method(D_METHOD("get_gravity"), &CPUParticles3D::get_gravity);
ClassDB::bind_method(D_METHOD("set_gravity", "accel_vec"), &CPUParticles3D::set_gravity);
@ -1577,9 +1594,10 @@ void CPUParticles3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR3_ARRAY, "emission_normals"), "set_emission_normals", "get_emission_normals");
ADD_PROPERTY(PropertyInfo(Variant::PACKED_COLOR_ARRAY, "emission_colors"), "set_emission_colors", "get_emission_colors");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "emission_ring_axis"), "set_emission_ring_axis", "get_emission_ring_axis");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_ring_height"), "set_emission_ring_height", "get_emission_ring_height");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_ring_radius"), "set_emission_ring_radius", "get_emission_ring_radius");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_ring_inner_radius"), "set_emission_ring_inner_radius", "get_emission_ring_inner_radius");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_ring_height", PROPERTY_HINT_RANGE, "0,1000,0.01,or_greater"), "set_emission_ring_height", "get_emission_ring_height");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_ring_radius", PROPERTY_HINT_RANGE, "0,1000,0.01,or_greater"), "set_emission_ring_radius", "get_emission_ring_radius");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_ring_inner_radius", PROPERTY_HINT_RANGE, "0,1000,0.01,or_greater"), "set_emission_ring_inner_radius", "get_emission_ring_inner_radius");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_ring_cone_angle", PROPERTY_HINT_RANGE, "0,90,0.01,degrees"), "set_emission_ring_cone_angle", "get_emission_ring_cone_angle");
ADD_GROUP("Particle Flags", "particle_flag_");
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "particle_flag_align_y"), "set_particle_flag", "get_particle_flag", PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY);
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "particle_flag_rotate_y"), "set_particle_flag", "get_particle_flag", PARTICLE_FLAG_ROTATE_Y);
@ -1716,6 +1734,7 @@ CPUParticles3D::CPUParticles3D() {
set_emission_ring_height(1);
set_emission_ring_radius(1);
set_emission_ring_inner_radius(0);
set_emission_ring_cone_angle(90);
set_gravity(Vector3(0, -9.8, 0));

View file

@ -178,6 +178,7 @@ private:
real_t emission_ring_height = 0.0;
real_t emission_ring_radius = 0.0;
real_t emission_ring_inner_radius = 0.0;
real_t emission_ring_cone_angle = 0.0;
Ref<Curve> scale_curve_x;
Ref<Curve> scale_curve_y;
@ -282,6 +283,7 @@ public:
void set_emission_ring_height(real_t p_height);
void set_emission_ring_radius(real_t p_radius);
void set_emission_ring_inner_radius(real_t p_radius);
void set_emission_ring_cone_angle(real_t p_angle);
void set_scale_curve_x(Ref<Curve> p_scale_curve);
void set_scale_curve_y(Ref<Curve> p_scale_curve);
void set_scale_curve_z(Ref<Curve> p_scale_curve);
@ -297,6 +299,7 @@ public:
real_t get_emission_ring_height() const;
real_t get_emission_ring_radius() const;
real_t get_emission_ring_inner_radius() const;
real_t get_emission_ring_cone_angle() const;
Ref<Curve> get_scale_curve_x() const;
Ref<Curve> get_scale_curve_y() const;
Ref<Curve> get_scale_curve_z() const;

View file

@ -110,6 +110,7 @@ void ParticleProcessMaterial::init_shaders() {
shader_names->emission_ring_height = "emission_ring_height";
shader_names->emission_ring_radius = "emission_ring_radius";
shader_names->emission_ring_inner_radius = "emission_ring_inner_radius";
shader_names->emission_ring_cone_angle = "emission_ring_cone_angle";
shader_names->emission_shape_offset = "emission_shape_offset";
shader_names->emission_shape_scale = "emission_shape_scale";
@ -269,6 +270,7 @@ void ParticleProcessMaterial::_update_shader() {
code += "uniform float " + shader_names->emission_ring_height + ";\n";
code += "uniform float " + shader_names->emission_ring_radius + ";\n";
code += "uniform float " + shader_names->emission_ring_inner_radius + ";\n";
code += "uniform float " + shader_names->emission_ring_cone_angle + ";\n";
} break;
case EMISSION_SHAPE_MAX: { // Max value for validity check.
break;
@ -643,8 +645,14 @@ void ParticleProcessMaterial::_update_shader() {
code += " pos = texelFetch(emission_texture_points, emission_tex_ofs, 0).xyz;\n";
}
if (emission_shape == EMISSION_SHAPE_RING) {
code += " float radius_clamped = max(0.001, emission_ring_radius);\n";
code += " float top_radius = max(radius_clamped - tan(radians(90.0 - emission_ring_cone_angle)) * emission_ring_height, 0.0);\n";
code += " float y_pos = rand_from_seed(alt_seed);\n";
code += " float skew = max(min(radius_clamped, top_radius) / max(radius_clamped, top_radius), 0.5);\n";
code += " y_pos = radius_clamped < top_radius ? pow(y_pos, skew) : 1.0 - pow(y_pos, skew);\n";
code += " float ring_spawn_angle = rand_from_seed(alt_seed) * 2.0 * pi;\n";
code += " float ring_random_radius = sqrt(rand_from_seed(alt_seed) * (emission_ring_radius * emission_ring_radius - emission_ring_inner_radius * emission_ring_inner_radius) + emission_ring_inner_radius * emission_ring_inner_radius);\n";
code += " float ring_random_radius = sqrt(rand_from_seed(alt_seed) * (radius_clamped * radius_clamped - emission_ring_inner_radius * emission_ring_inner_radius) + emission_ring_inner_radius * emission_ring_inner_radius);\n";
code += " ring_random_radius = mix(ring_random_radius, ring_random_radius * (top_radius / radius_clamped), y_pos);\n";
code += " vec3 axis = emission_ring_axis == vec3(0.0) ? vec3(0.0, 0.0, 1.0) : normalize(emission_ring_axis);\n";
code += " vec3 ortho_axis = vec3(0.0);\n";
code += " if (abs(axis) == vec3(1.0, 0.0, 0.0)) {\n";
@ -662,7 +670,7 @@ void ParticleProcessMaterial::_update_shader() {
code += " vec3(axis.z * axis.x * oc - axis.y * s, axis.z * axis.y * oc + axis.x * s, c + axis.z * axis.z * oc)\n";
code += " ) * ortho_axis;\n";
code += " ortho_axis = normalize(ortho_axis);\n";
code += " pos = ortho_axis * ring_random_radius + (rand_from_seed(alt_seed) * emission_ring_height - emission_ring_height / 2.0) * axis;\n";
code += " pos = ortho_axis * ring_random_radius + (y_pos * emission_ring_height - emission_ring_height / 2.0) * axis;\n";
}
code += " }\n";
code += " return pos * emission_shape_scale + emission_shape_offset;\n";
@ -1615,6 +1623,11 @@ void ParticleProcessMaterial::set_emission_ring_inner_radius(real_t p_radius) {
RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_ring_inner_radius, p_radius);
}
void ParticleProcessMaterial::set_emission_ring_cone_angle(real_t p_angle) {
emission_ring_cone_angle = p_angle;
RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_ring_cone_angle, p_angle);
}
void ParticleProcessMaterial::set_inherit_velocity_ratio(double p_ratio) {
inherit_emitter_velocity_ratio = p_ratio;
RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->inherit_emitter_velocity_ratio, p_ratio);
@ -1664,6 +1677,10 @@ real_t ParticleProcessMaterial::get_emission_ring_inner_radius() const {
return emission_ring_inner_radius;
}
real_t ParticleProcessMaterial::get_emission_ring_cone_angle() const {
return emission_ring_cone_angle;
}
void ParticleProcessMaterial::set_emission_shape_offset(const Vector3 &p_emission_shape_offset) {
emission_shape_offset = p_emission_shape_offset;
RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_shape_offset, p_emission_shape_offset);
@ -2015,6 +2032,9 @@ void ParticleProcessMaterial::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_emission_ring_inner_radius", "inner_radius"), &ParticleProcessMaterial::set_emission_ring_inner_radius);
ClassDB::bind_method(D_METHOD("get_emission_ring_inner_radius"), &ParticleProcessMaterial::get_emission_ring_inner_radius);
ClassDB::bind_method(D_METHOD("set_emission_ring_cone_angle", "cone_angle"), &ParticleProcessMaterial::set_emission_ring_cone_angle);
ClassDB::bind_method(D_METHOD("get_emission_ring_cone_angle"), &ParticleProcessMaterial::get_emission_ring_cone_angle);
ClassDB::bind_method(D_METHOD("set_emission_shape_offset", "emission_shape_offset"), &ParticleProcessMaterial::set_emission_shape_offset);
ClassDB::bind_method(D_METHOD("get_emission_shape_offset"), &ParticleProcessMaterial::get_emission_shape_offset);
@ -2096,9 +2116,10 @@ void ParticleProcessMaterial::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "emission_color_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_emission_color_texture", "get_emission_color_texture");
ADD_PROPERTY(PropertyInfo(Variant::INT, "emission_point_count", PROPERTY_HINT_RANGE, "0,1000000,1"), "set_emission_point_count", "get_emission_point_count");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "emission_ring_axis"), "set_emission_ring_axis", "get_emission_ring_axis");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_ring_height"), "set_emission_ring_height", "get_emission_ring_height");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_ring_radius"), "set_emission_ring_radius", "get_emission_ring_radius");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_ring_inner_radius"), "set_emission_ring_inner_radius", "get_emission_ring_inner_radius");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_ring_height", PROPERTY_HINT_RANGE, "0,1000,0.01,or_greater"), "set_emission_ring_height", "get_emission_ring_height");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_ring_radius", PROPERTY_HINT_RANGE, "0,1000,0.01,or_greater"), "set_emission_ring_radius", "get_emission_ring_radius");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_ring_inner_radius", PROPERTY_HINT_RANGE, "0,1000,0.01,or_greater"), "set_emission_ring_inner_radius", "get_emission_ring_inner_radius");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_ring_cone_angle", PROPERTY_HINT_RANGE, "0,90,0.01,degrees"), "set_emission_ring_cone_angle", "get_emission_ring_cone_angle");
ADD_SUBGROUP("Angle", "");
ADD_MIN_MAX_PROPERTY("angle", "-720,720,0.1,or_less,or_greater,degrees", PARAM_ANGLE);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "angle_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_ANGLE);
@ -2276,6 +2297,7 @@ ParticleProcessMaterial::ParticleProcessMaterial() :
set_emission_ring_height(1);
set_emission_ring_radius(1);
set_emission_ring_inner_radius(0);
set_emission_ring_cone_angle(90);
set_emission_shape_offset(Vector3(0.0, 0.0, 0.0));
set_emission_shape_scale(Vector3(1.0, 1.0, 1.0));

View file

@ -259,6 +259,7 @@ private:
StringName emission_ring_height;
StringName emission_ring_radius;
StringName emission_ring_inner_radius;
StringName emission_ring_cone_angle;
StringName emission_shape_offset;
StringName emission_shape_scale;
@ -325,6 +326,7 @@ private:
real_t emission_ring_height = 0.0f;
real_t emission_ring_radius = 0.0f;
real_t emission_ring_inner_radius = 0.0f;
real_t emission_ring_cone_angle = 0.0f;
int emission_point_count = 1;
Vector3 emission_shape_offset;
Vector3 emission_shape_scale;
@ -417,6 +419,7 @@ public:
void set_emission_ring_height(real_t p_height);
void set_emission_ring_radius(real_t p_radius);
void set_emission_ring_inner_radius(real_t p_radius);
void set_emission_ring_cone_angle(real_t p_angle);
void set_emission_point_count(int p_count);
EmissionShape get_emission_shape() const;
@ -429,6 +432,7 @@ public:
real_t get_emission_ring_height() const;
real_t get_emission_ring_radius() const;
real_t get_emission_ring_inner_radius() const;
real_t get_emission_ring_cone_angle() const;
int get_emission_point_count() const;
void set_turbulence_enabled(bool p_turbulence_enabled);