diff options
author | Juan Linietsky <reduzio@gmail.com> | 2017-06-21 16:25:45 -0300 |
---|---|---|
committer | Juan Linietsky <reduzio@gmail.com> | 2017-06-21 16:26:26 -0300 |
commit | 95560e02c5eed3561996ed2b1d1e47e26e8bb81c (patch) | |
tree | 8e53927435484e10e444e6e2f745b2ddc19029e6 | |
parent | 3c1fd26bb0c2e97b927343e5488aa6d646e2e27c (diff) |
2D GPU Particles working..
-rw-r--r-- | drivers/gles3/rasterizer_canvas_gles3.cpp | 163 | ||||
-rw-r--r-- | drivers/gles3/rasterizer_canvas_gles3.h | 4 | ||||
-rw-r--r-- | drivers/gles3/rasterizer_storage_gles3.cpp | 3 | ||||
-rw-r--r-- | drivers/gles3/shader_gles3.cpp | 2 | ||||
-rw-r--r-- | drivers/gles3/shaders/canvas.glsl | 44 | ||||
-rw-r--r-- | editor/icons/icon_audio_player.png | bin | 443 -> 0 bytes | |||
-rw-r--r-- | editor/plugins/particles_2d_editor_plugin.cpp | 303 | ||||
-rw-r--r-- | editor/plugins/particles_2d_editor_plugin.h | 18 | ||||
-rw-r--r-- | scene/2d/canvas_item.cpp | 14 | ||||
-rw-r--r-- | scene/2d/canvas_item.h | 2 | ||||
-rw-r--r-- | scene/2d/particles_2d.cpp | 1182 | ||||
-rw-r--r-- | scene/2d/particles_2d.h | 247 | ||||
-rw-r--r-- | scene/3d/particles.cpp | 196 | ||||
-rw-r--r-- | scene/3d/particles.h | 12 | ||||
-rw-r--r-- | scene/register_scene_types.cpp | 2 | ||||
-rw-r--r-- | servers/visual/rasterizer.h | 20 | ||||
-rw-r--r-- | servers/visual/visual_server_canvas.cpp | 19 | ||||
-rw-r--r-- | servers/visual/visual_server_canvas.h | 1 | ||||
-rw-r--r-- | servers/visual/visual_server_raster.h | 4 | ||||
-rw-r--r-- | servers/visual/visual_server_wrap_mt.h | 2 | ||||
-rw-r--r-- | servers/visual_server.h | 3 |
21 files changed, 970 insertions, 1271 deletions
diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp index a8895f7bfc..5d7929b9de 100644 --- a/drivers/gles3/rasterizer_canvas_gles3.cpp +++ b/drivers/gles3/rasterizer_canvas_gles3.cpp @@ -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" @@ -608,6 +609,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: { _set_texture_rect_mode(false); @@ -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); } diff --git a/drivers/gles3/rasterizer_canvas_gles3.h b/drivers/gles3/rasterizer_canvas_gles3.h index 4996f0a6b2..76e0fbd48f 100644 --- a/drivers/gles3/rasterizer_canvas_gles3.h +++ b/drivers/gles3/rasterizer_canvas_gles3.h @@ -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; diff --git a/drivers/gles3/rasterizer_storage_gles3.cpp b/drivers/gles3/rasterizer_storage_gles3.cpp index 85331342e9..81baf542c0 100644 --- a/drivers/gles3/rasterizer_storage_gles3.cpp +++ b/drivers/gles3/rasterizer_storage_gles3.cpp @@ -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]]; diff --git a/drivers/gles3/shader_gles3.cpp b/drivers/gles3/shader_gles3.cpp index 8c6d15c3b7..e08ef0ad12 100644 --- a/drivers/gles3/shader_gles3.cpp +++ b/drivers/gles3/shader_gles3.cpp @@ -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); diff --git a/drivers/gles3/shaders/canvas.glsl b/drivers/gles3/shaders/canvas.glsl index 7be6ee857a..00744d8dc2 100644 --- a/drivers/gles3/shaders/canvas.glsl +++ b/drivers/gles3/shaders/canvas.glsl @@ -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 diff --git a/editor/icons/icon_audio_player.png b/editor/icons/icon_audio_player.png Binary files differdeleted file mode 100644 index c3e6d6cafa..0000000000 --- a/editor/icons/icon_audio_player.png +++ /dev/null diff --git a/editor/plugins/particles_2d_editor_plugin.cpp b/editor/plugins/particles_2d_editor_plugin.cpp index c6c85d8be2..a759e5892f 100644 --- a/editor/plugins/particles_2d_editor_plugin.cpp +++ b/editor/plugins/particles_2d_editor_plugin.cpp @@ -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,78 +62,264 @@ void Particles2DEditorPlugin::_file_selected(const String &p_file) { print_line("file: " + p_file); - int epc = epoints->get_value(); + 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(); + + } break; + case MENU_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();*/ + } 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(p_file, img); - ERR_EXPLAIN(TTR("Error loading image:") + " " + p_file); + Error err = ImageLoader::load_image(source_emission_file, img); + ERR_EXPLAIN(TTR("Error loading image:") + " " + source_emission_file); ERR_FAIL_COND(err != OK); - img->convert(Image::FORMAT_LA8); - ERR_FAIL_COND(img->get_format() != Image::FORMAT_LA8); + 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); - PoolVector<uint8_t> data = img->get_data(); - PoolVector<uint8_t>::Read r = data.read(); + Vector<Point2> valid_positions; + Vector<Point2> valid_normals; + Vector<uint8_t> valid_colors; - Vector<Point2i> valid_positions; 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; - for (int i = 0; i < s.width * s.height; i++) { + { + 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 { - uint8_t a = r[i * 2 + 1]; - if (a > 128) { - valid_positions[vpc++] = Point2i(i % s.width, i / s.width); + 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<Point2> epoints; - epoints.resize(epc); - PoolVector<Point2>::Write w = epoints.write(); + PoolVector<uint8_t> texdata; + + int w = 2048; + int h = (vpc / 2048) + 1; - Size2 extents = Size2(img->get_width() * 0.5, img->get_height() * 0.5); + texdata.resize(w * h * 2 * sizeof(float)); - for (int i = 0; i < epc; i++) { + { + PoolVector<uint8_t>::Write tw = texdata.write(); + float *twf = (float *)tw.ptr(); + for (int i = 0; i < vpc; i++) { - Point2 p = valid_positions[Math::rand() % vpc]; - p -= s / 2; - w[i] = p / extents; + twf[i * 2 + 0] = valid_positions[i].x; + twf[i * 2 + 1] = valid_positions[i].y; + } } - w = PoolVector<Point2>::Write(); + img.instance(); + img->create(w, h, false, Image::FORMAT_RGF, texdata); - 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(); -} + Ref<ImageTexture> imgt; + imgt.instance(); + imgt->create_from_image(img, 0); -void Particles2DEditorPlugin::_menu_callback(int p_idx) { + pm->set_emission_point_texture(imgt); + pm->set_emission_point_count(vpc); - switch (p_idx) { - case MENU_LOAD_EMISSION_MASK: { + if (capture_colors) { - file->popup_centered_ratio(); + PoolVector<uint8_t> colordata; + colordata.resize(w * h * 4); //use RG texture - } break; - case MENU_CLEAR_EMISSION_MASK: { + { + PoolVector<uint8_t>::Write tw = colordata.write(); + for (int i = 0; i < vpc * 4; i++) { - 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(); - } break; + 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) { @@ -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() { diff --git a/editor/plugins/particles_2d_editor_plugin.h b/editor/plugins/particles_2d_editor_plugin.h index e532157c35..cea60fbeaf 100644 --- a/editor/plugins/particles_2d_editor_plugin.h +++ b/editor/plugins/particles_2d_editor_plugin.h @@ -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); diff --git a/scene/2d/canvas_item.cpp b/scene/2d/canvas_item.cpp index 471f529713..89b89a50d8 100644 --- a/scene/2d/canvas_item.cpp +++ b/scene/2d/canvas_item.cpp @@ -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())); diff --git a/scene/2d/canvas_item.h b/scene/2d/canvas_item.h index 595dd03057..906a08d219 100644 --- a/scene/2d/canvas_item.h +++ b/scene/2d/canvas_item.h @@ -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>()); diff --git a/scene/2d/particles_2d.cpp b/scene/2d/particles_2d.cpp index 21d64c5d64..7c864c3430 100644 --- a/scene/2d/particles_2d.cpp +++ b/scene/2d/particles_2d.cpp @@ -28,903 +28,234 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ #include "particles_2d.h" +#include "scene/3d/particles.h" #include "scene/scene_string_names.h" -void ParticleAttractor2D::_notification(int p_what) { - - switch (p_what) { - - case NOTIFICATION_ENTER_TREE: { - - _update_owner(); - - } break; - case NOTIFICATION_DRAW: { - - if (!get_tree()->is_editor_hint()) - return; - - Vector2 pv; - float dr = MIN(disable_radius, radius); - for (int i = 0; i <= 32; i++) { - Vector2 v(Math::sin(i / 32.0 * Math_PI * 2), Math::cos(i / 32.0 * Math_PI * 2)); - if (i > 0) { - draw_line(pv * radius, v * radius, Color(0, 0, 0.5, 0.9)); - if (dr > 0) { - draw_line(pv * dr, v * dr, Color(0.5, 0, 0.0, 0.9)); - } - } - pv = v; - } - - } break; - case NOTIFICATION_EXIT_TREE: { - if (owner) { - _set_owner(NULL); - } - - } break; - } -} - -void ParticleAttractor2D::_owner_exited() { - - ERR_FAIL_COND(!owner); - owner->attractors.erase(this); - owner = NULL; -} - -void ParticleAttractor2D::_update_owner() { - - if (!is_inside_tree() || !has_node(path)) { - _set_owner(NULL); - return; - } - - Node *n = get_node(path); - ERR_FAIL_COND(!n); - Particles2D *pn = n->cast_to<Particles2D>(); - if (!pn) { - _set_owner(NULL); - return; - } +void Particles2D::set_emitting(bool p_emitting) { - _set_owner(pn); + emitting = p_emitting; + VS::get_singleton()->particles_set_emitting(particles, emitting); } -void ParticleAttractor2D::_set_owner(Particles2D *p_owner) { - - if (owner == p_owner) - return; - - if (owner) { - owner->disconnect("tree_exited", this, "_owner_exited"); - owner->attractors.erase(this); - owner = NULL; - } - owner = p_owner; - - if (owner) { +void Particles2D::set_amount(int p_amount) { - owner->connect("tree_exited", this, "_owner_exited", varray(), CONNECT_ONESHOT); - owner->attractors.insert(this); - } + ERR_FAIL_COND(p_amount < 1); + amount = p_amount; + VS::get_singleton()->particles_set_amount(particles, amount); } +void Particles2D::set_lifetime(float p_lifetime) { -void ParticleAttractor2D::_bind_methods() { - - ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &ParticleAttractor2D::set_enabled); - ClassDB::bind_method(D_METHOD("is_enabled"), &ParticleAttractor2D::is_enabled); - - ClassDB::bind_method(D_METHOD("set_radius", "radius"), &ParticleAttractor2D::set_radius); - ClassDB::bind_method(D_METHOD("get_radius"), &ParticleAttractor2D::get_radius); - - ClassDB::bind_method(D_METHOD("set_disable_radius", "radius"), &ParticleAttractor2D::set_disable_radius); - ClassDB::bind_method(D_METHOD("get_disable_radius"), &ParticleAttractor2D::get_disable_radius); - - ClassDB::bind_method(D_METHOD("set_gravity", "gravity"), &ParticleAttractor2D::set_gravity); - ClassDB::bind_method(D_METHOD("get_gravity"), &ParticleAttractor2D::get_gravity); - - ClassDB::bind_method(D_METHOD("set_absorption", "absorption"), &ParticleAttractor2D::set_absorption); - ClassDB::bind_method(D_METHOD("get_absorption"), &ParticleAttractor2D::get_absorption); - - ClassDB::bind_method(D_METHOD("set_particles_path", "path"), &ParticleAttractor2D::set_particles_path); - ClassDB::bind_method(D_METHOD("get_particles_path"), &ParticleAttractor2D::get_particles_path); - - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "is_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "radius", PROPERTY_HINT_RANGE, "0.1,16000,0.1"), "set_radius", "get_radius"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "disable_radius", PROPERTY_HINT_RANGE, "0.1,16000,0.1"), "set_disable_radius", "get_disable_radius"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "gravity", PROPERTY_HINT_RANGE, "-512,512,0.01"), "set_gravity", "get_gravity"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "absorption", PROPERTY_HINT_RANGE, "0,512,0.01"), "set_absorption", "get_absorption"); - ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "particles_path", PROPERTY_HINT_RESOURCE_TYPE, "Particles2D"), "set_particles_path", "get_particles_path"); + ERR_FAIL_COND(p_lifetime <= 0); + lifetime = p_lifetime; + VS::get_singleton()->particles_set_lifetime(particles, lifetime); } +void Particles2D::set_pre_process_time(float p_time) { -void ParticleAttractor2D::set_enabled(bool p_enabled) { - - enabled = p_enabled; + pre_process_time = p_time; + VS::get_singleton()->particles_set_pre_process_time(particles, pre_process_time); } +void Particles2D::set_explosiveness_ratio(float p_ratio) { -bool ParticleAttractor2D::is_enabled() const { - - return enabled; + explosiveness_ratio = p_ratio; + VS::get_singleton()->particles_set_explosiveness_ratio(particles, explosiveness_ratio); } +void Particles2D::set_randomness_ratio(float p_ratio) { -void ParticleAttractor2D::set_radius(float p_radius) { - - radius = p_radius; - update(); + randomness_ratio = p_ratio; + VS::get_singleton()->particles_set_randomness_ratio(particles, randomness_ratio); } +void Particles2D::set_visibility_rect(const Rect2 &p_aabb) { -float ParticleAttractor2D::get_radius() const { - - return radius; -} + visibility_rect = p_aabb; + Rect3 aabb; + aabb.position.x = p_aabb.position.x; + aabb.position.y = p_aabb.position.y; + aabb.size.x = p_aabb.size.x; + aabb.size.y = p_aabb.size.y; -void ParticleAttractor2D::set_disable_radius(float p_disable_radius) { + VS::get_singleton()->particles_set_custom_aabb(particles, aabb); - disable_radius = p_disable_radius; + _change_notify("visibility_rect"); update(); } -float ParticleAttractor2D::get_disable_radius() const { - - return disable_radius; -} - -void ParticleAttractor2D::set_gravity(float p_gravity) { - - gravity = p_gravity; -} -float ParticleAttractor2D::get_gravity() const { - - return gravity; -} - -void ParticleAttractor2D::set_absorption(float p_absorption) { - - absorption = p_absorption; -} -float ParticleAttractor2D::get_absorption() const { - - return absorption; -} - -void ParticleAttractor2D::set_particles_path(NodePath p_path) { - - path = p_path; - _update_owner(); - update_configuration_warning(); -} -NodePath ParticleAttractor2D::get_particles_path() const { - - return path; -} +void Particles2D::set_use_local_coordinates(bool p_enable) { -String ParticleAttractor2D::get_configuration_warning() const { - - if (!has_node(path) || !get_node(path) || !get_node(path)->cast_to<Particles2D>()) { - return TTR("Path property must point to a valid Particles2D node to work."); + local_coords = p_enable; + VS::get_singleton()->particles_set_use_local_coordinates(particles, local_coords); + set_notify_transform(!p_enable); + if (!p_enable) { + _update_particle_emission_transform(); } - - return String(); -} - -ParticleAttractor2D::ParticleAttractor2D() { - - owner = NULL; - radius = 50; - disable_radius = 0; - gravity = 100; - absorption = 0; - path = String(".."); - enabled = true; } -/****************************************/ +void Particles2D::_update_particle_emission_transform() { -_FORCE_INLINE_ static float _rand_from_seed(uint64_t *seed) { + Transform2D xf2d = get_global_transform(); + Transform xf; + xf.basis.set_axis(0, Vector3(xf2d.get_axis(0).x, xf2d.get_axis(0).y, 0)); + xf.basis.set_axis(1, Vector3(xf2d.get_axis(1).x, xf2d.get_axis(1).y, 0)); + xf.set_origin(Vector3(xf2d.get_origin().x, xf2d.get_origin().y, 0)); - uint32_t r = Math::rand_from_seed(seed); - return 2.0f * (float)r / (float)Math::RANDOM_MAX - 1.0f; + VS::get_singleton()->particles_set_emission_transform(particles, xf); } -void Particles2D::_process_particles(float p_delta) { - - if (particles.size() == 0 || lifetime == 0) - return; - - p_delta *= time_scale; - - float frame_time = p_delta; - - if (emit_timeout > 0) { - time_to_live -= frame_time; - if (time_to_live < 0) { - - emitting = false; - _change_notify("config/emitting"); - }; - }; - - float next_time = time + frame_time; - - if (next_time > lifetime) - next_time = Math::fmod(next_time, lifetime); - - Particle *pdata = &particles[0]; - int particle_count = particles.size(); - Transform2D xform; - if (!local_space) - xform = get_global_transform(); +void Particles2D::set_process_material(const Ref<Material> &p_material) { - active_count = 0; - - PoolVector<Point2>::Read r; - int emission_point_count = 0; - if (emission_points.size()) { - - emission_point_count = emission_points.size(); - r = emission_points.read(); - } - - int attractor_count = 0; - AttractorCache *attractor_ptr = NULL; - - if (attractors.size()) { - if (attractors.size() != attractor_cache.size()) { - attractor_cache.resize(attractors.size()); - } - - int idx = 0; - Transform2D m; - if (local_space) { - m = get_global_transform().affine_inverse(); - } - for (Set<ParticleAttractor2D *>::Element *E = attractors.front(); E; E = E->next()) { - - attractor_cache[idx].pos = m.xform(E->get()->get_global_position()); - attractor_cache[idx].attractor = E->get(); - idx++; - } - - attractor_ptr = attractor_cache.ptr(); - attractor_count = attractor_cache.size(); - } - - for (int i = 0; i < particle_count; i++) { - - Particle &p = pdata[i]; - - float restart_time = (i * lifetime / particle_count) * explosiveness; - - bool restart = false; - - if (next_time < time) { - - if (restart_time > time || restart_time < next_time) - restart = true; - - } else if (restart_time > time && restart_time < next_time) { - restart = true; - } - - if (restart) { - - if (emitting) { - - p.pos = emissor_offset; - if (emission_point_count) { - - Vector2 ep = r[Math::rand() % emission_point_count]; - if (!local_space) { - p.pos = xform.xform(p.pos + ep * extents); - } else { - p.pos += ep * extents; - } - } else { - if (!local_space) { - p.pos = xform.xform(p.pos + Vector2(Math::random(-extents.x, extents.x), Math::random(-extents.y, extents.y))); - } else { - p.pos += Vector2(Math::random(-extents.x, extents.x), Math::random(-extents.y, extents.y)); - } - } - p.seed = Math::rand() % 12345678; - uint64_t rand_seed = p.seed * (i + 1); - - float angle = Math::deg2rad(param[PARAM_DIRECTION] + _rand_from_seed(&rand_seed) * param[PARAM_SPREAD]); - - p.velocity = Vector2(Math::sin(angle), Math::cos(angle)); - if (!local_space) { - - p.velocity = xform.basis_xform(p.velocity).normalized(); - } - - p.velocity *= param[PARAM_LINEAR_VELOCITY] + param[PARAM_LINEAR_VELOCITY] * _rand_from_seed(&rand_seed) * randomness[PARAM_LINEAR_VELOCITY]; - p.velocity += initial_velocity; - p.active = true; - p.rot = Math::deg2rad(param[PARAM_INITIAL_ANGLE] + param[PARAM_INITIAL_ANGLE] * randomness[PARAM_INITIAL_ANGLE] * _rand_from_seed(&rand_seed)); - active_count++; - - p.frame = Math::fmod(param[PARAM_ANIM_INITIAL_POS] + randomness[PARAM_ANIM_INITIAL_POS] * _rand_from_seed(&rand_seed), 1.0f); - - } else { - - p.active = false; - } - - } else { - - if (!p.active) - continue; - - uint64_t rand_seed = p.seed * (i + 1); - - Vector2 force; - - //apply gravity - float gravity_dir = Math::deg2rad(param[PARAM_GRAVITY_DIRECTION] + 180 * randomness[PARAM_GRAVITY_DIRECTION] * _rand_from_seed(&rand_seed)); - force += Vector2(Math::sin(gravity_dir), Math::cos(gravity_dir)) * (param[PARAM_GRAVITY_STRENGTH] + param[PARAM_GRAVITY_STRENGTH] * randomness[PARAM_GRAVITY_STRENGTH] * _rand_from_seed(&rand_seed)); - //apply radial - Vector2 rvec = (p.pos - emissor_offset).normalized(); - force += rvec * (param[PARAM_RADIAL_ACCEL] + param[PARAM_RADIAL_ACCEL] * randomness[PARAM_RADIAL_ACCEL] * _rand_from_seed(&rand_seed)); - //apply orbit - float orbitvel = (param[PARAM_ORBIT_VELOCITY] + param[PARAM_ORBIT_VELOCITY] * randomness[PARAM_ORBIT_VELOCITY] * _rand_from_seed(&rand_seed)); - if (orbitvel != 0) { - Vector2 rel = p.pos - xform.elements[2]; - Transform2D rot(orbitvel * frame_time, Vector2()); - p.pos = rot.xform(rel) + xform.elements[2]; - } - - Vector2 tvec = rvec.tangent(); - force += tvec * (param[PARAM_TANGENTIAL_ACCEL] + param[PARAM_TANGENTIAL_ACCEL] * randomness[PARAM_TANGENTIAL_ACCEL] * _rand_from_seed(&rand_seed)); - - for (int j = 0; j < attractor_count; j++) { - - Vector2 vec = (attractor_ptr[j].pos - p.pos); - float vl = vec.length(); - - if (!attractor_ptr[j].attractor->enabled || vl == 0 || vl > attractor_ptr[j].attractor->radius) - continue; - - force += vec * attractor_ptr[j].attractor->gravity; - float fvl = p.velocity.length(); - if (fvl && attractor_ptr[j].attractor->absorption) { - Vector2 target = vec.normalized(); - p.velocity = p.velocity.normalized().linear_interpolate(target, MIN(frame_time * attractor_ptr[j].attractor->absorption, 1)) * fvl; - } - - if (attractor_ptr[j].attractor->disable_radius && vl < attractor_ptr[j].attractor->disable_radius) { - p.active = false; - } - } - - p.velocity += force * frame_time; - - if (param[PARAM_DAMPING]) { - float dmp = param[PARAM_DAMPING] + param[PARAM_DAMPING] * randomness[PARAM_DAMPING] * _rand_from_seed(&rand_seed); - float v = p.velocity.length(); - v -= dmp * frame_time; - if (v <= 0) { - p.velocity = Vector2(); - } else { - p.velocity = p.velocity.normalized() * v; - } - } - - p.pos += p.velocity * frame_time; - p.rot += Math::lerp(param[PARAM_SPIN_VELOCITY], param[PARAM_SPIN_VELOCITY] * randomness[PARAM_SPIN_VELOCITY] * _rand_from_seed(&rand_seed), randomness[PARAM_SPIN_VELOCITY]) * frame_time; - float anim_spd = param[PARAM_ANIM_SPEED_SCALE] + param[PARAM_ANIM_SPEED_SCALE] * randomness[PARAM_ANIM_SPEED_SCALE] * _rand_from_seed(&rand_seed); - p.frame = Math::fposmod(p.frame + (frame_time / lifetime) * anim_spd, 1.0f); - - active_count++; - } + process_material = p_material; + Ref<ParticlesMaterial> pm = p_material; + if (pm.is_valid() && !pm->get_flag(ParticlesMaterial::FLAG_DISABLE_Z) && pm->get_gravity() == Vector3(0, -9.8, 0)) { + //likely a new material, modify it! + pm->set_flag(ParticlesMaterial::FLAG_DISABLE_Z, true); + pm->set_gravity(Vector3(0, 98, 0)); } + RID material_rid; + if (process_material.is_valid()) + material_rid = process_material->get_rid(); + VS::get_singleton()->particles_set_process_material(particles, material_rid); - time = Math::fmod(time + frame_time, lifetime); - if (!emitting && active_count == 0) { - emit_signal(SceneStringNames::get_singleton()->emission_finished); - set_process(false); - set_fixed_process(false); - } - - update(); -} - -void Particles2D::_notification(int p_what) { - - switch (p_what) { - - case NOTIFICATION_PROCESS: { - - _process_particles(get_process_delta_time()); - } break; - - case NOTIFICATION_FIXED_PROCESS: { - - _process_particles(get_fixed_process_delta_time()); - } break; - - case NOTIFICATION_ENTER_TREE: { - - float ppt = preprocess; - while (ppt > 0) { - _process_particles(0.1); - ppt -= 0.1; - } - } break; - case NOTIFICATION_DRAW: { - - if (particles.size() == 0 || lifetime == 0) - return; - - RID ci = get_canvas_item(); - Size2 size(1, 1); - Point2 center; - int total_frames = 1; - - if (!texture.is_null()) { - size = texture->get_size(); - size.x /= h_frames; - size.y /= v_frames; - total_frames = h_frames * v_frames; - } - - float time_pos = (time / lifetime); - - Particle *pdata = &particles[0]; - int particle_count = particles.size(); - - RID texrid; - - if (texture.is_valid()) - texrid = texture->get_rid(); - - Transform2D invxform; - if (!local_space) - invxform = get_global_transform().affine_inverse(); - - int start_particle = (int)(time * (float)particle_count / lifetime); - - for (int id = 0; id < particle_count; ++id) { - int i = start_particle + id; - if (i >= particle_count) { - i -= particle_count; - } - - Particle &p = pdata[i]; - if (!p.active) - continue; - - float ptime = ((float)i / particle_count) * explosiveness; - - if (ptime < time_pos) - ptime = time_pos - ptime; - else - ptime = (1.0 - ptime) + time_pos; - - uint64_t rand_seed = p.seed * (i + 1); - - Color color; - - if (gradient.is_valid()) { - color = gradient->get_color_at_offset(ptime); - } else { - color = default_color; - } - - { - float huerand = _rand_from_seed(&rand_seed); - float huerot = param[PARAM_HUE_VARIATION] + randomness[PARAM_HUE_VARIATION] * huerand; - - if (Math::abs(huerot) > CMP_EPSILON) { - - float h = color.get_h(); - float s = color.get_s(); - float v = color.get_v(); - float a = color.a; - //float preh=h; - h += huerot; - h = Math::abs(Math::fposmod(h, 1.0f)); - //print_line("rand: "+rtos(randomness[PARAM_HUE_VARIATION])+" rand: "+rtos(huerand)); - //print_line(itos(i)+":hue: "+rtos(preh)+" + "+rtos(huerot)+" = "+rtos(h)); - color.set_hsv(h, s, v); - color.a = a; - } - } - - float initial_size = param[PARAM_INITIAL_SIZE] + param[PARAM_INITIAL_SIZE] * _rand_from_seed(&rand_seed) * randomness[PARAM_INITIAL_SIZE]; - float final_size = param[PARAM_FINAL_SIZE] + param[PARAM_FINAL_SIZE] * _rand_from_seed(&rand_seed) * randomness[PARAM_FINAL_SIZE]; - - float size_mult = initial_size * (1.0 - ptime) + final_size * ptime; - - //Size2 rectsize=size * size_mult; - //rectsize=rectsize.floor(); - - //Rect2 r = Rect2(Vecto,rectsize); - - Transform2D xform; - - if (p.rot) { - - xform.set_rotation(p.rot); - xform.translate(-size * size_mult / 2.0); - xform.elements[2] += p.pos; - } else { - xform.elements[2] = -size * size_mult / 2.0; - xform.elements[2] += p.pos; - } - - if (!local_space) { - xform = invxform * xform; - } - - xform.scale_basis(Size2(size_mult, size_mult)); - - VisualServer::get_singleton()->canvas_item_add_set_transform(ci, xform); - - if (texrid.is_valid()) { - - Rect2 src_rect; - src_rect.size = size; - - if (total_frames > 1) { - int frame = Math::fast_ftoi(Math::floor(p.frame * total_frames)) % total_frames; - src_rect.position.x = size.x * (frame % h_frames); - src_rect.position.y = size.y * (frame / h_frames); - } - Rect2 dst_rect(Point2(), size); - if (flip_h) - dst_rect.size.x = -dst_rect.size.x; - if (flip_v) - dst_rect.size.y = -dst_rect.size.y; - - texture->draw_rect_region(ci, dst_rect, src_rect, color); - //VisualServer::get_singleton()->canvas_item_add_texture_rect(ci,r,texrid,false,color); - } else { - VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(), size), color); - } - } - - } break; - } + update_configuration_warning(); } -static const char *_particlesframe_property_names[Particles2D::PARAM_MAX] = { - "params/direction", - "params/spread", - "params/linear_velocity", - "params/spin_velocity", - "params/orbit_velocity", - "params/gravity_direction", - "params/gravity_strength", - "params/radial_accel", - "params/tangential_accel", - "params/damping", - "params/initial_angle", - "params/initial_size", - "params/final_size", - "params/hue_variation", - "params/anim_speed_scale", - "params/anim_initial_pos", -}; - -static const char *_particlesframe_property_rnames[Particles2D::PARAM_MAX] = { - "randomness/direction", - "randomness/spread", - "randomness/linear_velocity", - "randomness/spin_velocity", - "randomness/orbit_velocity", - "randomness/gravity_direction", - "randomness/gravity_strength", - "randomness/radial_accel", - "randomness/tangential_accel", - "randomness/damping", - "randomness/initial_angle", - "randomness/initial_size", - "randomness/final_size", - "randomness/hue_variation", - "randomness/anim_speed_scale", - "randomness/anim_initial_pos", -}; - -static const char *_particlesframe_property_ranges[Particles2D::PARAM_MAX] = { - "0,360,0.01", - "0,180,0.01", - "-1024,1024,0.01", - "-1024,1024,0.01", - "-1024,1024,0.01", - "0,360,0.01", - "0,1024,0.01", - "-128,128,0.01", - "-128,128,0.01", - "0,1024,0.001", - "0,360,0.01", - "0,1024,0.01", - "0,1024,0.01", - "0,1,0.01", - "0,128,0.01", - "0,1,0.01", -}; - -void Particles2D::set_emitting(bool p_emitting) { - - if (emitting == p_emitting) - return; - - if (p_emitting) { +void Particles2D::set_speed_scale(float p_scale) { - if (active_count == 0) - time = 0; - set_process(process_mode == PROCESS_IDLE); - set_fixed_process(process_mode == PROCESS_FIXED); - time_to_live = emit_timeout; - }; - emitting = p_emitting; - _change_notify("config/emitting"); + speed_scale = p_scale; + VS::get_singleton()->particles_set_speed_scale(particles, p_scale); } bool Particles2D::is_emitting() const { return emitting; } - -void Particles2D::set_process_mode(ProcessMode p_mode) { - - process_mode = p_mode; - const bool should_process = emitting || active_count != 0; - set_process(should_process && process_mode == PROCESS_IDLE); - set_fixed_process(should_process && process_mode == PROCESS_FIXED); -} - -Particles2D::ProcessMode Particles2D::get_process_mode() const { - - return process_mode; -} - -void Particles2D::set_amount(int p_amount) { - - ERR_FAIL_INDEX(p_amount, 1024 + 1); - - particles.resize(p_amount); -} int Particles2D::get_amount() const { - return particles.size(); -} - -void Particles2D::set_emit_timeout(float p_timeout) { - - emit_timeout = p_timeout; - time_to_live = p_timeout; -}; - -float Particles2D::get_emit_timeout() const { - - return emit_timeout; -}; - -void Particles2D::set_lifetime(float p_lifetime) { - - ERR_FAIL_INDEX(p_lifetime, 3600 + 1); - - lifetime = p_lifetime; + return amount; } float Particles2D::get_lifetime() const { return lifetime; } - -void Particles2D::set_time_scale(float p_time_scale) { - - time_scale = p_time_scale; -} -float Particles2D::get_time_scale() const { - - return time_scale; -} - -void Particles2D::set_pre_process_time(float p_pre_process_time) { - - preprocess = p_pre_process_time; -} - float Particles2D::get_pre_process_time() const { - return preprocess; -} - -void Particles2D::set_param(Parameter p_param, float p_value) { - - ERR_FAIL_INDEX(p_param, PARAM_MAX); - param[p_param] = p_value; -} -float Particles2D::get_param(Parameter p_param) const { - - ERR_FAIL_INDEX_V(p_param, PARAM_MAX, 0); - return param[p_param]; -} - -void Particles2D::set_randomness(Parameter p_param, float p_value) { - - ERR_FAIL_INDEX(p_param, PARAM_MAX); - randomness[p_param] = p_value; -} -float Particles2D::get_randomness(Parameter p_param) const { - - ERR_FAIL_INDEX_V(p_param, PARAM_MAX, 0); - return randomness[p_param]; -} - -void Particles2D::set_texture(const Ref<Texture> &p_texture) { - - texture = p_texture; -} - -Ref<Texture> Particles2D::get_texture() const { - - return texture; + return pre_process_time; } +float Particles2D::get_explosiveness_ratio() const { -void Particles2D::set_color(const Color &p_color) { - - default_color = p_color; + return explosiveness_ratio; } +float Particles2D::get_randomness_ratio() const { -Color Particles2D::get_color() const { - - return default_color; + return randomness_ratio; } +Rect2 Particles2D::get_visibility_rect() const { -void Particles2D::set_gradient(const Ref<Gradient> &p_gradient) { - - gradient = p_gradient; + return visibility_rect; } +bool Particles2D::get_use_local_coordinates() const { -Ref<Gradient> Particles2D::get_gradient() const { - - return gradient; + return local_coords; } +Ref<Material> Particles2D::get_process_material() const { -void Particles2D::set_emissor_offset(const Point2 &p_offset) { - - emissor_offset = p_offset; + return process_material; } -Point2 Particles2D::get_emissor_offset() const { +float Particles2D::get_speed_scale() const { - return emissor_offset; + return speed_scale; } -void Particles2D::set_use_local_space(bool p_use) { +void Particles2D::set_draw_order(DrawOrder p_order) { - local_space = p_use; + draw_order = p_order; + VS::get_singleton()->particles_set_draw_order(particles, VS::ParticlesDrawOrder(p_order)); } -bool Particles2D::is_using_local_space() const { +Particles2D::DrawOrder Particles2D::get_draw_order() const { - return local_space; + return draw_order; } -//Deprecated. Converts color phases to color ramp -void Particles2D::set_color_phases(int p_phases) { - - //Create color ramp if we have 2 or more phases. - //Otherwise first phase phase will be assigned to default color. - if (p_phases > 1 && gradient.is_null()) { - gradient = Ref<Gradient>(memnew(Gradient())); - } - if (gradient.is_valid()) { - gradient->get_points().resize(p_phases); - } +void Particles2D::set_fixed_fps(int p_count) { + fixed_fps = p_count; + VS::get_singleton()->particles_set_fixed_fps(particles, p_count); } -//Deprecated. -int Particles2D::get_color_phases() const { - - if (gradient.is_valid()) { - return gradient->get_points_count(); - } - return 0; +int Particles2D::get_fixed_fps() const { + return fixed_fps; } -//Deprecated. Converts color phases to color ramp -void Particles2D::set_color_phase_color(int p_phase, const Color &p_color) { - - ERR_FAIL_INDEX(p_phase, MAX_COLOR_PHASES); - if (gradient.is_valid()) { - if (gradient->get_points_count() > p_phase) - gradient->set_color(p_phase, p_color); - } else { - if (p_phase == 0) - default_color = p_color; - } +void Particles2D::set_fractional_delta(bool p_enable) { + fractional_delta = p_enable; + VS::get_singleton()->particles_set_fractional_delta(particles, p_enable); } -//Deprecated. -Color Particles2D::get_color_phase_color(int p_phase) const { - - ERR_FAIL_INDEX_V(p_phase, MAX_COLOR_PHASES, Color()); - if (gradient.is_valid()) { - return gradient->get_color(p_phase); - } - return Color(0, 0, 0, 1); +bool Particles2D::get_fractional_delta() const { + return fractional_delta; } -//Deprecated. Converts color phases to color ramp -void Particles2D::set_color_phase_pos(int p_phase, float p_pos) { - ERR_FAIL_INDEX(p_phase, MAX_COLOR_PHASES); - ERR_FAIL_COND(p_pos < 0.0 || p_pos > 1.0); - if (gradient.is_valid() && gradient->get_points_count() > p_phase) { - return gradient->set_offset(p_phase, p_pos); - } -} +String Particles2D::get_configuration_warning() const { -//Deprecated. -float Particles2D::get_color_phase_pos(int p_phase) const { + String warnings; - ERR_FAIL_INDEX_V(p_phase, MAX_COLOR_PHASES, 0); - if (gradient.is_valid()) { - return gradient->get_offset(p_phase); + if (process_material.is_null()) { + if (warnings != String()) + warnings += "\n"; + warnings += "- " + TTR("A material to process the particles is not assigned, so no behavior is imprinted."); } - return 0; -} - -void Particles2D::set_emission_half_extents(const Vector2 &p_extents) { - extents = p_extents; + return warnings; } -Vector2 Particles2D::get_emission_half_extents() const { +Rect2 Particles2D::capture_rect() const { - return extents; + Rect3 aabb = VS::get_singleton()->particles_get_current_aabb(particles); + Rect2 r; + r.position.x = aabb.position.x; + r.position.y = aabb.position.y; + r.size.x = aabb.size.x; + r.size.y = aabb.size.y; + return r; } -void Particles2D::set_initial_velocity(const Vector2 &p_velocity) { - - initial_velocity = p_velocity; -} -Vector2 Particles2D::get_initial_velocity() const { - - return initial_velocity; +void Particles2D::set_texture(const Ref<Texture> &p_texture) { + texture = p_texture; + update(); } -void Particles2D::pre_process(float p_delta) { - - _process_particles(p_delta); +Ref<Texture> Particles2D::get_texture() const { + return texture; } -void Particles2D::set_explosiveness(float p_value) { +void Particles2D::set_normal_map(const Ref<Texture> &p_normal_map) { - explosiveness = p_value; + normal_map = p_normal_map; + update(); } -float Particles2D::get_explosiveness() const { - - return explosiveness; +Ref<Texture> Particles2D::get_normal_map() const { + return normal_map; } -void Particles2D::set_flip_h(bool p_flip) { - - flip_h = p_flip; +void Particles2D::_validate_property(PropertyInfo &property) const { } -bool Particles2D::is_flipped_h() const { +void Particles2D::set_v_frames(int p_count) { - return flip_h; + ERR_FAIL_COND(p_count < 1); + v_frames = p_count; + update(); } -void Particles2D::set_flip_v(bool p_flip) { - - flip_v = p_flip; -} -bool Particles2D::is_flipped_v() const { +int Particles2D::get_v_frames() const { - return flip_v; + return v_frames; } -void Particles2D::set_h_frames(int p_frames) { +void Particles2D::set_h_frames(int p_count) { - ERR_FAIL_COND(p_frames < 1); - h_frames = p_frames; + ERR_FAIL_COND(p_count < 1); + h_frames = p_count; + update(); } int Particles2D::get_h_frames() const { @@ -932,215 +263,120 @@ int Particles2D::get_h_frames() const { return h_frames; } -void Particles2D::set_v_frames(int p_frames) { - - ERR_FAIL_COND(p_frames < 1); - v_frames = p_frames; -} -int Particles2D::get_v_frames() const { - - return v_frames; -} +void Particles2D::_notification(int p_what) { -void Particles2D::set_emission_points(const PoolVector<Vector2> &p_points) { + if (p_what == NOTIFICATION_DRAW) { - emission_points = p_points; -} + RID texture_rid; + if (texture.is_valid()) + texture_rid = texture->get_rid(); + RID normal_rid; + if (normal_map.is_valid()) + normal_rid = texture->get_rid(); -PoolVector<Vector2> Particles2D::get_emission_points() const { + VS::get_singleton()->canvas_item_add_particles(get_canvas_item(), particles, texture_rid, normal_rid, h_frames, v_frames); - return emission_points; -} + if (get_tree()->is_editor_hint() && (this == get_tree()->get_edited_scene_root() || get_tree()->get_edited_scene_root()->is_a_parent_of(this))) { -void Particles2D::reset() { + draw_rect(visibility_rect, Color(0, 0.7, 0.9, 0.4), false); + } + } - for (int i = 0; i < particles.size(); i++) { - particles[i].active = false; + if (p_what == NOTIFICATION_TRANSFORM_CHANGED) { + _update_particle_emission_transform(); } - time = 0; - active_count = 0; } void Particles2D::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_emitting", "active"), &Particles2D::set_emitting); - ClassDB::bind_method(D_METHOD("is_emitting"), &Particles2D::is_emitting); - - ClassDB::bind_method(D_METHOD("set_process_mode", "mode"), &Particles2D::set_process_mode); - ClassDB::bind_method(D_METHOD("get_process_mode"), &Particles2D::get_process_mode); - + ClassDB::bind_method(D_METHOD("set_emitting", "emitting"), &Particles2D::set_emitting); ClassDB::bind_method(D_METHOD("set_amount", "amount"), &Particles2D::set_amount); - ClassDB::bind_method(D_METHOD("get_amount"), &Particles2D::get_amount); + ClassDB::bind_method(D_METHOD("set_lifetime", "secs"), &Particles2D::set_lifetime); + ClassDB::bind_method(D_METHOD("set_pre_process_time", "secs"), &Particles2D::set_pre_process_time); + ClassDB::bind_method(D_METHOD("set_explosiveness_ratio", "ratio"), &Particles2D::set_explosiveness_ratio); + ClassDB::bind_method(D_METHOD("set_randomness_ratio", "ratio"), &Particles2D::set_randomness_ratio); + ClassDB::bind_method(D_METHOD("set_visibility_rect", "aabb"), &Particles2D::set_visibility_rect); + ClassDB::bind_method(D_METHOD("set_use_local_coordinates", "enable"), &Particles2D::set_use_local_coordinates); + ClassDB::bind_method(D_METHOD("set_fixed_fps", "fps"), &Particles2D::set_fixed_fps); + ClassDB::bind_method(D_METHOD("set_fractional_delta", "enable"), &Particles2D::set_fractional_delta); + ClassDB::bind_method(D_METHOD("set_process_material", "material:Material"), &Particles2D::set_process_material); + ClassDB::bind_method(D_METHOD("set_speed_scale", "scale"), &Particles2D::set_speed_scale); - ClassDB::bind_method(D_METHOD("set_lifetime", "lifetime"), &Particles2D::set_lifetime); + ClassDB::bind_method(D_METHOD("is_emitting"), &Particles2D::is_emitting); + ClassDB::bind_method(D_METHOD("get_amount"), &Particles2D::get_amount); ClassDB::bind_method(D_METHOD("get_lifetime"), &Particles2D::get_lifetime); - - ClassDB::bind_method(D_METHOD("set_time_scale", "time_scale"), &Particles2D::set_time_scale); - ClassDB::bind_method(D_METHOD("get_time_scale"), &Particles2D::get_time_scale); - - ClassDB::bind_method(D_METHOD("set_pre_process_time", "time"), &Particles2D::set_pre_process_time); ClassDB::bind_method(D_METHOD("get_pre_process_time"), &Particles2D::get_pre_process_time); - - ClassDB::bind_method(D_METHOD("set_emit_timeout", "value"), &Particles2D::set_emit_timeout); - ClassDB::bind_method(D_METHOD("get_emit_timeout"), &Particles2D::get_emit_timeout); - - ClassDB::bind_method(D_METHOD("set_param", "param", "value"), &Particles2D::set_param); - ClassDB::bind_method(D_METHOD("get_param", "param"), &Particles2D::get_param); - - ClassDB::bind_method(D_METHOD("set_randomness", "param", "value"), &Particles2D::set_randomness); - ClassDB::bind_method(D_METHOD("get_randomness", "param"), &Particles2D::get_randomness); - - ClassDB::bind_method(D_METHOD("set_texture:Texture", "texture"), &Particles2D::set_texture); + ClassDB::bind_method(D_METHOD("get_explosiveness_ratio"), &Particles2D::get_explosiveness_ratio); + ClassDB::bind_method(D_METHOD("get_randomness_ratio"), &Particles2D::get_randomness_ratio); + ClassDB::bind_method(D_METHOD("get_visibility_rect"), &Particles2D::get_visibility_rect); + ClassDB::bind_method(D_METHOD("get_use_local_coordinates"), &Particles2D::get_use_local_coordinates); + ClassDB::bind_method(D_METHOD("get_fixed_fps"), &Particles2D::get_fixed_fps); + ClassDB::bind_method(D_METHOD("get_fractional_delta"), &Particles2D::get_fractional_delta); + ClassDB::bind_method(D_METHOD("get_process_material:Material"), &Particles2D::get_process_material); + ClassDB::bind_method(D_METHOD("get_speed_scale"), &Particles2D::get_speed_scale); + + ClassDB::bind_method(D_METHOD("set_draw_order", "order"), &Particles2D::set_draw_order); + ClassDB::bind_method(D_METHOD("get_draw_order"), &Particles2D::get_draw_order); + + ClassDB::bind_method(D_METHOD("set_texture", "texture:Texture"), &Particles2D::set_texture); ClassDB::bind_method(D_METHOD("get_texture:Texture"), &Particles2D::get_texture); - ClassDB::bind_method(D_METHOD("set_color", "color"), &Particles2D::set_color); - ClassDB::bind_method(D_METHOD("get_color"), &Particles2D::get_color); - - ClassDB::bind_method(D_METHOD("set_gradient:Gradient", "gradient"), &Particles2D::set_gradient); - ClassDB::bind_method(D_METHOD("get_gradient:Gradient"), &Particles2D::get_gradient); - - ClassDB::bind_method(D_METHOD("set_emissor_offset", "offset"), &Particles2D::set_emissor_offset); - ClassDB::bind_method(D_METHOD("get_emissor_offset"), &Particles2D::get_emissor_offset); - - ClassDB::bind_method(D_METHOD("set_flip_h", "enable"), &Particles2D::set_flip_h); - ClassDB::bind_method(D_METHOD("is_flipped_h"), &Particles2D::is_flipped_h); - - ClassDB::bind_method(D_METHOD("set_flip_v", "enable"), &Particles2D::set_flip_v); - ClassDB::bind_method(D_METHOD("is_flipped_v"), &Particles2D::is_flipped_v); + ClassDB::bind_method(D_METHOD("set_normal_map", "texture:Texture"), &Particles2D::set_normal_map); + ClassDB::bind_method(D_METHOD("get_normal_map:Texture"), &Particles2D::get_normal_map); - ClassDB::bind_method(D_METHOD("set_h_frames", "enable"), &Particles2D::set_h_frames); - ClassDB::bind_method(D_METHOD("get_h_frames"), &Particles2D::get_h_frames); + ClassDB::bind_method(D_METHOD("capture_rect"), &Particles2D::capture_rect); - ClassDB::bind_method(D_METHOD("set_v_frames", "enable"), &Particles2D::set_v_frames); + 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_emission_half_extents", "extents"), &Particles2D::set_emission_half_extents); - ClassDB::bind_method(D_METHOD("get_emission_half_extents"), &Particles2D::get_emission_half_extents); - - ClassDB::bind_method(D_METHOD("set_color_phases", "phases"), &Particles2D::set_color_phases); - ClassDB::bind_method(D_METHOD("get_color_phases"), &Particles2D::get_color_phases); - - ClassDB::bind_method(D_METHOD("set_color_phase_color", "phase", "color"), &Particles2D::set_color_phase_color); - ClassDB::bind_method(D_METHOD("get_color_phase_color", "phase"), &Particles2D::get_color_phase_color); - - ClassDB::bind_method(D_METHOD("set_color_phase_pos", "phase", "pos"), &Particles2D::set_color_phase_pos); - ClassDB::bind_method(D_METHOD("get_color_phase_pos", "phase"), &Particles2D::get_color_phase_pos); - - ClassDB::bind_method(D_METHOD("pre_process", "time"), &Particles2D::pre_process); - ClassDB::bind_method(D_METHOD("reset"), &Particles2D::reset); - - ClassDB::bind_method(D_METHOD("set_use_local_space", "enable"), &Particles2D::set_use_local_space); - ClassDB::bind_method(D_METHOD("is_using_local_space"), &Particles2D::is_using_local_space); - - ClassDB::bind_method(D_METHOD("set_initial_velocity", "velocity"), &Particles2D::set_initial_velocity); - ClassDB::bind_method(D_METHOD("get_initial_velocity"), &Particles2D::get_initial_velocity); - - ClassDB::bind_method(D_METHOD("set_explosiveness", "amount"), &Particles2D::set_explosiveness); - ClassDB::bind_method(D_METHOD("get_explosiveness"), &Particles2D::get_explosiveness); - - ClassDB::bind_method(D_METHOD("set_emission_points", "points"), &Particles2D::set_emission_points); - ClassDB::bind_method(D_METHOD("get_emission_points"), &Particles2D::get_emission_points); - - ADD_SIGNAL(MethodInfo("emission_finished")); - - ADD_PROPERTY(PropertyInfo(Variant::INT, "config/amount", PROPERTY_HINT_EXP_RANGE, "1,1024"), "set_amount", "get_amount"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "config/lifetime", PROPERTY_HINT_EXP_RANGE, "0.1,3600,0.1"), "set_lifetime", "get_lifetime"); - ADD_PROPERTYNO(PropertyInfo(Variant::REAL, "config/time_scale", PROPERTY_HINT_EXP_RANGE, "0.01,128,0.01"), "set_time_scale", "get_time_scale"); - ADD_PROPERTYNZ(PropertyInfo(Variant::REAL, "config/preprocess", PROPERTY_HINT_EXP_RANGE, "0,3600,0.1"), "set_pre_process_time", "get_pre_process_time"); - ADD_PROPERTYNZ(PropertyInfo(Variant::REAL, "config/emit_timeout", PROPERTY_HINT_RANGE, "0,3600,0.1"), "set_emit_timeout", "get_emit_timeout"); - ADD_PROPERTYNO(PropertyInfo(Variant::BOOL, "config/emitting"), "set_emitting", "is_emitting"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "config/process_mode", PROPERTY_HINT_ENUM, "Fixed,Idle"), "set_process_mode", "get_process_mode"); - ADD_PROPERTYNZ(PropertyInfo(Variant::VECTOR2, "config/offset"), "set_emissor_offset", "get_emissor_offset"); - ADD_PROPERTYNZ(PropertyInfo(Variant::VECTOR2, "config/half_extents"), "set_emission_half_extents", "get_emission_half_extents"); - ADD_PROPERTYNO(PropertyInfo(Variant::BOOL, "config/local_space"), "set_use_local_space", "is_using_local_space"); - ADD_PROPERTYNO(PropertyInfo(Variant::REAL, "config/explosiveness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_explosiveness", "get_explosiveness"); - ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "config/flip_h"), "set_flip_h", "is_flipped_h"); - ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "config/flip_v"), "set_flip_v", "is_flipped_v"); - ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "config/texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture"); - ADD_PROPERTYNO(PropertyInfo(Variant::INT, "config/h_frames", PROPERTY_HINT_RANGE, "1,512,1"), "set_h_frames", "get_h_frames"); - ADD_PROPERTYNO(PropertyInfo(Variant::INT, "config/v_frames", PROPERTY_HINT_RANGE, "1,512,1"), "set_v_frames", "get_v_frames"); - - for (int i = 0; i < PARAM_MAX; i++) { - ADD_PROPERTYI(PropertyInfo(Variant::REAL, _particlesframe_property_names[i], PROPERTY_HINT_RANGE, _particlesframe_property_ranges[i]), "set_param", "get_param", i); - } - - for (int i = 0; i < PARAM_MAX; i++) { - ADD_PROPERTYINZ(PropertyInfo(Variant::REAL, _particlesframe_property_rnames[i], PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_randomness", "get_randomness", i); - } - - ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "color_phases/count", PROPERTY_HINT_RANGE, "0,4,1", 0), "set_color_phases", "get_color_phases"); - - //Backward compatibility. They will be converted to color ramp - for (int i = 0; i < MAX_COLOR_PHASES; i++) { - String phase = "phase_" + itos(i) + "/"; - ADD_PROPERTYI(PropertyInfo(Variant::REAL, phase + "pos", PROPERTY_HINT_RANGE, "0,1,0.01", 0), "set_color_phase_pos", "get_color_phase_pos", i); - ADD_PROPERTYI(PropertyInfo(Variant::COLOR, phase + "color", PROPERTY_HINT_NONE, "", 0), "set_color_phase_color", "get_color_phase_color", i); - } + 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); - ADD_PROPERTYNO(PropertyInfo(Variant::COLOR, "color/color"), "set_color", "get_color"); - ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "color/color_ramp", PROPERTY_HINT_RESOURCE_TYPE, "Gradient"), "set_gradient", "get_gradient"); - - ADD_PROPERTYNZ(PropertyInfo(Variant::POOL_VECTOR2_ARRAY, "emission_points", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_emission_points", "get_emission_points"); - - BIND_CONSTANT(PARAM_DIRECTION); - BIND_CONSTANT(PARAM_SPREAD); - BIND_CONSTANT(PARAM_LINEAR_VELOCITY); - BIND_CONSTANT(PARAM_SPIN_VELOCITY); - BIND_CONSTANT(PARAM_ORBIT_VELOCITY); - BIND_CONSTANT(PARAM_GRAVITY_DIRECTION); - BIND_CONSTANT(PARAM_GRAVITY_STRENGTH); - BIND_CONSTANT(PARAM_RADIAL_ACCEL); - BIND_CONSTANT(PARAM_TANGENTIAL_ACCEL); - BIND_CONSTANT(PARAM_DAMPING); - BIND_CONSTANT(PARAM_INITIAL_ANGLE); - BIND_CONSTANT(PARAM_INITIAL_SIZE); - BIND_CONSTANT(PARAM_FINAL_SIZE); - BIND_CONSTANT(PARAM_HUE_VARIATION); - BIND_CONSTANT(PARAM_ANIM_SPEED_SCALE); - BIND_CONSTANT(PARAM_ANIM_INITIAL_POS); - BIND_CONSTANT(PARAM_MAX); - - BIND_CONSTANT(MAX_COLOR_PHASES); + ADD_GROUP("Parameters", ""); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emitting"), "set_emitting", "is_emitting"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "amount", PROPERTY_HINT_RANGE, "1,100000,1"), "set_amount", "get_amount"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "lifetime", PROPERTY_HINT_RANGE, "0.01,600.0,0.01"), "set_lifetime", "get_lifetime"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "preprocess", PROPERTY_HINT_RANGE, "0.00,600.0,0.01"), "set_pre_process_time", "get_pre_process_time"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "speed_scale", PROPERTY_HINT_RANGE, "0.01,64,0.01"), "set_speed_scale", "get_speed_scale"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "explosiveness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_explosiveness_ratio", "get_explosiveness_ratio"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "randomness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_randomness_ratio", "get_randomness_ratio"); + ADD_PROPERTY(PropertyInfo(Variant::RECT3, "visibility_rect"), "set_visibility_rect", "get_visibility_rect"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "local_coords"), "set_use_local_coordinates", "get_use_local_coordinates"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "fixed_fps", PROPERTY_HINT_RANGE, "0,1000,1"), "set_fixed_fps", "get_fixed_fps"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "fract_delta"), "set_fractional_delta", "get_fractional_delta"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "draw_order", PROPERTY_HINT_ENUM, "Index,Lifetime"), "set_draw_order", "get_draw_order"); + ADD_GROUP("Process Material", "process_"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "process_material", PROPERTY_HINT_RESOURCE_TYPE, "ParticlesMaterial,ShaderMaterial"), "set_process_material", "get_process_material"); + 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_CONSTANT(DRAW_ORDER_INDEX); + BIND_CONSTANT(DRAW_ORDER_LIFETIME); } Particles2D::Particles2D() { - for (int i = 0; i < PARAM_MAX; i++) { - - param[i] = 0; - randomness[i] = 0; - } + particles = VS::get_singleton()->particles_create(); - set_param(PARAM_SPREAD, 10); - set_param(PARAM_LINEAR_VELOCITY, 20); - set_param(PARAM_GRAVITY_STRENGTH, 9.8); - set_param(PARAM_RADIAL_ACCEL, 0); - set_param(PARAM_TANGENTIAL_ACCEL, 0); - set_param(PARAM_INITIAL_ANGLE, 0.0); - set_param(PARAM_INITIAL_SIZE, 1.0); - set_param(PARAM_FINAL_SIZE, 1.0); - set_param(PARAM_ANIM_SPEED_SCALE, 1.0); - - set_color(Color(1, 1, 1, 1)); - - time = 0; - lifetime = 2; - emitting = false; - particles.resize(32); - active_count = -1; set_emitting(true); - process_mode = PROCESS_IDLE; - local_space = true; - preprocess = 0; - time_scale = 1.0; - - flip_h = false; - flip_v = false; - - v_frames = 1; + set_amount(8); + set_lifetime(1); + set_fixed_fps(0); + set_fractional_delta(true); + set_pre_process_time(0); + set_explosiveness_ratio(0); + set_randomness_ratio(0); + set_visibility_rect(Rect2(Vector2(-100, -100), Vector2(200, 200))); + set_use_local_coordinates(true); + set_speed_scale(1); h_frames = 1; + v_frames = 1; +} + +Particles2D::~Particles2D() { - emit_timeout = 0; - time_to_live = 0; - explosiveness = 1.0; + VS::get_singleton()->free(particles); } diff --git a/scene/2d/particles_2d.h b/scene/2d/particles_2d.h index 856beaa836..ab7dcb1464 100644 --- a/scene/2d/particles_2d.h +++ b/scene/2d/particles_2d.h @@ -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); - float get_lifetime() const; - - void set_time_scale(float p_time_scale); - float get_time_scale() const; + 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); - void set_pre_process_time(float p_pre_process_time); + bool is_emitting() const; + int get_amount() const; + float get_lifetime() const; 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_emission_half_extents(const Vector2 &p_extents); - Vector2 get_emission_half_extents() const; + void set_fixed_fps(int p_count); + int get_fixed_fps() const; - void set_param(Parameter p_param, float p_value); - float get_param(Parameter p_param) const; + void set_fractional_delta(bool p_enable); + bool get_fractional_delta() 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_gradient(const Ref<Gradient> &p_texture); - Ref<Gradient> get_gradient() const; - - void set_emissor_offset(const Point2 &p_offset); - Point2 get_emissor_offset() const; + void set_normal_map(const Ref<Texture> &p_normal_map); + Ref<Texture> get_normal_map() 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; + virtual String get_configuration_warning() const; - void set_emission_points(const PoolVector<Vector2> &p_points); - PoolVector<Vector2> get_emission_points() const; + void set_v_frames(int p_count); + int get_v_frames() 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 diff --git a/scene/3d/particles.cpp b/scene/3d/particles.cpp index 71079ea780..3394c1e204 100644 --- a/scene/3d/particles.cpp +++ b/scene/3d/particles.cpp @@ -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); diff --git a/scene/3d/particles.h b/scene/3d/particles.h index 63ebd7ed7b..dad8ed4585 100644 --- a/scene/3d/particles.h +++ b/scene/3d/particles.h @@ -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); diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index b2737353fb..d4ca55346a 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -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>(); diff --git a/servers/visual/rasterizer.h b/servers/visual/rasterizer.h index 07d6542b62..43452b714c 100644 --- a/servers/visual/rasterizer.h +++ b/servers/visual/rasterizer.h @@ -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; @@ -845,6 +856,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: { const Item::CommandCircle *circle = static_cast<const Item::CommandCircle *>(c); diff --git a/servers/visual/visual_server_canvas.cpp b/servers/visual/visual_server_canvas.cpp index 2ef1fd417b..616990b311 100644 --- a/servers/visual/visual_server_canvas.cpp +++ b/servers/visual/visual_server_canvas.cpp @@ -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); diff --git a/servers/visual/visual_server_canvas.h b/servers/visual/visual_server_canvas.h index 2c86b14c70..f78ef635f4 100644 --- a/servers/visual/visual_server_canvas.h +++ b/servers/visual/visual_server_canvas.h @@ -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); diff --git a/servers/visual/visual_server_raster.h b/servers/visual/visual_server_raster.h index c48bee7fa4..03af60dcd5 100644 --- a/servers/visual/visual_server_raster.h +++ b/servers/visual/visual_server_raster.h @@ -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) diff --git a/servers/visual/visual_server_wrap_mt.h b/servers/visual/visual_server_wrap_mt.h index 94cbfc815e..79a7805472 100644 --- a/servers/visual/visual_server_wrap_mt.h +++ b/servers/visual/visual_server_wrap_mt.h @@ -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) diff --git a/servers/visual_server.h b/servers/visual_server.h index 589fa80084..bc72c8a5a1 100644 --- a/servers/visual_server.h +++ b/servers/visual_server.h @@ -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; |