diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 115d349a5..b3a967a06 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -134,6 +134,8 @@ #include "editor/plugins/navigation_obstacle_2d_editor_plugin.h" #include "editor/plugins/navigation_obstacle_3d_editor_plugin.h" #include "editor/plugins/packed_scene_editor_plugin.h" +#include "editor/plugins/gpu_particles_2d_editor_plugin.h" +#include "editor/plugins/particles_editor_plugin.h" #include "editor/plugins/path_2d_editor_plugin.h" #include "editor/plugins/path_editor_plugin.h" #include "editor/plugins/polygon_2d_editor_plugin.h" @@ -7164,6 +7166,7 @@ EditorNode::EditorNode() { add_editor_plugin(memnew(AnimationTreeEditorPlugin(this))); add_editor_plugin(memnew(StyleBoxEditorPlugin(this))); add_editor_plugin(memnew(SpriteEditorPlugin(this))); + add_editor_plugin(memnew(GPUParticlesEditorPlugin(this))); add_editor_plugin(memnew(CPUParticles2DEditorPlugin(this))); add_editor_plugin(memnew(CPUParticlesEditorPlugin(this))); add_editor_plugin(memnew(ResourcePreloaderEditorPlugin(this))); @@ -7173,6 +7176,7 @@ EditorNode::EditorNode() { add_editor_plugin(memnew(SpriteFramesEditorPlugin(this))); add_editor_plugin(memnew(TextureRegionEditorPlugin(this))); add_editor_plugin(memnew(BakedLightmapEditorPlugin(this))); + add_editor_plugin(memnew(GPUParticles2DEditorPlugin(this))); add_editor_plugin(memnew(RoomManagerEditorPlugin(this))); add_editor_plugin(memnew(RoomEditorPlugin(this))); add_editor_plugin(memnew(OccluderEditorPlugin(this))); diff --git a/editor/icons/icon_particles.svg b/editor/icons/icon_g_p_u_particles.svg similarity index 100% rename from editor/icons/icon_particles.svg rename to editor/icons/icon_g_p_u_particles.svg diff --git a/editor/icons/icon_particles_2d.svg b/editor/icons/icon_g_p_u_particles_2d.svg similarity index 100% rename from editor/icons/icon_particles_2d.svg rename to editor/icons/icon_g_p_u_particles_2d.svg diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index e9bc6f0e1..4fccb5821 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -66,6 +66,7 @@ #include "editor/scene_tree_dock.h" #include "editor/script_editor_debugger.h" #include "scene/2d/light_2d.h" +#include "scene/2d/gpu_particles_2d.h" #include "scene/2d/polygon_2d.h" #include "scene/main/node_2d.h" @@ -6141,6 +6142,8 @@ void CanvasItemEditorViewport::_perform_drop_data() { Node *child; if (default_type == "Light2D") { child = memnew(Light2D); + } else if (default_type == "GPUParticles2D") { + child = memnew(GPUParticles2D); } else if (default_type == "Polygon2D") { child = memnew(Polygon2D); } else if (default_type == "TouchScreenButton") { diff --git a/editor/plugins/gpu_particles_2d_editor_plugin.cpp b/editor/plugins/gpu_particles_2d_editor_plugin.cpp index 27c74a45f..d919b2392 100644 --- a/editor/plugins/gpu_particles_2d_editor_plugin.cpp +++ b/editor/plugins/gpu_particles_2d_editor_plugin.cpp @@ -110,8 +110,7 @@ void GPUParticles2DEditorPlugin::_menu_callback(int p_idx) { } break; case MENU_OPTION_CONVERT_TO_CPU_PARTICLES: { CPUParticles2D *cpu_particles = memnew(CPUParticles2D); - //TODO - //cpu_particles->convert_from_particles(particles); + cpu_particles->convert_from_particles(particles); cpu_particles->set_name(particles->get_name()); cpu_particles->set_transform(particles->get_transform()); cpu_particles->set_visible(particles->is_visible()); diff --git a/editor/plugins/particles_editor_plugin.cpp b/editor/plugins/particles_editor_plugin.cpp index 058d44e3e..49fd485dd 100644 --- a/editor/plugins/particles_editor_plugin.cpp +++ b/editor/plugins/particles_editor_plugin.cpp @@ -56,8 +56,13 @@ #include "scene/gui/dialogs.h" #include "scene/gui/option_button.h" #include "scene/gui/spin_box.h" +#include "scene/gui/popup_menu.h" #include "scene/main/node.h" #include "scene/main/spatial.h" +#include "scene/3d/gpu_particles.h" +#include "scene/3d/cpu_particles.h" +#include "scene/resources/material/particles_material.h" +#include "editor/plugins/spatial_editor_plugin.h" bool ParticlesEditorBase::_generate(PoolVector &points, PoolVector &normals) { bool use_normals = emission_fill->get_selected() == 1; @@ -261,3 +266,266 @@ ParticlesEditorBase::ParticlesEditorBase() { emission_file_dialog->set_mode(EditorFileDialog::MODE_OPEN_FILE); } + +void GPUParticlesEditor::_node_removed(Node *p_node) { + if (p_node == node) { + node = nullptr; + hide(); + } +} + +void GPUParticlesEditor::_notification(int p_notification) { + if (p_notification == NOTIFICATION_ENTER_TREE) { + options->set_icon(options->get_popup()->get_theme_icon("GPUParticles", "EditorIcons")); + get_tree()->connect("node_removed", this, "_node_removed"); + } +} + +void GPUParticlesEditor::_menu_option(int p_option) { + switch (p_option) { + case MENU_OPTION_GENERATE_AABB: { + // Add one second to the default generation lifetime, since the progress is updated every second. + generate_seconds->set_value(MAX(1.0, trunc(node->get_lifetime()) + 1.0)); + + if (generate_seconds->get_value() >= 11.0 + CMP_EPSILON) { + // Only pop up the time dialog if the particle's lifetime is long enough to warrant shortening it. + generate_aabb->popup_centered_minsize(); + } else { + // Generate the visibility AABB immediately. + _generate_aabb(); + } + } break; + case MENU_OPTION_CREATE_EMISSION_VOLUME_FROM_MESH: { + Ref material = node->get_process_material(); + if (material.is_null()) { + EditorNode::get_singleton()->show_warning(TTR("A processor material of type 'ParticlesMaterial' is required.")); + return; + } + emission_file_dialog->popup_centered_ratio(); + + } break; + + case MENU_OPTION_CREATE_EMISSION_VOLUME_FROM_NODE: { + Ref material = node->get_process_material(); + if (material.is_null()) { + EditorNode::get_singleton()->show_warning(TTR("A processor material of type 'ParticlesMaterial' is required.")); + return; + } + + emission_tree_dialog->popup_centered_ratio(); + + } break; + case MENU_OPTION_CONVERT_TO_CPU_PARTICLES: { + CPUParticles *cpu_particles = memnew(CPUParticles); + cpu_particles->convert_from_particles(node); + cpu_particles->set_name(node->get_name()); + cpu_particles->set_transform(node->get_transform()); + cpu_particles->set_visible(node->is_visible()); + cpu_particles->set_pause_mode(node->get_pause_mode()); + + UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + ur->create_action(TTR("Convert to CPUParticles")); + ur->add_do_method(EditorNode::get_singleton()->get_scene_tree_dock(), "replace_node", node, cpu_particles, true, false); + ur->add_do_reference(cpu_particles); + ur->add_undo_method(EditorNode::get_singleton()->get_scene_tree_dock(), "replace_node", cpu_particles, node, false, false); + ur->add_undo_reference(node); + ur->commit_action(); + + } break; + case MENU_OPTION_RESTART: { + node->restart(); + + } break; + } +} + +void GPUParticlesEditor::_generate_aabb() { + float time = generate_seconds->get_value(); + + float running = 0.0; + + EditorProgress ep("gen_aabb", TTR("Generating Visibility AABB (Waiting for Particle Simulation)"), int(time)); + + bool was_emitting = node->is_emitting(); + if (!was_emitting) { + node->set_emitting(true); + OS::get_singleton()->delay_usec(1000); + } + + AABB 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); + + AABB capture = node->capture_aabb(); + if (rect == AABB()) { + rect = capture; + } else { + rect.merge_with(capture); + } + + running += (OS::get_singleton()->get_ticks_usec() - ticks) / 1000000.0; + } + + if (!was_emitting) { + node->set_emitting(false); + } + + UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + ur->create_action(TTR("Generate Visibility AABB")); + ur->add_do_method(node, "set_visibility_aabb", rect); + ur->add_undo_method(node, "set_visibility_aabb", node->get_visibility_aabb()); + ur->commit_action(); +} + +void GPUParticlesEditor::edit(GPUParticles *p_particles) { + base_node = p_particles; + node = p_particles; +} + +void GPUParticlesEditor::_generate_emission_points() { + /// hacer codigo aca + PoolVector points; + PoolVector normals; + + if (!_generate(points, normals)) { + return; + } + + int point_count = points.size(); + + int w = 2048; + int h = (point_count / 2048) + 1; + + PoolVector point_img; + point_img.resize(w * h * 3 * sizeof(float)); + + { + PoolVector::Write iw = point_img.write(); + memset(iw.ptr(), 0, w * h * 3 * sizeof(float)); + PoolVector::Read r = points.read(); + float *wf = (float *)iw.ptr(); + for (int i = 0; i < point_count; i++) { + wf[i * 3 + 0] = r[i].x; + wf[i * 3 + 1] = r[i].y; + wf[i * 3 + 2] = r[i].z; + } + } + + Ref image = memnew(Image(w, h, false, Image::FORMAT_RGBF, point_img)); + + Ref tex; + tex.instance(); + tex->create_from_image(image, Texture::FLAG_FILTER); + + Ref material = node->get_process_material(); + ERR_FAIL_COND(material.is_null()); + + if (normals.size() > 0) { + material->set_emission_shape(ParticlesMaterial::EMISSION_SHAPE_DIRECTED_POINTS); + material->set_emission_point_count(point_count); + material->set_emission_point_texture(tex); + + PoolVector point_img2; + point_img2.resize(w * h * 3 * sizeof(float)); + + { + PoolVector::Write iw = point_img2.write(); + memset(iw.ptr(), 0, w * h * 3 * sizeof(float)); + PoolVector::Read r = normals.read(); + float *wf = (float *)iw.ptr(); + for (int i = 0; i < point_count; i++) { + wf[i * 3 + 0] = r[i].x; + wf[i * 3 + 1] = r[i].y; + wf[i * 3 + 2] = r[i].z; + } + } + + Ref image2 = memnew(Image(w, h, false, Image::FORMAT_RGBF, point_img2)); + + Ref tex2; + tex2.instance(); + tex2->create_from_image(image2, Texture::FLAG_FILTER); + + material->set_emission_normal_texture(tex2); + } else { + material->set_emission_shape(ParticlesMaterial::EMISSION_SHAPE_POINTS); + material->set_emission_point_count(point_count); + material->set_emission_point_texture(tex); + } +} + +void GPUParticlesEditor::_bind_methods() { + ClassDB::bind_method("_menu_option", &GPUParticlesEditor::_menu_option); + ClassDB::bind_method("_generate_aabb", &GPUParticlesEditor::_generate_aabb); + ClassDB::bind_method("_node_removed", &GPUParticlesEditor::_node_removed); +} + +GPUParticlesEditor::GPUParticlesEditor() { + node = nullptr; + particles_editor_hb = memnew(HBoxContainer); + SpatialEditor::get_singleton()->add_control_to_menu_panel(particles_editor_hb); + options = memnew(MenuButton); + options->set_switch_on_hover(true); + particles_editor_hb->add_child(options); + particles_editor_hb->hide(); + + options->set_text(TTR("GPUParticles")); + options->get_popup()->add_item(TTR("Generate Visibility AABB"), MENU_OPTION_GENERATE_AABB); + options->get_popup()->add_separator(); + options->get_popup()->add_item(TTR("Create Emission Points From Mesh"), MENU_OPTION_CREATE_EMISSION_VOLUME_FROM_MESH); + options->get_popup()->add_item(TTR("Create Emission Points From Node"), MENU_OPTION_CREATE_EMISSION_VOLUME_FROM_NODE); + options->get_popup()->add_separator(); + options->get_popup()->add_item(TTR("Convert to CPUParticles"), MENU_OPTION_CONVERT_TO_CPU_PARTICLES); + options->get_popup()->add_separator(); + options->get_popup()->add_item(TTR("Restart"), MENU_OPTION_RESTART); + + options->get_popup()->connect("id_pressed", this, "_menu_option"); + + generate_aabb = memnew(ConfirmationDialog); + generate_aabb->set_title(TTR("Generate Visibility AABB")); + 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); + + add_child(generate_aabb); + + generate_aabb->connect("confirmed", this, "_generate_aabb"); +} + +void GPUParticlesEditorPlugin::edit(Object *p_object) { + particles_editor->edit(Object::cast_to(p_object)); +} + +bool GPUParticlesEditorPlugin::handles(Object *p_object) const { + return p_object->is_class("GPUParticles"); +} + +void GPUParticlesEditorPlugin::make_visible(bool p_visible) { + if (p_visible) { + particles_editor->show(); + particles_editor->particles_editor_hb->show(); + } else { + particles_editor->particles_editor_hb->hide(); + particles_editor->hide(); + particles_editor->edit(nullptr); + } +} + +GPUParticlesEditorPlugin::GPUParticlesEditorPlugin(EditorNode *p_node) { + editor = p_node; + particles_editor = memnew(GPUParticlesEditor); + editor->get_viewport()->add_child(particles_editor); + + particles_editor->hide(); +} + +GPUParticlesEditorPlugin::~GPUParticlesEditorPlugin() { +} + diff --git a/editor/plugins/particles_editor_plugin.h b/editor/plugins/particles_editor_plugin.h index df939987d..33d2f6a1c 100644 --- a/editor/plugins/particles_editor_plugin.h +++ b/editor/plugins/particles_editor_plugin.h @@ -49,6 +49,7 @@ class Panel; class SceneTreeDialog; class Spatial; class SpinBox; +class GPUParticles; struct Vector3; class ParticlesEditorBase : public Control { @@ -79,4 +80,58 @@ public: ParticlesEditorBase(); }; +class GPUParticlesEditor : public ParticlesEditorBase { + GDCLASS(GPUParticlesEditor, ParticlesEditorBase); + + ConfirmationDialog *generate_aabb; + SpinBox *generate_seconds; + GPUParticles *node; + + enum Menu { + + MENU_OPTION_GENERATE_AABB, + MENU_OPTION_CREATE_EMISSION_VOLUME_FROM_NODE, + MENU_OPTION_CREATE_EMISSION_VOLUME_FROM_MESH, + MENU_OPTION_CLEAR_EMISSION_VOLUME, + MENU_OPTION_CONVERT_TO_CPU_PARTICLES, + MENU_OPTION_RESTART, + + }; + + void _generate_aabb(); + + void _menu_option(int); + + friend class GPUParticlesEditorPlugin; + + virtual void _generate_emission_points(); + +protected: + void _notification(int p_notification); + void _node_removed(Node *p_node); + static void _bind_methods(); + +public: + void edit(GPUParticles *p_particles); + GPUParticlesEditor(); +}; + +class GPUParticlesEditorPlugin : public EditorPlugin { + GDCLASS(GPUParticlesEditorPlugin, EditorPlugin); + + GPUParticlesEditor *particles_editor; + EditorNode *editor; + +public: + virtual String get_name() const { return "GPUParticles"; } + bool has_main_screen() const { return false; } + virtual void edit(Object *p_object); + virtual bool handles(Object *p_object) const; + virtual void make_visible(bool p_visible); + + GPUParticlesEditorPlugin(EditorNode *p_node); + ~GPUParticlesEditorPlugin(); +}; + + #endif // PARTICLES_EDITOR_PLUGIN_H diff --git a/editor/plugins/spatial_editor_plugin.cpp b/editor/plugins/spatial_editor_plugin.cpp index 9a45b5395..4c908b299 100644 --- a/editor/plugins/spatial_editor_plugin.cpp +++ b/editor/plugins/spatial_editor_plugin.cpp @@ -7186,6 +7186,7 @@ void SpatialEditor::_register_all_gizmos() { add_gizmo_plugin(Ref(memnew(SpringArmSpatialGizmoPlugin))); add_gizmo_plugin(Ref(memnew(VehicleWheelSpatialGizmoPlugin))); add_gizmo_plugin(Ref(memnew(VisibilityNotifierGizmoPlugin))); + add_gizmo_plugin(Ref(memnew(GPUParticlesGizmoPlugin))); add_gizmo_plugin(Ref(memnew(CPUParticlesGizmoPlugin))); add_gizmo_plugin(Ref(memnew(ReflectionProbeGizmoPlugin))); add_gizmo_plugin(Ref(memnew(BakedIndirectLightGizmoPlugin))); diff --git a/editor/spatial_editor_gizmos.cpp b/editor/spatial_editor_gizmos.cpp index e14c26db5..4acba98a4 100644 --- a/editor/spatial_editor_gizmos.cpp +++ b/editor/spatial_editor_gizmos.cpp @@ -82,6 +82,7 @@ #include "scene/3d/sprite_3d.h" #include "scene/3d/vehicle_body.h" #include "scene/3d/visibility_notifier.h" +#include "scene/3d/gpu_particles.h" #include "scene/main/control.h" #include "scene/main/node.h" #include "scene/main/scene_tree.h" @@ -2661,6 +2662,169 @@ void CPUParticlesGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) { //// +GPUParticlesGizmoPlugin::GPUParticlesGizmoPlugin() { + Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/particles", Color(0.8, 0.7, 0.4)); + create_material("particles_material", gizmo_color); + gizmo_color.a = 0.1; + create_material("particles_solid_material", gizmo_color); + create_icon_material("particles_icon", SpatialEditor::get_singleton()->get_theme_icon("GizmoParticles", "EditorIcons")); + create_handle_material("handles"); +} + +bool GPUParticlesGizmoPlugin::has_gizmo(Spatial *p_spatial) { + return Object::cast_to(p_spatial) != nullptr; +} + +String GPUParticlesGizmoPlugin::get_gizmo_name() const { + return "GPUParticles"; +} + +int GPUParticlesGizmoPlugin::get_priority() const { + return -1; +} + +bool GPUParticlesGizmoPlugin::is_selectable_when_hidden() const { + return true; +} + +String GPUParticlesGizmoPlugin::get_handle_name(const EditorSpatialGizmo *p_gizmo, int p_id, bool p_secondary) const { + switch (p_id) { + case 0: + return "Size X"; + case 1: + return "Size Y"; + case 2: + return "Size Z"; + case 3: + return "Pos X"; + case 4: + return "Pos Y"; + case 5: + return "Pos Z"; + } + + return ""; +} +Variant GPUParticlesGizmoPlugin::get_handle_value(EditorSpatialGizmo *p_gizmo, int p_id, bool p_secondary) const { + GPUParticles *particles = Object::cast_to(p_gizmo->get_spatial_node()); + return particles->get_visibility_aabb(); +} +void GPUParticlesGizmoPlugin::set_handle(EditorSpatialGizmo *p_gizmo, int p_id, bool p_secondary, Camera *p_camera, const Point2 &p_point) { + GPUParticles *particles = Object::cast_to(p_gizmo->get_spatial_node()); + + Transform gt = particles->get_global_transform(); + Transform gi = gt.affine_inverse(); + + bool move = p_id >= 3; + p_id = p_id % 3; + + AABB aabb = particles->get_visibility_aabb(); + Vector3 ray_from = p_camera->project_ray_origin(p_point); + Vector3 ray_dir = p_camera->project_ray_normal(p_point); + + Vector3 sg[2] = { gi.xform(ray_from), gi.xform(ray_from + ray_dir * 4096) }; + + Vector3 ofs = aabb.position + aabb.size * 0.5; + + Vector3 axis; + axis[p_id] = 1.0; + + if (move) { + Vector3 ra, rb; + Geometry::get_closest_points_between_segments(ofs - axis * 4096, ofs + axis * 4096, sg[0], sg[1], ra, rb); + + float d = ra[p_id]; + if (SpatialEditor::get_singleton()->is_snap_enabled()) { + d = Math::stepify(d, SpatialEditor::get_singleton()->get_translate_snap()); + } + + aabb.position[p_id] = d - 1.0 - aabb.size[p_id] * 0.5; + particles->set_visibility_aabb(aabb); + + } else { + Vector3 ra, rb; + Geometry::get_closest_points_between_segments(ofs, ofs + axis * 4096, sg[0], sg[1], ra, rb); + + float d = ra[p_id] - ofs[p_id]; + if (SpatialEditor::get_singleton()->is_snap_enabled()) { + d = Math::stepify(d, SpatialEditor::get_singleton()->get_translate_snap()); + } + + if (d < 0.001) { + d = 0.001; + } + //resize + aabb.position[p_id] = (aabb.position[p_id] + aabb.size[p_id] * 0.5) - d; + aabb.size[p_id] = d * 2; + particles->set_visibility_aabb(aabb); + } +} + +void GPUParticlesGizmoPlugin::commit_handle(EditorSpatialGizmo *p_gizmo, int p_id, bool p_secondary, const Variant &p_restore, bool p_cancel) { + GPUParticles *particles = Object::cast_to(p_gizmo->get_spatial_node()); + + if (p_cancel) { + particles->set_visibility_aabb(p_restore); + return; + } + + UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo(); + ur->create_action(TTR("Change Particles AABB")); + ur->add_do_method(particles, "set_visibility_aabb", particles->get_visibility_aabb()); + ur->add_undo_method(particles, "set_visibility_aabb", p_restore); + ur->commit_action(); +} + +void GPUParticlesGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) { + GPUParticles *particles = Object::cast_to(p_gizmo->get_spatial_node()); + + p_gizmo->clear(); + + Vector lines; + AABB aabb = particles->get_visibility_aabb(); + + for (int i = 0; i < 12; i++) { + Vector3 a, b; + aabb.get_edge(i, a, b); + lines.push_back(a); + lines.push_back(b); + } + + Vector handles; + + for (int i = 0; i < 3; i++) { + Vector3 ax; + ax[i] = aabb.position[i] + aabb.size[i]; + ax[(i + 1) % 3] = aabb.position[(i + 1) % 3] + aabb.size[(i + 1) % 3] * 0.5; + ax[(i + 2) % 3] = aabb.position[(i + 2) % 3] + aabb.size[(i + 2) % 3] * 0.5; + handles.push_back(ax); + } + + Vector3 center = aabb.position + aabb.size * 0.5; + for (int i = 0; i < 3; i++) { + Vector3 ax; + ax[i] = 1.0; + handles.push_back(center + ax); + lines.push_back(center); + lines.push_back(center + ax); + } + + Ref material = get_material("particles_material", p_gizmo); + Ref icon = get_material("particles_icon", p_gizmo); + + p_gizmo->add_lines(lines, material); + + if (p_gizmo->is_selected()) { + Ref solid_material = get_material("particles_solid_material", p_gizmo); + p_gizmo->add_solid_box(solid_material, aabb.get_size(), aabb.get_position() + aabb.get_size() / 2.0); + } + + p_gizmo->add_handles(handles, get_material("handles")); + p_gizmo->add_unscaled_billboard(icon, 0.05); +} + +//// + ReflectionProbeGizmoPlugin::ReflectionProbeGizmoPlugin() { Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/reflection_probe", Color(0.6, 1, 0.5)); diff --git a/editor/spatial_editor_gizmos.h b/editor/spatial_editor_gizmos.h index 34245e031..6a55c232c 100644 --- a/editor/spatial_editor_gizmos.h +++ b/editor/spatial_editor_gizmos.h @@ -436,6 +436,24 @@ public: CPUParticlesGizmoPlugin(); }; +class GPUParticlesGizmoPlugin : public EditorSpatialGizmoPlugin { + GDCLASS(GPUParticlesGizmoPlugin, EditorSpatialGizmoPlugin); + +public: + bool has_gizmo(Spatial *p_spatial); + String get_gizmo_name() const; + int get_priority() const; + bool is_selectable_when_hidden() const; + void redraw(EditorSpatialGizmo *p_gizmo); + + String get_handle_name(const EditorSpatialGizmo *p_gizmo, int p_id, bool p_secondary) const; + Variant get_handle_value(EditorSpatialGizmo *p_gizmo, int p_id, bool p_secondary) const; + void set_handle(EditorSpatialGizmo *p_gizmo, int p_id, bool p_secondary, Camera *p_camera, const Point2 &p_point); + void commit_handle(EditorSpatialGizmo *p_gizmo, int p_id, bool p_secondary, const Variant &p_restore, bool p_cancel = false); + + GPUParticlesGizmoPlugin(); +}; + class ReflectionProbeGizmoPlugin : public EditorSpatialGizmoPlugin { GDCLASS(ReflectionProbeGizmoPlugin, EditorSpatialGizmoPlugin); diff --git a/scene/2d/cpu_particles_2d.cpp b/scene/2d/cpu_particles_2d.cpp index 9d4787576..02163a69c 100644 --- a/scene/2d/cpu_particles_2d.cpp +++ b/scene/2d/cpu_particles_2d.cpp @@ -37,6 +37,7 @@ #include "core/os/os.h" #include "scene/main/canvas_item.h" +#include "scene/2d/gpu_particles_2d.h" #include "scene/resources/material/particles_material.h" #include "scene/resources/texture.h" #include "servers/rendering_server.h" @@ -1282,6 +1283,86 @@ void CPUParticles2D::_notification(int p_what) { #endif } +void CPUParticles2D::convert_from_particles(Node *p_particles) { + GPUParticles2D *particles = Object::cast_to(p_particles); + ERR_FAIL_COND_MSG(!particles, "Only GPUParticles2D nodes can be converted to CPUParticles2D."); + + set_emitting(particles->is_emitting()); + set_amount(particles->get_amount()); + set_lifetime(particles->get_lifetime()); + set_one_shot(particles->get_one_shot()); + set_pre_process_time(particles->get_pre_process_time()); + set_explosiveness_ratio(particles->get_explosiveness_ratio()); + set_randomness_ratio(particles->get_randomness_ratio()); + set_use_local_coordinates(particles->get_use_local_coordinates()); + set_fixed_fps(particles->get_fixed_fps()); + set_fractional_delta(particles->get_fractional_delta()); + set_speed_scale(particles->get_speed_scale()); + set_draw_order(DrawOrder(particles->get_draw_order())); + set_texture(particles->get_texture()); + + Ref mat = particles->get_material(); + if (mat.is_valid()) { + set_material(mat); + } + + Ref material = particles->get_process_material(); + if (material.is_null()) { + return; + } + + Vector3 dir = material->get_direction(); + set_direction(Vector2(dir.x, dir.y)); + set_spread(material->get_spread()); + + set_color(material->get_color()); + + Ref gt = material->get_color_ramp(); + if (gt.is_valid()) { + set_color_ramp(gt->get_gradient()); + } + + Ref gti = material->get_color_initial_ramp(); + if (gti.is_valid()) { + set_color_initial_ramp(gti->get_gradient()); + } + + set_particle_flag(FLAG_ALIGN_Y_TO_VELOCITY, material->get_flag(ParticlesMaterial::FLAG_ALIGN_Y_TO_VELOCITY)); + + set_emission_shape(EmissionShape(material->get_emission_shape())); + set_emission_sphere_radius(material->get_emission_sphere_radius()); + Vector2 rect_extents = Vector2(material->get_emission_box_extents().x, material->get_emission_box_extents().y); + set_emission_rect_extents(rect_extents); + + Vector2 gravity = Vector2(material->get_gravity().x, material->get_gravity().y); + set_gravity(gravity); + set_lifetime_randomness(material->get_lifetime_randomness()); + +#define CONVERT_PARAM(m_param) \ + set_param(m_param, material->get_param(ParticlesMaterial::m_param)); \ + { \ + Ref ctex = material->get_param_texture(ParticlesMaterial::m_param); \ + if (ctex.is_valid()) \ + set_param_curve(m_param, ctex->get_curve()); \ + } \ + set_param_randomness(m_param, material->get_param_randomness(ParticlesMaterial::m_param)); + + CONVERT_PARAM(PARAM_INITIAL_LINEAR_VELOCITY); + CONVERT_PARAM(PARAM_ANGULAR_VELOCITY); + CONVERT_PARAM(PARAM_ORBIT_VELOCITY); + CONVERT_PARAM(PARAM_LINEAR_ACCEL); + CONVERT_PARAM(PARAM_RADIAL_ACCEL); + CONVERT_PARAM(PARAM_TANGENTIAL_ACCEL); + CONVERT_PARAM(PARAM_DAMPING); + CONVERT_PARAM(PARAM_ANGLE); + CONVERT_PARAM(PARAM_SCALE); + CONVERT_PARAM(PARAM_HUE_VARIATION); + CONVERT_PARAM(PARAM_ANIM_SPEED); + CONVERT_PARAM(PARAM_ANIM_OFFSET); + +#undef CONVERT_PARAM +} + void CPUParticles2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_emitting", "emitting"), &CPUParticles2D::set_emitting); ClassDB::bind_method(D_METHOD("set_amount", "amount"), &CPUParticles2D::set_amount); @@ -1393,6 +1474,8 @@ void CPUParticles2D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_gravity"), &CPUParticles2D::get_gravity); ClassDB::bind_method(D_METHOD("set_gravity", "accel_vec"), &CPUParticles2D::set_gravity); + ClassDB::bind_method(D_METHOD("convert_from_particles", "particles"), &CPUParticles2D::convert_from_particles); + ClassDB::bind_method(D_METHOD("_update_render_thread"), &CPUParticles2D::_update_render_thread); ClassDB::bind_method(D_METHOD("_texture_changed"), &CPUParticles2D::_texture_changed); diff --git a/scene/2d/cpu_particles_2d.h b/scene/2d/cpu_particles_2d.h index 7e2c2d197..1b7cd9ccb 100644 --- a/scene/2d/cpu_particles_2d.h +++ b/scene/2d/cpu_particles_2d.h @@ -356,6 +356,8 @@ public: void restart(); + void convert_from_particles(Node *p_particles); + CPUParticles2D(); ~CPUParticles2D(); }; diff --git a/scene/2d/visibility_notifier_2d.cpp b/scene/2d/visibility_notifier_2d.cpp index 38cc45bdc..ce8de6d9d 100644 --- a/scene/2d/visibility_notifier_2d.cpp +++ b/scene/2d/visibility_notifier_2d.cpp @@ -32,6 +32,7 @@ #include "visibility_notifier_2d.h" #include "core/config/engine.h" +#include "gpu_particles_2d.h" #include "scene/2d/animated_sprite.h" #include "scene/2d/physics_body_2d.h" #include "scene/animation/animation_player.h" @@ -215,6 +216,13 @@ void VisibilityEnabler2D::_find_nodes(Node *p_node) { } } + { + GPUParticles2D *ps = Object::cast_to(p_node); + if (ps) { + add = true; + } + } + if (add) { p_node->connect(SceneStringNames::get_singleton()->tree_exiting, this, "_node_removed", varray(p_node), CONNECT_ONESHOT); nodes[p_node] = meta; @@ -309,6 +317,14 @@ void VisibilityEnabler2D::_change_node_state(Node *p_node, bool p_enabled) { } } } + + if (enabler[ENABLER_PAUSE_PARTICLES]) { + GPUParticles2D *ps = Object::cast_to(p_node); + + if (ps) { + ps->set_emitting(p_enabled); + } + } } void VisibilityEnabler2D::_node_removed(Node *p_node) { diff --git a/scene/3d/cpu_particles.cpp b/scene/3d/cpu_particles.cpp index 76d735dcf..ff23133f9 100644 --- a/scene/3d/cpu_particles.cpp +++ b/scene/3d/cpu_particles.cpp @@ -33,6 +33,7 @@ #include "core/os/os.h" #include "scene/3d/camera.h" +#include "scene/3d/gpu_particles.h" #include "scene/main/viewport.h" #include "scene/resources/curve.h" #include "scene/resources/gradient.h" @@ -1313,6 +1314,85 @@ void CPUParticles::_notification(int p_what) { } } +void CPUParticles::convert_from_particles(Node *p_particles) { + GPUParticles *particles = Object::cast_to(p_particles); + ERR_FAIL_COND_MSG(!particles, "Only GPUParticles nodes can be converted to CPUParticles."); + + set_emitting(particles->is_emitting()); + set_amount(particles->get_amount()); + set_lifetime(particles->get_lifetime()); + set_one_shot(particles->get_one_shot()); + set_pre_process_time(particles->get_pre_process_time()); + set_explosiveness_ratio(particles->get_explosiveness_ratio()); + set_randomness_ratio(particles->get_randomness_ratio()); + set_use_local_coordinates(particles->get_use_local_coordinates()); + set_fixed_fps(particles->get_fixed_fps()); + set_fractional_delta(particles->get_fractional_delta()); + set_speed_scale(particles->get_speed_scale()); + set_draw_order(DrawOrder(particles->get_draw_order())); + set_mesh(particles->get_draw_pass_mesh(0)); + + Ref material = particles->get_process_material(); + if (material.is_null()) { + return; + } + + set_direction(material->get_direction()); + set_spread(material->get_spread()); + set_flatness(material->get_flatness()); + + set_color(material->get_color()); + + Ref gt = material->get_color_ramp(); + if (gt.is_valid()) { + set_color_ramp(gt->get_gradient()); + } + + Ref gti = material->get_color_initial_ramp(); + if (gti.is_valid()) { + set_color_initial_ramp(gti->get_gradient()); + } + + set_particle_flag(FLAG_ALIGN_Y_TO_VELOCITY, material->get_flag(ParticlesMaterial::FLAG_ALIGN_Y_TO_VELOCITY)); + set_particle_flag(FLAG_ROTATE_Y, material->get_flag(ParticlesMaterial::FLAG_ROTATE_Y)); + set_particle_flag(FLAG_DISABLE_Z, material->get_flag(ParticlesMaterial::FLAG_DISABLE_Z)); + + set_emission_shape(EmissionShape(material->get_emission_shape())); + set_emission_sphere_radius(material->get_emission_sphere_radius()); + set_emission_box_extents(material->get_emission_box_extents()); + set_emission_ring_height(material->get_emission_ring_height()); + set_emission_ring_inner_radius(material->get_emission_ring_inner_radius()); + set_emission_ring_radius(material->get_emission_ring_radius()); + set_emission_ring_axis(material->get_emission_ring_axis()); + + set_gravity(material->get_gravity()); + set_lifetime_randomness(material->get_lifetime_randomness()); + +#define CONVERT_PARAM(m_param) \ + set_param(m_param, material->get_param(ParticlesMaterial::m_param)); \ + { \ + Ref ctex = material->get_param_texture(ParticlesMaterial::m_param); \ + if (ctex.is_valid()) \ + set_param_curve(m_param, ctex->get_curve()); \ + } \ + set_param_randomness(m_param, material->get_param_randomness(ParticlesMaterial::m_param)); + + CONVERT_PARAM(PARAM_INITIAL_LINEAR_VELOCITY); + CONVERT_PARAM(PARAM_ANGULAR_VELOCITY); + CONVERT_PARAM(PARAM_ORBIT_VELOCITY); + CONVERT_PARAM(PARAM_LINEAR_ACCEL); + CONVERT_PARAM(PARAM_RADIAL_ACCEL); + CONVERT_PARAM(PARAM_TANGENTIAL_ACCEL); + CONVERT_PARAM(PARAM_DAMPING); + CONVERT_PARAM(PARAM_ANGLE); + CONVERT_PARAM(PARAM_SCALE); + CONVERT_PARAM(PARAM_HUE_VARIATION); + CONVERT_PARAM(PARAM_ANIM_SPEED); + CONVERT_PARAM(PARAM_ANIM_OFFSET); + +#undef CONVERT_PARAM +} + void CPUParticles::_bind_methods() { ClassDB::bind_method(D_METHOD("set_emitting", "emitting"), &CPUParticles::set_emitting); ClassDB::bind_method(D_METHOD("set_amount", "amount"), &CPUParticles::set_amount); @@ -1435,6 +1515,8 @@ void CPUParticles::_bind_methods() { ClassDB::bind_method(D_METHOD("get_gravity"), &CPUParticles::get_gravity); ClassDB::bind_method(D_METHOD("set_gravity", "accel_vec"), &CPUParticles::set_gravity); + ClassDB::bind_method(D_METHOD("convert_from_particles", "particles"), &CPUParticles::convert_from_particles); + ClassDB::bind_method(D_METHOD("_update_render_thread"), &CPUParticles::_update_render_thread); ADD_GROUP("Emission Shape", "emission_"); diff --git a/scene/3d/cpu_particles.h b/scene/3d/cpu_particles.h index 344febafc..770aa8943 100644 --- a/scene/3d/cpu_particles.h +++ b/scene/3d/cpu_particles.h @@ -357,6 +357,8 @@ public: void restart(); + void convert_from_particles(Node *p_particles); + CPUParticles(); ~CPUParticles(); }; diff --git a/scene/3d/room_manager.cpp b/scene/3d/room_manager.cpp index d1629bf62..07f0d5684 100644 --- a/scene/3d/room_manager.cpp +++ b/scene/3d/room_manager.cpp @@ -44,6 +44,7 @@ #include "room_group.h" #include "scene/3d/camera.h" #include "scene/3d/light.h" +#include "scene/3d/gpu_particles.h" #include "scene/3d/sprite_3d.h" #include "scene/resources/mesh/multimesh.h" #include "scene/resources/world_3d.h" @@ -1869,13 +1870,11 @@ bool RoomManager::_bound_findpoints_geom_instance(GeometryInstance *p_gi, Vector } // Particles have a "visibility aabb" we can use for this - /* - Particles *particles = Object::cast_to(p_gi); + GPUParticles *particles = Object::cast_to(p_gi); if (particles) { r_aabb = particles->get_global_transform().xform(particles->get_visibility_aabb()); return true; } - */ // Fallback path for geometry that is not recognised // (including CPUParticles, which will need to rely on an expansion margin) diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index adcdea3fc..d4b433144 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -59,6 +59,7 @@ #include "scene/2d/navigation_polygon_instance.h" #include "scene/2d/parallax_background.h" #include "scene/2d/parallax_layer.h" +#include "scene/2d/gpu_particles_2d.h" #include "scene/2d/path_2d.h" #include "scene/2d/physics_body_2d.h" #include "scene/2d/polygon_2d.h" @@ -213,6 +214,7 @@ #include "scene/3d/navigation_obstacle.h" #include "scene/3d/occluder.h" #include "scene/3d/path.h" +#include "scene/3d/gpu_particles.h" #include "scene/3d/physics_body.h" #include "scene/3d/physics_joint.h" #include "scene/3d/portal.h" @@ -460,6 +462,7 @@ void register_scene_types() { ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); + ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); @@ -536,6 +539,7 @@ void register_scene_types() { CanvasItemMaterial::init_shaders(); ClassDB::register_class(); ClassDB::register_class(); + ClassDB::register_class(); //ClassDB::register_class(); ClassDB::register_class(); //ClassDB::register_type();