mirror of
				https://github.com/godotengine/godot.git
				synced 2025-10-30 21:21:10 +00:00 
			
		
		
		
	2D GPU Particles working..
This commit is contained in:
		
							parent
							
								
									3c1fd26bb0
								
							
						
					
					
						commit
						95560e02c5
					
				
					 21 changed files with 1039 additions and 1340 deletions
				
			
		|  | @ -28,6 +28,7 @@ | |||
| /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */ | ||||
| /*************************************************************************/ | ||||
| #include "rasterizer_canvas_gles3.h" | ||||
| #include "servers/visual/visual_server_raster.h" | ||||
| 
 | ||||
| #include "global_config.h" | ||||
| #include "os/os.h" | ||||
|  | @ -607,6 +608,133 @@ void RasterizerCanvasGLES3::_canvas_item_render_commands(Item *p_item, Item *cur | |||
| 				} | ||||
| 				_draw_polygon(polygon->indices.ptr(), polygon->count, polygon->points.size(), polygon->points.ptr(), polygon->uvs.ptr(), polygon->colors.ptr(), polygon->colors.size() == 1); | ||||
| 
 | ||||
| 			} break; | ||||
| 			case Item::Command::TYPE_PARTICLES: { | ||||
| 
 | ||||
| 				Item::CommandParticles *particles_cmd = static_cast<Item::CommandParticles *>(c); | ||||
| 
 | ||||
| 				RasterizerStorageGLES3::Particles *particles = storage->particles_owner.getornull(particles_cmd->particles); | ||||
| 				if (!particles) | ||||
| 					break; | ||||
| 
 | ||||
| 				glVertexAttrib4f(VS::ARRAY_COLOR, 1, 1, 1, 1); //not used, so keep white
 | ||||
| 
 | ||||
| 				VisualServerRaster::redraw_request(); | ||||
| 
 | ||||
| 				storage->particles_request_process(particles_cmd->particles); | ||||
| 				//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; | ||||
| 				_set_texture_rect_mode(false); | ||||
| 
 | ||||
| 				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)); | ||||
| 					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) { | ||||
| 
 | ||||
| 					Transform2D inv_xf; | ||||
| 					inv_xf.set_axis(0, Vector2(particles->emission_transform.basis.get_axis(0).x, particles->emission_transform.basis.get_axis(0).y)); | ||||
| 					inv_xf.set_axis(1, Vector2(particles->emission_transform.basis.get_axis(1).x, particles->emission_transform.basis.get_axis(1).y)); | ||||
| 					inv_xf.set_origin(Vector2(particles->emission_transform.get_origin().x, particles->emission_transform.get_origin().y)); | ||||
| 					inv_xf.affine_invert(); | ||||
| 
 | ||||
| 					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
 | ||||
| 
 | ||||
| 				int stride = sizeof(float) * 4 * 6; | ||||
| 
 | ||||
| 				int amount = particles->amount; | ||||
| 
 | ||||
| 				if (particles->draw_order != VS::PARTICLES_DRAW_ORDER_LIFETIME) { | ||||
| 
 | ||||
| 					glEnableVertexAttribArray(8); //xform x
 | ||||
| 					glVertexAttribPointer(8, 4, GL_FLOAT, GL_FALSE, stride, ((uint8_t *)NULL) + sizeof(float) * 4 * 3); | ||||
| 					glVertexAttribDivisor(8, 1); | ||||
| 					glEnableVertexAttribArray(9); //xform y
 | ||||
| 					glVertexAttribPointer(9, 4, GL_FLOAT, GL_FALSE, stride, ((uint8_t *)NULL) + sizeof(float) * 4 * 4); | ||||
| 					glVertexAttribDivisor(9, 1); | ||||
| 					glEnableVertexAttribArray(10); //xform z
 | ||||
| 					glVertexAttribPointer(10, 4, GL_FLOAT, GL_FALSE, stride, ((uint8_t *)NULL) + sizeof(float) * 4 * 5); | ||||
| 					glVertexAttribDivisor(10, 1); | ||||
| 					glEnableVertexAttribArray(11); //color
 | ||||
| 					glVertexAttribPointer(11, 4, GL_FLOAT, GL_FALSE, stride, ((uint8_t *)NULL) + 0); | ||||
| 					glVertexAttribDivisor(11, 1); | ||||
| 					glEnableVertexAttribArray(12); //custom
 | ||||
| 					glVertexAttribPointer(12, 4, GL_FLOAT, GL_FALSE, stride, ((uint8_t *)NULL) + sizeof(float) * 4 * 2); | ||||
| 					glVertexAttribDivisor(12, 1); | ||||
| 
 | ||||
| 					glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, amount); | ||||
| 				} else { | ||||
| 					//split
 | ||||
| 
 | ||||
| 					int stride = sizeof(float) * 4 * 6; | ||||
| 					int split = int(Math::ceil(particles->phase * particles->amount)); | ||||
| 
 | ||||
| 					if (amount - split > 0) { | ||||
| 						glEnableVertexAttribArray(8); //xform x
 | ||||
| 						glVertexAttribPointer(8, 4, GL_FLOAT, GL_FALSE, stride, ((uint8_t *)NULL) + stride * split + sizeof(float) * 4 * 3); | ||||
| 						glVertexAttribDivisor(8, 1); | ||||
| 						glEnableVertexAttribArray(9); //xform y
 | ||||
| 						glVertexAttribPointer(9, 4, GL_FLOAT, GL_FALSE, stride, ((uint8_t *)NULL) + stride * split + sizeof(float) * 4 * 4); | ||||
| 						glVertexAttribDivisor(9, 1); | ||||
| 						glEnableVertexAttribArray(10); //xform z
 | ||||
| 						glVertexAttribPointer(10, 4, GL_FLOAT, GL_FALSE, stride, ((uint8_t *)NULL) + stride * split + sizeof(float) * 4 * 5); | ||||
| 						glVertexAttribDivisor(10, 1); | ||||
| 						glEnableVertexAttribArray(11); //color
 | ||||
| 						glVertexAttribPointer(11, 4, GL_FLOAT, GL_FALSE, stride, ((uint8_t *)NULL) + stride * split + 0); | ||||
| 						glVertexAttribDivisor(11, 1); | ||||
| 						glEnableVertexAttribArray(12); //custom
 | ||||
| 						glVertexAttribPointer(12, 4, GL_FLOAT, GL_FALSE, stride, ((uint8_t *)NULL) + stride * split + sizeof(float) * 4 * 2); | ||||
| 						glVertexAttribDivisor(12, 1); | ||||
| 
 | ||||
| 						glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, amount - split); | ||||
| 					} | ||||
| 
 | ||||
| 					if (split > 0) { | ||||
| 						glEnableVertexAttribArray(8); //xform x
 | ||||
| 						glVertexAttribPointer(8, 4, GL_FLOAT, GL_FALSE, stride, ((uint8_t *)NULL) + sizeof(float) * 4 * 3); | ||||
| 						glVertexAttribDivisor(8, 1); | ||||
| 						glEnableVertexAttribArray(9); //xform y
 | ||||
| 						glVertexAttribPointer(9, 4, GL_FLOAT, GL_FALSE, stride, ((uint8_t *)NULL) + sizeof(float) * 4 * 4); | ||||
| 						glVertexAttribDivisor(9, 1); | ||||
| 						glEnableVertexAttribArray(10); //xform z
 | ||||
| 						glVertexAttribPointer(10, 4, GL_FLOAT, GL_FALSE, stride, ((uint8_t *)NULL) + sizeof(float) * 4 * 5); | ||||
| 						glVertexAttribDivisor(10, 1); | ||||
| 						glEnableVertexAttribArray(11); //color
 | ||||
| 						glVertexAttribPointer(11, 4, GL_FLOAT, GL_FALSE, stride, ((uint8_t *)NULL) + 0); | ||||
| 						glVertexAttribDivisor(11, 1); | ||||
| 						glEnableVertexAttribArray(12); //custom
 | ||||
| 						glVertexAttribPointer(12, 4, GL_FLOAT, GL_FALSE, stride, ((uint8_t *)NULL) + sizeof(float) * 4 * 2); | ||||
| 						glVertexAttribDivisor(12, 1); | ||||
| 
 | ||||
| 						glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, split); | ||||
| 					} | ||||
| 				} | ||||
| 
 | ||||
| 				glBindVertexArray(0); | ||||
| 
 | ||||
| 				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); | ||||
| 
 | ||||
| 			} break; | ||||
| 			case Item::Command::TYPE_CIRCLE: { | ||||
| 
 | ||||
|  | @ -1351,7 +1479,39 @@ void RasterizerCanvasGLES3::initialize() { | |||
| 		glBindVertexArray(0); | ||||
| 		glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind
 | ||||
| 	} | ||||
| 	{ | ||||
| 		//particle quad buffers
 | ||||
| 
 | ||||
| 		glGenBuffers(1, &data.particle_quad_vertices); | ||||
| 		glBindBuffer(GL_ARRAY_BUFFER, data.particle_quad_vertices); | ||||
| 		{ | ||||
| 			//quad of size 1, with pivot on the center for particles, then regular UVS. Color is general plus fetched from particle
 | ||||
| 			const float qv[16] = { | ||||
| 				-0.5, -0.5, | ||||
| 				0.0, 0.0, | ||||
| 				-0.5, 0.5, | ||||
| 				0.0, 1.0, | ||||
| 				0.5, 0.5, | ||||
| 				1.0, 1.0, | ||||
| 				0.5, -0.5, | ||||
| 				1.0, 0.0 | ||||
| 			}; | ||||
| 
 | ||||
| 			glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 16, qv, GL_STATIC_DRAW); | ||||
| 		} | ||||
| 
 | ||||
| 		glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind
 | ||||
| 
 | ||||
| 		glGenVertexArrays(1, &data.particle_quad_array); | ||||
| 		glBindVertexArray(data.particle_quad_array); | ||||
| 		glBindBuffer(GL_ARRAY_BUFFER, data.particle_quad_vertices); | ||||
| 		glEnableVertexAttribArray(VS::ARRAY_VERTEX); | ||||
| 		glVertexAttribPointer(VS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, 0); | ||||
| 		glEnableVertexAttribArray(VS::ARRAY_TEX_UV); | ||||
| 		glVertexAttribPointer(VS::ARRAY_TEX_UV, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, (float *)0 + 2); | ||||
| 		glBindVertexArray(0); | ||||
| 		glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind
 | ||||
| 	} | ||||
| 	{ | ||||
| 
 | ||||
| 		uint32_t poly_size = GLOBAL_DEF("rendering/buffers/canvas_polygon_buffer_size_kb", 128); | ||||
|  | @ -1428,6 +1588,9 @@ void RasterizerCanvasGLES3::finalize() { | |||
| 	glDeleteBuffers(1, &data.canvas_quad_vertices); | ||||
| 	glDeleteVertexArrays(1, &data.canvas_quad_array); | ||||
| 
 | ||||
| 	glDeleteBuffers(1, &data.canvas_quad_vertices); | ||||
| 	glDeleteVertexArrays(1, &data.canvas_quad_array); | ||||
| 
 | ||||
| 	glDeleteVertexArrays(1, &data.polygon_buffer_pointer_array); | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -51,6 +51,10 @@ public: | |||
| 		GLuint polygon_buffer_quad_arrays[4]; | ||||
| 		GLuint polygon_buffer_pointer_array; | ||||
| 		GLuint polygon_index_buffer; | ||||
| 
 | ||||
| 		GLuint particle_quad_vertices; | ||||
| 		GLuint particle_quad_array; | ||||
| 
 | ||||
| 		uint32_t polygon_buffer_size; | ||||
| 
 | ||||
| 	} data; | ||||
|  |  | |||
|  | @ -2326,6 +2326,9 @@ void RasterizerStorageGLES3::_update_material(Material *material) { | |||
| 			if (E->get().order < 0) | ||||
| 				continue; // texture, does not go here
 | ||||
| 
 | ||||
| 			//if (material->shader->mode == VS::SHADER_PARTICLES) {
 | ||||
| 			//	print_line("uniform " + String(E->key()) + " order " + itos(E->get().order) + " offset " + itos(material->shader->ubo_offsets[E->get().order]));
 | ||||
| 			//}
 | ||||
| 			//regular uniform
 | ||||
| 			uint8_t *data = &local_ubo[material->shader->ubo_offsets[E->get().order]]; | ||||
| 
 | ||||
|  |  | |||
|  | @ -361,6 +361,8 @@ ShaderGLES3::Version *ShaderGLES3::get_current_version() { | |||
| 		ERR_FAIL_V(NULL); | ||||
| 	} | ||||
| 
 | ||||
| 	//_display_error_with_code("pepo", strings);
 | ||||
| 
 | ||||
| 	/* FRAGMENT SHADER */ | ||||
| 
 | ||||
| 	strings.resize(strings_base_size); | ||||
|  |  | |||
|  | @ -11,11 +11,26 @@ uniform vec4 src_rect; | |||
| 
 | ||||
| #else | ||||
| 
 | ||||
| #ifdef USE_INSTANCING | ||||
| 
 | ||||
| layout(location=8) in highp vec4 instance_xform0; | ||||
| layout(location=9) in highp vec4 instance_xform1; | ||||
| layout(location=10) in highp vec4 instance_xform2; | ||||
| layout(location=11) in lowp vec4 instance_color; | ||||
| 
 | ||||
| #ifdef USE_INSTANCE_CUSTOM | ||||
| layout(location=12) in highp vec4 instance_custom_data; | ||||
| #endif | ||||
| 
 | ||||
| #endif | ||||
| 
 | ||||
| layout(location=4) in highp vec2 uv_attrib; | ||||
| 
 | ||||
| //skeletn | ||||
| #endif | ||||
| 
 | ||||
| uniform highp vec2 color_texpixel_size; | ||||
| 
 | ||||
| 
 | ||||
| layout(std140) uniform CanvasItemData { //ubo:0 | ||||
| 
 | ||||
|  | @ -64,7 +79,10 @@ 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 | ||||
| 
 | ||||
| VERTEX_SHADER_GLOBALS | ||||
| 
 | ||||
|  | @ -82,6 +100,12 @@ void main() { | |||
| 
 | ||||
| 	vec4 vertex_color = color_attrib; | ||||
| 
 | ||||
| #ifdef USE_INSTANCING | ||||
| 	mat4 extra_matrix2 = extra_matrix * transpose(mat4(instance_xform0,instance_xform1,instance_xform2,vec4(0.0,0.0,0.0,1.0))); | ||||
| 	vertex_color*=instance_color; | ||||
| #else | ||||
| 	mat4 extra_matrix2 = extra_matrix; | ||||
| #endif | ||||
| 
 | ||||
| #ifdef USE_TEXTURE_RECT | ||||
| 
 | ||||
|  | @ -95,6 +119,22 @@ void main() { | |||
| #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_data.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 / v_frames); | ||||
| 
 | ||||
| #endif | ||||
| 
 | ||||
| #define extra_matrix extra_matrix2 | ||||
| 
 | ||||
| { | ||||
| 	vec2 src_vtx=outvec.xy; | ||||
| 
 | ||||
|  | @ -107,6 +147,8 @@ VERTEX_SHADER_CODE | |||
| 	outvec = modelview_matrix * outvec; | ||||
| #endif | ||||
| 
 | ||||
| #undef extra_matrix | ||||
| 
 | ||||
| 	color_interp = vertex_color; | ||||
| 
 | ||||
| #ifdef USE_PIXEL_SNAP | ||||
|  |  | |||
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 443 B | 
|  | @ -31,8 +31,8 @@ | |||
| 
 | ||||
| #include "canvas_item_editor_plugin.h" | ||||
| #include "io/image_loader.h" | ||||
| #include "scene/3d/particles.h" | ||||
| #include "scene/gui/separator.h" | ||||
| 
 | ||||
| void Particles2DEditorPlugin::edit(Object *p_object) { | ||||
| 
 | ||||
| 	if (p_object) { | ||||
|  | @ -62,65 +62,16 @@ void Particles2DEditorPlugin::_file_selected(const String &p_file) { | |||
| 
 | ||||
| 	print_line("file: " + p_file); | ||||
| 
 | ||||
| 	int epc = epoints->get_value(); | ||||
| 
 | ||||
| 	Ref<Image> img; | ||||
| 	img.instance(); | ||||
| 	Error err = ImageLoader::load_image(p_file, img); | ||||
| 	ERR_EXPLAIN(TTR("Error loading image:") + " " + p_file); | ||||
| 	ERR_FAIL_COND(err != OK); | ||||
| 
 | ||||
| 	img->convert(Image::FORMAT_LA8); | ||||
| 	ERR_FAIL_COND(img->get_format() != Image::FORMAT_LA8); | ||||
| 	Size2i s = Size2(img->get_width(), img->get_height()); | ||||
| 	ERR_FAIL_COND(s.width == 0 || s.height == 0); | ||||
| 
 | ||||
| 	PoolVector<uint8_t> data = img->get_data(); | ||||
| 	PoolVector<uint8_t>::Read r = data.read(); | ||||
| 
 | ||||
| 	Vector<Point2i> valid_positions; | ||||
| 	valid_positions.resize(s.width * s.height); | ||||
| 	int vpc = 0; | ||||
| 
 | ||||
| 	for (int i = 0; i < s.width * s.height; i++) { | ||||
| 
 | ||||
| 		uint8_t a = r[i * 2 + 1]; | ||||
| 		if (a > 128) { | ||||
| 			valid_positions[vpc++] = Point2i(i % s.width, i / s.width); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	valid_positions.resize(vpc); | ||||
| 
 | ||||
| 	ERR_EXPLAIN(TTR("No pixels with transparency > 128 in image..")); | ||||
| 	ERR_FAIL_COND(valid_positions.size() == 0); | ||||
| 
 | ||||
| 	PoolVector<Point2> epoints; | ||||
| 	epoints.resize(epc); | ||||
| 	PoolVector<Point2>::Write w = epoints.write(); | ||||
| 
 | ||||
| 	Size2 extents = Size2(img->get_width() * 0.5, img->get_height() * 0.5); | ||||
| 
 | ||||
| 	for (int i = 0; i < epc; i++) { | ||||
| 
 | ||||
| 		Point2 p = valid_positions[Math::rand() % vpc]; | ||||
| 		p -= s / 2; | ||||
| 		w[i] = p / extents; | ||||
| 	} | ||||
| 
 | ||||
| 	w = PoolVector<Point2>::Write(); | ||||
| 
 | ||||
| 	undo_redo->create_action(TTR("Set Emission Mask")); | ||||
| 	undo_redo->add_do_method(particles, "set_emission_points", epoints); | ||||
| 	undo_redo->add_do_method(particles, "set_emission_half_extents", extents); | ||||
| 	undo_redo->add_undo_method(particles, "set_emission_points", particles->get_emission_points()); | ||||
| 	undo_redo->add_undo_method(particles, "set_emission_half_extents", particles->get_emission_half_extents()); | ||||
| 	undo_redo->commit_action(); | ||||
| 	source_emission_file = p_file; | ||||
| 	emission_mask->popup_centered_minsize(); | ||||
| } | ||||
| 
 | ||||
| void Particles2DEditorPlugin::_menu_callback(int p_idx) { | ||||
| 
 | ||||
| 	switch (p_idx) { | ||||
| 		case MENU_GENERATE_VISIBILITY_RECT: { | ||||
| 			generate_aabb->popup_centered_minsize(); | ||||
| 		} break; | ||||
| 		case MENU_LOAD_EMISSION_MASK: { | ||||
| 
 | ||||
| 			file->popup_centered_ratio(); | ||||
|  | @ -128,14 +79,249 @@ void Particles2DEditorPlugin::_menu_callback(int p_idx) { | |||
| 		} break; | ||||
| 		case MENU_CLEAR_EMISSION_MASK: { | ||||
| 
 | ||||
| 			undo_redo->create_action(TTR("Clear Emission Mask")); | ||||
| 			emission_mask->popup_centered_minsize(); | ||||
| 
 | ||||
| 			/*undo_redo->create_action(TTR("Clear Emission Mask"));
 | ||||
| 			undo_redo->add_do_method(particles, "set_emission_points", PoolVector<Vector2>()); | ||||
| 			undo_redo->add_undo_method(particles, "set_emission_points", particles->get_emission_points()); | ||||
| 			undo_redo->commit_action(); | ||||
| 			undo_redo->commit_action();*/ | ||||
| 		} break; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void Particles2DEditorPlugin::_generate_visibility_rect() { | ||||
| 
 | ||||
| 	float time = generate_seconds->get_value(); | ||||
| 
 | ||||
| 	float running = 0.0; | ||||
| 
 | ||||
| 	EditorProgress ep("gen_aabb", TTR("Generating AABB"), int(time)); | ||||
| 
 | ||||
| 	Rect2 rect; | ||||
| 	while (running < time) { | ||||
| 
 | ||||
| 		uint64_t ticks = OS::get_singleton()->get_ticks_usec(); | ||||
| 		ep.step("Generating..", int(running), true); | ||||
| 		OS::get_singleton()->delay_usec(1000); | ||||
| 
 | ||||
| 		Rect2 capture = particles->capture_rect(); | ||||
| 		if (rect == Rect2()) | ||||
| 			rect = capture; | ||||
| 		else | ||||
| 			rect = rect.merge(capture); | ||||
| 
 | ||||
| 		running += (OS::get_singleton()->get_ticks_usec() - ticks) / 1000000.0; | ||||
| 	} | ||||
| 
 | ||||
| 	particles->set_visibility_rect(rect); | ||||
| } | ||||
| 
 | ||||
| void Particles2DEditorPlugin::_generate_emission_mask() { | ||||
| 
 | ||||
| 	Ref<ParticlesMaterial> pm = particles->get_process_material(); | ||||
| 	if (!pm.is_valid()) { | ||||
| 		EditorNode::get_singleton()->show_warning(TTR("Can only set point into a ParticlesMaterial process material")); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	Ref<Image> img; | ||||
| 	img.instance(); | ||||
| 	Error err = ImageLoader::load_image(source_emission_file, img); | ||||
| 	ERR_EXPLAIN(TTR("Error loading image:") + " " + source_emission_file); | ||||
| 	ERR_FAIL_COND(err != OK); | ||||
| 
 | ||||
| 	if (img->is_compressed()) { | ||||
| 		img->decompress(); | ||||
| 	} | ||||
| 	img->convert(Image::FORMAT_RGBA8); | ||||
| 	ERR_FAIL_COND(img->get_format() != Image::FORMAT_RGBA8); | ||||
| 	Size2i s = Size2(img->get_width(), img->get_height()); | ||||
| 	ERR_FAIL_COND(s.width == 0 || s.height == 0); | ||||
| 
 | ||||
| 	Vector<Point2> valid_positions; | ||||
| 	Vector<Point2> valid_normals; | ||||
| 	Vector<uint8_t> valid_colors; | ||||
| 
 | ||||
| 	valid_positions.resize(s.width * s.height); | ||||
| 
 | ||||
| 	EmissionMode emode = (EmissionMode)emission_mask_mode->get_selected(); | ||||
| 
 | ||||
| 	if (emode == EMISSION_MODE_BORDER_DIRECTED) { | ||||
| 		valid_normals.resize(s.width * s.height); | ||||
| 	} | ||||
| 
 | ||||
| 	bool capture_colors = emission_colors->is_pressed(); | ||||
| 
 | ||||
| 	if (capture_colors) { | ||||
| 		valid_colors.resize(s.width * s.height * 4); | ||||
| 	} | ||||
| 
 | ||||
| 	int vpc = 0; | ||||
| 
 | ||||
| 	{ | ||||
| 		PoolVector<uint8_t> data = img->get_data(); | ||||
| 		PoolVector<uint8_t>::Read r = data.read(); | ||||
| 
 | ||||
| 		for (int i = 0; i < s.width; i++) { | ||||
| 			for (int j = 0; j < s.height; j++) { | ||||
| 
 | ||||
| 				uint8_t a = r[(j * s.width + i) * 4 + 3]; | ||||
| 
 | ||||
| 				if (a > 128) { | ||||
| 
 | ||||
| 					if (emode == EMISSION_MODE_SOLID) { | ||||
| 
 | ||||
| 						if (capture_colors) { | ||||
| 							valid_colors[vpc * 4 + 0] = r[(j * s.width + i) * 4 + 0]; | ||||
| 							valid_colors[vpc * 4 + 1] = r[(j * s.width + i) * 4 + 1]; | ||||
| 							valid_colors[vpc * 4 + 2] = r[(j * s.width + i) * 4 + 2]; | ||||
| 							valid_colors[vpc * 4 + 3] = r[(j * s.width + i) * 4 + 3]; | ||||
| 						} | ||||
| 						valid_positions[vpc++] = Point2(i, j); | ||||
| 
 | ||||
| 					} else { | ||||
| 
 | ||||
| 						bool on_border = false; | ||||
| 						for (int x = i - 1; x <= i + 1; x++) { | ||||
| 							for (int y = j - 1; y <= j + 1; y++) { | ||||
| 
 | ||||
| 								if (x < 0 || y < 0 || x >= s.width || y >= s.height || r[(y * s.width + x) * 4 + 3] <= 128) { | ||||
| 									on_border = true; | ||||
| 									break; | ||||
| 								} | ||||
| 							} | ||||
| 
 | ||||
| 							if (on_border) | ||||
| 								break; | ||||
| 						} | ||||
| 
 | ||||
| 						if (on_border) { | ||||
| 							valid_positions[vpc] = Point2(i, j); | ||||
| 
 | ||||
| 							if (emode == EMISSION_MODE_BORDER_DIRECTED) { | ||||
| 								Vector2 normal; | ||||
| 								for (int x = i - 2; x <= i + 2; x++) { | ||||
| 									for (int y = j - 2; y <= j + 2; y++) { | ||||
| 
 | ||||
| 										if (x == i && y == j) | ||||
| 											continue; | ||||
| 
 | ||||
| 										if (x < 0 || y < 0 || x >= s.width || y >= s.height || r[(y * s.width + x) * 4 + 3] <= 128) { | ||||
| 											normal += Vector2(x - i, y - j).normalized(); | ||||
| 										} | ||||
| 									} | ||||
| 								} | ||||
| 
 | ||||
| 								normal.normalize(); | ||||
| 								valid_normals[vpc] = normal; | ||||
| 							} | ||||
| 
 | ||||
| 							if (capture_colors) { | ||||
| 								valid_colors[vpc * 4 + 0] = r[(j * s.width + i) * 4 + 0]; | ||||
| 								valid_colors[vpc * 4 + 1] = r[(j * s.width + i) * 4 + 1]; | ||||
| 								valid_colors[vpc * 4 + 2] = r[(j * s.width + i) * 4 + 2]; | ||||
| 								valid_colors[vpc * 4 + 3] = r[(j * s.width + i) * 4 + 3]; | ||||
| 							} | ||||
| 
 | ||||
| 							vpc++; | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	valid_positions.resize(vpc); | ||||
| 	if (valid_normals.size()) { | ||||
| 		valid_normals.resize(vpc); | ||||
| 	} | ||||
| 
 | ||||
| 	ERR_EXPLAIN(TTR("No pixels with transparency > 128 in image..")); | ||||
| 	ERR_FAIL_COND(valid_positions.size() == 0); | ||||
| 
 | ||||
| 	PoolVector<uint8_t> texdata; | ||||
| 
 | ||||
| 	int w = 2048; | ||||
| 	int h = (vpc / 2048) + 1; | ||||
| 
 | ||||
| 	texdata.resize(w * h * 2 * sizeof(float)); | ||||
| 
 | ||||
| 	{ | ||||
| 		PoolVector<uint8_t>::Write tw = texdata.write(); | ||||
| 		float *twf = (float *)tw.ptr(); | ||||
| 		for (int i = 0; i < vpc; i++) { | ||||
| 
 | ||||
| 			twf[i * 2 + 0] = valid_positions[i].x; | ||||
| 			twf[i * 2 + 1] = valid_positions[i].y; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	img.instance(); | ||||
| 	img->create(w, h, false, Image::FORMAT_RGF, texdata); | ||||
| 
 | ||||
| 	Ref<ImageTexture> imgt; | ||||
| 	imgt.instance(); | ||||
| 	imgt->create_from_image(img, 0); | ||||
| 
 | ||||
| 	pm->set_emission_point_texture(imgt); | ||||
| 	pm->set_emission_point_count(vpc); | ||||
| 
 | ||||
| 	if (capture_colors) { | ||||
| 
 | ||||
| 		PoolVector<uint8_t> colordata; | ||||
| 		colordata.resize(w * h * 4); //use RG texture
 | ||||
| 
 | ||||
| 		{ | ||||
| 			PoolVector<uint8_t>::Write tw = colordata.write(); | ||||
| 			for (int i = 0; i < vpc * 4; i++) { | ||||
| 
 | ||||
| 				tw[i] = valid_colors[i]; | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		img.instance(); | ||||
| 		img->create(w, h, false, Image::FORMAT_RGBA8, colordata); | ||||
| 
 | ||||
| 		imgt.instance(); | ||||
| 		imgt->create_from_image(img, 0); | ||||
| 		pm->set_emission_color_texture(imgt); | ||||
| 	} | ||||
| 
 | ||||
| 	if (valid_normals.size()) { | ||||
| 		pm->set_emission_shape(ParticlesMaterial::EMISSION_SHAPE_DIRECTED_POINTS); | ||||
| 
 | ||||
| 		PoolVector<uint8_t> normdata; | ||||
| 		normdata.resize(w * h * 2 * sizeof(float)); //use RG texture
 | ||||
| 
 | ||||
| 		{ | ||||
| 			PoolVector<uint8_t>::Write tw = normdata.write(); | ||||
| 			float *twf = (float *)tw.ptr(); | ||||
| 			for (int i = 0; i < vpc; i++) { | ||||
| 				twf[i * 2 + 0] = valid_normals[i].x; | ||||
| 				twf[i * 2 + 1] = valid_normals[i].y; | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		img.instance(); | ||||
| 		img->create(w, h, false, Image::FORMAT_RGF, normdata); | ||||
| 
 | ||||
| 		imgt.instance(); | ||||
| 		imgt->create_from_image(img, 0); | ||||
| 		pm->set_emission_normal_texture(imgt); | ||||
| 
 | ||||
| 	} else { | ||||
| 		pm->set_emission_shape(ParticlesMaterial::EMISSION_SHAPE_POINTS); | ||||
| 	} | ||||
| 
 | ||||
| 	/*undo_redo->create_action(TTR("Set Emission Mask"));
 | ||||
| 	undo_redo->add_do_method(particles, "set_emission_points", epoints); | ||||
| 	undo_redo->add_do_method(particles, "set_emission_half_extents", extents); | ||||
| 	undo_redo->add_undo_method(particles, "set_emission_points", particles->get_emission_points()); | ||||
| 	undo_redo->add_undo_method(particles, "set_emission_half_extents", particles->get_emission_half_extents()); | ||||
| 	undo_redo->commit_action(); | ||||
| 	*/ | ||||
| } | ||||
| 
 | ||||
| void Particles2DEditorPlugin::_notification(int p_what) { | ||||
| 
 | ||||
| 	if (p_what == NOTIFICATION_ENTER_TREE) { | ||||
|  | @ -150,6 +336,8 @@ void Particles2DEditorPlugin::_bind_methods() { | |||
| 
 | ||||
| 	ClassDB::bind_method(D_METHOD("_menu_callback"), &Particles2DEditorPlugin::_menu_callback); | ||||
| 	ClassDB::bind_method(D_METHOD("_file_selected"), &Particles2DEditorPlugin::_file_selected); | ||||
| 	ClassDB::bind_method(D_METHOD("_generate_visibility_rect"), &Particles2DEditorPlugin::_generate_visibility_rect); | ||||
| 	ClassDB::bind_method(D_METHOD("_generate_emission_mask"), &Particles2DEditorPlugin::_generate_emission_mask); | ||||
| } | ||||
| 
 | ||||
| Particles2DEditorPlugin::Particles2DEditorPlugin(EditorNode *p_node) { | ||||
|  | @ -165,8 +353,10 @@ Particles2DEditorPlugin::Particles2DEditorPlugin(EditorNode *p_node) { | |||
| 	toolbar->add_child(memnew(VSeparator)); | ||||
| 
 | ||||
| 	menu = memnew(MenuButton); | ||||
| 	menu->get_popup()->add_item(TTR("Generate Visibility Rect"), MENU_GENERATE_VISIBILITY_RECT); | ||||
| 	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_item(TTR("Clear Emission Mask"), MENU_CLEAR_EMISSION_MASK);
 | ||||
| 	menu->set_text("Particles"); | ||||
| 	toolbar->add_child(menu); | ||||
| 
 | ||||
|  | @ -185,6 +375,37 @@ Particles2DEditorPlugin::Particles2DEditorPlugin(EditorNode *p_node) { | |||
| 	epoints->set_step(1); | ||||
| 	epoints->set_value(512); | ||||
| 	file->get_vbox()->add_margin_child(TTR("Generated Point Count:"), epoints); | ||||
| 
 | ||||
| 	generate_aabb = memnew(ConfirmationDialog); | ||||
| 	generate_aabb->set_title(TTR("Generate Visibility Rect")); | ||||
| 	VBoxContainer *genvb = memnew(VBoxContainer); | ||||
| 	generate_aabb->add_child(genvb); | ||||
| 	generate_seconds = memnew(SpinBox); | ||||
| 	genvb->add_margin_child(TTR("Generation Time (sec):"), generate_seconds); | ||||
| 	generate_seconds->set_min(0.1); | ||||
| 	generate_seconds->set_max(25); | ||||
| 	generate_seconds->set_value(2); | ||||
| 
 | ||||
| 	toolbar->add_child(generate_aabb); | ||||
| 
 | ||||
| 	generate_aabb->connect("confirmed", this, "_generate_visibility_rect"); | ||||
| 
 | ||||
| 	emission_mask = memnew(ConfirmationDialog); | ||||
| 	emission_mask->set_title(TTR("Generate Visibility Rect")); | ||||
| 	VBoxContainer *emvb = memnew(VBoxContainer); | ||||
| 	emission_mask->add_child(emvb); | ||||
| 	emission_mask_mode = memnew(OptionButton); | ||||
| 	emvb->add_margin_child(TTR("Emission Mask"), emission_mask_mode); | ||||
| 	emission_mask_mode->add_item("Solid Pixels", EMISSION_MODE_SOLID); | ||||
| 	emission_mask_mode->add_item("Border Pixels", EMISSION_MODE_BORDER); | ||||
| 	emission_mask_mode->add_item("Directed Border Pixels", EMISSION_MODE_BORDER_DIRECTED); | ||||
| 	emission_colors = memnew(CheckBox); | ||||
| 	emission_colors->set_text(TTR("Capture from Pixel")); | ||||
| 	emvb->add_margin_child(TTR("Emission Colors"), emission_colors); | ||||
| 
 | ||||
| 	toolbar->add_child(emission_mask); | ||||
| 
 | ||||
| 	emission_mask->connect("confirmed", this, "_generate_emission_mask"); | ||||
| } | ||||
| 
 | ||||
| Particles2DEditorPlugin::~Particles2DEditorPlugin() { | ||||
|  |  | |||
|  | @ -44,10 +44,17 @@ class Particles2DEditorPlugin : public EditorPlugin { | |||
| 
 | ||||
| 	enum { | ||||
| 
 | ||||
| 		MENU_GENERATE_VISIBILITY_RECT, | ||||
| 		MENU_LOAD_EMISSION_MASK, | ||||
| 		MENU_CLEAR_EMISSION_MASK | ||||
| 	}; | ||||
| 
 | ||||
| 	enum EmissionMode { | ||||
| 		EMISSION_MODE_SOLID, | ||||
| 		EMISSION_MODE_BORDER, | ||||
| 		EMISSION_MODE_BORDER_DIRECTED | ||||
| 	}; | ||||
| 
 | ||||
| 	Particles2D *particles; | ||||
| 
 | ||||
| 	EditorFileDialog *file; | ||||
|  | @ -58,9 +65,20 @@ class Particles2DEditorPlugin : public EditorPlugin { | |||
| 
 | ||||
| 	SpinBox *epoints; | ||||
| 
 | ||||
| 	ConfirmationDialog *generate_aabb; | ||||
| 	SpinBox *generate_seconds; | ||||
| 
 | ||||
| 	ConfirmationDialog *emission_mask; | ||||
| 	OptionButton *emission_mask_mode; | ||||
| 	CheckBox *emission_colors; | ||||
| 
 | ||||
| 	String source_emission_file; | ||||
| 
 | ||||
| 	UndoRedo *undo_redo; | ||||
| 	void _file_selected(const String &p_file); | ||||
| 	void _menu_callback(int p_idx); | ||||
| 	void _generate_visibility_rect(); | ||||
| 	void _generate_emission_mask(); | ||||
| 
 | ||||
| protected: | ||||
| 	void _notification(int p_what); | ||||
|  |  | |||
|  | @ -417,14 +417,22 @@ void CanvasItem::draw_line(const Point2 &p_from, const Point2 &p_to, const Color | |||
| 	VisualServer::get_singleton()->canvas_item_add_line(canvas_item, p_from, p_to, p_color, p_width, p_antialiased); | ||||
| } | ||||
| 
 | ||||
| void CanvasItem::draw_rect(const Rect2 &p_rect, const Color &p_color) { | ||||
| void CanvasItem::draw_rect(const Rect2 &p_rect, const Color &p_color, bool p_filled) { | ||||
| 
 | ||||
| 	if (!drawing) { | ||||
| 		ERR_EXPLAIN("Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); | ||||
| 		ERR_FAIL(); | ||||
| 	} | ||||
| 
 | ||||
| 	VisualServer::get_singleton()->canvas_item_add_rect(canvas_item, p_rect, p_color); | ||||
| 	if (p_filled) { | ||||
| 
 | ||||
| 		VisualServer::get_singleton()->canvas_item_add_rect(canvas_item, p_rect, p_color); | ||||
| 	} else { | ||||
| 		VisualServer::get_singleton()->canvas_item_add_line(canvas_item, p_rect.position, p_rect.position + Size2(p_rect.size.width, 0), p_color); | ||||
| 		VisualServer::get_singleton()->canvas_item_add_line(canvas_item, p_rect.position, p_rect.position + Size2(0, p_rect.size.height), p_color); | ||||
| 		VisualServer::get_singleton()->canvas_item_add_line(canvas_item, p_rect.position + Point2(0, p_rect.size.height), p_rect.position + p_rect.size, p_color); | ||||
| 		VisualServer::get_singleton()->canvas_item_add_line(canvas_item, p_rect.position + Point2(p_rect.size.width, 0), p_rect.position + p_rect.size, p_color); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void CanvasItem::draw_circle(const Point2 &p_pos, float p_radius, const Color &p_color) { | ||||
|  | @ -754,7 +762,7 @@ void CanvasItem::_bind_methods() { | |||
| 	//ClassDB::bind_method(D_METHOD("get_transform"),&CanvasItem::get_transform);
 | ||||
| 
 | ||||
| 	ClassDB::bind_method(D_METHOD("draw_line", "from", "to", "color", "width", "antialiased"), &CanvasItem::draw_line, DEFVAL(1.0), DEFVAL(false)); | ||||
| 	ClassDB::bind_method(D_METHOD("draw_rect", "rect", "color"), &CanvasItem::draw_rect); | ||||
| 	ClassDB::bind_method(D_METHOD("draw_rect", "rect", "color", "filled"), &CanvasItem::draw_rect, DEFVAL(true)); | ||||
| 	ClassDB::bind_method(D_METHOD("draw_circle", "pos", "radius", "color"), &CanvasItem::draw_circle); | ||||
| 	ClassDB::bind_method(D_METHOD("draw_texture", "texture:Texture", "pos", "modulate", "normal_map:Texture"), &CanvasItem::draw_texture, DEFVAL(Color(1, 1, 1, 1)), DEFVAL(Variant())); | ||||
| 	ClassDB::bind_method(D_METHOD("draw_texture_rect", "texture:Texture", "rect", "tile", "modulate", "transpose", "normal_map:Texture"), &CanvasItem::draw_texture_rect, DEFVAL(Color(1, 1, 1)), DEFVAL(false), DEFVAL(Variant())); | ||||
|  |  | |||
|  | @ -156,7 +156,7 @@ public: | |||
| 	/* DRAWING API */ | ||||
| 
 | ||||
| 	void draw_line(const Point2 &p_from, const Point2 &p_to, const Color &p_color, float p_width = 1.0, bool p_antialiased = false); | ||||
| 	void draw_rect(const Rect2 &p_rect, const Color &p_color); | ||||
| 	void draw_rect(const Rect2 &p_rect, const Color &p_color, bool p_filled = true); | ||||
| 	void draw_circle(const Point2 &p_pos, float p_radius, const Color &p_color); | ||||
| 	void draw_texture(const Ref<Texture> &p_texture, const Point2 &p_pos, const Color &p_modulate = Color(1, 1, 1, 1), const Ref<Texture> &p_normal_map = Ref<Texture>()); | ||||
| 	void draw_texture_rect(const Ref<Texture> &p_texture, const Rect2 &p_rect, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, const Ref<Texture> &p_normal_map = Ref<Texture>()); | ||||
|  |  | |||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -34,235 +34,98 @@ | |||
| #include "scene/resources/color_ramp.h" | ||||
| #include "scene/resources/texture.h" | ||||
| 
 | ||||
| class Particles2D; | ||||
| class ParticleAttractor2D : public Node2D { | ||||
| 
 | ||||
| 	GDCLASS(ParticleAttractor2D, Node2D); | ||||
| 
 | ||||
| 	friend class Particles2D; | ||||
| 	bool enabled; | ||||
| 	float radius; | ||||
| 	float disable_radius; | ||||
| 	float gravity; | ||||
| 	float absorption; | ||||
| 	NodePath path; | ||||
| 
 | ||||
| 	Particles2D *owner; | ||||
| 
 | ||||
| 	void _update_owner(); | ||||
| 	void _owner_exited(); | ||||
| 	void _set_owner(Particles2D *p_owner); | ||||
| 
 | ||||
| 	void _notification(int p_what); | ||||
| 	static void _bind_methods(); | ||||
| 
 | ||||
| public: | ||||
| 	void set_enabled(bool p_enabled); | ||||
| 	bool is_enabled() const; | ||||
| 
 | ||||
| 	void set_radius(float p_radius); | ||||
| 	float get_radius() const; | ||||
| 
 | ||||
| 	void set_disable_radius(float p_disable_radius); | ||||
| 	float get_disable_radius() const; | ||||
| 
 | ||||
| 	void set_gravity(float p_gravity); | ||||
| 	float get_gravity() const; | ||||
| 
 | ||||
| 	void set_absorption(float p_absorption); | ||||
| 	float get_absorption() const; | ||||
| 
 | ||||
| 	void set_particles_path(NodePath p_path); | ||||
| 	NodePath get_particles_path() const; | ||||
| 
 | ||||
| 	virtual String get_configuration_warning() const; | ||||
| 
 | ||||
| 	ParticleAttractor2D(); | ||||
| }; | ||||
| 
 | ||||
| class Particles2D : public Node2D { | ||||
| 
 | ||||
| 	GDCLASS(Particles2D, Node2D); | ||||
| private: | ||||
| 	GDCLASS(Particles2D, Node2D) | ||||
| 
 | ||||
| public: | ||||
| 	enum Parameter { | ||||
| 		PARAM_DIRECTION, | ||||
| 		PARAM_SPREAD, | ||||
| 		PARAM_LINEAR_VELOCITY, | ||||
| 		PARAM_SPIN_VELOCITY, | ||||
| 		PARAM_ORBIT_VELOCITY, | ||||
| 		PARAM_GRAVITY_DIRECTION, | ||||
| 		PARAM_GRAVITY_STRENGTH, | ||||
| 		PARAM_RADIAL_ACCEL, | ||||
| 		PARAM_TANGENTIAL_ACCEL, | ||||
| 		PARAM_DAMPING, | ||||
| 		PARAM_INITIAL_ANGLE, | ||||
| 		PARAM_INITIAL_SIZE, | ||||
| 		PARAM_FINAL_SIZE, | ||||
| 		PARAM_HUE_VARIATION, | ||||
| 		PARAM_ANIM_SPEED_SCALE, | ||||
| 		PARAM_ANIM_INITIAL_POS, | ||||
| 		PARAM_MAX | ||||
| 	}; | ||||
| 
 | ||||
| 	enum { | ||||
| 		MAX_COLOR_PHASES = 4 | ||||
| 	}; | ||||
| 
 | ||||
| 	enum ProcessMode { | ||||
| 		PROCESS_FIXED, | ||||
| 		PROCESS_IDLE, | ||||
| 	enum DrawOrder { | ||||
| 		DRAW_ORDER_INDEX, | ||||
| 		DRAW_ORDER_LIFETIME, | ||||
| 	}; | ||||
| 
 | ||||
| private: | ||||
| 	float param[PARAM_MAX]; | ||||
| 	float randomness[PARAM_MAX]; | ||||
| 	RID particles; | ||||
| 
 | ||||
| 	struct Particle { | ||||
| 		bool active; | ||||
| 		Point2 pos; | ||||
| 		Vector2 velocity; | ||||
| 		float rot; | ||||
| 		float frame; | ||||
| 		uint64_t seed; | ||||
| 		Particle() { | ||||
| 			active = false; | ||||
| 			seed = 123465789; | ||||
| 			rot = 0; | ||||
| 			frame = 0; | ||||
| 		} | ||||
| 	}; | ||||
| 
 | ||||
| 	Vector<Particle> particles; | ||||
| 
 | ||||
| 	struct AttractorCache { | ||||
| 
 | ||||
| 		Vector2 pos; | ||||
| 		ParticleAttractor2D *attractor; | ||||
| 	}; | ||||
| 
 | ||||
| 	Vector<AttractorCache> attractor_cache; | ||||
| 
 | ||||
| 	float explosiveness; | ||||
| 	float preprocess; | ||||
| 	float lifetime; | ||||
| 	bool emitting; | ||||
| 	bool local_space; | ||||
| 	float emit_timeout; | ||||
| 	float time_to_live; | ||||
| 	float time_scale; | ||||
| 	bool flip_h; | ||||
| 	bool flip_v; | ||||
| 	int h_frames; | ||||
| 	int amount; | ||||
| 	float lifetime; | ||||
| 	float pre_process_time; | ||||
| 	float explosiveness_ratio; | ||||
| 	float randomness_ratio; | ||||
| 	float speed_scale; | ||||
| 	Rect2 visibility_rect; | ||||
| 	bool local_coords; | ||||
| 	int fixed_fps; | ||||
| 	bool fractional_delta; | ||||
| 	int v_frames; | ||||
| 	Point2 emissor_offset; | ||||
| 	Vector2 initial_velocity; | ||||
| 	Vector2 extents; | ||||
| 	PoolVector<Vector2> emission_points; | ||||
| 	int h_frames; | ||||
| 
 | ||||
| 	ProcessMode process_mode; | ||||
| 	Ref<Material> process_material; | ||||
| 
 | ||||
| 	float time; | ||||
| 	int active_count; | ||||
| 	DrawOrder draw_order; | ||||
| 
 | ||||
| 	Ref<Texture> texture; | ||||
| 	Ref<Texture> normal_map; | ||||
| 
 | ||||
| 	//If no color ramp is set then default color is used. Created as simple alternative to color_ramp.
 | ||||
| 	Color default_color; | ||||
| 	Ref<Gradient> gradient; | ||||
| 
 | ||||
| 	void _process_particles(float p_delta); | ||||
| 	friend class ParticleAttractor2D; | ||||
| 
 | ||||
| 	Set<ParticleAttractor2D *> attractors; | ||||
| 	void _update_particle_emission_transform(); | ||||
| 
 | ||||
| protected: | ||||
| 	void _notification(int p_what); | ||||
| 	static void _bind_methods(); | ||||
| 	virtual void _validate_property(PropertyInfo &property) const; | ||||
| 	void _notification(int p_what); | ||||
| 
 | ||||
| public: | ||||
| 	void set_emitting(bool p_emitting); | ||||
| 	bool is_emitting() const; | ||||
| 
 | ||||
| 	void set_process_mode(ProcessMode p_mode); | ||||
| 	ProcessMode get_process_mode() const; | ||||
| 
 | ||||
| 	void set_amount(int p_amount); | ||||
| 	int get_amount() const; | ||||
| 
 | ||||
| 	void set_lifetime(float p_lifetime); | ||||
| 	void set_pre_process_time(float p_time); | ||||
| 	void set_explosiveness_ratio(float p_ratio); | ||||
| 	void set_randomness_ratio(float p_ratio); | ||||
| 	void set_visibility_rect(const Rect2 &p_aabb); | ||||
| 	void set_use_local_coordinates(bool p_enable); | ||||
| 	void set_process_material(const Ref<Material> &p_material); | ||||
| 	void set_speed_scale(float p_scale); | ||||
| 
 | ||||
| 	bool is_emitting() const; | ||||
| 	int get_amount() const; | ||||
| 	float get_lifetime() const; | ||||
| 
 | ||||
| 	void set_time_scale(float p_time_scale); | ||||
| 	float get_time_scale() const; | ||||
| 
 | ||||
| 	void set_pre_process_time(float p_pre_process_time); | ||||
| 	float get_pre_process_time() const; | ||||
| 	float get_explosiveness_ratio() const; | ||||
| 	float get_randomness_ratio() const; | ||||
| 	Rect2 get_visibility_rect() const; | ||||
| 	bool get_use_local_coordinates() const; | ||||
| 	Ref<Material> get_process_material() const; | ||||
| 	float get_speed_scale() const; | ||||
| 
 | ||||
| 	void set_emit_timeout(float p_timeout); | ||||
| 	float get_emit_timeout() const; | ||||
| 	void set_fixed_fps(int p_count); | ||||
| 	int get_fixed_fps() const; | ||||
| 
 | ||||
| 	void set_emission_half_extents(const Vector2 &p_extents); | ||||
| 	Vector2 get_emission_half_extents() const; | ||||
| 	void set_fractional_delta(bool p_enable); | ||||
| 	bool get_fractional_delta() const; | ||||
| 
 | ||||
| 	void set_param(Parameter p_param, float p_value); | ||||
| 	float get_param(Parameter p_param) const; | ||||
| 
 | ||||
| 	void set_randomness(Parameter p_randomness, float p_value); | ||||
| 	float get_randomness(Parameter p_randomness) const; | ||||
| 
 | ||||
| 	void set_explosiveness(float p_value); | ||||
| 	float get_explosiveness() const; | ||||
| 
 | ||||
| 	void set_flip_h(bool p_flip); | ||||
| 	bool is_flipped_h() const; | ||||
| 
 | ||||
| 	void set_flip_v(bool p_flip); | ||||
| 	bool is_flipped_v() const; | ||||
| 
 | ||||
| 	void set_h_frames(int p_frames); | ||||
| 	int get_h_frames() const; | ||||
| 
 | ||||
| 	void set_v_frames(int p_frames); | ||||
| 	int get_v_frames() const; | ||||
| 
 | ||||
| 	void set_color_phases(int p_phases); | ||||
| 	int get_color_phases() const; | ||||
| 
 | ||||
| 	void set_color_phase_color(int p_phase, const Color &p_color); | ||||
| 	Color get_color_phase_color(int p_phase) const; | ||||
| 
 | ||||
| 	void set_color_phase_pos(int p_phase, float p_pos); | ||||
| 	float get_color_phase_pos(int p_phase) const; | ||||
| 	void set_draw_order(DrawOrder p_order); | ||||
| 	DrawOrder get_draw_order() const; | ||||
| 
 | ||||
| 	void set_texture(const Ref<Texture> &p_texture); | ||||
| 	Ref<Texture> get_texture() const; | ||||
| 
 | ||||
| 	void set_color(const Color &p_color); | ||||
| 	Color get_color() const; | ||||
| 	void set_normal_map(const Ref<Texture> &p_normal_map); | ||||
| 	Ref<Texture> get_normal_map() const; | ||||
| 
 | ||||
| 	void set_gradient(const Ref<Gradient> &p_texture); | ||||
| 	Ref<Gradient> get_gradient() const; | ||||
| 	virtual String get_configuration_warning() const; | ||||
| 
 | ||||
| 	void set_emissor_offset(const Point2 &p_offset); | ||||
| 	Point2 get_emissor_offset() const; | ||||
| 	void set_v_frames(int p_count); | ||||
| 	int get_v_frames() const; | ||||
| 
 | ||||
| 	void set_use_local_space(bool p_use); | ||||
| 	bool is_using_local_space() const; | ||||
| 
 | ||||
| 	void set_initial_velocity(const Vector2 &p_velocity); | ||||
| 	Vector2 get_initial_velocity() const; | ||||
| 
 | ||||
| 	void set_emission_points(const PoolVector<Vector2> &p_points); | ||||
| 	PoolVector<Vector2> get_emission_points() const; | ||||
| 
 | ||||
| 	void pre_process(float p_delta); | ||||
| 	void reset(); | ||||
| 	void set_h_frames(int p_count); | ||||
| 	int get_h_frames() const; | ||||
| 
 | ||||
| 	Rect2 capture_rect() const; | ||||
| 	Particles2D(); | ||||
| 	~Particles2D(); | ||||
| }; | ||||
| 
 | ||||
| VARIANT_ENUM_CAST(Particles2D::ProcessMode); | ||||
| VARIANT_ENUM_CAST(Particles2D::Parameter); | ||||
| VARIANT_ENUM_CAST(Particles2D::DrawOrder) | ||||
| 
 | ||||
| #endif // PARTICLES_FRAME_H
 | ||||
|  |  | |||
|  | @ -404,6 +404,7 @@ void ParticlesMaterial::init_shaders() { | |||
| 	shader_names->emission_texture_point_count = "emission_texture_point_count"; | ||||
| 	shader_names->emission_texture_points = "emission_texture_points"; | ||||
| 	shader_names->emission_texture_normal = "emission_texture_normal"; | ||||
| 	shader_names->emission_texture_color = "emission_texture_color"; | ||||
| 
 | ||||
| 	shader_names->trail_divisor = "trail_divisor"; | ||||
| 	shader_names->trail_size_modifier = "trail_size_modifier"; | ||||
|  | @ -481,6 +482,28 @@ void ParticlesMaterial::_update_shader() { | |||
| 	code += "uniform float anim_speed_random;\n"; | ||||
| 	code += "uniform float anim_offset_random;\n"; | ||||
| 
 | ||||
| 	switch (emission_shape) { | ||||
| 		case EMISSION_SHAPE_POINT: { | ||||
| 			//do none
 | ||||
| 		} break; | ||||
| 		case EMISSION_SHAPE_SPHERE: { | ||||
| 			code += "uniform float emission_sphere_radius;\n"; | ||||
| 		} break; | ||||
| 		case EMISSION_SHAPE_BOX: { | ||||
| 			code += "uniform vec3 emission_box_extents;\n"; | ||||
| 		} break; | ||||
| 		case EMISSION_SHAPE_DIRECTED_POINTS: { | ||||
| 			code += "uniform sampler2D emission_texture_normal : hint_black;\n"; | ||||
| 		} //fallthrough
 | ||||
| 		case EMISSION_SHAPE_POINTS: { | ||||
| 			code += "uniform sampler2D emission_texture_points : hint_black;\n"; | ||||
| 			code += "uniform int emission_texture_point_count;\n"; | ||||
| 			if (emission_color_texture.is_valid()) { | ||||
| 				code += "uniform sampler2D emission_texture_color : hint_white;\n"; | ||||
| 			} | ||||
| 		} break; | ||||
| 	} | ||||
| 
 | ||||
| 	code += "uniform vec4 color_value : hint_color;\n"; | ||||
| 
 | ||||
| 	code += "uniform int trail_divisor;\n"; | ||||
|  | @ -515,25 +538,6 @@ void ParticlesMaterial::_update_shader() { | |||
| 	if (tex_parameters[PARAM_ANIM_OFFSET].is_valid()) | ||||
| 		code += "uniform sampler2D anim_offset_texture;\n"; | ||||
| 
 | ||||
| 	switch (emission_shape) { | ||||
| 		case EMISSION_SHAPE_POINT: { | ||||
| 			//do none
 | ||||
| 		} break; | ||||
| 		case EMISSION_SHAPE_SPHERE: { | ||||
| 			code += "uniform float emission_sphere_radius;\n"; | ||||
| 		} break; | ||||
| 		case EMISSION_SHAPE_BOX: { | ||||
| 			code += "uniform vec3 emission_box_extents;\n"; | ||||
| 		} break; | ||||
| 		case EMISSION_SHAPE_DIRECTED_POINTS: { | ||||
| 			code += "uniform sampler2D emission_texture_normal : hint_black;\n"; | ||||
| 		} //fallthrough
 | ||||
| 		case EMISSION_SHAPE_POINTS: { | ||||
| 			code += "uniform sampler2D emission_texture_points : hint_black;\n"; | ||||
| 			code += "uniform int emission_texture_point_count;\n"; | ||||
| 		} break; | ||||
| 	} | ||||
| 
 | ||||
| 	if (trail_size_modifier.is_valid()) { | ||||
| 		code += "uniform sampler2D trail_size_modifier;\n"; | ||||
| 	} | ||||
|  | @ -576,6 +580,11 @@ void ParticlesMaterial::_update_shader() { | |||
| 	code += "\n"; | ||||
| 	code += "\n"; | ||||
| 	code += "\n"; | ||||
| 	if (emission_shape >= EMISSION_SHAPE_POINTS) { | ||||
| 		code += " int point = min(emission_texture_point_count-1,int(rand_from_seed(alt_seed) * float(emission_texture_point_count)));\n"; | ||||
| 		code += " ivec2 emission_tex_size = textureSize( emission_texture_points, 0 );\n"; | ||||
| 		code += " ivec2 emission_tex_ofs = ivec2( point % emission_tex_size.x, point / emission_tex_size.x );\n"; | ||||
| 	} | ||||
| 	code += " if (RESTART) {\n"; | ||||
| 
 | ||||
| 	if (tex_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) | ||||
|  | @ -593,11 +602,21 @@ void ParticlesMaterial::_update_shader() { | |||
| 	else | ||||
| 		code += "    float tex_anim_offset = 0.0;\n"; | ||||
| 
 | ||||
| 	code += "    float angle1 = rand_from_seed(alt_seed)*spread*3.1416;\n"; | ||||
| 	code += "    float angle2 = rand_from_seed(alt_seed)*20.0*3.1416; // make it more random like\n"; | ||||
| 	code += "    vec3 rot_xz=vec3( sin(angle1), 0.0, cos(angle1) );\n"; | ||||
| 	code += "    vec3 rot = vec3( cos(angle2)*rot_xz.x,sin(angle2)*rot_xz.x, rot_xz.z);\n"; | ||||
| 	code += "    VELOCITY=(rot*initial_linear_velocity+rot*initial_linear_velocity_random*rand_from_seed(alt_seed));\n"; | ||||
| 	if (flags[FLAG_DISABLE_Z]) { | ||||
| 
 | ||||
| 		code += "    float angle1 = rand_from_seed(alt_seed)*spread*3.1416;\n"; | ||||
| 		code += "    vec3 rot=vec3( cos(angle1), sin(angle1),0.0 );\n"; | ||||
| 		code += "    VELOCITY=(rot*initial_linear_velocity+rot*initial_linear_velocity_random*rand_from_seed(alt_seed));\n"; | ||||
| 
 | ||||
| 	} else { | ||||
| 		//initiate velocity spread in 3D
 | ||||
| 		code += "    float angle1 = rand_from_seed(alt_seed)*spread*3.1416;\n"; | ||||
| 		code += "    float angle2 = rand_from_seed(alt_seed)*20.0*3.1416; // make it more random like\n"; | ||||
| 		code += "    vec3 rot_xz=vec3( sin(angle1), 0.0, cos(angle1) );\n"; | ||||
| 		code += "    vec3 rot = vec3( cos(angle2)*rot_xz.x,sin(angle2)*rot_xz.x, rot_xz.z);\n"; | ||||
| 		code += "    VELOCITY=(rot*initial_linear_velocity+rot*initial_linear_velocity_random*rand_from_seed(alt_seed));\n"; | ||||
| 	} | ||||
| 
 | ||||
| 	code += "    float base_angle=(initial_angle+tex_angle)*mix(1.0,angle_rand,initial_angle_random);\n"; | ||||
| 	code += "    CUSTOM.x=base_angle*3.1416/180.0;\n"; //angle
 | ||||
| 	code += "    CUSTOM.y=0.0;\n"; //phase
 | ||||
|  | @ -614,21 +633,31 @@ void ParticlesMaterial::_update_shader() { | |||
| 		} break; | ||||
| 		case EMISSION_SHAPE_POINTS: | ||||
| 		case EMISSION_SHAPE_DIRECTED_POINTS: { | ||||
| 			code += "    int point = min(emission_texture_point_count-1,int(rand_from_seed(alt_seed) * float(emission_texture_point_count)));\n"; | ||||
| 			code += "    ivec2 tex_size = textureSize( emission_texture_points, 0 );\n"; | ||||
| 			code += "    ivec2 tex_ofs = ivec2( point % tex_size.x, point / tex_size.x );\n"; | ||||
| 			code += "    TRANSFORM[3].xyz = texelFetch(emission_texture_points, tex_ofs,0).xyz;\n"; | ||||
| 			code += "    TRANSFORM[3].xyz = texelFetch(emission_texture_points, emission_tex_ofs,0).xyz;\n"; | ||||
| 
 | ||||
| 			if (emission_shape == EMISSION_SHAPE_DIRECTED_POINTS) { | ||||
| 				code += "    vec3 normal = texelFetch(emission_texture_normal, tex_ofs,0).xyz;\n"; | ||||
| 				code += "    vec3 v0 = abs(normal.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(0, 1.0, 0.0);\n"; | ||||
| 				code += "    vec3 tangent = normalize(cross(v0, normal));\n"; | ||||
| 				code += "    vec3 bitangent = normalize(cross(tangent, normal));\n"; | ||||
| 				code += "    VELOCITY = mat3(tangent,bitangent,normal) * VELOCITY;\n"; | ||||
| 				if (flags[FLAG_DISABLE_Z]) { | ||||
| 
 | ||||
| 					code += "    mat2 rotm;"; | ||||
| 					code += "    rotm[0]=texelFetch(emission_texture_normal, emission_tex_ofs,0).xy;\n"; | ||||
| 					code += "    rotm[1]=rotm[0].yx * vec2(1.0,-1.0);\n"; | ||||
| 					code += "    VELOCITY.xy = rotm * VELOCITY.xy;\n"; | ||||
| 				} else { | ||||
| 					code += "    vec3 normal = texelFetch(emission_texture_normal, emission_tex_ofs,0).xyz;\n"; | ||||
| 					code += "    vec3 v0 = abs(normal.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(0, 1.0, 0.0);\n"; | ||||
| 					code += "    vec3 tangent = normalize(cross(v0, normal));\n"; | ||||
| 					code += "    vec3 bitangent = normalize(cross(tangent, normal));\n"; | ||||
| 					code += "    VELOCITY = mat3(tangent,bitangent,normal) * VELOCITY;\n"; | ||||
| 				} | ||||
| 			} | ||||
| 		} break; | ||||
| 	} | ||||
| 	code += "    VELOCITY = (EMISSION_TRANSFORM * vec4(VELOCITY,0.0)).xyz;\n"; | ||||
| 	code += "    TRANSFORM = EMISSION_TRANSFORM * TRANSFORM;\n"; | ||||
| 	if (flags[FLAG_DISABLE_Z]) { | ||||
| 		code += "    VELOCITY.z=0.0;\n"; | ||||
| 		code += "    TRANSFORM[3].z=0.0;\n"; | ||||
| 	} | ||||
| 
 | ||||
| 	code += " } else {\n"; | ||||
| 
 | ||||
|  | @ -685,6 +714,9 @@ void ParticlesMaterial::_update_shader() { | |||
| 
 | ||||
| 	code += "    vec3 force = gravity; \n"; | ||||
| 	code += "    vec3 pos = TRANSFORM[3].xyz; \n"; | ||||
| 	if (flags[FLAG_DISABLE_Z]) { | ||||
| 		code += "    pos.z=0.0; \n"; | ||||
| 	} | ||||
| 	code += "    //apply linear acceleration\n"; | ||||
| 	code += "    force+=normalize(VELOCITY) * (linear_accel+tex_linear_accel)*mix(1.0,rand_from_seed(alt_seed),linear_accel_random);\n"; | ||||
| 	code += "    //apply radial acceleration\n"; | ||||
|  | @ -693,11 +725,17 @@ void ParticlesMaterial::_update_shader() { | |||
| 	code += "	//org=p_transform.origin;\n"; | ||||
| 	code += "    force+=normalize(pos-org) * (radial_accel+tex_radial_accel)*mix(1.0,rand_from_seed(alt_seed),radial_accel_random);\n"; | ||||
| 	code += "    //apply tangential acceleration;\n"; | ||||
| 	code += "    force+=normalize(cross(normalize(pos-org),normalize(gravity))) * ((tangent_accel+tex_tangent_accel)*mix(1.0,rand_from_seed(alt_seed),radial_accel_random));\n"; | ||||
| 	if (flags[FLAG_DISABLE_Z]) { | ||||
| 		code += "    force+=vec3(normalize((pos-org).yx * vec2(-1.0,1.0)),0.0) * ((tangent_accel+tex_tangent_accel)*mix(1.0,rand_from_seed(alt_seed),radial_accel_random));\n"; | ||||
| 
 | ||||
| 	} else { | ||||
| 		code += "    force+=normalize(cross(normalize(pos-org),normalize(gravity))) * ((tangent_accel+tex_tangent_accel)*mix(1.0,rand_from_seed(alt_seed),radial_accel_random));\n"; | ||||
| 	} | ||||
| 	code += "    //apply attractor forces\n"; | ||||
| 	code += "    VELOCITY+=force * DELTA;\n"; | ||||
| 	if (tex_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) | ||||
| 	if (tex_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) { | ||||
| 		code += "    VELOCITY=normalize(VELOCITY)*tex_linear_velocity;\n"; | ||||
| 	} | ||||
| 	code += "    if (damping+tex_damping>0.0) {\n"; | ||||
| 	code += "    \n"; | ||||
| 	code += "    	float v = length(VELOCITY);\n"; | ||||
|  | @ -709,9 +747,16 @@ void ParticlesMaterial::_update_shader() { | |||
| 	code += "    		VELOCITY=normalize(VELOCITY) * v;\n"; | ||||
| 	code += "	}\n"; | ||||
| 	code += "    }\n"; | ||||
| 	code += "    float base_angle=(initial_angle+tex_angle)*mix(1.0,angle_rand,initial_angle_random)*3.1416/180.0;\n"; | ||||
| 	code += "    CUSTOM.x=((base_angle+tex_angle)+CUSTOM.y*LIFETIME*(angular_velocity+tex_angular_velocity)*mix(1.0,rand_from_seed(alt_seed)*2.0-1.0,angular_velocity_random))*3.1416/180.0;\n"; //angle
 | ||||
| 	code += "    CUSTOM.z=(anim_offset+tex_anim_offset)*mix(1.0,anim_offset_rand,anim_offset_random)+CUSTOM.y*LIFETIME*(anim_speed+tex_anim_speed)*mix(1.0,rand_from_seed(alt_seed),anim_speed_random);\n"; //angle
 | ||||
| 	code += "    float base_angle=(initial_angle+tex_angle)*mix(1.0,angle_rand,initial_angle_random);\n"; | ||||
| 	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*3.1416/180.0;\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
 | ||||
|  | @ -747,28 +792,40 @@ void ParticlesMaterial::_update_shader() { | |||
| 	} else { | ||||
| 		code += " COLOR = color_value * hue_rot_mat;\n"; | ||||
| 	} | ||||
| 	if (emission_color_texture.is_valid() && emission_shape >= EMISSION_SHAPE_POINTS) { | ||||
| 		code += " COLOR*= texelFetch(emission_texture_color,emission_tex_ofs,0);\n"; | ||||
| 	} | ||||
| 	if (trail_color_modifier.is_valid()) { | ||||
| 		code += "if (trail_divisor>1) { COLOR*=textureLod(trail_color_modifier,vec2(float(int(NUMBER)%trail_divisor)/float(trail_divisor-1),0.0),0.0); }\n"; | ||||
| 	} | ||||
| 	code += "\n"; | ||||
| 	//orient particle Y towards velocity
 | ||||
| 	if (flags[FLAG_ALIGN_Y_TO_VELOCITY]) { | ||||
| 		code += " if (length(VELOCITY)>0.0) {TRANSFORM[1].xyz=normalize(VELOCITY);} else {TRANSFORM[1].xyz=normalize(TRANSFORM[1].xyz);}\n"; | ||||
| 		code += " if (TRANSFORM[1].xyz==normalize(TRANSFORM[0].xyz)) {\n"; | ||||
| 		code += "\tTRANSFORM[0].xyz=normalize(cross(normalize(TRANSFORM[1].xyz),normalize(TRANSFORM[2].xyz)));\n"; | ||||
| 		code += "\tTRANSFORM[2].xyz=normalize(cross(normalize(TRANSFORM[0].xyz),normalize(TRANSFORM[1].xyz)));\n"; | ||||
| 		code += " } else {\n"; | ||||
| 		code += "\tTRANSFORM[2].xyz=normalize(cross(normalize(TRANSFORM[0].xyz),normalize(TRANSFORM[1].xyz)));\n"; | ||||
| 		code += "\tTRANSFORM[0].xyz=normalize(cross(normalize(TRANSFORM[1].xyz),normalize(TRANSFORM[2].xyz)));\n"; | ||||
| 		code += " }\n"; | ||||
| 
 | ||||
| 	if (flags[FLAG_DISABLE_Z]) { | ||||
| 
 | ||||
| 		code += " TRANSFORM[0]=vec4(cos(CUSTOM.x),-sin(CUSTOM.x),0.0,0.0);\n"; | ||||
| 		code += " TRANSFORM[1]=vec4(sin(CUSTOM.x),cos(CUSTOM.x),0.0,0.0);\n"; | ||||
| 		code += " TRANSFORM[2]=vec4(0.0,0.0,1.0,0.0);\n"; | ||||
| 
 | ||||
| 	} else { | ||||
| 		code += "\tTRANSFORM[0].xyz=normalize(TRANSFORM[0].xyz);\n"; | ||||
| 		code += "\tTRANSFORM[1].xyz=normalize(TRANSFORM[1].xyz);\n"; | ||||
| 		code += "\tTRANSFORM[2].xyz=normalize(TRANSFORM[2].xyz);\n"; | ||||
| 	} | ||||
| 	//turn particle by rotation in Y
 | ||||
| 	if (flags[FLAG_ROTATE_Y]) { | ||||
| 		code += "\tTRANSFORM = TRANSFORM * mat4( vec4(cos(CUSTOM.x),0.0,-sin(CUSTOM.x),0.0), vec4(0.0,1.0,0.0,0.0),vec4(sin(CUSTOM.x),0.0,cos(CUSTOM.x),0.0),vec4(0.0,0.0,0.0,1.0));\n"; | ||||
| 		//orient particle Y towards velocity
 | ||||
| 		if (flags[FLAG_ALIGN_Y_TO_VELOCITY]) { | ||||
| 			code += " if (length(VELOCITY)>0.0) {TRANSFORM[1].xyz=normalize(VELOCITY);} else {TRANSFORM[1].xyz=normalize(TRANSFORM[1].xyz);}\n"; | ||||
| 			code += " if (TRANSFORM[1].xyz==normalize(TRANSFORM[0].xyz)) {\n"; | ||||
| 			code += "\tTRANSFORM[0].xyz=normalize(cross(normalize(TRANSFORM[1].xyz),normalize(TRANSFORM[2].xyz)));\n"; | ||||
| 			code += "\tTRANSFORM[2].xyz=normalize(cross(normalize(TRANSFORM[0].xyz),normalize(TRANSFORM[1].xyz)));\n"; | ||||
| 			code += " } else {\n"; | ||||
| 			code += "\tTRANSFORM[2].xyz=normalize(cross(normalize(TRANSFORM[0].xyz),normalize(TRANSFORM[1].xyz)));\n"; | ||||
| 			code += "\tTRANSFORM[0].xyz=normalize(cross(normalize(TRANSFORM[1].xyz),normalize(TRANSFORM[2].xyz)));\n"; | ||||
| 			code += " }\n"; | ||||
| 		} else { | ||||
| 			code += "\tTRANSFORM[0].xyz=normalize(TRANSFORM[0].xyz);\n"; | ||||
| 			code += "\tTRANSFORM[1].xyz=normalize(TRANSFORM[1].xyz);\n"; | ||||
| 			code += "\tTRANSFORM[2].xyz=normalize(TRANSFORM[2].xyz);\n"; | ||||
| 		} | ||||
| 		//turn particle by rotation in Y
 | ||||
| 		if (flags[FLAG_ROTATE_Y]) { | ||||
| 			code += "\tTRANSFORM = TRANSFORM * mat4( vec4(cos(CUSTOM.x),0.0,-sin(CUSTOM.x),0.0), vec4(0.0,1.0,0.0,0.0),vec4(sin(CUSTOM.x),0.0,cos(CUSTOM.x),0.0),vec4(0.0,0.0,0.0,1.0));\n"; | ||||
| 		} | ||||
| 	} | ||||
| 	//scale by scale
 | ||||
| 	code += " float base_scale=mix(scale*tex_scale,1.0,scale_random*scale_rand);\n"; | ||||
|  | @ -779,6 +836,10 @@ void ParticlesMaterial::_update_shader() { | |||
| 	code += " TRANSFORM[0].xyz*=base_scale;\n"; | ||||
| 	code += " TRANSFORM[1].xyz*=base_scale;\n"; | ||||
| 	code += " TRANSFORM[2].xyz*=base_scale;\n"; | ||||
| 	if (flags[FLAG_DISABLE_Z]) { | ||||
| 		code += " VELOCITY.z=0.0;\n"; | ||||
| 		code += " TRANSFORM[3].z=0.0;\n"; | ||||
| 	} | ||||
| 	code += "}\n"; | ||||
| 	code += "\n"; | ||||
| 
 | ||||
|  | @ -1130,6 +1191,16 @@ void ParticlesMaterial::set_emission_normal_texture(const Ref<Texture> &p_normal | |||
| 	VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_texture_normal, texture); | ||||
| } | ||||
| 
 | ||||
| void ParticlesMaterial::set_emission_color_texture(const Ref<Texture> &p_colors) { | ||||
| 
 | ||||
| 	emission_color_texture = p_colors; | ||||
| 	RID texture; | ||||
| 	if (p_colors.is_valid()) | ||||
| 		texture = p_colors->get_rid(); | ||||
| 	VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_texture_color, texture); | ||||
| 	_queue_shader_change(); | ||||
| } | ||||
| 
 | ||||
| void ParticlesMaterial::set_emission_point_count(int p_count) { | ||||
| 
 | ||||
| 	emission_point_count = p_count; | ||||
|  | @ -1158,6 +1229,11 @@ Ref<Texture> ParticlesMaterial::get_emission_normal_texture() const { | |||
| 	return emission_normal_texture; | ||||
| } | ||||
| 
 | ||||
| Ref<Texture> ParticlesMaterial::get_emission_color_texture() const { | ||||
| 
 | ||||
| 	return emission_color_texture; | ||||
| } | ||||
| 
 | ||||
| int ParticlesMaterial::get_emission_point_count() const { | ||||
| 
 | ||||
| 	return emission_point_count; | ||||
|  | @ -1247,7 +1323,7 @@ void ParticlesMaterial::_validate_property(PropertyInfo &property) const { | |||
| 		property.usage = 0; | ||||
| 	} | ||||
| 
 | ||||
| 	if (property.name == "emission_point_texture" && (emission_shape != EMISSION_SHAPE_POINTS && emission_shape != EMISSION_SHAPE_DIRECTED_POINTS)) { | ||||
| 	if ((property.name == "emission_point_texture" || property.name == "emission_color_texture") && (emission_shape < EMISSION_SHAPE_POINTS)) { | ||||
| 		property.usage = 0; | ||||
| 	} | ||||
| 
 | ||||
|  | @ -1301,6 +1377,9 @@ void ParticlesMaterial::_bind_methods() { | |||
| 	ClassDB::bind_method(D_METHOD("set_emission_normal_texture", "texture:Texture"), &ParticlesMaterial::set_emission_normal_texture); | ||||
| 	ClassDB::bind_method(D_METHOD("get_emission_normal_texture:Texture"), &ParticlesMaterial::get_emission_normal_texture); | ||||
| 
 | ||||
| 	ClassDB::bind_method(D_METHOD("set_emission_color_texture", "texture:Texture"), &ParticlesMaterial::set_emission_color_texture); | ||||
| 	ClassDB::bind_method(D_METHOD("get_emission_color_texture:Texture"), &ParticlesMaterial::get_emission_color_texture); | ||||
| 
 | ||||
| 	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); | ||||
| 
 | ||||
|  | @ -1326,10 +1405,12 @@ void ParticlesMaterial::_bind_methods() { | |||
| 	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_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::INT, "emission_point_count", PROPERTY_HINT_RANGE, "0,1000000,1"), "set_emission_point_count", "get_emission_point_count"); | ||||
| 	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_rotate_y"), "set_flag", "get_flag", FLAG_ROTATE_Y); | ||||
| 	ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flag_disable_z"), "set_flag", "get_flag", FLAG_DISABLE_Z); | ||||
| 	ADD_GROUP("Spread", ""); | ||||
| 	ADD_PROPERTY(PropertyInfo(Variant::REAL, "spread", PROPERTY_HINT_RANGE, "0,180,0.01"), "set_spread", "get_spread"); | ||||
| 	ADD_PROPERTY(PropertyInfo(Variant::REAL, "flatness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_flatness", "get_flatness"); | ||||
|  | @ -1379,12 +1460,13 @@ void ParticlesMaterial::_bind_methods() { | |||
| 	ADD_PROPERTYI(PropertyInfo(Variant::REAL, "hue_variation_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_HUE_VARIATION); | ||||
| 	ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "hue_variation_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_HUE_VARIATION); | ||||
| 	ADD_GROUP("Animation", "anim_"); | ||||
| 	ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anim_speed", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param", "get_param", PARAM_ANIM_SPEED); | ||||
| 	ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anim_speed", PROPERTY_HINT_RANGE, "0,128,0.01"), "set_param", "get_param", PARAM_ANIM_SPEED); | ||||
| 	ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anim_speed_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ANIM_SPEED); | ||||
| 	ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "anim_speed_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_ANIM_SPEED); | ||||
| 	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_CONSTANT(PARAM_INITIAL_LINEAR_VELOCITY); | ||||
| 	BIND_CONSTANT(PARAM_ANGULAR_VELOCITY); | ||||
|  |  | |||
|  | @ -154,6 +154,8 @@ public: | |||
| 	enum Flags { | ||||
| 		FLAG_ALIGN_Y_TO_VELOCITY, | ||||
| 		FLAG_ROTATE_Y, | ||||
| 		FLAG_DISABLE_Z, | ||||
| 		FLAG_ANIM_LOOP, | ||||
| 		FLAG_MAX | ||||
| 	}; | ||||
| 
 | ||||
|  | @ -171,11 +173,12 @@ private: | |||
| 		struct { | ||||
| 			uint32_t texture_mask : 16; | ||||
| 			uint32_t texture_color : 1; | ||||
| 			uint32_t flags : 2; | ||||
| 			uint32_t flags : 4; | ||||
| 			uint32_t emission_shape : 2; | ||||
| 			uint32_t trail_size_texture : 1; | ||||
| 			uint32_t trail_color_texture : 1; | ||||
| 			uint32_t invalid_key : 1; | ||||
| 			uint32_t has_emission_color : 1; | ||||
| 		}; | ||||
| 
 | ||||
| 		uint32_t key; | ||||
|  | @ -213,6 +216,7 @@ private: | |||
| 		mk.emission_shape = emission_shape; | ||||
| 		mk.trail_color_texture = trail_color_modifier.is_valid() ? 1 : 0; | ||||
| 		mk.trail_size_texture = trail_size_modifier.is_valid() ? 1 : 0; | ||||
| 		mk.has_emission_color = emission_shape >= EMISSION_SHAPE_POINTS && emission_color_texture.is_valid(); | ||||
| 
 | ||||
| 		return mk; | ||||
| 	} | ||||
|  | @ -269,6 +273,7 @@ private: | |||
| 		StringName emission_texture_point_count; | ||||
| 		StringName emission_texture_points; | ||||
| 		StringName emission_texture_normal; | ||||
| 		StringName emission_texture_color; | ||||
| 
 | ||||
| 		StringName trail_divisor; | ||||
| 		StringName trail_size_modifier; | ||||
|  | @ -302,8 +307,11 @@ private: | |||
| 	Vector3 emission_box_extents; | ||||
| 	Ref<Texture> emission_point_texture; | ||||
| 	Ref<Texture> emission_normal_texture; | ||||
| 	Ref<Texture> emission_color_texture; | ||||
| 	int emission_point_count; | ||||
| 
 | ||||
| 	bool anim_loop; | ||||
| 
 | ||||
| 	int trail_divisor; | ||||
| 
 | ||||
| 	Ref<CurveTexture> trail_size_modifier; | ||||
|  | @ -347,6 +355,7 @@ public: | |||
| 	void set_emission_box_extents(Vector3 p_extents); | ||||
| 	void set_emission_point_texture(const Ref<Texture> &p_points); | ||||
| 	void set_emission_normal_texture(const Ref<Texture> &p_normals); | ||||
| 	void set_emission_color_texture(const Ref<Texture> &p_colors); | ||||
| 	void set_emission_point_count(int p_count); | ||||
| 
 | ||||
| 	EmissionShape get_emission_shape() const; | ||||
|  | @ -354,6 +363,7 @@ public: | |||
| 	Vector3 get_emission_box_extents() const; | ||||
| 	Ref<Texture> get_emission_point_texture() const; | ||||
| 	Ref<Texture> get_emission_normal_texture() const; | ||||
| 	Ref<Texture> get_emission_color_texture() const; | ||||
| 	int get_emission_point_count() const; | ||||
| 
 | ||||
| 	void set_trail_divisor(int p_divisor); | ||||
|  |  | |||
|  | @ -471,7 +471,7 @@ void register_scene_types() { | |||
| 	ClassDB::register_virtual_class<CanvasItem>(); | ||||
| 	ClassDB::register_class<Node2D>(); | ||||
| 	ClassDB::register_class<Particles2D>(); | ||||
| 	ClassDB::register_class<ParticleAttractor2D>(); | ||||
| 	//ClassDB::register_class<ParticleAttractor2D>();
 | ||||
| 	ClassDB::register_class<Sprite>(); | ||||
| 	//ClassDB::register_type<ViewportSprite>();
 | ||||
| 	ClassDB::register_class<SpriteFrames>(); | ||||
|  |  | |||
|  | @ -612,6 +612,7 @@ public: | |||
| 				TYPE_POLYGON, | ||||
| 				TYPE_MESH, | ||||
| 				TYPE_MULTIMESH, | ||||
| 				TYPE_PARTICLES, | ||||
| 				TYPE_CIRCLE, | ||||
| 				TYPE_TRANSFORM, | ||||
| 				TYPE_CLIP_IGNORE, | ||||
|  | @ -707,6 +708,16 @@ public: | |||
| 			CommandMultiMesh() { type = TYPE_MULTIMESH; } | ||||
| 		}; | ||||
| 
 | ||||
| 		struct CommandParticles : public Command { | ||||
| 
 | ||||
| 			RID particles; | ||||
| 			RID texture; | ||||
| 			RID normal_map; | ||||
| 			int h_frames; | ||||
| 			int v_frames; | ||||
| 			CommandParticles() { type = TYPE_PARTICLES; } | ||||
| 		}; | ||||
| 
 | ||||
| 		struct CommandCircle : public Command { | ||||
| 
 | ||||
| 			Point2 pos; | ||||
|  | @ -844,6 +855,15 @@ public: | |||
| 
 | ||||
| 						r = Rect2(aabb.position.x, aabb.position.y, aabb.size.x, aabb.size.y); | ||||
| 
 | ||||
| 					} break; | ||||
| 					case Item::Command::TYPE_PARTICLES: { | ||||
| 
 | ||||
| 						const Item::CommandParticles *particles_cmd = static_cast<const Item::CommandParticles *>(c); | ||||
| 						if (particles_cmd->particles.is_valid()) { | ||||
| 							Rect3 aabb = RasterizerStorage::base_singleton->particles_get_aabb(particles_cmd->particles); | ||||
| 							r = Rect2(aabb.position.x, aabb.position.y, aabb.size.x, aabb.size.y); | ||||
| 						} | ||||
| 
 | ||||
| 					} break; | ||||
| 					case Item::Command::TYPE_CIRCLE: { | ||||
| 
 | ||||
|  |  | |||
|  | @ -637,6 +637,25 @@ 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) { | ||||
| 
 | ||||
| 	Item *canvas_item = canvas_item_owner.getornull(p_item); | ||||
| 	ERR_FAIL_COND(!canvas_item); | ||||
| 
 | ||||
| 	Item::CommandParticles *part = memnew(Item::CommandParticles); | ||||
| 	ERR_FAIL_COND(!part); | ||||
| 	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); | ||||
| 
 | ||||
| 	canvas_item->commands.push_back(part); | ||||
| } | ||||
| 
 | ||||
| void VisualServerCanvas::canvas_item_add_multimesh(RID p_item, RID p_mesh, RID p_skeleton) { | ||||
| 
 | ||||
| 	Item *canvas_item = canvas_item_owner.getornull(p_item); | ||||
|  |  | |||
|  | @ -173,6 +173,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>(), 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_skeleton = RID()); | ||||
| 	void canvas_item_add_multimesh(RID p_item, RID p_mesh, RID p_skeleton = 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_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); | ||||
|  |  | |||
|  | @ -877,7 +877,8 @@ public: | |||
| 	BIND2(particles_set_draw_passes, RID, int) | ||||
| 	BIND3(particles_set_draw_pass_mesh, RID, int, RID) | ||||
| 
 | ||||
| 	BIND1R(Rect3, particles_get_current_aabb, RID); | ||||
| 	BIND1R(Rect3, particles_get_current_aabb, RID) | ||||
| 	BIND2(particles_set_emission_transform, RID, const Transform &) | ||||
| 
 | ||||
| #undef BINDBASE | ||||
| //from now on, calls forwarded to this singleton
 | ||||
|  | @ -1049,6 +1050,7 @@ public: | |||
| 	BIND8(canvas_item_add_triangle_array, RID, const Vector<int> &, const Vector<Point2> &, const Vector<Color> &, const Vector<Point2> &, RID, int, RID) | ||||
| 	BIND3(canvas_item_add_mesh, RID, const RID &, RID) | ||||
| 	BIND3(canvas_item_add_multimesh, RID, RID, RID) | ||||
| 	BIND6(canvas_item_add_particles, RID, RID, RID, RID, int, int) | ||||
| 	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) | ||||
|  |  | |||
|  | @ -320,6 +320,7 @@ public: | |||
| 
 | ||||
| 	FUNC2(particles_set_draw_passes, RID, int) | ||||
| 	FUNC3(particles_set_draw_pass_mesh, RID, int, RID) | ||||
| 	FUNC2(particles_set_emission_transform, RID, const Transform &) | ||||
| 
 | ||||
| 	FUNC1R(Rect3, particles_get_current_aabb, RID) | ||||
| 
 | ||||
|  | @ -476,6 +477,7 @@ public: | |||
| 	FUNC8(canvas_item_add_triangle_array, RID, const Vector<int> &, const Vector<Point2> &, const Vector<Color> &, const Vector<Point2> &, RID, int, RID) | ||||
| 	FUNC3(canvas_item_add_mesh, RID, const RID &, RID) | ||||
| 	FUNC3(canvas_item_add_multimesh, RID, RID, RID) | ||||
| 	FUNC6(canvas_item_add_particles, RID, RID, RID, RID, int, int) | ||||
| 	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) | ||||
|  |  | |||
|  | @ -501,6 +501,8 @@ public: | |||
| 
 | ||||
| 	virtual Rect3 particles_get_current_aabb(RID p_particles) = 0; | ||||
| 
 | ||||
| 	virtual void particles_set_emission_transform(RID p_particles, const Transform &p_transform) = 0; //this is only used for 2D, in 3D it's automatic
 | ||||
| 
 | ||||
| 	/* CAMERA API */ | ||||
| 
 | ||||
| 	virtual RID camera_create() = 0; | ||||
|  | @ -793,6 +795,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>(), 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_skeleton = RID()) = 0; | ||||
| 	virtual void canvas_item_add_multimesh(RID p_item, RID p_mesh, RID p_skeleton = 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_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; | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Juan Linietsky
						Juan Linietsky