Parcourir la source

Merge pull request #22622 from JFonS/fix_particle_animation

 Remove animation loop from ParticlesMaterial + improvements to CPUParticles2D
Rémi Verschelde il y a 6 ans
Parent
commit
4c63772f54

+ 1 - 0
drivers/gles2/rasterizer_storage_gles2.h

@@ -429,6 +429,7 @@ public:
 
 			int light_mode;
 			*/
+
 			bool uses_screen_texture;
 			bool uses_screen_uv;
 			bool uses_time;

+ 1 - 8
drivers/gles3/rasterizer_canvas_gles3.cpp

@@ -967,7 +967,6 @@ void RasterizerCanvasGLES3::_canvas_item_render_commands(Item *p_item, Item *cur
 				//enable instancing
 
 				state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_INSTANCE_CUSTOM, true);
-				state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_PARTICLES, true);
 				state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_INSTANCING, true);
 				//reset shader and force rebind
 				state.using_texture_rect = true;
@@ -976,10 +975,8 @@ void RasterizerCanvasGLES3::_canvas_item_render_commands(Item *p_item, Item *cur
 				RasterizerStorageGLES3::Texture *texture = _bind_canvas_texture(particles_cmd->texture, particles_cmd->normal_map);
 
 				if (texture) {
-					Size2 texpixel_size(1.0 / (texture->width / particles_cmd->h_frames), 1.0 / (texture->height / particles_cmd->v_frames));
+					Size2 texpixel_size(1.0 / texture->width, 1.0 / texture->height);
 					state.canvas_shader.set_uniform(CanvasShaderGLES3::COLOR_TEXPIXEL_SIZE, texpixel_size);
-				} else {
-					state.canvas_shader.set_uniform(CanvasShaderGLES3::COLOR_TEXPIXEL_SIZE, Vector2(1.0, 1.0));
 				}
 
 				if (!particles->use_local_coords) {
@@ -993,9 +990,6 @@ void RasterizerCanvasGLES3::_canvas_item_render_commands(Item *p_item, Item *cur
 					state.canvas_shader.set_uniform(CanvasShaderGLES3::MODELVIEW_MATRIX, state.final_transform * inv_xf);
 				}
 
-				state.canvas_shader.set_uniform(CanvasShaderGLES3::H_FRAMES, particles_cmd->h_frames);
-				state.canvas_shader.set_uniform(CanvasShaderGLES3::V_FRAMES, particles_cmd->v_frames);
-
 				glBindVertexArray(data.particle_quad_array); //use particle quad array
 				glBindBuffer(GL_ARRAY_BUFFER, particles->particle_buffers[0]); //bind particle buffer
 
@@ -1073,7 +1067,6 @@ void RasterizerCanvasGLES3::_canvas_item_render_commands(Item *p_item, Item *cur
 
 				state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_INSTANCE_CUSTOM, false);
 				state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_INSTANCING, false);
-				state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_PARTICLES, false);
 				state.using_texture_rect = true;
 				_set_texture_rect_mode(false);
 

+ 1 - 0
drivers/gles3/rasterizer_storage_gles3.h

@@ -449,6 +449,7 @@ public:
 			};
 
 			int light_mode;
+
 			bool uses_screen_texture;
 			bool uses_screen_uv;
 			bool uses_time;

+ 0 - 19
drivers/gles3/shaders/canvas.glsl

@@ -92,11 +92,6 @@ const bool at_light_pass = true;
 const bool at_light_pass = false;
 #endif
 
-#ifdef USE_PARTICLES
-uniform int h_frames;
-uniform int v_frames;
-#endif
-
 #if defined(USE_MATERIAL)
 
 /* clang-format off */
@@ -143,20 +138,6 @@ void main() {
 	highp vec4 outvec = vec4(vertex, 0.0, 1.0);
 #endif
 
-#ifdef USE_PARTICLES
-	//scale by texture size
-	outvec.xy /= color_texpixel_size;
-
-	//compute h and v frames and adjust UV interp for animation
-	int total_frames = h_frames * v_frames;
-	int frame = min(int(float(total_frames) * instance_custom.z), total_frames - 1);
-	float frame_w = 1.0 / float(h_frames);
-	float frame_h = 1.0 / float(v_frames);
-	uv_interp.x = uv_interp.x * frame_w + frame_w * float(frame % h_frames);
-	uv_interp.y = uv_interp.y * frame_h + frame_h * float(frame / h_frames);
-
-#endif
-
 #define extra_matrix extra_matrix_instance
 
 	{

+ 22 - 0
editor/plugins/particles_2d_editor_plugin.cpp

@@ -32,6 +32,7 @@
 
 #include "canvas_item_editor_plugin.h"
 #include "core/io/image_loader.h"
+#include "scene/2d/cpu_particles_2d.h"
 #include "scene/gui/separator.h"
 #include "scene/resources/particles_material.h"
 
@@ -82,6 +83,25 @@ void Particles2DEditorPlugin::_menu_callback(int p_idx) {
 
 			emission_mask->popup_centered_minsize();
 		} break;
+		case MENU_OPTION_CONVERT_TO_CPU_PARTICLES: {
+
+			UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo();
+
+			CPUParticles2D *cpu_particles = memnew(CPUParticles2D);
+			cpu_particles->convert_from_particles(particles);
+			cpu_particles->set_name(particles->get_name());
+			cpu_particles->set_transform(particles->get_transform());
+			cpu_particles->set_visible(particles->is_visible());
+			cpu_particles->set_pause_mode(particles->get_pause_mode());
+
+			undo_redo->create_action("Replace Particles by CPUParticles");
+			undo_redo->add_do_method(particles, "replace_by", cpu_particles);
+			undo_redo->add_undo_method(cpu_particles, "replace_by", particles);
+			undo_redo->add_do_reference(cpu_particles);
+			undo_redo->add_undo_reference(particles);
+			undo_redo->commit_action();
+
+		} break;
 	}
 }
 
@@ -355,6 +375,8 @@ Particles2DEditorPlugin::Particles2DEditorPlugin(EditorNode *p_node) {
 	menu->get_popup()->add_separator();
 	menu->get_popup()->add_item(TTR("Load Emission Mask"), MENU_LOAD_EMISSION_MASK);
 	//	menu->get_popup()->add_item(TTR("Clear Emission Mask"), MENU_CLEAR_EMISSION_MASK);
+	menu->get_popup()->add_separator();
+	menu->get_popup()->add_item(TTR("Convert to CPUParticles"), MENU_OPTION_CONVERT_TO_CPU_PARTICLES);
 	menu->set_text(TTR("Particles"));
 	toolbar->add_child(menu);
 

+ 2 - 1
editor/plugins/particles_2d_editor_plugin.h

@@ -46,7 +46,8 @@ class Particles2DEditorPlugin : public EditorPlugin {
 
 		MENU_GENERATE_VISIBILITY_RECT,
 		MENU_LOAD_EMISSION_MASK,
-		MENU_CLEAR_EMISSION_MASK
+		MENU_CLEAR_EMISSION_MASK,
+		MENU_OPTION_CONVERT_TO_CPU_PARTICLES
 	};
 
 	enum EmissionMode {

+ 105 - 1
scene/2d/canvas_item.cpp

@@ -44,12 +44,19 @@
 Mutex *CanvasItemMaterial::material_mutex = NULL;
 SelfList<CanvasItemMaterial>::List CanvasItemMaterial::dirty_materials;
 Map<CanvasItemMaterial::MaterialKey, CanvasItemMaterial::ShaderData> CanvasItemMaterial::shader_map;
+CanvasItemMaterial::ShaderNames *CanvasItemMaterial::shader_names = NULL;
 
 void CanvasItemMaterial::init_shaders() {
 
 #ifndef NO_THREADS
 	material_mutex = Mutex::create();
 #endif
+
+	shader_names = memnew(ShaderNames);
+
+	shader_names->particles_anim_h_frames = "particles_anim_h_frames";
+	shader_names->particles_anim_v_frames = "particles_anim_v_frames";
+	shader_names->particles_anim_loop = "particles_anim_loop";
 }
 
 void CanvasItemMaterial::finish_shaders() {
@@ -102,7 +109,37 @@ void CanvasItemMaterial::_update_shader() {
 		case LIGHT_MODE_UNSHADED: code += ",unshaded"; break;
 		case LIGHT_MODE_LIGHT_ONLY: code += ",light_only"; break;
 	}
-	code += ";\n"; //that's it.
+
+	code += ";\n";
+
+	if (particles_animation) {
+
+		code += "uniform int particles_anim_h_frames;\n";
+		code += "uniform int particles_anim_v_frames;\n";
+		code += "uniform bool particles_anim_loop;\n";
+
+		code += "void vertex() {\n";
+
+		code += "\tfloat h_frames = float(particles_anim_h_frames);\n";
+		code += "\tfloat v_frames = float(particles_anim_v_frames);\n";
+
+		code += "\tVERTEX.xy /= TEXTURE_PIXEL_SIZE * vec2(h_frames, v_frames);\n";
+
+		code += "\tint total_frames = particles_anim_h_frames * particles_anim_v_frames;\n";
+		code += "\tint frame = int(float(total_frames) * INSTANCE_CUSTOM.z);\n";
+		code += "\tif (particles_anim_loop) {\n";
+		code += "\t\tframe = abs(frame) % total_frames;\n";
+		code += "\t} else {\n";
+		code += "\t\tframe = clamp(frame, 0, total_frames - 1);\n";
+		code += "\t}\n";
+
+		code += "\tfloat frame_w = 1.0 / h_frames;\n";
+		code += "\tfloat frame_h = 1.0 / v_frames;\n";
+		code += "\tUV.x = UV.x * frame_w + frame_w * float(frame % particles_anim_h_frames);\n";
+		code += "\tUV.y = UV.y * frame_h + frame_h * float(frame / particles_anim_v_frames);\n";
+
+		code += "}\n";
+	}
 
 	ShaderData shader_data;
 	shader_data.shader = VS::get_singleton()->shader_create();
@@ -177,7 +214,52 @@ CanvasItemMaterial::LightMode CanvasItemMaterial::get_light_mode() const {
 	return light_mode;
 }
 
+void CanvasItemMaterial::set_particles_animation(bool p_particles_anim) {
+	particles_animation = p_particles_anim;
+	_queue_shader_change();
+	_change_notify();
+}
+
+bool CanvasItemMaterial::get_particles_animation() const {
+	return particles_animation;
+}
+
+void CanvasItemMaterial::set_particles_anim_h_frames(int p_frames) {
+
+	particles_anim_h_frames = p_frames;
+	VS::get_singleton()->material_set_param(_get_material(), shader_names->particles_anim_h_frames, p_frames);
+}
+
+int CanvasItemMaterial::get_particles_anim_h_frames() const {
+
+	return particles_anim_h_frames;
+}
+void CanvasItemMaterial::set_particles_anim_v_frames(int p_frames) {
+
+	particles_anim_v_frames = p_frames;
+	VS::get_singleton()->material_set_param(_get_material(), shader_names->particles_anim_v_frames, p_frames);
+}
+
+int CanvasItemMaterial::get_particles_anim_v_frames() const {
+
+	return particles_anim_v_frames;
+}
+
+void CanvasItemMaterial::set_particles_anim_loop(bool p_loop) {
+
+	particles_anim_loop = p_loop;
+	VS::get_singleton()->material_set_param(_get_material(), shader_names->particles_anim_loop, particles_anim_loop);
+}
+
+bool CanvasItemMaterial::get_particles_anim_loop() const {
+
+	return particles_anim_loop;
+}
+
 void CanvasItemMaterial::_validate_property(PropertyInfo &property) const {
+	if (property.name.begins_with("particles_anim_") && !particles_animation) {
+		property.usage = 0;
+	}
 }
 
 RID CanvasItemMaterial::get_shader_rid() const {
@@ -199,8 +281,25 @@ void CanvasItemMaterial::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("set_light_mode", "light_mode"), &CanvasItemMaterial::set_light_mode);
 	ClassDB::bind_method(D_METHOD("get_light_mode"), &CanvasItemMaterial::get_light_mode);
 
+	ClassDB::bind_method(D_METHOD("set_particles_animation", "particles_anim"), &CanvasItemMaterial::set_particles_animation);
+	ClassDB::bind_method(D_METHOD("get_particles_animation"), &CanvasItemMaterial::get_particles_animation);
+
+	ClassDB::bind_method(D_METHOD("set_particles_anim_h_frames", "frames"), &CanvasItemMaterial::set_particles_anim_h_frames);
+	ClassDB::bind_method(D_METHOD("get_particles_anim_h_frames"), &CanvasItemMaterial::get_particles_anim_h_frames);
+
+	ClassDB::bind_method(D_METHOD("set_particles_anim_v_frames", "frames"), &CanvasItemMaterial::set_particles_anim_v_frames);
+	ClassDB::bind_method(D_METHOD("get_particles_anim_v_frames"), &CanvasItemMaterial::get_particles_anim_v_frames);
+
+	ClassDB::bind_method(D_METHOD("set_particles_anim_loop", "loop"), &CanvasItemMaterial::set_particles_anim_loop);
+	ClassDB::bind_method(D_METHOD("get_particles_anim_loop"), &CanvasItemMaterial::get_particles_anim_loop);
+
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "blend_mode", PROPERTY_HINT_ENUM, "Mix,Add,Sub,Mul,Premult Alpha"), "set_blend_mode", "get_blend_mode");
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "light_mode", PROPERTY_HINT_ENUM, "Normal,Unshaded,Light Only"), "set_light_mode", "get_light_mode");
+	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "particles_animation"), "set_particles_animation", "get_particles_animation");
+
+	ADD_PROPERTY(PropertyInfo(Variant::INT, "particles_anim_h_frames", PROPERTY_HINT_RANGE, "1,128,1"), "set_particles_anim_h_frames", "get_particles_anim_h_frames");
+	ADD_PROPERTY(PropertyInfo(Variant::INT, "particles_anim_v_frames", PROPERTY_HINT_RANGE, "1,128,1"), "set_particles_anim_v_frames", "get_particles_anim_v_frames");
+	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "particles_anim_loop"), "set_particles_anim_loop", "get_particles_anim_loop");
 
 	BIND_ENUM_CONSTANT(BLEND_MODE_MIX);
 	BIND_ENUM_CONSTANT(BLEND_MODE_ADD);
@@ -218,6 +317,11 @@ CanvasItemMaterial::CanvasItemMaterial() :
 
 	blend_mode = BLEND_MODE_MIX;
 	light_mode = LIGHT_MODE_NORMAL;
+	particles_animation = false;
+
+	set_particles_anim_h_frames(1);
+	set_particles_anim_v_frames(1);
+	set_particles_anim_loop(false);
 
 	current_key.key = 0;
 	current_key.invalid_key = 1;

+ 26 - 0
scene/2d/canvas_item.h

@@ -70,6 +70,7 @@ private:
 		struct {
 			uint32_t blend_mode : 4;
 			uint32_t light_mode : 4;
+			uint32_t particles_animation : 1;
 			uint32_t invalid_key : 1;
 		};
 
@@ -80,6 +81,14 @@ private:
 		}
 	};
 
+	struct ShaderNames {
+		StringName particles_anim_h_frames;
+		StringName particles_anim_v_frames;
+		StringName particles_anim_loop;
+	};
+
+	static ShaderNames *shader_names;
+
 	struct ShaderData {
 		RID shader;
 		int users;
@@ -95,6 +104,7 @@ private:
 		mk.key = 0;
 		mk.blend_mode = blend_mode;
 		mk.light_mode = light_mode;
+		mk.particles_animation = particles_animation;
 		return mk;
 	}
 
@@ -108,6 +118,11 @@ private:
 
 	BlendMode blend_mode;
 	LightMode light_mode;
+	bool particles_animation;
+
+	int particles_anim_h_frames;
+	int particles_anim_v_frames;
+	bool particles_anim_loop;
 
 protected:
 	static void _bind_methods();
@@ -120,6 +135,17 @@ public:
 	void set_light_mode(LightMode p_light_mode);
 	LightMode get_light_mode() const;
 
+	void set_particles_animation(bool p_particles_anim);
+	bool get_particles_animation() const;
+
+	void set_particles_anim_h_frames(int p_frames);
+	int get_particles_anim_h_frames() const;
+	void set_particles_anim_v_frames(int p_frames);
+	int get_particles_anim_v_frames() const;
+
+	void set_particles_anim_loop(bool p_frames);
+	bool get_particles_anim_loop() const;
+
 	static void init_shaders();
 	static void finish_shaders();
 	static void flush_changes();

+ 50 - 39
scene/2d/cpu_particles_2d.cpp

@@ -29,8 +29,9 @@
 /*************************************************************************/
 
 #include "cpu_particles_2d.h"
-
-//#include "scene/resources/particles_material.h"
+#include "particles_2d.h"
+#include "scene/2d/canvas_item.h"
+#include "scene/resources/particles_material.h"
 #include "servers/visual_server.h"
 
 void CPUParticles2D::set_emitting(bool p_emitting) {
@@ -152,19 +153,13 @@ CPUParticles2D::DrawOrder CPUParticles2D::get_draw_order() const {
 	return draw_order;
 }
 
-void CPUParticles2D::_update_mesh_texture() {
+void CPUParticles2D::_generate_mesh_texture() {
 
-	Size2 tex_size;
-	if (texture.is_valid()) {
-		tex_size = texture->get_size();
-	} else {
-		tex_size = Size2(1, 1);
-	}
 	PoolVector<Vector2> vertices;
-	vertices.push_back(-tex_size * 0.5);
-	vertices.push_back(-tex_size * 0.5 + Vector2(tex_size.x, 0));
-	vertices.push_back(-tex_size * 0.5 + Vector2(tex_size.x, tex_size.y));
-	vertices.push_back(-tex_size * 0.5 + Vector2(0, tex_size.y));
+	vertices.push_back(Vector2(-0.5, -0.5));
+	vertices.push_back(Vector2(0.5, -0.5));
+	vertices.push_back(Vector2(0.5, 0.5));
+	vertices.push_back(Vector2(-0.5, 0.5));
 	PoolVector<Vector2> uvs;
 	uvs.push_back(Vector2(0, 0));
 	uvs.push_back(Vector2(1, 0));
@@ -198,7 +193,6 @@ void CPUParticles2D::set_texture(const Ref<Texture> &p_texture) {
 
 	texture = p_texture;
 	update();
-	_update_mesh_texture();
 }
 
 Ref<Texture> CPUParticles2D::get_texture() const {
@@ -237,6 +231,14 @@ String CPUParticles2D::get_configuration_warning() const {
 
 	String warnings;
 
+	CanvasItemMaterial *mat = Object::cast_to<CanvasItemMaterial>(get_material().ptr());
+
+	if (get_material().is_null() || (mat && !mat->get_particles_animation())) {
+		if (warnings != String())
+			warnings += "\n";
+		warnings += "- " + TTR("CPUParticles2D animation requires the usage of a CanvasItemMaterial with \"Particles Animation\" enabled.");
+	}
+
 	return warnings;
 }
 
@@ -396,6 +398,7 @@ bool CPUParticles2D::get_particle_flag(Flags p_flag) const {
 void CPUParticles2D::set_emission_shape(EmissionShape p_shape) {
 
 	emission_shape = p_shape;
+	_change_notify();
 }
 
 void CPUParticles2D::set_emission_sphere_radius(float p_radius) {
@@ -479,6 +482,15 @@ void CPUParticles2D::_validate_property(PropertyInfo &property) const {
 	if (property.name == "emission_normals" && emission_shape != EMISSION_SHAPE_DIRECTED_POINTS) {
 		property.usage = 0;
 	}
+
+	if (property.name == "emission_points" && emission_shape != EMISSION_SHAPE_POINTS && emission_shape != EMISSION_SHAPE_DIRECTED_POINTS) {
+		property.usage = 0;
+	}
+
+	if (property.name == "emission_colors" && emission_shape != EMISSION_SHAPE_POINTS && emission_shape != EMISSION_SHAPE_DIRECTED_POINTS) {
+		property.usage = 0;
+	}
+
 	/*
 	if (property.name.begins_with("orbit_") && !flags[FLAG_DISABLE_Z]) {
 		property.usage = 0;
@@ -531,7 +543,7 @@ void CPUParticles2D::_particles_process(float p_delta) {
 	if (!local_coords) {
 		emission_xform = get_global_transform();
 		velocity_xform = emission_xform;
-		emission_xform[2] = Vector2();
+		velocity_xform[2] = Vector2();
 	}
 
 	for (int i = 0; i < pcount; i++) {
@@ -618,9 +630,12 @@ void CPUParticles2D::_particles_process(float p_delta) {
 			p.velocity = rot * parameters[PARAM_INITIAL_LINEAR_VELOCITY] * Math::lerp(1.0f, float(Math::randf()), randomness[PARAM_INITIAL_LINEAR_VELOCITY]);
 
 			float base_angle = (parameters[PARAM_ANGLE] + tex_angle) * Math::lerp(1.0f, p.angle_rand, randomness[PARAM_ANGLE]);
-			p.custom[0] = Math::deg2rad(base_angle); //angle
-			p.custom[1] = 0.0; //phase
-			p.custom[2] = (parameters[PARAM_ANIM_OFFSET] + tex_anim_offset) * Math::lerp(1.0f, p.anim_offset_rand, randomness[PARAM_ANIM_OFFSET]); //animation offset (0-1)
+			p.rotation = Math::deg2rad(base_angle);
+
+			p.custom[0] = 0.0; // unused
+			p.custom[1] = 0.0; // phase [0..1]
+			p.custom[2] = (parameters[PARAM_ANIM_OFFSET] + tex_anim_offset) * Math::lerp(1.0f, p.anim_offset_rand, randomness[PARAM_ANIM_OFFSET]); //animation phase [0..1]
+			p.custom[3] = 0.0;
 			p.transform = Transform2D();
 			p.time = 0;
 			p.base_color = Color(1, 1, 1, 1);
@@ -767,14 +782,9 @@ void CPUParticles2D::_particles_process(float p_delta) {
 			}
 			float base_angle = (parameters[PARAM_ANGLE] + tex_angle) * Math::lerp(1.0f, p.angle_rand, randomness[PARAM_ANGLE]);
 			base_angle += p.custom[1] * lifetime * (parameters[PARAM_ANGULAR_VELOCITY] + tex_angular_velocity) * Math::lerp(1.0f, rand_from_seed(alt_seed) * 2.0f - 1.0f, randomness[PARAM_ANGULAR_VELOCITY]);
-			p.custom[0] = Math::deg2rad(base_angle); //angle
-			p.custom[2] = (parameters[PARAM_ANIM_OFFSET] + tex_anim_offset) * Math::lerp(1.0f, p.anim_offset_rand, randomness[PARAM_ANIM_OFFSET]) + p.custom[1] * (parameters[PARAM_ANIM_SPEED] + tex_anim_speed) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_ANIM_SPEED]); //angle
-			if (flags[FLAG_ANIM_LOOP]) {
-				p.custom[2] = Math::fmod(p.custom[2], 1.0f); //loop
-
-			} else {
-				p.custom[2] = CLAMP(p.custom[2], 0.0f, 1.0); //0 to 1 only
-			}
+			p.rotation = Math::deg2rad(base_angle); //angle
+			float animation_phase = (parameters[PARAM_ANIM_OFFSET] + tex_anim_offset) * Math::lerp(1.0f, p.anim_offset_rand, randomness[PARAM_ANIM_OFFSET]) + p.custom[1] * (parameters[PARAM_ANIM_SPEED] + tex_anim_speed) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_ANIM_SPEED]);
+			p.custom[2] = animation_phase;
 		}
 		//apply color
 		//apply hue rotation
@@ -825,8 +835,8 @@ void CPUParticles2D::_particles_process(float p_delta) {
 			}
 
 		} else {
-			p.transform.elements[0] = Vector2(Math::cos(p.custom[0]), -Math::sin(p.custom[0]));
-			p.transform.elements[1] = Vector2(Math::sin(p.custom[0]), Math::cos(p.custom[0]));
+			p.transform.elements[0] = Vector2(Math::cos(p.rotation), -Math::sin(p.rotation));
+			p.transform.elements[1] = Vector2(Math::sin(p.rotation), Math::cos(p.rotation));
 		}
 
 		//scale by scale
@@ -1058,8 +1068,7 @@ void CPUParticles2D::_notification(int p_what) {
 }
 
 void CPUParticles2D::convert_from_particles(Node *p_particles) {
-#if 0
-	Particles *particles = Object::cast_to<Particles>(p_particles);
+	Particles2D *particles = Object::cast_to<Particles2D>(p_particles);
 	ERR_FAIL_COND(!particles);
 
 	set_emitting(particles->is_emitting());
@@ -1074,7 +1083,12 @@ void CPUParticles2D::convert_from_particles(Node *p_particles) {
 	set_fractional_delta(particles->get_fractional_delta());
 	set_speed_scale(particles->get_speed_scale());
 	set_draw_order(DrawOrder(particles->get_draw_order()));
-	set_mesh(particles->get_draw_pass_mesh(0));
+	set_texture(particles->get_texture());
+
+	Ref<Material> mat = particles->get_material();
+	if (mat.is_valid()) {
+		set_material(mat);
+	}
 
 	Ref<ParticlesMaterial> material = particles->get_process_material();
 	if (material.is_null())
@@ -1091,15 +1105,14 @@ void CPUParticles2D::convert_from_particles(Node *p_particles) {
 	}
 
 	set_particle_flag(FLAG_ALIGN_Y_TO_VELOCITY, material->get_flag(ParticlesMaterial::FLAG_ALIGN_Y_TO_VELOCITY));
-	set_particle_flag(FLAG_ROTATE_Y, material->get_flag(ParticlesMaterial::FLAG_ROTATE_Y));
-	set_particle_flag(FLAG_DISABLE_Z, material->get_flag(ParticlesMaterial::FLAG_DISABLE_Z));
-	set_particle_flag(FLAG_ANIM_LOOP, material->get_flag(ParticlesMaterial::FLAG_ANIM_LOOP));
 
 	set_emission_shape(EmissionShape(material->get_emission_shape()));
 	set_emission_sphere_radius(material->get_emission_sphere_radius());
-	set_emission_rect_extents(material->get_emission_rect_extents());
+	Vector2 rect_extents = Vector2(material->get_emission_box_extents().x, material->get_emission_box_extents().y);
+	set_emission_rect_extents(rect_extents);
 
-	set_gravity(material->get_gravity());
+	Vector2 gravity = Vector2(material->get_gravity().x, material->get_gravity().y);
+	set_gravity(gravity);
 
 #define CONVERT_PARAM(m_param)                                                            \
 	set_param(m_param, material->get_param(ParticlesMaterial::m_param));                  \
@@ -1123,7 +1136,6 @@ void CPUParticles2D::convert_from_particles(Node *p_particles) {
 	CONVERT_PARAM(PARAM_ANIM_OFFSET);
 
 #undef CONVERT_PARAM
-#endif
 }
 
 void CPUParticles2D::_bind_methods() {
@@ -1301,7 +1313,6 @@ void CPUParticles2D::_bind_methods() {
 	ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anim_offset", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param", "get_param", PARAM_ANIM_OFFSET);
 	ADD_PROPERTYI(PropertyInfo(Variant::REAL, "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, "Curve"), "set_param_curve", "get_param_curve", PARAM_ANIM_OFFSET);
-	ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "anim_loop"), "set_particle_flag", "get_particle_flag", FLAG_ANIM_LOOP);
 
 	BIND_ENUM_CONSTANT(PARAM_INITIAL_LINEAR_VELOCITY);
 	BIND_ENUM_CONSTANT(PARAM_ANGULAR_VELOCITY);
@@ -1385,7 +1396,7 @@ CPUParticles2D::CPUParticles2D() {
 	update_mutex = Mutex::create();
 #endif
 
-	_update_mesh_texture();
+	_generate_mesh_texture();
 }
 
 CPUParticles2D::~CPUParticles2D() {

+ 11 - 3
scene/2d/cpu_particles_2d.h

@@ -68,7 +68,6 @@ public:
 
 	enum Flags {
 		FLAG_ALIGN_Y_TO_VELOCITY,
-		FLAG_ANIM_LOOP,
 		FLAG_MAX
 	};
 
@@ -87,6 +86,7 @@ private:
 		Transform2D transform;
 		Color color;
 		float custom[4];
+		float rotation;
 		Vector2 velocity;
 		bool active;
 		float angle_rand;
@@ -168,7 +168,6 @@ private:
 	PoolVector<Color> emission_colors;
 	int emission_point_count;
 
-	bool anim_loop;
 	Vector2 gravity;
 
 	void _particles_process(float p_delta);
@@ -178,7 +177,7 @@ private:
 
 	void _update_render_thread();
 
-	void _update_mesh_texture();
+	void _generate_mesh_texture();
 
 protected:
 	static void _bind_methods();
@@ -223,6 +222,15 @@ public:
 	void set_texture(const Ref<Texture> &p_texture);
 	Ref<Texture> get_texture() const;
 
+	void set_h_frames(int p_frames);
+	int get_h_frames();
+
+	void set_v_frames(int p_frames);
+	int get_v_frames();
+
+	void set_loop_animation(bool p_loop);
+	bool get_loop_animation() const;
+
 	void set_normalmap(const Ref<Texture> &p_normalmap);
 	Ref<Texture> get_normalmap() const;
 

+ 1 - 35
scene/2d/particles_2d.cpp

@@ -257,30 +257,6 @@ Ref<Texture> Particles2D::get_normal_map() const {
 void Particles2D::_validate_property(PropertyInfo &property) const {
 }
 
-void Particles2D::set_v_frames(int p_count) {
-
-	ERR_FAIL_COND(p_count < 1);
-	v_frames = p_count;
-	update();
-}
-
-int Particles2D::get_v_frames() const {
-
-	return v_frames;
-}
-
-void Particles2D::set_h_frames(int p_count) {
-
-	ERR_FAIL_COND(p_count < 1);
-	h_frames = p_count;
-	update();
-}
-
-int Particles2D::get_h_frames() const {
-
-	return h_frames;
-}
-
 void Particles2D::restart() {
 	VS::get_singleton()->particles_restart(particles);
 }
@@ -296,7 +272,7 @@ void Particles2D::_notification(int p_what) {
 		if (normal_map.is_valid())
 			normal_rid = normal_map->get_rid();
 
-		VS::get_singleton()->canvas_item_add_particles(get_canvas_item(), particles, texture_rid, normal_rid, h_frames, v_frames);
+		VS::get_singleton()->canvas_item_add_particles(get_canvas_item(), particles, texture_rid, normal_rid);
 
 #ifdef TOOLS_ENABLED
 		if (Engine::get_singleton()->is_editor_hint() && (this == get_tree()->get_edited_scene_root() || get_tree()->get_edited_scene_root()->is_a_parent_of(this))) {
@@ -361,12 +337,6 @@ void Particles2D::_bind_methods() {
 
 	ClassDB::bind_method(D_METHOD("capture_rect"), &Particles2D::capture_rect);
 
-	ClassDB::bind_method(D_METHOD("set_v_frames", "frames"), &Particles2D::set_v_frames);
-	ClassDB::bind_method(D_METHOD("get_v_frames"), &Particles2D::get_v_frames);
-
-	ClassDB::bind_method(D_METHOD("set_h_frames", "frames"), &Particles2D::set_h_frames);
-	ClassDB::bind_method(D_METHOD("get_h_frames"), &Particles2D::get_h_frames);
-
 	ClassDB::bind_method(D_METHOD("restart"), &Particles2D::restart);
 
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emitting"), "set_emitting", "is_emitting");
@@ -389,8 +359,6 @@ void Particles2D::_bind_methods() {
 	ADD_GROUP("Textures", "");
 	ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture");
 	ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "normal_map", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_normal_map", "get_normal_map");
-	ADD_PROPERTY(PropertyInfo(Variant::INT, "h_frames", PROPERTY_HINT_RANGE, "1,1024,1"), "set_h_frames", "get_h_frames");
-	ADD_PROPERTY(PropertyInfo(Variant::INT, "v_frames", PROPERTY_HINT_RANGE, "1,1024,1"), "set_v_frames", "get_v_frames");
 
 	BIND_ENUM_CONSTANT(DRAW_ORDER_INDEX);
 	BIND_ENUM_CONSTANT(DRAW_ORDER_LIFETIME);
@@ -413,8 +381,6 @@ Particles2D::Particles2D() {
 	set_use_local_coordinates(true);
 	set_draw_order(DRAW_ORDER_INDEX);
 	set_speed_scale(1);
-	h_frames = 1;
-	v_frames = 1;
 }
 
 Particles2D::~Particles2D() {

+ 0 - 8
scene/2d/particles_2d.h

@@ -59,8 +59,6 @@ private:
 	bool local_coords;
 	int fixed_fps;
 	bool fractional_delta;
-	int v_frames;
-	int h_frames;
 
 	Ref<Material> process_material;
 
@@ -118,12 +116,6 @@ public:
 
 	virtual String get_configuration_warning() const;
 
-	void set_v_frames(int p_count);
-	int get_v_frames() const;
-
-	void set_h_frames(int p_count);
-	int get_h_frames() const;
-
 	void restart();
 	Rect2 capture_rect() const;
 	Particles2D();

+ 0 - 8
scene/3d/cpu_particles.cpp

@@ -781,12 +781,6 @@ void CPUParticles::_particles_process(float p_delta) {
 			base_angle += p.custom[1] * lifetime * (parameters[PARAM_ANGULAR_VELOCITY] + tex_angular_velocity) * Math::lerp(1.0f, rand_from_seed(alt_seed) * 2.0f - 1.0f, randomness[PARAM_ANGULAR_VELOCITY]);
 			p.custom[0] = Math::deg2rad(base_angle); //angle
 			p.custom[2] = (parameters[PARAM_ANIM_OFFSET] + tex_anim_offset) * Math::lerp(1.0f, p.anim_offset_rand, randomness[PARAM_ANIM_OFFSET]) + p.custom[1] * (parameters[PARAM_ANIM_SPEED] + tex_anim_speed) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_ANIM_SPEED]); //angle
-			if (flags[FLAG_ANIM_LOOP]) {
-				p.custom[2] = Math::fmod(p.custom[2], 1.0f); //loop
-
-			} else {
-				p.custom[2] = CLAMP(p.custom[2], 0.0f, 1.0); //0 to 1 only
-			}
 		}
 		//apply color
 		//apply hue rotation
@@ -1151,7 +1145,6 @@ void CPUParticles::convert_from_particles(Node *p_particles) {
 	set_particle_flag(FLAG_ALIGN_Y_TO_VELOCITY, material->get_flag(ParticlesMaterial::FLAG_ALIGN_Y_TO_VELOCITY));
 	set_particle_flag(FLAG_ROTATE_Y, material->get_flag(ParticlesMaterial::FLAG_ROTATE_Y));
 	set_particle_flag(FLAG_DISABLE_Z, material->get_flag(ParticlesMaterial::FLAG_DISABLE_Z));
-	set_particle_flag(FLAG_ANIM_LOOP, material->get_flag(ParticlesMaterial::FLAG_ANIM_LOOP));
 
 	set_emission_shape(EmissionShape(material->get_emission_shape()));
 	set_emission_sphere_radius(material->get_emission_sphere_radius());
@@ -1357,7 +1350,6 @@ void CPUParticles::_bind_methods() {
 	ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anim_offset", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param", "get_param", PARAM_ANIM_OFFSET);
 	ADD_PROPERTYI(PropertyInfo(Variant::REAL, "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, "Curve"), "set_param_curve", "get_param_curve", PARAM_ANIM_OFFSET);
-	ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "anim_loop"), "set_particle_flag", "get_particle_flag", FLAG_ANIM_LOOP);
 
 	BIND_ENUM_CONSTANT(PARAM_INITIAL_LINEAR_VELOCITY);
 	BIND_ENUM_CONSTANT(PARAM_ANGULAR_VELOCITY);

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

@@ -70,7 +70,6 @@ public:
 		FLAG_ALIGN_Y_TO_VELOCITY,
 		FLAG_ROTATE_Y,
 		FLAG_DISABLE_Z,
-		FLAG_ANIM_LOOP,
 		FLAG_MAX
 	};
 
@@ -170,7 +169,6 @@ private:
 	PoolVector<Color> emission_colors;
 	int emission_point_count;
 
-	bool anim_loop;
 	Vector3 gravity;
 
 	void _particles_process(float p_delta);

+ 6 - 6
scene/resources/material.cpp

@@ -612,7 +612,7 @@ void SpatialMaterial::_update_shader() {
 			//handle animation
 			code += "\tfloat particle_total_frames = float(particles_anim_h_frames * particles_anim_v_frames);\n";
 			code += "\tfloat particle_frame = floor(INSTANCE_CUSTOM.z * float(particle_total_frames));\n";
-			code += "\tif (particles_anim_loop) particle_frame=clamp(particle_frame,0.0,particle_total_frames-1.0); else particle_frame=mod(particle_frame,float(particle_total_frames));\n";
+			code += "\tif (!particles_anim_loop) particle_frame=clamp(particle_frame,0.0,particle_total_frames-1.0); else particle_frame=mod(particle_frame,float(particle_total_frames));\n";
 			code += "\tUV /= vec2(float(particles_anim_h_frames),float(particles_anim_v_frames));\n";
 			code += "\tUV += vec2(mod(particle_frame,float(particles_anim_h_frames)) / float(particles_anim_h_frames), floor(particle_frame / float(particles_anim_h_frames)) / float(particles_anim_v_frames));\n";
 		} break;
@@ -1541,13 +1541,13 @@ int SpatialMaterial::get_particles_anim_v_frames() const {
 	return particles_anim_v_frames;
 }
 
-void SpatialMaterial::set_particles_anim_loop(int p_frames) {
+void SpatialMaterial::set_particles_anim_loop(bool p_loop) {
 
-	particles_anim_loop = p_frames;
-	VS::get_singleton()->material_set_param(_get_material(), shader_names->particles_anim_loop, p_frames);
+	particles_anim_loop = p_loop;
+	VS::get_singleton()->material_set_param(_get_material(), shader_names->particles_anim_loop, particles_anim_loop);
 }
 
-int SpatialMaterial::get_particles_anim_loop() const {
+bool SpatialMaterial::get_particles_anim_loop() const {
 
 	return particles_anim_loop;
 }
@@ -1898,7 +1898,7 @@ void SpatialMaterial::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("set_particles_anim_v_frames", "frames"), &SpatialMaterial::set_particles_anim_v_frames);
 	ClassDB::bind_method(D_METHOD("get_particles_anim_v_frames"), &SpatialMaterial::get_particles_anim_v_frames);
 
-	ClassDB::bind_method(D_METHOD("set_particles_anim_loop", "frames"), &SpatialMaterial::set_particles_anim_loop);
+	ClassDB::bind_method(D_METHOD("set_particles_anim_loop", "loop"), &SpatialMaterial::set_particles_anim_loop);
 	ClassDB::bind_method(D_METHOD("get_particles_anim_loop"), &SpatialMaterial::get_particles_anim_loop);
 
 	ClassDB::bind_method(D_METHOD("set_depth_deep_parallax", "enable"), &SpatialMaterial::set_depth_deep_parallax);

+ 2 - 2
scene/resources/material.h

@@ -574,8 +574,8 @@ public:
 	void set_particles_anim_v_frames(int p_frames);
 	int get_particles_anim_v_frames() const;
 
-	void set_particles_anim_loop(int p_frames);
-	int get_particles_anim_loop() const;
+	void set_particles_anim_loop(bool p_loop);
+	bool get_particles_anim_loop() const;
 
 	void set_grow_enabled(bool p_enable);
 	bool is_grow_enabled() const;

+ 0 - 7
scene/resources/particles_material.cpp

@@ -463,12 +463,6 @@ void ParticlesMaterial::_update_shader() {
 	code += "		base_angle += CUSTOM.y * LIFETIME * (angular_velocity + tex_angular_velocity) * mix(1.0, rand_from_seed(alt_seed) * 2.0 - 1.0, angular_velocity_random);\n";
 	code += "		CUSTOM.x = base_angle * degree_to_rad;\n"; // angle
 	code += "		CUSTOM.z = (anim_offset + tex_anim_offset) * mix(1.0, anim_offset_rand, anim_offset_random) + CUSTOM.y * (anim_speed + tex_anim_speed) * mix(1.0, rand_from_seed(alt_seed), anim_speed_random);\n"; // angle
-	if (flags[FLAG_ANIM_LOOP]) {
-		code += "		CUSTOM.z = mod(CUSTOM.z, 1.0);\n"; // loop
-
-	} else {
-		code += "		CUSTOM.z = clamp(CUSTOM.z, 0.0, 1.0);\n"; // 0 to 1 only
-	}
 	code += "	}\n";
 	// apply color
 	// apply hue rotation
@@ -1175,7 +1169,6 @@ void ParticlesMaterial::_bind_methods() {
 	ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anim_offset", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param", "get_param", PARAM_ANIM_OFFSET);
 	ADD_PROPERTYI(PropertyInfo(Variant::REAL, "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_PROPERTYI(PropertyInfo(Variant::BOOL, "anim_loop"), "set_flag", "get_flag", FLAG_ANIM_LOOP);
 
 	BIND_ENUM_CONSTANT(PARAM_INITIAL_LINEAR_VELOCITY);
 	BIND_ENUM_CONSTANT(PARAM_ANGULAR_VELOCITY);

+ 0 - 1
scene/resources/particles_material.h

@@ -60,7 +60,6 @@ public:
 		FLAG_ALIGN_Y_TO_VELOCITY,
 		FLAG_ROTATE_Y,
 		FLAG_DISABLE_Z,
-		FLAG_ANIM_LOOP,
 		FLAG_MAX
 	};
 

+ 0 - 2
servers/visual/rasterizer.h

@@ -800,8 +800,6 @@ public:
 			RID particles;
 			RID texture;
 			RID normal_map;
-			int h_frames;
-			int v_frames;
 			CommandParticles() { type = TYPE_PARTICLES; }
 		};
 

+ 1 - 0
servers/visual/shader_types.cpp

@@ -199,6 +199,7 @@ ShaderTypes::ShaderTypes() {
 	shader_modes[VS::SHADER_CANVAS_ITEM].functions["vertex"].built_ins["TIME"] = constt(ShaderLanguage::TYPE_FLOAT);
 	shader_modes[VS::SHADER_CANVAS_ITEM].functions["vertex"].built_ins["INSTANCE_CUSTOM"] = constt(ShaderLanguage::TYPE_VEC4);
 	shader_modes[VS::SHADER_CANVAS_ITEM].functions["vertex"].built_ins["AT_LIGHT_PASS"] = constt(ShaderLanguage::TYPE_BOOL);
+	shader_modes[VS::SHADER_CANVAS_ITEM].functions["vertex"].built_ins["TEXTURE_PIXEL_SIZE"] = constt(ShaderLanguage::TYPE_VEC2);
 	shader_modes[VS::SHADER_CANVAS_ITEM].functions["vertex"].can_discard = false;
 
 	shader_modes[VS::SHADER_CANVAS_ITEM].functions["fragment"].built_ins["FRAGCOORD"] = constt(ShaderLanguage::TYPE_VEC4);

+ 1 - 3
servers/visual/visual_server_canvas.cpp

@@ -776,7 +776,7 @@ void VisualServerCanvas::canvas_item_add_mesh(RID p_item, const RID &p_mesh, RID
 
 	canvas_item->commands.push_back(m);
 }
-void VisualServerCanvas::canvas_item_add_particles(RID p_item, RID p_particles, RID p_texture, RID p_normal, int p_h_frames, int p_v_frames) {
+void VisualServerCanvas::canvas_item_add_particles(RID p_item, RID p_particles, RID p_texture, RID p_normal) {
 
 	Item *canvas_item = canvas_item_owner.getornull(p_item);
 	ERR_FAIL_COND(!canvas_item);
@@ -786,8 +786,6 @@ void VisualServerCanvas::canvas_item_add_particles(RID p_item, RID p_particles,
 	part->particles = p_particles;
 	part->texture = p_texture;
 	part->normal_map = p_normal;
-	part->h_frames = p_h_frames;
-	part->v_frames = p_v_frames;
 
 	//take the chance and request processing for them, at least once until they become visible again
 	VSG::storage->particles_request_process(p_particles);

+ 1 - 1
servers/visual/visual_server_canvas.h

@@ -186,7 +186,7 @@ public:
 	void canvas_item_add_triangle_array(RID p_item, const Vector<int> &p_indices, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), const Vector<int> &p_bones = Vector<int>(), const Vector<float> &p_weights = Vector<float>(), RID p_texture = RID(), int p_count = -1, RID p_normal_map = RID());
 	void canvas_item_add_mesh(RID p_item, const RID &p_mesh, RID p_texture = RID(), RID p_normal_map = RID());
 	void canvas_item_add_multimesh(RID p_item, RID p_mesh, RID p_texture = RID(), RID p_normal_map = RID());
-	void canvas_item_add_particles(RID p_item, RID p_particles, RID p_texture, RID p_normal, int p_h_frames, int p_v_frames);
+	void canvas_item_add_particles(RID p_item, RID p_particles, RID p_texture, RID p_normal);
 	void canvas_item_add_set_transform(RID p_item, const Transform2D &p_transform);
 	void canvas_item_add_clip_ignore(RID p_item, bool p_ignore);
 	void canvas_item_set_sort_children_by_y(RID p_item, bool p_enable);

+ 1 - 1
servers/visual/visual_server_raster.h

@@ -599,7 +599,7 @@ public:
 	BIND10(canvas_item_add_triangle_array, RID, const Vector<int> &, const Vector<Point2> &, const Vector<Color> &, const Vector<Point2> &, const Vector<int> &, const Vector<float> &, RID, int, RID)
 	BIND4(canvas_item_add_mesh, RID, const RID &, RID, RID)
 	BIND4(canvas_item_add_multimesh, RID, RID, RID, RID)
-	BIND6(canvas_item_add_particles, RID, RID, RID, RID, int, int)
+	BIND4(canvas_item_add_particles, RID, RID, RID, RID)
 	BIND2(canvas_item_add_set_transform, RID, const Transform2D &)
 	BIND2(canvas_item_add_clip_ignore, RID, bool)
 	BIND2(canvas_item_set_sort_children_by_y, RID, bool)

+ 1 - 1
servers/visual/visual_server_wrap_mt.h

@@ -516,7 +516,7 @@ public:
 	FUNC10(canvas_item_add_triangle_array, RID, const Vector<int> &, const Vector<Point2> &, const Vector<Color> &, const Vector<Point2> &, const Vector<int> &, const Vector<float> &, RID, int, RID)
 	FUNC4(canvas_item_add_mesh, RID, const RID &, RID, RID)
 	FUNC4(canvas_item_add_multimesh, RID, RID, RID, RID)
-	FUNC6(canvas_item_add_particles, RID, RID, RID, RID, int, int)
+	FUNC4(canvas_item_add_particles, RID, RID, RID, RID)
 	FUNC2(canvas_item_add_set_transform, RID, const Transform2D &)
 	FUNC2(canvas_item_add_clip_ignore, RID, bool)
 	FUNC2(canvas_item_set_sort_children_by_y, RID, bool)

+ 1 - 1
servers/visual_server.cpp

@@ -1978,7 +1978,7 @@ void VisualServer::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("canvas_item_add_triangle_array", "item", "indices", "points", "colors", "uvs", "bones", "weights", "texture", "count", "normal_map"), &VisualServer::canvas_item_add_triangle_array, DEFVAL(Vector<Point2>()), DEFVAL(Vector<int>()), DEFVAL(Vector<float>()), DEFVAL(RID()), DEFVAL(-1), DEFVAL(RID()));
 	ClassDB::bind_method(D_METHOD("canvas_item_add_mesh", "item", "mesh", "texture", "normal_map"), &VisualServer::canvas_item_add_mesh, DEFVAL(RID()));
 	ClassDB::bind_method(D_METHOD("canvas_item_add_multimesh", "item", "mesh", "texture", "normal_map"), &VisualServer::canvas_item_add_multimesh, DEFVAL(RID()));
-	ClassDB::bind_method(D_METHOD("canvas_item_add_particles", "item", "particles", "texture", "normal_map", "h_frames", "v_frames"), &VisualServer::canvas_item_add_particles);
+	ClassDB::bind_method(D_METHOD("canvas_item_add_particles", "item", "particles", "texture", "normal_map"), &VisualServer::canvas_item_add_particles);
 	ClassDB::bind_method(D_METHOD("canvas_item_add_set_transform", "item", "transform"), &VisualServer::canvas_item_add_set_transform);
 	ClassDB::bind_method(D_METHOD("canvas_item_add_clip_ignore", "item", "ignore"), &VisualServer::canvas_item_add_clip_ignore);
 	ClassDB::bind_method(D_METHOD("canvas_item_set_sort_children_by_y", "item", "enabled"), &VisualServer::canvas_item_set_sort_children_by_y);

+ 1 - 1
servers/visual_server.h

@@ -895,7 +895,7 @@ public:
 	virtual void canvas_item_add_triangle_array(RID p_item, const Vector<int> &p_indices, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), const Vector<int> &p_bones = Vector<int>(), const Vector<float> &p_weights = Vector<float>(), RID p_texture = RID(), int p_count = -1, RID p_normal_map = RID()) = 0;
 	virtual void canvas_item_add_mesh(RID p_item, const RID &p_mesh, RID p_texture = RID(), RID p_normal_map = RID()) = 0;
 	virtual void canvas_item_add_multimesh(RID p_item, RID p_mesh, RID p_texture = RID(), RID p_normal_map = RID()) = 0;
-	virtual void canvas_item_add_particles(RID p_item, RID p_particles, RID p_texture, RID p_normal_map, int p_h_frames, int p_v_frames) = 0;
+	virtual void canvas_item_add_particles(RID p_item, RID p_particles, RID p_texture, RID p_normal_map) = 0;
 	virtual void canvas_item_add_set_transform(RID p_item, const Transform2D &p_transform) = 0;
 	virtual void canvas_item_add_clip_ignore(RID p_item, bool p_ignore) = 0;
 	virtual void canvas_item_set_sort_children_by_y(RID p_item, bool p_enable) = 0;