Skip to content

Commit 10a391f

Browse files
committed
Merge pull request godotengine#76859 from HolonProduction/emission-finished-gpu
Add `finished` signal to GPUParticles
2 parents be2724a + 05ca45a commit 10a391f

6 files changed

Lines changed: 132 additions & 13 deletions

File tree

doc/classes/GPUParticles2D.xml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
<return type="Rect2" />
1919
<description>
2020
Returns a rectangle containing the positions of all existing particles.
21+
[b]Note:[/b] When using threaded rendering this method synchronizes the rendering thread. Calling it often may have a negative impact on performance.
2122
</description>
2223
</method>
2324
<method name="emit_particle">
@@ -108,6 +109,14 @@
108109
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.
109110
</member>
110111
</members>
112+
<signals>
113+
<signal name="finished">
114+
<description>
115+
Emitted when all active particles have finished processing. When [member one_shot] is disabled, particles will process continuously, so this is never emitted.
116+
[b]Note:[/b] Due to the particles being computed on the GPU there might be a delay before the signal gets emitted.
117+
</description>
118+
</signal>
119+
</signals>
111120
<constants>
112121
<constant name="DRAW_ORDER_INDEX" value="0" enum="DrawOrder">
113122
Particles are drawn in the order emitted.

doc/classes/GPUParticles3D.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,14 @@
130130
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.
131131
</member>
132132
</members>
133+
<signals>
134+
<signal name="finished">
135+
<description>
136+
Emitted when all active particles have finished processing. When [member one_shot] is disabled, particles will process continuously, so this is never emitted.
137+
[b]Note:[/b] Due to the particles being computed on the GPU there might be a delay before the signal gets emitted.
138+
</description>
139+
</signal>
140+
</signals>
133141
<constants>
134142
<constant name="DRAW_ORDER_INDEX" value="0" enum="DrawOrder">
135143
Particles are drawn in the order emitted.

scene/2d/gpu_particles_2d.cpp

Lines changed: 50 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,19 +32,37 @@
3232

3333
#include "core/core_string_names.h"
3434
#include "scene/resources/particle_process_material.h"
35+
#include "scene/scene_string_names.h"
3536

3637
#ifdef TOOLS_ENABLED
3738
#include "core/config/engine.h"
3839
#endif
3940

4041
void GPUParticles2D::set_emitting(bool p_emitting) {
41-
RS::get_singleton()->particles_set_emitting(particles, p_emitting);
42+
// Do not return even if `p_emitting == emitting` because `emitting` is just an approximation.
4243

4344
if (p_emitting && one_shot) {
45+
if (!active && !emitting) {
46+
// Last cycle ended.
47+
active = true;
48+
time = 0;
49+
signal_cancled = false;
50+
emission_time = lifetime;
51+
active_time = lifetime * (2 - explosiveness_ratio);
52+
} else {
53+
signal_cancled = true;
54+
}
4455
set_process_internal(true);
4556
} else if (!p_emitting) {
46-
set_process_internal(false);
57+
if (one_shot) {
58+
set_process_internal(true);
59+
} else {
60+
set_process_internal(false);
61+
}
4762
}
63+
64+
emitting = p_emitting;
65+
RS::get_singleton()->particles_set_emitting(particles, p_emitting);
4866
}
4967

5068
void GPUParticles2D::set_amount(int p_amount) {
@@ -211,7 +229,7 @@ void GPUParticles2D::set_speed_scale(double p_scale) {
211229
}
212230

213231
bool GPUParticles2D::is_emitting() const {
214-
return RS::get_singleton()->particles_get_emitting(particles);
232+
return emitting;
215233
}
216234

217235
int GPUParticles2D::get_amount() const {
@@ -405,6 +423,16 @@ NodePath GPUParticles2D::get_sub_emitter() const {
405423
void GPUParticles2D::restart() {
406424
RS::get_singleton()->particles_restart(particles);
407425
RS::get_singleton()->particles_set_emitting(particles, true);
426+
427+
emitting = true;
428+
active = true;
429+
signal_cancled = false;
430+
time = 0;
431+
emission_time = lifetime;
432+
active_time = lifetime * (2 - explosiveness_ratio);
433+
if (one_shot) {
434+
set_process_internal(true);
435+
}
408436
}
409437

410438
void GPUParticles2D::_notification(int p_what) {
@@ -570,9 +598,23 @@ void GPUParticles2D::_notification(int p_what) {
570598
} break;
571599

572600
case NOTIFICATION_INTERNAL_PROCESS: {
573-
if (one_shot && !is_emitting()) {
574-
notify_property_list_changed();
575-
set_process_internal(false);
601+
if (one_shot) {
602+
time += get_process_delta_time();
603+
if (time > emission_time) {
604+
emitting = false;
605+
if (!active) {
606+
set_process_internal(false);
607+
}
608+
}
609+
if (time > active_time) {
610+
if (active && !signal_cancled) {
611+
emit_signal(SceneStringNames::get_singleton()->finished);
612+
}
613+
active = false;
614+
if (!emitting) {
615+
set_process_internal(false);
616+
}
617+
}
576618
}
577619
} break;
578620
}
@@ -638,6 +680,8 @@ void GPUParticles2D::_bind_methods() {
638680
ClassDB::bind_method(D_METHOD("set_trail_section_subdivisions", "subdivisions"), &GPUParticles2D::set_trail_section_subdivisions);
639681
ClassDB::bind_method(D_METHOD("get_trail_section_subdivisions"), &GPUParticles2D::get_trail_section_subdivisions);
640682

683+
ADD_SIGNAL(MethodInfo("finished"));
684+
641685
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emitting"), "set_emitting", "is_emitting");
642686
ADD_PROPERTY_DEFAULT("emitting", true); // Workaround for doctool in headless mode, as dummy rasterizer always returns false.
643687
ADD_PROPERTY(PropertyInfo(Variant::INT, "amount", PROPERTY_HINT_RANGE, "1,1000000,1,exp"), "set_amount", "get_amount");

scene/2d/gpu_particles_2d.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ class GPUParticles2D : public Node2D {
4747
private:
4848
RID particles;
4949

50+
bool emitting = false;
51+
bool active = false;
52+
bool signal_cancled = false;
5053
bool one_shot = false;
5154
int amount = 0;
5255
double lifetime = 0.0;
@@ -78,6 +81,10 @@ class GPUParticles2D : public Node2D {
7881
int trail_sections = 8;
7982
int trail_section_subdivisions = 4;
8083

84+
double time = 0.0;
85+
double emission_time = 0.0;
86+
double active_time = 0.0;
87+
8188
RID mesh;
8289

8390
void _attach_sub_emitter();

scene/3d/gpu_particles_3d.cpp

Lines changed: 50 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,19 +31,37 @@
3131
#include "gpu_particles_3d.h"
3232

3333
#include "scene/resources/particle_process_material.h"
34+
#include "scene/scene_string_names.h"
3435

3536
AABB GPUParticles3D::get_aabb() const {
3637
return AABB();
3738
}
3839

3940
void GPUParticles3D::set_emitting(bool p_emitting) {
40-
RS::get_singleton()->particles_set_emitting(particles, p_emitting);
41+
// Do not return even if `p_emitting == emitting` because `emitting` is just an approximation.
4142

4243
if (p_emitting && one_shot) {
44+
if (!active && !emitting) {
45+
// Last cycle ended.
46+
active = true;
47+
time = 0;
48+
signal_cancled = false;
49+
emission_time = lifetime;
50+
active_time = lifetime * (2 - explosiveness_ratio);
51+
} else {
52+
signal_cancled = true;
53+
}
4354
set_process_internal(true);
4455
} else if (!p_emitting) {
45-
set_process_internal(false);
56+
if (one_shot) {
57+
set_process_internal(true);
58+
} else {
59+
set_process_internal(false);
60+
}
4661
}
62+
63+
emitting = p_emitting;
64+
RS::get_singleton()->particles_set_emitting(particles, p_emitting);
4765
}
4866

4967
void GPUParticles3D::set_amount(int p_amount) {
@@ -122,7 +140,7 @@ void GPUParticles3D::set_collision_base_size(real_t p_size) {
122140
}
123141

124142
bool GPUParticles3D::is_emitting() const {
125-
return RS::get_singleton()->particles_get_emitting(particles);
143+
return emitting;
126144
}
127145

128146
int GPUParticles3D::get_amount() const {
@@ -373,6 +391,16 @@ PackedStringArray GPUParticles3D::get_configuration_warnings() const {
373391
void GPUParticles3D::restart() {
374392
RenderingServer::get_singleton()->particles_restart(particles);
375393
RenderingServer::get_singleton()->particles_set_emitting(particles, true);
394+
395+
emitting = true;
396+
active = true;
397+
signal_cancled = false;
398+
time = 0;
399+
emission_time = lifetime * (1 - explosiveness_ratio);
400+
active_time = lifetime * (2 - explosiveness_ratio);
401+
if (one_shot) {
402+
set_process_internal(true);
403+
}
376404
}
377405

378406
AABB GPUParticles3D::capture_aabb() const {
@@ -425,9 +453,23 @@ void GPUParticles3D::_notification(int p_what) {
425453
// Use internal process when emitting and one_shot is on so that when
426454
// the shot ends the editor can properly update.
427455
case NOTIFICATION_INTERNAL_PROCESS: {
428-
if (one_shot && !is_emitting()) {
429-
notify_property_list_changed();
430-
set_process_internal(false);
456+
if (one_shot) {
457+
time += get_process_delta_time();
458+
if (time > emission_time) {
459+
emitting = false;
460+
if (!active) {
461+
set_process_internal(false);
462+
}
463+
}
464+
if (time > active_time) {
465+
if (active && !signal_cancled) {
466+
emit_signal(SceneStringNames::get_singleton()->finished);
467+
}
468+
active = false;
469+
if (!emitting) {
470+
set_process_internal(false);
471+
}
472+
}
431473
}
432474
} break;
433475

@@ -571,6 +613,8 @@ void GPUParticles3D::_bind_methods() {
571613
ClassDB::bind_method(D_METHOD("set_transform_align", "align"), &GPUParticles3D::set_transform_align);
572614
ClassDB::bind_method(D_METHOD("get_transform_align"), &GPUParticles3D::get_transform_align);
573615

616+
ADD_SIGNAL(MethodInfo("finished"));
617+
574618
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emitting"), "set_emitting", "is_emitting");
575619
ADD_PROPERTY_DEFAULT("emitting", true); // Workaround for doctool in headless mode, as dummy rasterizer always returns false.
576620
ADD_PROPERTY(PropertyInfo(Variant::INT, "amount", PROPERTY_HINT_RANGE, "1,1000000,1,exp"), "set_amount", "get_amount");

scene/3d/gpu_particles_3d.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,10 @@ class GPUParticles3D : public GeometryInstance3D {
6060
private:
6161
RID particles;
6262

63-
bool one_shot;
63+
bool emitting = false;
64+
bool active = false;
65+
bool signal_cancled = false;
66+
bool one_shot = false;
6467
int amount = 0;
6568
double lifetime = 0.0;
6669
double pre_process_time = 0.0;
@@ -87,6 +90,10 @@ class GPUParticles3D : public GeometryInstance3D {
8790
Vector<Ref<Mesh>> draw_passes;
8891
Ref<Skin> skin;
8992

93+
double time = 0.0;
94+
double emission_time = 0.0;
95+
double active_time = 0.0;
96+
9097
void _attach_sub_emitter();
9198

9299
void _skinning_changed();

0 commit comments

Comments
 (0)