summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYuri Sizov <yuris@humnom.net>2023-07-14 18:50:56 +0200
committerYuri Sizov <yuris@humnom.net>2023-07-14 18:50:56 +0200
commit0f7625ab46a64b3f5da2b09969ebabf38df9a6e9 (patch)
tree5300150473caa2b83de54e6c58116ae18b72d486
parenta91f0e6bca51c7f3a492f0038478bd73813eb1c2 (diff)
parent5322e255addbcef1773d8bd22482765187c75113 (diff)
downloadredot-engine-0f7625ab46a64b3f5da2b09969ebabf38df9a6e9.tar.gz
Merge pull request #76859 from HolonProduction/emission-finished-gpu
Add `finished` signal to GPUParticles
-rw-r--r--doc/classes/GPUParticles2D.xml9
-rw-r--r--doc/classes/GPUParticles3D.xml8
-rw-r--r--scene/2d/gpu_particles_2d.cpp56
-rw-r--r--scene/2d/gpu_particles_2d.h7
-rw-r--r--scene/3d/gpu_particles_3d.cpp56
-rw-r--r--scene/3d/gpu_particles_3d.h9
6 files changed, 132 insertions, 13 deletions
diff --git a/doc/classes/GPUParticles2D.xml b/doc/classes/GPUParticles2D.xml
index c4193a5b01..e6d3c130ca 100644
--- a/doc/classes/GPUParticles2D.xml
+++ b/doc/classes/GPUParticles2D.xml
@@ -18,6 +18,7 @@
<return type="Rect2" />
<description>
Returns a rectangle containing the positions of all existing particles.
+ [b]Note:[/b] When using threaded rendering this method synchronizes the rendering thread. Calling it often may have a negative impact on performance.
</description>
</method>
<method name="emit_particle">
@@ -108,6 +109,14 @@
Grow the rect if particles suddenly appear/disappear when the node enters/exits the screen. The [Rect2] can be grown via code or with the [b]Particles → Generate Visibility Rect[/b] editor tool.
</member>
</members>
+ <signals>
+ <signal name="finished">
+ <description>
+ Emitted when all active particles have finished processing. When [member one_shot] is disabled, particles will process continuously, so this is never emitted.
+ [b]Note:[/b] Due to the particles being computed on the GPU there might be a delay before the signal gets emitted.
+ </description>
+ </signal>
+ </signals>
<constants>
<constant name="DRAW_ORDER_INDEX" value="0" enum="DrawOrder">
Particles are drawn in the order emitted.
diff --git a/doc/classes/GPUParticles3D.xml b/doc/classes/GPUParticles3D.xml
index 8338535a24..0dc7c23315 100644
--- a/doc/classes/GPUParticles3D.xml
+++ b/doc/classes/GPUParticles3D.xml
@@ -130,6 +130,14 @@
Grow the box if particles suddenly appear/disappear when the node enters/exits the screen. The [AABB] can be grown via code or with the [b]Particles → Generate AABB[/b] editor tool.
</member>
</members>
+ <signals>
+ <signal name="finished">
+ <description>
+ Emitted when all active particles have finished processing. When [member one_shot] is disabled, particles will process continuously, so this is never emitted.
+ [b]Note:[/b] Due to the particles being computed on the GPU there might be a delay before the signal gets emitted.
+ </description>
+ </signal>
+ </signals>
<constants>
<constant name="DRAW_ORDER_INDEX" value="0" enum="DrawOrder">
Particles are drawn in the order emitted.
diff --git a/scene/2d/gpu_particles_2d.cpp b/scene/2d/gpu_particles_2d.cpp
index 735159c660..06215245a1 100644
--- a/scene/2d/gpu_particles_2d.cpp
+++ b/scene/2d/gpu_particles_2d.cpp
@@ -32,19 +32,37 @@
#include "core/core_string_names.h"
#include "scene/resources/particle_process_material.h"
+#include "scene/scene_string_names.h"
#ifdef TOOLS_ENABLED
#include "core/config/engine.h"
#endif
void GPUParticles2D::set_emitting(bool p_emitting) {
- RS::get_singleton()->particles_set_emitting(particles, p_emitting);
+ // Do not return even if `p_emitting == emitting` because `emitting` is just an approximation.
if (p_emitting && one_shot) {
+ if (!active && !emitting) {
+ // Last cycle ended.
+ active = true;
+ time = 0;
+ signal_cancled = false;
+ emission_time = lifetime;
+ active_time = lifetime * (2 - explosiveness_ratio);
+ } else {
+ signal_cancled = true;
+ }
set_process_internal(true);
} else if (!p_emitting) {
- set_process_internal(false);
+ if (one_shot) {
+ set_process_internal(true);
+ } else {
+ set_process_internal(false);
+ }
}
+
+ emitting = p_emitting;
+ RS::get_singleton()->particles_set_emitting(particles, p_emitting);
}
void GPUParticles2D::set_amount(int p_amount) {
@@ -211,7 +229,7 @@ void GPUParticles2D::set_speed_scale(double p_scale) {
}
bool GPUParticles2D::is_emitting() const {
- return RS::get_singleton()->particles_get_emitting(particles);
+ return emitting;
}
int GPUParticles2D::get_amount() const {
@@ -405,6 +423,16 @@ NodePath GPUParticles2D::get_sub_emitter() const {
void GPUParticles2D::restart() {
RS::get_singleton()->particles_restart(particles);
RS::get_singleton()->particles_set_emitting(particles, true);
+
+ emitting = true;
+ active = true;
+ signal_cancled = false;
+ time = 0;
+ emission_time = lifetime;
+ active_time = lifetime * (2 - explosiveness_ratio);
+ if (one_shot) {
+ set_process_internal(true);
+ }
}
void GPUParticles2D::_notification(int p_what) {
@@ -570,9 +598,23 @@ void GPUParticles2D::_notification(int p_what) {
} break;
case NOTIFICATION_INTERNAL_PROCESS: {
- if (one_shot && !is_emitting()) {
- notify_property_list_changed();
- set_process_internal(false);
+ if (one_shot) {
+ time += get_process_delta_time();
+ if (time > emission_time) {
+ emitting = false;
+ if (!active) {
+ set_process_internal(false);
+ }
+ }
+ if (time > active_time) {
+ if (active && !signal_cancled) {
+ emit_signal(SceneStringNames::get_singleton()->finished);
+ }
+ active = false;
+ if (!emitting) {
+ set_process_internal(false);
+ }
+ }
}
} break;
}
@@ -638,6 +680,8 @@ void GPUParticles2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_trail_section_subdivisions", "subdivisions"), &GPUParticles2D::set_trail_section_subdivisions);
ClassDB::bind_method(D_METHOD("get_trail_section_subdivisions"), &GPUParticles2D::get_trail_section_subdivisions);
+ ADD_SIGNAL(MethodInfo("finished"));
+
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emitting"), "set_emitting", "is_emitting");
ADD_PROPERTY_DEFAULT("emitting", true); // Workaround for doctool in headless mode, as dummy rasterizer always returns false.
ADD_PROPERTY(PropertyInfo(Variant::INT, "amount", PROPERTY_HINT_RANGE, "1,1000000,1,exp"), "set_amount", "get_amount");
diff --git a/scene/2d/gpu_particles_2d.h b/scene/2d/gpu_particles_2d.h
index e518ffec6f..3131698e5c 100644
--- a/scene/2d/gpu_particles_2d.h
+++ b/scene/2d/gpu_particles_2d.h
@@ -47,6 +47,9 @@ public:
private:
RID particles;
+ bool emitting = false;
+ bool active = false;
+ bool signal_cancled = false;
bool one_shot = false;
int amount = 0;
double lifetime = 0.0;
@@ -78,6 +81,10 @@ private:
int trail_sections = 8;
int trail_section_subdivisions = 4;
+ double time = 0.0;
+ double emission_time = 0.0;
+ double active_time = 0.0;
+
RID mesh;
void _attach_sub_emitter();
diff --git a/scene/3d/gpu_particles_3d.cpp b/scene/3d/gpu_particles_3d.cpp
index 4d0bc8b02f..fb889c928d 100644
--- a/scene/3d/gpu_particles_3d.cpp
+++ b/scene/3d/gpu_particles_3d.cpp
@@ -31,19 +31,37 @@
#include "gpu_particles_3d.h"
#include "scene/resources/particle_process_material.h"
+#include "scene/scene_string_names.h"
AABB GPUParticles3D::get_aabb() const {
return AABB();
}
void GPUParticles3D::set_emitting(bool p_emitting) {
- RS::get_singleton()->particles_set_emitting(particles, p_emitting);
+ // Do not return even if `p_emitting == emitting` because `emitting` is just an approximation.
if (p_emitting && one_shot) {
+ if (!active && !emitting) {
+ // Last cycle ended.
+ active = true;
+ time = 0;
+ signal_cancled = false;
+ emission_time = lifetime;
+ active_time = lifetime * (2 - explosiveness_ratio);
+ } else {
+ signal_cancled = true;
+ }
set_process_internal(true);
} else if (!p_emitting) {
- set_process_internal(false);
+ if (one_shot) {
+ set_process_internal(true);
+ } else {
+ set_process_internal(false);
+ }
}
+
+ emitting = p_emitting;
+ RS::get_singleton()->particles_set_emitting(particles, p_emitting);
}
void GPUParticles3D::set_amount(int p_amount) {
@@ -122,7 +140,7 @@ void GPUParticles3D::set_collision_base_size(real_t p_size) {
}
bool GPUParticles3D::is_emitting() const {
- return RS::get_singleton()->particles_get_emitting(particles);
+ return emitting;
}
int GPUParticles3D::get_amount() const {
@@ -373,6 +391,16 @@ PackedStringArray GPUParticles3D::get_configuration_warnings() const {
void GPUParticles3D::restart() {
RenderingServer::get_singleton()->particles_restart(particles);
RenderingServer::get_singleton()->particles_set_emitting(particles, true);
+
+ emitting = true;
+ active = true;
+ signal_cancled = false;
+ time = 0;
+ emission_time = lifetime * (1 - explosiveness_ratio);
+ active_time = lifetime * (2 - explosiveness_ratio);
+ if (one_shot) {
+ set_process_internal(true);
+ }
}
AABB GPUParticles3D::capture_aabb() const {
@@ -425,9 +453,23 @@ void GPUParticles3D::_notification(int p_what) {
// Use internal process when emitting and one_shot is on so that when
// the shot ends the editor can properly update.
case NOTIFICATION_INTERNAL_PROCESS: {
- if (one_shot && !is_emitting()) {
- notify_property_list_changed();
- set_process_internal(false);
+ if (one_shot) {
+ time += get_process_delta_time();
+ if (time > emission_time) {
+ emitting = false;
+ if (!active) {
+ set_process_internal(false);
+ }
+ }
+ if (time > active_time) {
+ if (active && !signal_cancled) {
+ emit_signal(SceneStringNames::get_singleton()->finished);
+ }
+ active = false;
+ if (!emitting) {
+ set_process_internal(false);
+ }
+ }
}
} break;
@@ -571,6 +613,8 @@ void GPUParticles3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_transform_align", "align"), &GPUParticles3D::set_transform_align);
ClassDB::bind_method(D_METHOD("get_transform_align"), &GPUParticles3D::get_transform_align);
+ ADD_SIGNAL(MethodInfo("finished"));
+
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emitting"), "set_emitting", "is_emitting");
ADD_PROPERTY_DEFAULT("emitting", true); // Workaround for doctool in headless mode, as dummy rasterizer always returns false.
ADD_PROPERTY(PropertyInfo(Variant::INT, "amount", PROPERTY_HINT_RANGE, "1,1000000,1,exp"), "set_amount", "get_amount");
diff --git a/scene/3d/gpu_particles_3d.h b/scene/3d/gpu_particles_3d.h
index 474f5500f8..dba6a8f2ab 100644
--- a/scene/3d/gpu_particles_3d.h
+++ b/scene/3d/gpu_particles_3d.h
@@ -60,7 +60,10 @@ public:
private:
RID particles;
- bool one_shot;
+ bool emitting = false;
+ bool active = false;
+ bool signal_cancled = false;
+ bool one_shot = false;
int amount = 0;
double lifetime = 0.0;
double pre_process_time = 0.0;
@@ -87,6 +90,10 @@ private:
Vector<Ref<Mesh>> draw_passes;
Ref<Skin> skin;
+ double time = 0.0;
+ double emission_time = 0.0;
+ double active_time = 0.0;
+
void _attach_sub_emitter();
void _skinning_changed();