Browse Source

Merge pull request #47801 from QbieShay/ring-emitter-3.x

Rémi Verschelde 4 years ago
parent
commit
0053b31592

+ 18 - 3
doc/classes/CPUParticles.xml

@@ -173,15 +173,27 @@
 		<member name="emission_box_extents" type="Vector3" setter="set_emission_box_extents" getter="get_emission_box_extents">
 		<member name="emission_box_extents" type="Vector3" setter="set_emission_box_extents" getter="get_emission_box_extents">
 			The rectangle's extents if [member emission_shape] is set to [constant EMISSION_SHAPE_BOX].
 			The rectangle's extents if [member emission_shape] is set to [constant EMISSION_SHAPE_BOX].
 		</member>
 		</member>
-		<member name="emission_colors" type="PoolColorArray" setter="set_emission_colors" getter="get_emission_colors" default="PoolColorArray(  )">
+		<member name="emission_colors" type="PoolColorArray" setter="set_emission_colors" getter="get_emission_colors">
 			Sets the [Color]s to modulate particles by when using [constant EMISSION_SHAPE_POINTS] or [constant EMISSION_SHAPE_DIRECTED_POINTS].
 			Sets the [Color]s to modulate particles by when using [constant EMISSION_SHAPE_POINTS] or [constant EMISSION_SHAPE_DIRECTED_POINTS].
 		</member>
 		</member>
 		<member name="emission_normals" type="PoolVector3Array" setter="set_emission_normals" getter="get_emission_normals">
 		<member name="emission_normals" type="PoolVector3Array" setter="set_emission_normals" getter="get_emission_normals">
 			Sets the direction the particles will be emitted in when using [constant EMISSION_SHAPE_DIRECTED_POINTS].
 			Sets the direction the particles will be emitted in when using [constant EMISSION_SHAPE_DIRECTED_POINTS].
 		</member>
 		</member>
-		<member name="emission_points" type="PoolVector3Array" setter="set_emission_points" getter="get_emission_points" default="PoolVector3Array(  )">
+		<member name="emission_points" type="PoolVector3Array" setter="set_emission_points" getter="get_emission_points">
 			Sets the initial positions to spawn particles when using [constant EMISSION_SHAPE_POINTS] or [constant EMISSION_SHAPE_DIRECTED_POINTS].
 			Sets the initial positions to spawn particles when using [constant EMISSION_SHAPE_POINTS] or [constant EMISSION_SHAPE_DIRECTED_POINTS].
 		</member>
 		</member>
+		<member name="emission_ring_axis" type="Vector3" setter="set_emission_ring_axis" getter="get_emission_ring_axis">
+			The axis for the ring shaped emitter when using [constant EMISSION_SHAPE_RING].
+		</member>
+		<member name="emission_ring_height" type="float" setter="set_emission_ring_height" getter="get_emission_ring_height">
+			The height for the ring shaped emitter when using [constant EMISSION_SHAPE_RING].
+		</member>
+		<member name="emission_ring_inner_radius" type="float" setter="set_emission_ring_inner_radius" getter="get_emission_ring_inner_radius">
+			The inner radius for the ring shaped emitter when using [constant EMISSION_SHAPE_RING].
+		</member>
+		<member name="emission_ring_radius" type="float" setter="set_emission_ring_radius" getter="get_emission_ring_radius">
+			The radius for the ring shaped emitter when using [constant EMISSION_SHAPE_RING].
+		</member>
 		<member name="emission_shape" type="int" setter="set_emission_shape" getter="get_emission_shape" enum="CPUParticles.EmissionShape" default="0">
 		<member name="emission_shape" type="int" setter="set_emission_shape" getter="get_emission_shape" enum="CPUParticles.EmissionShape" default="0">
 			Particles will be emitted inside this region. See [enum EmissionShape] for possible values.
 			Particles will be emitted inside this region. See [enum EmissionShape] for possible values.
 		</member>
 		</member>
@@ -380,7 +392,10 @@
 		<constant name="EMISSION_SHAPE_DIRECTED_POINTS" value="4" enum="EmissionShape">
 		<constant name="EMISSION_SHAPE_DIRECTED_POINTS" value="4" enum="EmissionShape">
 			Particles will be emitted at a position chosen randomly among [member emission_points]. Particle velocity and rotation will be set based on [member emission_normals]. Particle color will be modulated by [member emission_colors].
 			Particles will be emitted at a position chosen randomly among [member emission_points]. Particle velocity and rotation will be set based on [member emission_normals]. Particle color will be modulated by [member emission_colors].
 		</constant>
 		</constant>
-		<constant name="EMISSION_SHAPE_MAX" value="5" enum="EmissionShape">
+		<constant name="EMISSION_SHAPE_RING" value="5" enum="EmissionShape">
+			Particles will be emitted in a ring or cylinder.
+		</constant>
+		<constant name="EMISSION_SHAPE_MAX" value="6" enum="EmissionShape">
 			Represents the size of the [enum EmissionShape] enum.
 			Represents the size of the [enum EmissionShape] enum.
 		</constant>
 		</constant>
 	</constants>
 	</constants>

+ 16 - 1
doc/classes/ParticlesMaterial.xml

@@ -164,6 +164,18 @@
 		<member name="emission_point_texture" type="Texture" setter="set_emission_point_texture" getter="get_emission_point_texture">
 		<member name="emission_point_texture" type="Texture" setter="set_emission_point_texture" getter="get_emission_point_texture">
 			Particles will be emitted at positions determined by sampling this texture at a random position. Used with [constant EMISSION_SHAPE_POINTS] and [constant EMISSION_SHAPE_DIRECTED_POINTS]. Can be created automatically from mesh or node by selecting "Create Emission Points from Mesh/Node" under the "Particles" tool in the toolbar.
 			Particles will be emitted at positions determined by sampling this texture at a random position. Used with [constant EMISSION_SHAPE_POINTS] and [constant EMISSION_SHAPE_DIRECTED_POINTS]. Can be created automatically from mesh or node by selecting "Create Emission Points from Mesh/Node" under the "Particles" tool in the toolbar.
 		</member>
 		</member>
+		<member name="emission_ring_axis" type="Vector3" setter="set_emission_ring_axis" getter="get_emission_ring_axis" default="Vector3( 0, 0, 1 )">
+			The axis of the ring when using the emitter [constant EMISSION_SHAPE_RING].
+		</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>
+		<member name="emission_ring_inner_radius" type="float" setter="set_emission_ring_inner_radius" getter="get_emission_ring_inner_radius">
+			The inner radius of the ring when using the emitter [constant EMISSION_SHAPE_RING].
+		</member>
+		<member name="emission_ring_radius" type="float" setter="set_emission_ring_radius" getter="get_emission_ring_radius">
+			The radius of the ring when using the emitter [constant EMISSION_SHAPE_RING].
+		</member>
 		<member name="emission_shape" type="int" setter="set_emission_shape" getter="get_emission_shape" enum="ParticlesMaterial.EmissionShape" default="0">
 		<member name="emission_shape" type="int" setter="set_emission_shape" getter="get_emission_shape" enum="ParticlesMaterial.EmissionShape" default="0">
 			Particles will be emitted inside this region. Use [enum EmissionShape] constants for values.
 			Particles will be emitted inside this region. Use [enum EmissionShape] constants for values.
 		</member>
 		</member>
@@ -329,7 +341,10 @@
 		<constant name="EMISSION_SHAPE_DIRECTED_POINTS" value="4" enum="EmissionShape">
 		<constant name="EMISSION_SHAPE_DIRECTED_POINTS" value="4" enum="EmissionShape">
 			Particles will be emitted at a position determined by sampling a random point on the [member emission_point_texture]. Particle velocity and rotation will be set based on [member emission_normal_texture]. Particle color will be modulated by [member emission_color_texture].
 			Particles will be emitted at a position determined by sampling a random point on the [member emission_point_texture]. Particle velocity and rotation will be set based on [member emission_normal_texture]. Particle color will be modulated by [member emission_color_texture].
 		</constant>
 		</constant>
-		<constant name="EMISSION_SHAPE_MAX" value="5" enum="EmissionShape">
+		<constant name="EMISSION_SHAPE_RING" value="5" enum="EmissionShape">
+			Particles will be emitted in a ring or cylinder.
+		</constant>
+		<constant name="EMISSION_SHAPE_MAX" value="6" enum="EmissionShape">
 			Represents the size of the [enum EmissionShape] enum.
 			Represents the size of the [enum EmissionShape] enum.
 		</constant>
 		</constant>
 	</constants>
 	</constants>

+ 78 - 2
scene/3d/cpu_particles.cpp

@@ -389,6 +389,21 @@ void CPUParticles::set_emission_normals(const PoolVector<Vector3> &p_normals) {
 void CPUParticles::set_emission_colors(const PoolVector<Color> &p_colors) {
 void CPUParticles::set_emission_colors(const PoolVector<Color> &p_colors) {
 	emission_colors = p_colors;
 	emission_colors = p_colors;
 }
 }
+void CPUParticles::set_emission_ring_height(float p_height) {
+	emission_ring_height = p_height;
+}
+
+void CPUParticles::set_emission_ring_radius(float p_radius) {
+	emission_ring_radius = p_radius;
+}
+
+void CPUParticles::set_emission_ring_inner_radius(float p_offset) {
+	emission_ring_inner_radius = p_offset;
+}
+
+void CPUParticles::set_emission_ring_axis(Vector3 p_axis) {
+	emission_ring_axis = p_axis;
+}
 
 
 float CPUParticles::get_emission_sphere_radius() const {
 float CPUParticles::get_emission_sphere_radius() const {
 	return emission_sphere_radius;
 	return emission_sphere_radius;
@@ -407,6 +422,22 @@ PoolVector<Color> CPUParticles::get_emission_colors() const {
 	return emission_colors;
 	return emission_colors;
 }
 }
 
 
+float CPUParticles::get_emission_ring_height() const {
+	return emission_ring_height;
+}
+
+float CPUParticles::get_emission_ring_inner_radius() const {
+	return emission_ring_inner_radius;
+}
+
+float CPUParticles::get_emission_ring_radius() const {
+	return emission_ring_radius;
+}
+
+Vector3 CPUParticles::get_emission_ring_axis() const {
+	return emission_ring_axis;
+}
+
 CPUParticles::EmissionShape CPUParticles::get_emission_shape() const {
 CPUParticles::EmissionShape CPUParticles::get_emission_shape() const {
 	return emission_shape;
 	return emission_shape;
 }
 }
@@ -431,7 +462,7 @@ void CPUParticles::_validate_property(PropertyInfo &property) const {
 		property.usage = 0;
 		property.usage = 0;
 	}
 	}
 
 
-	if ((property.name == "emission_point_texture" || property.name == "emission_color_texture") && (emission_shape < EMISSION_SHAPE_POINTS)) {
+	if ((property.name == "emission_points" || property.name == "emission_colors") && (emission_shape != EMISSION_SHAPE_POINTS) && (emission_shape != EMISSION_SHAPE_DIRECTED_POINTS)) {
 		property.usage = 0;
 		property.usage = 0;
 	}
 	}
 
 
@@ -439,6 +470,10 @@ void CPUParticles::_validate_property(PropertyInfo &property) const {
 		property.usage = 0;
 		property.usage = 0;
 	}
 	}
 
 
+	if (property.name.begins_with("emission_ring") && emission_shape != EMISSION_SHAPE_RING) {
+		property.usage = 0;
+	}
+
 	if (property.name.begins_with("orbit_") && !flags[FLAG_DISABLE_Z]) {
 	if (property.name.begins_with("orbit_") && !flags[FLAG_DISABLE_Z]) {
 		property.usage = 0;
 		property.usage = 0;
 	}
 	}
@@ -734,6 +769,21 @@ void CPUParticles::_particles_process(float p_delta) {
 						p.base_color = emission_colors.get(random_idx);
 						p.base_color = emission_colors.get(random_idx);
 					}
 					}
 				} break;
 				} break;
+				case EMISSION_SHAPE_RING: {
+					float ring_random_angle = Math::randf() * 2.0 * Math_PI;
+					float ring_random_radius = Math::randf() * (emission_ring_radius - emission_ring_inner_radius) + emission_ring_inner_radius;
+					Vector3 axis = emission_ring_axis.normalized();
+					Vector3 ortho_axis = Vector3();
+					if (axis == Vector3(1.0, 0.0, 0.0)) {
+						ortho_axis = Vector3(0.0, 1.0, 0.0).cross(axis);
+					} else {
+						ortho_axis = Vector3(1.0, 0.0, 0.0).cross(axis);
+					}
+					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;
+				}
 				case EMISSION_SHAPE_MAX: { // Max value for validity check.
 				case EMISSION_SHAPE_MAX: { // Max value for validity check.
 					break;
 					break;
 				}
 				}
@@ -1194,6 +1244,10 @@ void CPUParticles::convert_from_particles(Node *p_particles) {
 	set_emission_shape(EmissionShape(material->get_emission_shape()));
 	set_emission_shape(EmissionShape(material->get_emission_shape()));
 	set_emission_sphere_radius(material->get_emission_sphere_radius());
 	set_emission_sphere_radius(material->get_emission_sphere_radius());
 	set_emission_box_extents(material->get_emission_box_extents());
 	set_emission_box_extents(material->get_emission_box_extents());
+	set_emission_ring_height(material->get_emission_ring_height());
+	set_emission_ring_inner_radius(material->get_emission_ring_inner_radius());
+	set_emission_ring_radius(material->get_emission_ring_radius());
+	set_emission_ring_axis(material->get_emission_ring_axis());
 
 
 	set_gravity(material->get_gravity());
 	set_gravity(material->get_gravity());
 	set_lifetime_randomness(material->get_lifetime_randomness());
 	set_lifetime_randomness(material->get_lifetime_randomness());
@@ -1327,6 +1381,18 @@ void CPUParticles::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("set_emission_colors", "array"), &CPUParticles::set_emission_colors);
 	ClassDB::bind_method(D_METHOD("set_emission_colors", "array"), &CPUParticles::set_emission_colors);
 	ClassDB::bind_method(D_METHOD("get_emission_colors"), &CPUParticles::get_emission_colors);
 	ClassDB::bind_method(D_METHOD("get_emission_colors"), &CPUParticles::get_emission_colors);
 
 
+	ClassDB::bind_method(D_METHOD("set_emission_ring_radius", "radius"), &CPUParticles::set_emission_ring_radius);
+	ClassDB::bind_method(D_METHOD("get_emission_ring_radius"), &CPUParticles::get_emission_ring_radius);
+
+	ClassDB::bind_method(D_METHOD("set_emission_ring_inner_radius", "offset"), &CPUParticles::set_emission_ring_inner_radius);
+	ClassDB::bind_method(D_METHOD("get_emission_ring_inner_radius"), &CPUParticles::get_emission_ring_inner_radius);
+
+	ClassDB::bind_method(D_METHOD("set_emission_ring_height", "height"), &CPUParticles::set_emission_ring_height);
+	ClassDB::bind_method(D_METHOD("get_emission_ring_height"), &CPUParticles::get_emission_ring_height);
+
+	ClassDB::bind_method(D_METHOD("set_emission_ring_axis", "axis"), &CPUParticles::set_emission_ring_axis);
+	ClassDB::bind_method(D_METHOD("get_emission_ring_axis"), &CPUParticles::get_emission_ring_axis);
+
 	ClassDB::bind_method(D_METHOD("get_gravity"), &CPUParticles::get_gravity);
 	ClassDB::bind_method(D_METHOD("get_gravity"), &CPUParticles::get_gravity);
 	ClassDB::bind_method(D_METHOD("set_gravity", "accel_vec"), &CPUParticles::set_gravity);
 	ClassDB::bind_method(D_METHOD("set_gravity", "accel_vec"), &CPUParticles::set_gravity);
 
 
@@ -1335,12 +1401,17 @@ void CPUParticles::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("_update_render_thread"), &CPUParticles::_update_render_thread);
 	ClassDB::bind_method(D_METHOD("_update_render_thread"), &CPUParticles::_update_render_thread);
 
 
 	ADD_GROUP("Emission Shape", "emission_");
 	ADD_GROUP("Emission Shape", "emission_");
-	ADD_PROPERTY(PropertyInfo(Variant::INT, "emission_shape", PROPERTY_HINT_ENUM, "Point,Sphere,Box,Points,Directed Points", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_emission_shape", "get_emission_shape");
+	ADD_PROPERTY(PropertyInfo(Variant::INT, "emission_shape", PROPERTY_HINT_ENUM, "Point,Sphere,Box,Points,Directed Points, Ring", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_emission_shape", "get_emission_shape");
 	ADD_PROPERTY(PropertyInfo(Variant::REAL, "emission_sphere_radius", PROPERTY_HINT_RANGE, "0.01,128,0.01"), "set_emission_sphere_radius", "get_emission_sphere_radius");
 	ADD_PROPERTY(PropertyInfo(Variant::REAL, "emission_sphere_radius", PROPERTY_HINT_RANGE, "0.01,128,0.01"), "set_emission_sphere_radius", "get_emission_sphere_radius");
 	ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "emission_box_extents"), "set_emission_box_extents", "get_emission_box_extents");
 	ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "emission_box_extents"), "set_emission_box_extents", "get_emission_box_extents");
 	ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR3_ARRAY, "emission_points"), "set_emission_points", "get_emission_points");
 	ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR3_ARRAY, "emission_points"), "set_emission_points", "get_emission_points");
 	ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR3_ARRAY, "emission_normals"), "set_emission_normals", "get_emission_normals");
 	ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR3_ARRAY, "emission_normals"), "set_emission_normals", "get_emission_normals");
 	ADD_PROPERTY(PropertyInfo(Variant::POOL_COLOR_ARRAY, "emission_colors"), "set_emission_colors", "get_emission_colors");
 	ADD_PROPERTY(PropertyInfo(Variant::POOL_COLOR_ARRAY, "emission_colors"), "set_emission_colors", "get_emission_colors");
+	ADD_PROPERTY(PropertyInfo(Variant::REAL, "emission_ring_radius", PROPERTY_HINT_RANGE, "0.01,1000,0.01,or_greater"), "set_emission_ring_radius", "get_emission_ring_radius");
+	ADD_PROPERTY(PropertyInfo(Variant::REAL, "emission_ring_inner_radius", PROPERTY_HINT_RANGE, "0.0,1000,0.01,or_greater"), "set_emission_ring_inner_radius", "get_emission_ring_inner_radius");
+	ADD_PROPERTY(PropertyInfo(Variant::REAL, "emission_ring_height", PROPERTY_HINT_RANGE, "0.0,100,0.01,or_greater"), "set_emission_ring_height", "get_emission_ring_height");
+	ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "emission_ring_axis"), "set_emission_ring_axis", "get_emission_ring_axis");
+
 	ADD_GROUP("Flags", "flag_");
 	ADD_GROUP("Flags", "flag_");
 	ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flag_align_y"), "set_particle_flag", "get_particle_flag", FLAG_ALIGN_Y_TO_VELOCITY);
 	ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flag_align_y"), "set_particle_flag", "get_particle_flag", FLAG_ALIGN_Y_TO_VELOCITY);
 	ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flag_rotate_y"), "set_particle_flag", "get_particle_flag", FLAG_ROTATE_Y);
 	ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flag_rotate_y"), "set_particle_flag", "get_particle_flag", FLAG_ROTATE_Y);
@@ -1426,6 +1497,7 @@ void CPUParticles::_bind_methods() {
 	BIND_ENUM_CONSTANT(EMISSION_SHAPE_BOX);
 	BIND_ENUM_CONSTANT(EMISSION_SHAPE_BOX);
 	BIND_ENUM_CONSTANT(EMISSION_SHAPE_POINTS);
 	BIND_ENUM_CONSTANT(EMISSION_SHAPE_POINTS);
 	BIND_ENUM_CONSTANT(EMISSION_SHAPE_DIRECTED_POINTS);
 	BIND_ENUM_CONSTANT(EMISSION_SHAPE_DIRECTED_POINTS);
+	BIND_ENUM_CONSTANT(EMISSION_SHAPE_RING);
 	BIND_ENUM_CONSTANT(EMISSION_SHAPE_MAX);
 	BIND_ENUM_CONSTANT(EMISSION_SHAPE_MAX);
 }
 }
 
 
@@ -1476,6 +1548,10 @@ CPUParticles::CPUParticles() {
 	set_emission_shape(EMISSION_SHAPE_POINT);
 	set_emission_shape(EMISSION_SHAPE_POINT);
 	set_emission_sphere_radius(1);
 	set_emission_sphere_radius(1);
 	set_emission_box_extents(Vector3(1, 1, 1));
 	set_emission_box_extents(Vector3(1, 1, 1));
+	set_emission_ring_height(1.0);
+	set_emission_ring_radius(1.0);
+	set_emission_ring_inner_radius(0.0);
+	set_emission_ring_axis(Vector3(0.0, 0.0, 1.0));
 
 
 	set_gravity(Vector3(0, -9.8, 0));
 	set_gravity(Vector3(0, -9.8, 0));
 
 

+ 13 - 0
scene/3d/cpu_particles.h

@@ -77,6 +77,7 @@ public:
 		EMISSION_SHAPE_BOX,
 		EMISSION_SHAPE_BOX,
 		EMISSION_SHAPE_POINTS,
 		EMISSION_SHAPE_POINTS,
 		EMISSION_SHAPE_DIRECTED_POINTS,
 		EMISSION_SHAPE_DIRECTED_POINTS,
+		EMISSION_SHAPE_RING,
 		EMISSION_SHAPE_MAX
 		EMISSION_SHAPE_MAX
 	};
 	};
 
 
@@ -172,6 +173,10 @@ private:
 	PoolVector<Vector3> emission_normals;
 	PoolVector<Vector3> emission_normals;
 	PoolVector<Color> emission_colors;
 	PoolVector<Color> emission_colors;
 	int emission_point_count;
 	int emission_point_count;
+	float emission_ring_height;
+	float emission_ring_inner_radius;
+	float emission_ring_radius;
+	Vector3 emission_ring_axis;
 
 
 	Vector3 gravity;
 	Vector3 gravity;
 
 
@@ -269,6 +274,10 @@ public:
 	void set_emission_normals(const PoolVector<Vector3> &p_normals);
 	void set_emission_normals(const PoolVector<Vector3> &p_normals);
 	void set_emission_colors(const PoolVector<Color> &p_colors);
 	void set_emission_colors(const PoolVector<Color> &p_colors);
 	void set_emission_point_count(int p_count);
 	void set_emission_point_count(int p_count);
+	void set_emission_ring_height(float p_height);
+	void set_emission_ring_inner_radius(float p_inner_radius);
+	void set_emission_ring_radius(float p_radius);
+	void set_emission_ring_axis(Vector3 p_axis);
 
 
 	EmissionShape get_emission_shape() const;
 	EmissionShape get_emission_shape() const;
 	float get_emission_sphere_radius() const;
 	float get_emission_sphere_radius() const;
@@ -277,6 +286,10 @@ public:
 	PoolVector<Vector3> get_emission_normals() const;
 	PoolVector<Vector3> get_emission_normals() const;
 	PoolVector<Color> get_emission_colors() const;
 	PoolVector<Color> get_emission_colors() const;
 	int get_emission_point_count() const;
 	int get_emission_point_count() const;
+	float get_emission_ring_height() const;
+	float get_emission_ring_inner_radius() const;
+	float get_emission_ring_radius() const;
+	Vector3 get_emission_ring_axis() const;
 
 
 	void set_gravity(const Vector3 &p_gravity);
 	void set_gravity(const Vector3 &p_gravity);
 	Vector3 get_gravity() const;
 	Vector3 get_gravity() const;

+ 96 - 2
scene/resources/particles_material.cpp

@@ -90,6 +90,10 @@ void ParticlesMaterial::init_shaders() {
 	shader_names->emission_texture_points = "emission_texture_points";
 	shader_names->emission_texture_points = "emission_texture_points";
 	shader_names->emission_texture_normal = "emission_texture_normal";
 	shader_names->emission_texture_normal = "emission_texture_normal";
 	shader_names->emission_texture_color = "emission_texture_color";
 	shader_names->emission_texture_color = "emission_texture_color";
+	shader_names->emission_ring_height = "ring_height";
+	shader_names->emission_ring_inner_radius = "ring_inner_radius";
+	shader_names->emission_ring_radius = "ring_radius";
+	shader_names->emission_ring_axis = "ring_axis";
 
 
 	shader_names->trail_divisor = "trail_divisor";
 	shader_names->trail_divisor = "trail_divisor";
 	shader_names->trail_size_modifier = "trail_size_modifier";
 	shader_names->trail_size_modifier = "trail_size_modifier";
@@ -187,6 +191,12 @@ void ParticlesMaterial::_update_shader() {
 				code += "uniform sampler2D emission_texture_color : hint_white;\n";
 				code += "uniform sampler2D emission_texture_color : hint_white;\n";
 			}
 			}
 		} break;
 		} break;
+		case EMISSION_SHAPE_RING: {
+			code += "uniform float ring_radius = 2.0;\n";
+			code += "uniform float ring_height = 1.0;\n";
+			code += "uniform float ring_inner_radius = 0.0;\n";
+			code += "uniform vec3 ring_axis = vec3(0.0, 0.0, 1.0);\n";
+		} break;
 		case EMISSION_SHAPE_MAX: { // Max value for validity check.
 		case EMISSION_SHAPE_MAX: { // Max value for validity check.
 			break;
 			break;
 		}
 		}
@@ -386,6 +396,28 @@ void ParticlesMaterial::_update_shader() {
 				}
 				}
 			}
 			}
 		} break;
 		} break;
+		case EMISSION_SHAPE_RING: {
+			code += "		float ring_spawn_angle = rand_from_seed(alt_seed) * 2.0 * pi;\n";
+			code += "		float ring_random_radius = rand_from_seed(alt_seed) * (ring_radius - ring_inner_radius) + ring_inner_radius;\n";
+			code += "		vec3 axis = normalize(ring_axis);\n";
+			code += "		vec3 ortho_axis = vec3(0.0);\n";
+			code += "		if (axis == vec3(1.0, 0.0, 0.0)) {\n";
+			code += "			ortho_axis = cross(axis, vec3(0.0, 1.0, 0.0));\n";
+			code += "		} else {\n";
+			code += " 			ortho_axis = cross(axis, vec3(1.0, 0.0, 0.0));\n";
+			code += "		}\n";
+			code += "		ortho_axis = normalize(ortho_axis);\n";
+			code += "		float s = sin(ring_spawn_angle);\n";
+			code += "		float c = cos(ring_spawn_angle);\n";
+			code += "		float oc = 1.0 - c;\n";
+			code += "		ortho_axis = mat3(\n";
+			code += "			vec3(c + axis.x * axis.x * oc, axis.x * axis.y * oc - axis.z * s, axis.x * axis.z *oc + axis.y * s),\n";
+			code += "			vec3(axis.x * axis.y * oc + s * axis.z, c + axis.y * axis.y * oc, axis.y * axis.z * oc - axis.x * s),\n";
+			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 += "		TRANSFORM[3].xyz = ortho_axis * ring_random_radius + (rand_from_seed(alt_seed) * ring_height - ring_height / 2.0) * axis;\n";
+		} break;
 		case EMISSION_SHAPE_MAX: { // Max value for validity check.
 		case EMISSION_SHAPE_MAX: { // Max value for validity check.
 			break;
 			break;
 		}
 		}
@@ -939,6 +971,26 @@ void ParticlesMaterial::set_emission_point_count(int p_count) {
 	VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_texture_point_count, p_count);
 	VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_texture_point_count, p_count);
 }
 }
 
 
+void ParticlesMaterial::set_emission_ring_height(float p_height) {
+	emission_ring_height = p_height;
+	VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_ring_height, p_height);
+}
+
+void ParticlesMaterial::set_emission_ring_radius(float p_radius) {
+	emission_ring_radius = p_radius;
+	VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_ring_radius, p_radius);
+}
+
+void ParticlesMaterial::set_emission_ring_inner_radius(float p_offset) {
+	emission_ring_inner_radius = p_offset;
+	VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_ring_inner_radius, p_offset);
+}
+
+void ParticlesMaterial::set_emission_ring_axis(Vector3 p_axis) {
+	emission_ring_axis = p_axis;
+	VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_ring_axis, p_axis);
+}
+
 ParticlesMaterial::EmissionShape ParticlesMaterial::get_emission_shape() const {
 ParticlesMaterial::EmissionShape ParticlesMaterial::get_emission_shape() const {
 	return emission_shape;
 	return emission_shape;
 }
 }
@@ -964,6 +1016,22 @@ int ParticlesMaterial::get_emission_point_count() const {
 	return emission_point_count;
 	return emission_point_count;
 }
 }
 
 
+float ParticlesMaterial::get_emission_ring_height() const {
+	return emission_ring_height;
+}
+
+float ParticlesMaterial::get_emission_ring_inner_radius() const {
+	return emission_ring_inner_radius;
+}
+
+float ParticlesMaterial::get_emission_ring_radius() const {
+	return emission_ring_radius;
+}
+
+Vector3 ParticlesMaterial::get_emission_ring_axis() const {
+	return emission_ring_axis;
+}
+
 void ParticlesMaterial::set_trail_divisor(int p_divisor) {
 void ParticlesMaterial::set_trail_divisor(int p_divisor) {
 	trail_divisor = p_divisor;
 	trail_divisor = p_divisor;
 	VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->trail_divisor, p_divisor);
 	VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->trail_divisor, p_divisor);
@@ -1039,7 +1107,7 @@ void ParticlesMaterial::_validate_property(PropertyInfo &property) const {
 		property.usage = 0;
 		property.usage = 0;
 	}
 	}
 
 
-	if ((property.name == "emission_point_texture" || property.name == "emission_color_texture") && (emission_shape < EMISSION_SHAPE_POINTS)) {
+	if ((property.name == "emission_point_texture" || property.name == "emission_color_texture") && (emission_shape != EMISSION_SHAPE_POINTS) && emission_shape != EMISSION_SHAPE_DIRECTED_POINTS) {
 		property.usage = 0;
 		property.usage = 0;
 	}
 	}
 
 
@@ -1051,6 +1119,10 @@ void ParticlesMaterial::_validate_property(PropertyInfo &property) const {
 		property.usage = 0;
 		property.usage = 0;
 	}
 	}
 
 
+	if ((property.name == "emission_ring_radius" || property.name == "emission_ring_height" || property.name == "emission_ring_inner_radius") && emission_shape != EMISSION_SHAPE_RING) {
+		property.usage = 0;
+	}
+
 	if (property.name.begins_with("orbit_") && !flags[FLAG_DISABLE_Z]) {
 	if (property.name.begins_with("orbit_") && !flags[FLAG_DISABLE_Z]) {
 		property.usage = 0;
 		property.usage = 0;
 	}
 	}
@@ -1109,6 +1181,18 @@ 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("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("get_emission_point_count"), &ParticlesMaterial::get_emission_point_count);
 
 
+	ClassDB::bind_method(D_METHOD("set_emission_ring_radius", "radius"), &ParticlesMaterial::set_emission_ring_radius);
+	ClassDB::bind_method(D_METHOD("get_emission_ring_radius"), &ParticlesMaterial::get_emission_ring_radius);
+
+	ClassDB::bind_method(D_METHOD("set_emission_ring_inner_radius", "offset"), &ParticlesMaterial::set_emission_ring_inner_radius);
+	ClassDB::bind_method(D_METHOD("get_emission_ring_inner_radius"), &ParticlesMaterial::get_emission_ring_inner_radius);
+
+	ClassDB::bind_method(D_METHOD("set_emission_ring_height", "height"), &ParticlesMaterial::set_emission_ring_height);
+	ClassDB::bind_method(D_METHOD("get_emission_ring_height"), &ParticlesMaterial::get_emission_ring_height);
+
+	ClassDB::bind_method(D_METHOD("set_emission_ring_axis", "axis"), &ParticlesMaterial::set_emission_ring_axis);
+	ClassDB::bind_method(D_METHOD("get_emission_ring_axis"), &ParticlesMaterial::get_emission_ring_axis);
+
 	ClassDB::bind_method(D_METHOD("set_trail_divisor", "divisor"), &ParticlesMaterial::set_trail_divisor);
 	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("get_trail_divisor"), &ParticlesMaterial::get_trail_divisor);
 
 
@@ -1131,13 +1215,18 @@ void ParticlesMaterial::_bind_methods() {
 	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_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_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_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::INT, "emission_shape", PROPERTY_HINT_ENUM, "Point,Sphere,Box,Points,Directed Points,Ring"), "set_emission_shape", "get_emission_shape");
 	ADD_PROPERTY(PropertyInfo(Variant::REAL, "emission_sphere_radius", PROPERTY_HINT_RANGE, "0.01,128,0.01,or_greater"), "set_emission_sphere_radius", "get_emission_sphere_radius");
 	ADD_PROPERTY(PropertyInfo(Variant::REAL, "emission_sphere_radius", PROPERTY_HINT_RANGE, "0.01,128,0.01,or_greater"), "set_emission_sphere_radius", "get_emission_sphere_radius");
 	ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "emission_box_extents"), "set_emission_box_extents", "get_emission_box_extents");
 	ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "emission_box_extents"), "set_emission_box_extents", "get_emission_box_extents");
 	ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "emission_point_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_emission_point_texture", "get_emission_point_texture");
 	ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "emission_point_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_emission_point_texture", "get_emission_point_texture");
 	ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "emission_normal_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_emission_normal_texture", "get_emission_normal_texture");
 	ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "emission_normal_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_emission_normal_texture", "get_emission_normal_texture");
 	ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "emission_color_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_emission_color_texture", "get_emission_color_texture");
 	ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "emission_color_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "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::INT, "emission_point_count", PROPERTY_HINT_RANGE, "0,1000000,1"), "set_emission_point_count", "get_emission_point_count");
+	ADD_PROPERTY(PropertyInfo(Variant::REAL, "emission_ring_radius", PROPERTY_HINT_RANGE, "0.01,1000,0.01,or_greater"), "set_emission_ring_radius", "get_emission_ring_radius");
+	ADD_PROPERTY(PropertyInfo(Variant::REAL, "emission_ring_inner_radius", PROPERTY_HINT_RANGE, "0.0,1000,0.01,or_greater"), "set_emission_ring_inner_radius", "get_emission_ring_inner_radius");
+	ADD_PROPERTY(PropertyInfo(Variant::REAL, "emission_ring_height", PROPERTY_HINT_RANGE, "0.0,100,0.01,or_greater"), "set_emission_ring_height", "get_emission_ring_height");
+	ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "emission_ring_axis"), "set_emission_ring_axis", "get_emission_ring_axis");
+
 	ADD_GROUP("Flags", "flag_");
 	ADD_GROUP("Flags", "flag_");
 	ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flag_align_y"), "set_flag", "get_flag", FLAG_ALIGN_Y_TO_VELOCITY);
 	ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flag_align_y"), "set_flag", "get_flag", FLAG_ALIGN_Y_TO_VELOCITY);
 	ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flag_rotate_y"), "set_flag", "get_flag", FLAG_ROTATE_Y);
 	ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flag_rotate_y"), "set_flag", "get_flag", FLAG_ROTATE_Y);
@@ -1223,6 +1312,7 @@ void ParticlesMaterial::_bind_methods() {
 	BIND_ENUM_CONSTANT(EMISSION_SHAPE_BOX);
 	BIND_ENUM_CONSTANT(EMISSION_SHAPE_BOX);
 	BIND_ENUM_CONSTANT(EMISSION_SHAPE_POINTS);
 	BIND_ENUM_CONSTANT(EMISSION_SHAPE_POINTS);
 	BIND_ENUM_CONSTANT(EMISSION_SHAPE_DIRECTED_POINTS);
 	BIND_ENUM_CONSTANT(EMISSION_SHAPE_DIRECTED_POINTS);
+	BIND_ENUM_CONSTANT(EMISSION_SHAPE_RING);
 	BIND_ENUM_CONSTANT(EMISSION_SHAPE_MAX);
 	BIND_ENUM_CONSTANT(EMISSION_SHAPE_MAX);
 }
 }
 
 
@@ -1250,6 +1340,10 @@ ParticlesMaterial::ParticlesMaterial() :
 	set_gravity(Vector3(0, -9.8, 0));
 	set_gravity(Vector3(0, -9.8, 0));
 	set_lifetime_randomness(0);
 	set_lifetime_randomness(0);
 	emission_point_count = 1;
 	emission_point_count = 1;
+	set_emission_ring_height(1.0);
+	set_emission_ring_inner_radius(0.0);
+	set_emission_ring_radius(2.0);
+	set_emission_ring_axis(Vector3(0.0, 0.0, 1.0));
 
 
 	for (int i = 0; i < PARAM_MAX; i++) {
 	for (int i = 0; i < PARAM_MAX; i++) {
 		set_param_randomness(Parameter(i), 0);
 		set_param_randomness(Parameter(i), 0);

+ 17 - 0
scene/resources/particles_material.h

@@ -68,6 +68,7 @@ public:
 		EMISSION_SHAPE_BOX,
 		EMISSION_SHAPE_BOX,
 		EMISSION_SHAPE_POINTS,
 		EMISSION_SHAPE_POINTS,
 		EMISSION_SHAPE_DIRECTED_POINTS,
 		EMISSION_SHAPE_DIRECTED_POINTS,
+		EMISSION_SHAPE_RING,
 		EMISSION_SHAPE_MAX
 		EMISSION_SHAPE_MAX
 	};
 	};
 
 
@@ -177,6 +178,10 @@ private:
 		StringName emission_texture_points;
 		StringName emission_texture_points;
 		StringName emission_texture_normal;
 		StringName emission_texture_normal;
 		StringName emission_texture_color;
 		StringName emission_texture_color;
+		StringName emission_ring_radius;
+		StringName emission_ring_inner_radius;
+		StringName emission_ring_height;
+		StringName emission_ring_axis;
 
 
 		StringName trail_divisor;
 		StringName trail_divisor;
 		StringName trail_size_modifier;
 		StringName trail_size_modifier;
@@ -215,6 +220,10 @@ private:
 	Ref<Texture> emission_normal_texture;
 	Ref<Texture> emission_normal_texture;
 	Ref<Texture> emission_color_texture;
 	Ref<Texture> emission_color_texture;
 	int emission_point_count;
 	int emission_point_count;
+	float emission_ring_height;
+	float emission_ring_radius;
+	float emission_ring_inner_radius;
+	Vector3 emission_ring_axis;
 
 
 	bool anim_loop;
 	bool anim_loop;
 
 
@@ -268,6 +277,10 @@ public:
 	void set_emission_normal_texture(const Ref<Texture> &p_normals);
 	void set_emission_normal_texture(const Ref<Texture> &p_normals);
 	void set_emission_color_texture(const Ref<Texture> &p_colors);
 	void set_emission_color_texture(const Ref<Texture> &p_colors);
 	void set_emission_point_count(int p_count);
 	void set_emission_point_count(int p_count);
+	void set_emission_ring_radius(float p_radius);
+	void set_emission_ring_inner_radius(float p_offset);
+	void set_emission_ring_height(float p_height);
+	void set_emission_ring_axis(Vector3 p_axis);
 
 
 	EmissionShape get_emission_shape() const;
 	EmissionShape get_emission_shape() const;
 	float get_emission_sphere_radius() const;
 	float get_emission_sphere_radius() const;
@@ -276,6 +289,10 @@ public:
 	Ref<Texture> get_emission_normal_texture() const;
 	Ref<Texture> get_emission_normal_texture() const;
 	Ref<Texture> get_emission_color_texture() const;
 	Ref<Texture> get_emission_color_texture() const;
 	int get_emission_point_count() const;
 	int get_emission_point_count() const;
+	float get_emission_ring_radius() const;
+	float get_emission_ring_inner_radius() const;
+	float get_emission_ring_height() const;
+	Vector3 get_emission_ring_axis() const;
 
 
 	void set_trail_divisor(int p_divisor);
 	void set_trail_divisor(int p_divisor);
 	int get_trail_divisor() const;
 	int get_trail_divisor() const;