diff --git a/editor/plugins/particles_editor_plugin.cpp b/editor/plugins/particles_editor_plugin.cpp index 036fc7d9904..a83576d1f39 100644 --- a/editor/plugins/particles_editor_plugin.cpp +++ b/editor/plugins/particles_editor_plugin.cpp @@ -291,22 +291,29 @@ Particles2DEditorPlugin::Particles2DEditorPlugin() { emission_mask->connect(SceneStringName(confirmed), callable_mp(this, &Particles2DEditorPlugin::_generate_emission_mask)); } -void GPUParticles2DEditorPlugin::_selection_changed() { +void Particles2DEditorPlugin::_selection_changed() { List selected_nodes = EditorNode::get_singleton()->get_editor_selection()->get_selected_node_list(); if (selected_particles.is_empty() && selected_nodes.is_empty()) { return; } - for (GPUParticles2D *particles : selected_particles) { - particles->set_show_visibility_rect(false); + for (Node *particles : selected_particles) { + if (GPUParticles2D *gpu_particles = Object::cast_to(particles)) { + gpu_particles->set_show_gizmos(false); + } else if (CPUParticles2D *cpu_particles = Object::cast_to(particles)) { + cpu_particles->set_show_gizmos(false); + } } + selected_particles.clear(); for (Node *node : selected_nodes) { - GPUParticles2D *selected_particle = Object::cast_to(node); - if (selected_particle) { - selected_particle->set_show_visibility_rect(true); - selected_particles.push_back(selected_particle); + if (GPUParticles2D *selected_gpu_particle = Object::cast_to(node)) { + selected_gpu_particle->set_show_gizmos(true); + selected_particles.push_back(selected_gpu_particle); + } else if (CPUParticles2D *selected_cpu_particle = Object::cast_to(node)) { + selected_cpu_particle->set_show_gizmos(true); + selected_particles.push_back(selected_cpu_particle); } } } @@ -353,10 +360,10 @@ void GPUParticles2DEditorPlugin::_generate_visibility_rect() { undo_redo->commit_action(); } -void GPUParticles2DEditorPlugin::_notification(int p_what) { +void Particles2DEditorPlugin::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { - EditorNode::get_singleton()->get_editor_selection()->connect("selection_changed", callable_mp(this, &GPUParticles2DEditorPlugin::_selection_changed)); + EditorNode::get_singleton()->get_editor_selection()->connect("selection_changed", callable_mp(this, &Particles2DEditorPlugin::_selection_changed)); } break; } } diff --git a/editor/plugins/particles_editor_plugin.h b/editor/plugins/particles_editor_plugin.h index d4459765c46..d6f8ee8e3df 100644 --- a/editor/plugins/particles_editor_plugin.h +++ b/editor/plugins/particles_editor_plugin.h @@ -37,6 +37,7 @@ class CheckBox; class ConfirmationDialog; class EditorFileDialog; class GPUParticles2D; +class CPUParticles2D; class HBoxContainer; class MenuButton; class OptionButton; @@ -87,6 +88,8 @@ protected: MENU_LOAD_EMISSION_MASK = 100, }; + List selected_particles; + enum EmissionMode { EMISSION_MODE_SOLID, EMISSION_MODE_BORDER, @@ -106,6 +109,8 @@ protected: void _file_selected(const String &p_file); void _get_base_emission_mask(PackedVector2Array &r_valid_positions, PackedVector2Array &r_valid_normals, PackedByteArray &r_valid_colors, Vector2i &r_image_size); virtual void _generate_emission_mask() = 0; + void _notification(int p_what); + void _selection_changed(); public: Particles2DEditorPlugin(); @@ -118,17 +123,12 @@ class GPUParticles2DEditorPlugin : public Particles2DEditorPlugin { MENU_GENERATE_VISIBILITY_RECT = 200, }; - List selected_particles; - ConfirmationDialog *generate_visibility_rect = nullptr; SpinBox *generate_seconds = nullptr; - void _selection_changed(); void _generate_visibility_rect(); protected: - void _notification(int p_what); - void _menu_callback(int p_idx) override; void _add_menu_options(PopupMenu *p_menu) override; diff --git a/scene/2d/cpu_particles_2d.cpp b/scene/2d/cpu_particles_2d.cpp index 76cb24867a9..a53362efd6c 100644 --- a/scene/2d/cpu_particles_2d.cpp +++ b/scene/2d/cpu_particles_2d.cpp @@ -104,7 +104,14 @@ void CPUParticles2D::set_use_local_coordinates(bool p_enable) { // We only need NOTIFICATION_TRANSFORM_CHANGED // when following an interpolated target. + +#ifdef TOOLS_ENABLED + set_notify_transform(_interpolation_data.interpolated_follow || (Engine::get_singleton()->is_editor_hint() && !local_coords)); +#else set_notify_transform(_interpolation_data.interpolated_follow); +#endif + + queue_redraw(); } void CPUParticles2D::set_speed_scale(double p_scale) { @@ -461,14 +468,35 @@ void CPUParticles2D::set_emission_shape(EmissionShape p_shape) { ERR_FAIL_INDEX(p_shape, EMISSION_SHAPE_MAX); emission_shape = p_shape; notify_property_list_changed(); +#ifdef TOOLS_ENABLED + if (Engine::get_singleton()->is_editor_hint()) { + queue_redraw(); + } +#endif } void CPUParticles2D::set_emission_sphere_radius(real_t p_radius) { + if (p_radius == emission_sphere_radius) { + return; + } emission_sphere_radius = p_radius; +#ifdef TOOLS_ENABLED + if (Engine::get_singleton()->is_editor_hint()) { + queue_redraw(); + } +#endif } void CPUParticles2D::set_emission_rect_extents(Vector2 p_extents) { + if (p_extents == emission_rect_extents) { + return; + } emission_rect_extents = p_extents; +#ifdef TOOLS_ENABLED + if (Engine::get_singleton()->is_editor_hint()) { + queue_redraw(); + } +#endif } void CPUParticles2D::set_emission_points(const Vector &p_points) { @@ -556,6 +584,16 @@ void CPUParticles2D::set_seed(uint32_t p_seed) { seed = p_seed; } +#ifdef TOOLS_ENABLED +void CPUParticles2D::set_show_gizmos(bool p_show_gizmos) { + if (show_gizmos == p_show_gizmos) { + return; + } + show_gizmos = p_show_gizmos; + queue_redraw(); +} +#endif + uint32_t CPUParticles2D::get_seed() const { return seed; } @@ -1211,6 +1249,13 @@ void CPUParticles2D::_notification(int p_what) { } RS::get_singleton()->canvas_item_add_multimesh(get_canvas_item(), multimesh, texrid); + +#ifdef TOOLS_ENABLED + if (show_gizmos) { + _draw_emission_gizmo(); + } +#endif + } break; case NOTIFICATION_INTERNAL_PROCESS: { @@ -1232,6 +1277,11 @@ void CPUParticles2D::_notification(int p_what) { _interpolation_data.global_xform_curr = get_global_transform(); } } +#ifdef TOOLS_ENABLED + if (!local_coords) { + queue_redraw(); + } +#endif } break; case NOTIFICATION_RESET_PHYSICS_INTERPOLATION: { @@ -1242,6 +1292,30 @@ void CPUParticles2D::_notification(int p_what) { } } +#ifdef TOOLS_ENABLED +void CPUParticles2D::_draw_emission_gizmo() { + Color emission_ring_color = Color(0.8, 0.7, 0.4, 0.4); + Transform2D gizmo_transform; + if (!local_coords) { + gizmo_transform = get_global_transform(); + } + + draw_set_transform_matrix(gizmo_transform); + + switch (emission_shape) { + case CPUParticles2D::EMISSION_SHAPE_RECTANGLE: + draw_rect(Rect2(-emission_rect_extents, emission_rect_extents * 2.0), emission_ring_color, false); + break; + case CPUParticles2D::EMISSION_SHAPE_SPHERE: + case CPUParticles2D::EMISSION_SHAPE_SPHERE_SURFACE: + draw_circle(Vector2(), emission_sphere_radius, emission_ring_color, false); + break; + default: + break; + } +} +#endif + void CPUParticles2D::convert_from_particles(Node *p_particles) { GPUParticles2D *gpu_particles = Object::cast_to(p_particles); ERR_FAIL_NULL_MSG(gpu_particles, "Only GPUParticles2D nodes can be converted to CPUParticles2D."); diff --git a/scene/2d/cpu_particles_2d.h b/scene/2d/cpu_particles_2d.h index 4c0ffff417a..681cb6fef24 100644 --- a/scene/2d/cpu_particles_2d.h +++ b/scene/2d/cpu_particles_2d.h @@ -148,6 +148,10 @@ private: Transform2D inv_emission_transform; +#ifdef TOOLS_ENABLED + bool show_gizmos = false; +#endif + DrawOrder draw_order = DRAW_ORDER_INDEX; Ref texture; @@ -211,6 +215,9 @@ private: protected: static void _bind_methods(); void _notification(int p_what); +#ifdef TOOLS_ENABLED + void _draw_emission_gizmo(); +#endif void _validate_property(PropertyInfo &p_property) const; #ifndef DISABLE_DEPRECATED @@ -257,6 +264,9 @@ public: bool get_use_fixed_seed() const; void set_seed(uint32_t p_seed); +#ifdef TOOLS_ENABLED + void set_show_gizmos(bool p_show_gizmos); +#endif uint32_t get_seed() const; void request_particles_process(real_t p_requested_process_time); diff --git a/scene/2d/gpu_particles_2d.cpp b/scene/2d/gpu_particles_2d.cpp index 81c9aa82dd4..f778dd03069 100644 --- a/scene/2d/gpu_particles_2d.cpp +++ b/scene/2d/gpu_particles_2d.cpp @@ -141,7 +141,22 @@ void GPUParticles2D::_update_particle_emission_transform() { } void GPUParticles2D::set_process_material(const Ref &p_material) { + if (process_material == p_material) { + return; + } + + if (process_material.is_valid() && process_material->is_class("ParticleProcessMaterial")) { + process_material->disconnect("emission_shape_changed", callable_mp((CanvasItem *)this, &GPUParticles2D::queue_redraw)); + } + process_material = p_material; + + if (process_material.is_valid() && process_material->is_class("ParticleProcessMaterial")) { + process_material->connect("emission_shape_changed", callable_mp((CanvasItem *)this, &GPUParticles2D::queue_redraw)); + } + + queue_redraw(); + Ref pm = p_material; if (pm.is_valid() && !pm->get_particle_flag(ParticleProcessMaterial::PARTICLE_FLAG_DISABLE_Z) && pm->get_gravity() == Vector3(0, -9.8, 0)) { // Likely a new (3D) material, modify it to match 2D space @@ -195,8 +210,11 @@ void GPUParticles2D::set_interp_to_end(float p_interp) { } #ifdef TOOLS_ENABLED -void GPUParticles2D::set_show_visibility_rect(bool p_show_visibility_rect) { - show_visibility_rect = p_show_visibility_rect; +void GPUParticles2D::set_show_gizmos(bool p_show_gizmos) { + if (show_gizmos == p_show_gizmos) { + return; + } + show_gizmos = p_show_gizmos; queue_redraw(); } #endif @@ -704,8 +722,9 @@ void GPUParticles2D::_notification(int p_what) { RS::get_singleton()->canvas_item_add_particles(get_canvas_item(), particles, texture_rid); #ifdef TOOLS_ENABLED - if (show_visibility_rect) { + if (show_gizmos) { draw_rect(visibility_rect, Color(0, 0.7, 0.9, 0.4), false); + _draw_emission_gizmo(); } #endif } break; @@ -781,6 +800,60 @@ void GPUParticles2D::_notification(int p_what) { } } +#ifdef TOOLS_ENABLED +void GPUParticles2D::_draw_emission_gizmo() { + Ref pm = process_material; + Color emission_ring_color = Color(0.8, 0.7, 0.4, 0.4); + if (pm.is_null()) { + return; + } + draw_set_transform( + Vector2(pm->get_emission_shape_offset().x, pm->get_emission_shape_offset().y), + 0.0, + Vector2(pm->get_emission_shape_scale().x, pm->get_emission_shape_scale().y)); + + switch (pm->get_emission_shape()) { + case ParticleProcessMaterial::EmissionShape::EMISSION_SHAPE_BOX: { + Vector2 extents2d = Vector2(pm->get_emission_box_extents().x, pm->get_emission_box_extents().y); + draw_rect(Rect2(-extents2d, extents2d * 2.0), emission_ring_color, false); + break; + } + case ParticleProcessMaterial::EmissionShape::EMISSION_SHAPE_SPHERE: + case ParticleProcessMaterial::EmissionShape::EMISSION_SHAPE_SPHERE_SURFACE: { + draw_circle(Vector2(), pm->get_emission_sphere_radius(), emission_ring_color, false); + break; + } + case ParticleProcessMaterial::EmissionShape::EMISSION_SHAPE_RING: { + Vector3 ring_axis = pm->get_emission_ring_axis(); + if (ring_axis.is_equal_approx(Vector3(0.0, 0.0, 1.0)) || ring_axis.is_zero_approx()) { + draw_circle(Vector2(), pm->get_emission_ring_inner_radius(), emission_ring_color, false); + draw_circle(Vector2(), pm->get_emission_ring_radius(), emission_ring_color, false); + } else { + Vector2 a = Vector2(pm->get_emission_ring_height() / -2.0, pm->get_emission_ring_radius() / -1.0); + Vector2 b = Vector2(-a.x, MIN(a.y + tan((90.0 - pm->get_emission_ring_cone_angle()) * 0.01745329) * pm->get_emission_ring_height(), 0.0)); + Vector2 c = Vector2(b.x, -b.y); + Vector2 d = Vector2(a.x, -a.y); + if (ring_axis.is_equal_approx(Vector3(1.0, 0.0, 0.0))) { + Vector pos = { a, b, b, c, c, d, d, a }; + draw_multiline(pos, emission_ring_color); + } else if (ring_axis.is_equal_approx(Vector3(0.0, 1.0, 0.0))) { + a = Vector2(a.y, a.x); + b = Vector2(b.y, b.x); + c = Vector2(c.y, c.x); + d = Vector2(d.y, d.x); + Vector pos = { a, b, b, c, c, d, d, a }; + draw_multiline(pos, emission_ring_color); + } + } + break; + } + default: { + break; + } + } +} +#endif + void GPUParticles2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_emitting", "emitting"), &GPUParticles2D::set_emitting); ClassDB::bind_method(D_METHOD("set_amount", "amount"), &GPUParticles2D::set_amount); diff --git a/scene/2d/gpu_particles_2d.h b/scene/2d/gpu_particles_2d.h index ca832698c16..d91146e72e3 100644 --- a/scene/2d/gpu_particles_2d.h +++ b/scene/2d/gpu_particles_2d.h @@ -69,7 +69,7 @@ private: uint32_t seed = 0; bool use_fixed_seed = false; #ifdef TOOLS_ENABLED - bool show_visibility_rect = false; + bool show_gizmos = false; #endif Ref process_material; @@ -101,6 +101,9 @@ protected: static void _bind_methods(); void _validate_property(PropertyInfo &p_property) const; void _notification(int p_what); +#ifdef TOOLS_ENABLED + void _draw_emission_gizmo(); +#endif void _update_collision_size(); #ifndef DISABLE_DEPRECATED @@ -129,7 +132,7 @@ public: void request_particles_process(real_t p_requested_process_time); #ifdef TOOLS_ENABLED - void set_show_visibility_rect(bool p_show_visibility_rect); + void set_show_gizmos(bool p_show_gizmos); #endif bool is_emitting() const;