diff --git a/doc/classes/CPUParticles2D.xml b/doc/classes/CPUParticles2D.xml
index 20195756d34..c45b9e1bc93 100644
--- a/doc/classes/CPUParticles2D.xml
+++ b/doc/classes/CPUParticles2D.xml
@@ -49,9 +49,10 @@
+
Requests the particles to process for extra process time during a single frame.
- Useful for particle playback, if used in combination with [member use_fixed_seed] or by calling [method restart] with parameter [code]keep_seed[/code] set to [code]true[/code].
+ [param process_time] defines the time that the particles will process while emitting is on. [param process_time_residual] defines the time that particles will process with emitting turned off for the simulation. When combined with [member speed_scale] set to [code]0.0[/code], this is useful to be able to seek a particle system timeline.
diff --git a/doc/classes/CPUParticles3D.xml b/doc/classes/CPUParticles3D.xml
index bce790ec442..91f404520f1 100644
--- a/doc/classes/CPUParticles3D.xml
+++ b/doc/classes/CPUParticles3D.xml
@@ -55,9 +55,10 @@
+
Requests the particles to process for extra process time during a single frame.
- Useful for particle playback, if used in combination with [member use_fixed_seed] or by calling [method restart] with parameter [code]keep_seed[/code] set to [code]true[/code].
+ [param process_time] defines the time that the particles will process while emitting is on. [param process_time_residual] defines the time that particles will process with emitting turned off for the simulation. When combined with [member speed_scale] set to [code]0.0[/code], this is useful to be able to seek a particle system timeline.
diff --git a/doc/classes/GPUParticles2D.xml b/doc/classes/GPUParticles2D.xml
index d7ccd2e35fd..45d90eda4ab 100644
--- a/doc/classes/GPUParticles2D.xml
+++ b/doc/classes/GPUParticles2D.xml
@@ -44,9 +44,10 @@
+
Requests the particles to process for extra process time during a single frame.
- Useful for particle playback, if used in combination with [member use_fixed_seed] or by calling [method restart] with parameter [code]keep_seed[/code] set to [code]true[/code].
+ [param process_time] defines the time that the particles will process while emitting is on. [param process_time_residual] defines the time that particles will process with emitting turned off for the simulation. When combined with [member speed_scale] set to [code]0.0[/code], this is useful to be able to seek a particle system timeline.
diff --git a/doc/classes/GPUParticles3D.xml b/doc/classes/GPUParticles3D.xml
index 24bce24a74b..fcc5f06f3e6 100644
--- a/doc/classes/GPUParticles3D.xml
+++ b/doc/classes/GPUParticles3D.xml
@@ -49,9 +49,10 @@
+
Requests the particles to process for extra process time during a single frame.
- Useful for particle playback, if used in combination with [member use_fixed_seed] or by calling [method restart] with parameter [code]keep_seed[/code] set to [code]true[/code].
+ [param process_time] defines the time that the particles will process while emitting is on. [param process_time_residual] defines the time that particles will process with emitting turned off for the simulation. When combined with [member speed_scale] set to [code]0.0[/code], this is useful to be able to seek a particle system timeline.
diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml
index bde6a57f6d2..c59590a39d0 100644
--- a/doc/classes/RenderingServer.xml
+++ b/doc/classes/RenderingServer.xml
@@ -3103,9 +3103,11 @@
-
+
+
- Requests particles to process for extra process time during a single frame.
+ Requests the particles to process for extra process time during a single frame.
+ [param process_time] defines the time that the particles will process while emitting is on. [param process_time_residual] defines the time that particles will process with emitting turned off for the simulation. When combined with the particles' speed scale set to [code]0.0[/code], this is useful to be able to seek a particle system timeline.
diff --git a/drivers/gles3/storage/particles_storage.cpp b/drivers/gles3/storage/particles_storage.cpp
index 1a44ad9f405..1f37fcb4fc7 100644
--- a/drivers/gles3/storage/particles_storage.cpp
+++ b/drivers/gles3/storage/particles_storage.cpp
@@ -223,10 +223,11 @@ void ParticlesStorage::particles_set_pre_process_time(RID p_particles, double p_
particles->pre_process_time = p_time;
}
-void ParticlesStorage::particles_request_process_time(RID p_particles, real_t p_request_process_time) {
+void ParticlesStorage::particles_request_process_time(RID p_particles, real_t p_request_process_time, real_t p_request_process_time_residual) {
Particles *particles = particles_owner.get_or_null(p_particles);
ERR_FAIL_NULL(particles);
particles->request_process_time = p_request_process_time;
+ particles->request_process_time_residual = p_request_process_time_residual;
}
void ParticlesStorage::particles_set_seed(RID p_particles, uint32_t p_seed) {
@@ -1063,6 +1064,7 @@ void ParticlesStorage::update_particles() {
particles->prev_phase = 0;
particles->clear = true;
particles->restart_request = false;
+ particles->frame_remainder = 0.0;
}
if (particles->inactive && !particles->emitting) {
@@ -1112,20 +1114,50 @@ void ParticlesStorage::update_particles() {
fixed_fps = particles->fixed_fps;
}
- if (particles->clear && particles->pre_process_time > 0.0) {
- double frame_time;
- if (fixed_fps > 0) {
- frame_time = 1.0 / fixed_fps;
- } else {
- frame_time = 1.0 / 30.0;
+ // Request process and pre-process block
+ {
+ float todo = particles->clear ? particles->pre_process_time : 0;
+ todo = todo > particles->request_process_time ? todo : particles->request_process_time;
+ todo = todo > particles->request_process_time_residual ? todo : particles->request_process_time_residual;
+
+ if (todo > 0.0) {
+ real_t frame_time;
+ if (fixed_fps > 0) {
+ frame_time = 1.0 / fixed_fps;
+ } else {
+ frame_time = 1.0 / 30.0;
+ }
+
+ float tmp_scale = particles->speed_scale;
+ // We need this otherwise the speed scale of the particle system influences the `todo`.
+ particles->speed_scale = 1.0;
+ if (particles->clear) {
+ todo = particles->pre_process_time;
+ while (todo > 0.00001) {
+ _particles_process(particles, frame_time > todo ? todo : frame_time);
+ todo -= frame_time;
+ }
+ }
+ if (particles->request_process_time > 0.0) {
+ todo = particles->request_process_time;
+ while (todo > 0.0) {
+ _particles_process(particles, frame_time > todo ? todo : frame_time);
+ todo -= frame_time;
+ }
+ }
+ if (particles->request_process_time_residual > 0.0) {
+ particles->emitting = false;
+ todo = particles->request_process_time_residual;
+ while (todo > 0.0) {
+ _particles_process(particles, frame_time > todo ? todo : frame_time);
+ todo -= frame_time;
+ }
+ }
+ particles->speed_scale = tmp_scale;
}
- double todo = particles->pre_process_time;
-
- while (todo >= 0) {
- _particles_process(particles, frame_time);
- todo -= frame_time;
- }
+ particles->request_process_time = 0.0;
+ particles->request_process_time_residual = 0.0;
}
double time_scale = MAX(particles->speed_scale, 0.0);
diff --git a/drivers/gles3/storage/particles_storage.h b/drivers/gles3/storage/particles_storage.h
index 22884c67bd9..c31d3425be9 100644
--- a/drivers/gles3/storage/particles_storage.h
+++ b/drivers/gles3/storage/particles_storage.h
@@ -157,6 +157,7 @@ private:
double lifetime = 1.0;
double pre_process_time = 0.0;
real_t request_process_time = 0.0;
+ real_t request_process_time_residual = 0.0;
real_t explosiveness = 0.0;
real_t randomness = 0.0;
bool restart_request = false;
@@ -331,7 +332,7 @@ public:
virtual void particles_set_lifetime(RID p_particles, double p_lifetime) override;
virtual void particles_set_one_shot(RID p_particles, bool p_one_shot) override;
virtual void particles_set_pre_process_time(RID p_particles, double p_time) override;
- virtual void particles_request_process_time(RID p_particles, real_t p_request_process_time) override;
+ virtual void particles_request_process_time(RID p_particles, real_t p_request_process_time, real_t p_request_process_time_residual) override;
virtual void particles_set_explosiveness_ratio(RID p_particles, real_t p_ratio) override;
virtual void particles_set_randomness_ratio(RID p_particles, real_t p_ratio) override;
virtual void particles_set_custom_aabb(RID p_particles, const AABB &p_aabb) override;
diff --git a/misc/extension_api_validation/4.6-stable/GH-109142.txt b/misc/extension_api_validation/4.6-stable/GH-109142.txt
new file mode 100644
index 00000000000..1bff8456790
--- /dev/null
+++ b/misc/extension_api_validation/4.6-stable/GH-109142.txt
@@ -0,0 +1,10 @@
+GH-109142
+---------
+
+Validate extension JSON: Error: Field 'classes/CPUParticles3D/methods/request_particles_process/arguments': size changed value in new API, from 1 to 2.
+Validate extension JSON: Error: Field 'classes/CPUParticles2D/methods/request_particles_process/arguments': size changed value in new API, from 1 to 2.
+Validate extension JSON: Error: Field 'classes/GPUParticles2D/methods/request_particles_process/arguments': size changed value in new API, from 1 to 2.
+Validate extension JSON: Error: Field 'classes/GPUParticles3D/methods/request_particles_process/arguments': size changed value in new API, from 1 to 2.
+Validate extension JSON: Error: Field 'classes/RenderingServer/methods/particles_request_process_time/arguments': size changed value in new API, from 2 to 3.
+
+Optional argument added. Compatibility methods registered.
diff --git a/scene/2d/cpu_particles_2d.compat.inc b/scene/2d/cpu_particles_2d.compat.inc
index 7df8da283ea..5fd2b3578eb 100644
--- a/scene/2d/cpu_particles_2d.compat.inc
+++ b/scene/2d/cpu_particles_2d.compat.inc
@@ -38,8 +38,13 @@ void CPUParticles2D::_restart_bind_compat_92089() {
restart(false);
}
+void CPUParticles2D::_request_particles_process_bind_compat_109142(real_t p_time) {
+ request_particles_process(p_time, 0.);
+}
+
void CPUParticles2D::_bind_compatibility_methods() {
ClassDB::bind_compatibility_method(D_METHOD("restart"), &CPUParticles2D::_restart_bind_compat_92089);
+ ClassDB::bind_compatibility_method(D_METHOD("request_particles_process", "process_time"), &CPUParticles2D::_request_particles_process_bind_compat_109142);
}
#endif // DISABLE_DEPRECATED
diff --git a/scene/2d/cpu_particles_2d.cpp b/scene/2d/cpu_particles_2d.cpp
index dfc0768cece..96070ec58c2 100644
--- a/scene/2d/cpu_particles_2d.cpp
+++ b/scene/2d/cpu_particles_2d.cpp
@@ -632,8 +632,10 @@ uint32_t CPUParticles2D::get_seed() const {
return seed;
}
-void CPUParticles2D::request_particles_process(real_t p_requested_process_time) {
- _requested_process_time = p_requested_process_time;
+void CPUParticles2D::request_particles_process(real_t p_request_process_time, real_t p_request_process_time_residual) {
+ _request_process_time = p_request_process_time;
+ _request_process_time_residual = p_request_process_time_residual;
+ _update_internal();
}
void CPUParticles2D::_validate_property(PropertyInfo &p_property) const {
@@ -724,28 +726,58 @@ void CPUParticles2D::_update_internal() {
cycle = 0;
return;
}
+
_set_do_redraw(true);
+ {
+ float todo = time == 0 ? pre_process_time : 0;
+ todo = todo > _request_process_time ? todo : _request_process_time;
+ todo = todo > _request_process_time_residual ? todo : _request_process_time_residual;
+
+ if (todo > 0.0) {
+ real_t frame_time;
+ if (fixed_fps > 0) {
+ frame_time = 1.0 / fixed_fps;
+ } else {
+ frame_time = 1.0 / 30.0;
+ }
+
+ float tmp_scale = speed_scale;
+ // We need this otherwise the speed scale of the particle system influences the `todo`.
+ speed_scale = 1.0;
+ if (time == 0) {
+ todo = pre_process_time;
+ while (todo > 0.0) {
+ _particles_process(frame_time > todo ? todo : frame_time);
+ todo -= frame_time;
+ }
+ }
+ if (_request_process_time > 0.0) {
+ todo = _request_process_time;
+ emitting = true;
+ while (todo > 0.0) {
+ _particles_process(frame_time > todo ? todo : frame_time);
+ todo -= frame_time;
+ }
+ }
+ if (_request_process_time_residual > 0.0) {
+ emitting = false;
+ todo = _request_process_time_residual;
+ while (todo > 0.0) {
+ _particles_process(frame_time > todo ? todo : frame_time);
+ todo -= frame_time;
+ }
+ }
+ speed_scale = tmp_scale;
+ }
+ _request_process_time = 0;
+ _request_process_time_residual = 0;
+ }
double frame_time;
if (fixed_fps > 0) {
frame_time = 1.0 / fixed_fps;
} else {
frame_time = 1.0 / 30.0;
}
- double todo = _requested_process_time;
- _requested_process_time = 0;
- if (time == 0 && pre_process_time > 0.0) {
- todo += pre_process_time;
- }
- real_t tmp_speed = speed_scale;
- speed_scale = 1.0;
- while (todo > 0) {
- _particles_process(frame_time);
- todo -= frame_time;
- }
- speed_scale = tmp_speed;
-
- todo = 0.0;
-
if (fixed_fps > 0) {
double decr = frame_time;
@@ -755,7 +787,7 @@ void CPUParticles2D::_update_internal() {
} else if (ldelta <= 0.0) { //unlikely but..
ldelta = 0.001;
}
- todo = frame_remainder + ldelta;
+ double todo = frame_remainder + ldelta;
while (todo >= frame_time) {
_particles_process(frame_time);
@@ -975,7 +1007,7 @@ void CPUParticles2D::_particles_process(double p_delta) {
} else if (!p.active) {
continue;
- } else if (p.time > p.lifetime) {
+ } else if (p.time >= p.lifetime) {
p.active = false;
tv = 1.0;
} else {
@@ -1466,7 +1498,7 @@ void CPUParticles2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_fixed_fps", "fps"), &CPUParticles2D::set_fixed_fps);
ClassDB::bind_method(D_METHOD("set_fractional_delta", "enable"), &CPUParticles2D::set_fractional_delta);
ClassDB::bind_method(D_METHOD("set_speed_scale", "scale"), &CPUParticles2D::set_speed_scale);
- ClassDB::bind_method(D_METHOD("request_particles_process", "process_time"), &CPUParticles2D::request_particles_process);
+ ClassDB::bind_method(D_METHOD("request_particles_process", "process_time", "process_time_residual"), &CPUParticles2D::request_particles_process, DEFVAL(0.0));
ClassDB::bind_method(D_METHOD("is_emitting"), &CPUParticles2D::is_emitting);
ClassDB::bind_method(D_METHOD("get_amount"), &CPUParticles2D::get_amount);
diff --git a/scene/2d/cpu_particles_2d.h b/scene/2d/cpu_particles_2d.h
index 970f361e6db..314f7156cf6 100644
--- a/scene/2d/cpu_particles_2d.h
+++ b/scene/2d/cpu_particles_2d.h
@@ -129,7 +129,8 @@ private:
double lifetime = 1.0;
double pre_process_time = 0.0;
- double _requested_process_time = 0.0;
+ real_t _request_process_time = 0.0;
+ real_t _request_process_time_residual = 0.0;
real_t explosiveness_ratio = 0.0;
real_t randomness_ratio = 0.0;
double lifetime_randomness = 0.0;
@@ -218,6 +219,7 @@ protected:
#ifndef DISABLE_DEPRECATED
void _restart_bind_compat_92089();
+ void _request_particles_process_bind_compat_109142(real_t p_time);
static void _bind_compatibility_methods();
#endif
@@ -265,7 +267,7 @@ public:
#endif
uint32_t get_seed() const;
- void request_particles_process(real_t p_requested_process_time);
+ void request_particles_process(real_t p_requested_process_time, real_t p_requested_process_time_residual = 0.);
///////////////////
diff --git a/scene/2d/gpu_particles_2d.compat.inc b/scene/2d/gpu_particles_2d.compat.inc
index 9dfed63d5db..d06ae09e083 100644
--- a/scene/2d/gpu_particles_2d.compat.inc
+++ b/scene/2d/gpu_particles_2d.compat.inc
@@ -38,8 +38,13 @@ void GPUParticles2D::_restart_bind_compat_92089() {
restart(false);
}
+void GPUParticles2D::_request_particles_process_bind_compat_109142(real_t p_time) {
+ request_particles_process(p_time, 0.);
+}
+
void GPUParticles2D::_bind_compatibility_methods() {
ClassDB::bind_compatibility_method(D_METHOD("restart"), &GPUParticles2D::_restart_bind_compat_92089);
+ ClassDB::bind_compatibility_method(D_METHOD("request_particles_process", "process_time"), &GPUParticles2D::_request_particles_process_bind_compat_109142);
}
#endif // DISABLE_DEPRECATED
diff --git a/scene/2d/gpu_particles_2d.cpp b/scene/2d/gpu_particles_2d.cpp
index 91c0f3d076d..7c908036f9b 100644
--- a/scene/2d/gpu_particles_2d.cpp
+++ b/scene/2d/gpu_particles_2d.cpp
@@ -371,8 +371,15 @@ uint32_t GPUParticles2D::get_seed() const {
return seed;
}
-void GPUParticles2D::request_particles_process(real_t p_requested_process_time) {
- RS::get_singleton()->particles_request_process_time(particles, p_requested_process_time);
+void GPUParticles2D::request_particles_process(real_t p_requested_process_time, real_t p_request_process_time_residual) {
+ RS::get_singleton()->particles_request_process_time(particles, p_requested_process_time, p_request_process_time_residual);
+ if (p_requested_process_time > 0.0) {
+ emitting = true;
+ RS::get_singleton()->particles_set_emitting(particles, true);
+ }
+ if (p_request_process_time_residual > 0.0) {
+ emitting = false;
+ }
}
PackedStringArray GPUParticles2D::get_configuration_warnings() const {
@@ -884,7 +891,7 @@ void GPUParticles2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_collision_base_size", "size"), &GPUParticles2D::set_collision_base_size);
ClassDB::bind_method(D_METHOD("set_interp_to_end", "interp"), &GPUParticles2D::set_interp_to_end);
- ClassDB::bind_method(D_METHOD("request_particles_process", "process_time"), &GPUParticles2D::request_particles_process);
+ ClassDB::bind_method(D_METHOD("request_particles_process", "process_time", "process_time_residual"), &GPUParticles2D::request_particles_process, DEFVAL(0));
ClassDB::bind_method(D_METHOD("is_emitting"), &GPUParticles2D::is_emitting);
ClassDB::bind_method(D_METHOD("get_amount"), &GPUParticles2D::get_amount);
diff --git a/scene/2d/gpu_particles_2d.h b/scene/2d/gpu_particles_2d.h
index 60ef3fb7cf4..ae72de249d5 100644
--- a/scene/2d/gpu_particles_2d.h
+++ b/scene/2d/gpu_particles_2d.h
@@ -107,6 +107,7 @@ protected:
#ifndef DISABLE_DEPRECATED
void _restart_bind_compat_92089();
+ void _request_particles_process_bind_compat_109142(real_t p_time);
static void _bind_compatibility_methods();
#endif
@@ -128,7 +129,7 @@ public:
void set_trail_sections(int p_sections);
void set_trail_section_subdivisions(int p_subdivisions);
void set_interp_to_end(float p_interp);
- void request_particles_process(real_t p_requested_process_time);
+ void request_particles_process(real_t p_requested_process_time, real_t p_requested_process_time_residual);
#ifdef TOOLS_ENABLED
void set_show_gizmos(bool p_show_gizmos);
diff --git a/scene/3d/cpu_particles_3d.compat.inc b/scene/3d/cpu_particles_3d.compat.inc
index c39e3a89b7d..cc0f3a8308e 100644
--- a/scene/3d/cpu_particles_3d.compat.inc
+++ b/scene/3d/cpu_particles_3d.compat.inc
@@ -38,8 +38,13 @@ void CPUParticles3D::_restart_bind_compat_92089() {
restart(false);
}
+void CPUParticles3D::_request_particles_process_bind_compat_109142(real_t p_time) {
+ request_particles_process(p_time, 0.);
+}
+
void CPUParticles3D::_bind_compatibility_methods() {
ClassDB::bind_compatibility_method(D_METHOD("restart"), &CPUParticles3D::_restart_bind_compat_92089);
+ ClassDB::bind_compatibility_method(D_METHOD("request_particles_process", "process_time"), &CPUParticles3D::_request_particles_process_bind_compat_109142);
}
#endif // DISABLE_DEPRECATED
diff --git a/scene/3d/cpu_particles_3d.cpp b/scene/3d/cpu_particles_3d.cpp
index 4f344276ced..e8e7fb7471f 100644
--- a/scene/3d/cpu_particles_3d.cpp
+++ b/scene/3d/cpu_particles_3d.cpp
@@ -587,8 +587,10 @@ uint32_t CPUParticles3D::get_seed() const {
return seed;
}
-void CPUParticles3D::request_particles_process(real_t p_requested_process_time) {
- _requested_process_time = p_requested_process_time;
+void CPUParticles3D::request_particles_process(real_t p_request_process_time, real_t p_request_process_time_residual) {
+ _request_process_time = p_request_process_time;
+ _request_process_time_residual = p_request_process_time_residual;
+ _update_internal();
}
void CPUParticles3D::_validate_property(PropertyInfo &p_property) const {
@@ -672,25 +674,58 @@ void CPUParticles3D::_update_internal() {
bool processed = false;
+ {
+ float todo = time == 0 ? pre_process_time : 0;
+ todo = todo > _request_process_time ? todo : _request_process_time;
+ todo = todo > _request_process_time_residual ? todo : _request_process_time_residual;
+
+ if (todo > 0.0) {
+ real_t frame_time;
+ if (fixed_fps > 0) {
+ frame_time = 1.0 / fixed_fps;
+ } else {
+ frame_time = 1.0 / 30.0;
+ }
+
+ float tmp_scale = speed_scale;
+ // We need this otherwise the speed scale of the particle system influences the TODO.
+ speed_scale = 1.0;
+ if (time == 0) {
+ todo = pre_process_time;
+ while (todo > 0.0) {
+ _particles_process(frame_time > todo ? todo : frame_time);
+ todo -= frame_time;
+ }
+ }
+ if (_request_process_time > 0.0) {
+ todo = _request_process_time;
+ emitting = true;
+ while (todo > 0.0) {
+ _particles_process(frame_time > todo ? todo : frame_time);
+ todo -= frame_time;
+ }
+ }
+ if (_request_process_time_residual > 0.0) {
+ emitting = false;
+ todo = _request_process_time_residual;
+ while (todo > 0.0) {
+ _particles_process(frame_time > todo ? todo : frame_time);
+ todo -= frame_time;
+ }
+ }
+ speed_scale = tmp_scale;
+ processed = true;
+ }
+ _request_process_time = 0;
+ _request_process_time_residual = 0;
+ }
+
double frame_time;
if (fixed_fps > 0) {
frame_time = 1.0 / fixed_fps;
} else {
frame_time = 1.0 / 30.0;
}
- double todo = _requested_process_time;
- _requested_process_time = 0.;
- if (time == 0 && pre_process_time > 0.0) {
- todo += pre_process_time;
- }
- real_t tmp_speed = speed_scale;
- speed_scale = 1.0;
- while (todo > 0) {
- _particles_process(frame_time);
- todo -= frame_time;
- }
- speed_scale = tmp_speed;
- todo = 0.0;
if (fixed_fps > 0) {
double decr = frame_time;
@@ -701,7 +736,7 @@ void CPUParticles3D::_update_internal() {
} else if (ldelta <= 0.0) { //unlikely but..
ldelta = 0.001;
}
- todo = frame_remainder + ldelta;
+ double todo = frame_remainder + ldelta;
while (todo >= frame_time) {
_particles_process(frame_time);
@@ -1549,7 +1584,7 @@ void CPUParticles3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_seed"), &CPUParticles3D::get_seed);
ClassDB::bind_method(D_METHOD("restart", "keep_seed"), &CPUParticles3D::restart, DEFVAL(false));
- ClassDB::bind_method(D_METHOD("request_particles_process", "process_time"), &CPUParticles3D::request_particles_process);
+ ClassDB::bind_method(D_METHOD("request_particles_process", "process_time", "process_time_residual"), &CPUParticles3D::request_particles_process, DEFVAL(0.0));
ClassDB::bind_method(D_METHOD("capture_aabb"), &CPUParticles3D::capture_aabb);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emitting", PROPERTY_HINT_ONESHOT), "set_emitting", "is_emitting");
diff --git a/scene/3d/cpu_particles_3d.h b/scene/3d/cpu_particles_3d.h
index e0f93a7de33..0fec503a236 100644
--- a/scene/3d/cpu_particles_3d.h
+++ b/scene/3d/cpu_particles_3d.h
@@ -138,7 +138,8 @@ private:
double lifetime = 1.0;
double pre_process_time = 0.0;
- double _requested_process_time = 0.0;
+ real_t _request_process_time = 0.0;
+ real_t _request_process_time_residual = 0.0;
real_t explosiveness_ratio = 0.0;
real_t randomness_ratio = 0.0;
double lifetime_randomness = 0.0;
@@ -213,6 +214,7 @@ protected:
#ifndef DISABLE_DEPRECATED
void _restart_bind_compat_92089();
+ void _request_particles_process_bind_compat_109142(real_t p_time);
static void _bind_compatibility_methods();
#endif
@@ -261,7 +263,7 @@ public:
void set_seed(uint32_t p_seed);
uint32_t get_seed() const;
- void request_particles_process(real_t p_requested_process_time);
+ void request_particles_process(real_t p_requested_process_time, real_t p_request_process_time_residual = 0.0f);
///////////////////
diff --git a/scene/3d/gpu_particles_3d.compat.inc b/scene/3d/gpu_particles_3d.compat.inc
index 6509b2e415f..b77a0c0efd6 100644
--- a/scene/3d/gpu_particles_3d.compat.inc
+++ b/scene/3d/gpu_particles_3d.compat.inc
@@ -38,8 +38,13 @@ void GPUParticles3D::_restart_bind_compat_92089() {
restart(false);
}
+void GPUParticles3D::_request_particles_process_bind_compat_109142(real_t p_time) {
+ request_particles_process(p_time, 0.);
+}
+
void GPUParticles3D::_bind_compatibility_methods() {
ClassDB::bind_compatibility_method(D_METHOD("restart"), &GPUParticles3D::_restart_bind_compat_92089);
+ ClassDB::bind_compatibility_method(D_METHOD("request_particles_process", "process_time"), &GPUParticles3D::_request_particles_process_bind_compat_109142);
}
#endif // DISABLE_DEPRECATED
diff --git a/scene/3d/gpu_particles_3d.cpp b/scene/3d/gpu_particles_3d.cpp
index 78039aa1580..1dfcfe5f643 100644
--- a/scene/3d/gpu_particles_3d.cpp
+++ b/scene/3d/gpu_particles_3d.cpp
@@ -482,8 +482,20 @@ void GPUParticles3D::_validate_property(PropertyInfo &p_property) const {
}
}
-void GPUParticles3D::request_particles_process(real_t p_requested_process_time) {
- RS::get_singleton()->particles_request_process_time(particles, p_requested_process_time);
+void GPUParticles3D::request_particles_process(real_t p_requested_process_time, real_t p_request_process_time_residual) {
+ RS::get_singleton()->particles_request_process_time(particles, p_requested_process_time, p_request_process_time_residual);
+ // Setting emitting independently from set_emitting is important here
+ // we assume to be in a controlled process situation.
+ // setting RS emission to true is necessary to ensure particles will emit when
+ // regular process time is > than zero but the particles are already in trailing mode.
+ // this could also be done on the GScript/tool side.
+ if (p_requested_process_time > 0.0) {
+ emitting = true;
+ RS::get_singleton()->particles_set_emitting(particles, true);
+ }
+ if (p_request_process_time_residual > 0.0) {
+ emitting = false;
+ }
}
void GPUParticles3D::emit_particle(const Transform3D &p_transform, const Vector3 &p_velocity, const Color &p_color, const Color &p_custom, uint32_t p_emit_flags) {
@@ -850,7 +862,7 @@ void GPUParticles3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_amount_ratio", "ratio"), &GPUParticles3D::set_amount_ratio);
ClassDB::bind_method(D_METHOD("get_amount_ratio"), &GPUParticles3D::get_amount_ratio);
- ClassDB::bind_method(D_METHOD("request_particles_process", "process_time"), &GPUParticles3D::request_particles_process);
+ ClassDB::bind_method(D_METHOD("request_particles_process", "process_time", "process_time_residual"), &GPUParticles3D::request_particles_process, DEFVAL(0.0));
ADD_SIGNAL(MethodInfo("finished"));
diff --git a/scene/3d/gpu_particles_3d.h b/scene/3d/gpu_particles_3d.h
index e94a8622f90..12940fa98f5 100644
--- a/scene/3d/gpu_particles_3d.h
+++ b/scene/3d/gpu_particles_3d.h
@@ -116,6 +116,7 @@ protected:
#ifndef DISABLE_DEPRECATED
void _restart_bind_compat_92089();
+ void _request_particles_process_bind_compat_109142(real_t p_time);
static void _bind_compatibility_methods();
#endif
@@ -200,7 +201,7 @@ public:
void set_seed(uint32_t p_seed);
uint32_t get_seed() const;
- void request_particles_process(real_t p_requested_process_time);
+ void request_particles_process(real_t p_requested_process_time, real_t p_request_process_time_residual = 0.0f);
enum EmitFlags {
EMIT_FLAG_POSITION = RSE::PARTICLES_EMIT_FLAG_POSITION,
diff --git a/servers/rendering/dummy/storage/particles_storage.h b/servers/rendering/dummy/storage/particles_storage.h
index 6c91a6246c7..9b356803620 100644
--- a/servers/rendering/dummy/storage/particles_storage.h
+++ b/servers/rendering/dummy/storage/particles_storage.h
@@ -52,7 +52,7 @@ public:
virtual void particles_set_lifetime(RID p_particles, double p_lifetime) override {}
virtual void particles_set_one_shot(RID p_particles, bool p_one_shot) override {}
virtual void particles_set_pre_process_time(RID p_particles, double p_time) override {}
- virtual void particles_request_process_time(RID p_particles, real_t p_request_process_time) override {}
+ virtual void particles_request_process_time(RID p_particles, real_t p_request_process_time, real_t p_particles_request_process_time) override {}
virtual void particles_set_explosiveness_ratio(RID p_particles, real_t p_ratio) override {}
virtual void particles_set_randomness_ratio(RID p_particles, real_t p_ratio) override {}
virtual void particles_set_seed(RID p_particles, uint32_t p_seed) override {}
diff --git a/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp b/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp
index 19a58cb21e7..7bbdf645f78 100644
--- a/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp
@@ -375,10 +375,11 @@ void ParticlesStorage::particles_set_pre_process_time(RID p_particles, double p_
particles->pre_process_time = p_time;
}
-void ParticlesStorage::particles_request_process_time(RID p_particles, real_t p_request_process_time) {
+void ParticlesStorage::particles_request_process_time(RID p_particles, real_t p_request_process_time, real_t p_request_process_time_residual) {
Particles *particles = particles_owner.get_or_null(p_particles);
ERR_FAIL_NULL(particles);
particles->request_process_time = p_request_process_time;
+ particles->request_process_time_residual = p_request_process_time_residual;
}
void ParticlesStorage::particles_set_explosiveness_ratio(RID p_particles, real_t p_ratio) {
@@ -836,7 +837,7 @@ void ParticlesStorage::_particles_process(Particles *p_particles, double p_delta
p_particles->particles_material_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, particles_shader.default_shader_rd, 1);
}
- double new_phase = Math::fmod((double)p_particles->phase + (p_delta / p_particles->lifetime), 1.0);
+ double new_phase = Math::fmod((double)(p_particles->phase + (p_delta / p_particles->lifetime)), 1.0);
//move back history (if there is any)
for (uint32_t i = p_particles->frame_history.size() - 1; i > 0; i--) {
@@ -1461,13 +1462,13 @@ void ParticlesStorage::update_particles() {
particles->prev_phase = 0;
particles->clear = true;
particles->restart_request = false;
+ particles->frame_remainder = 0.0;
}
if (particles->inactive && !particles->emitting) {
//go next
continue;
}
-
if (particles->emitting) {
if (particles->inactive) {
//restart system from scratch
@@ -1558,13 +1559,12 @@ void ParticlesStorage::update_particles() {
}
}
- double todo = particles->request_process_time;
- if (particles->clear) {
- todo += particles->pre_process_time;
- }
+ float todo = particles->clear ? particles->pre_process_time : 0;
+ todo = todo > particles->request_process_time ? todo : particles->request_process_time;
+ todo = todo > particles->request_process_time_residual ? todo : particles->request_process_time_residual;
if (todo > 0.0) {
- double frame_time;
+ real_t frame_time;
if (fixed_fps > 0) {
frame_time = 1.0 / fixed_fps;
} else {
@@ -1574,19 +1574,38 @@ void ParticlesStorage::update_particles() {
float tmp_scale = particles->speed_scale;
// We need this otherwise the speed scale of the particle system influences the TODO.
particles->speed_scale = 1.0;
- while (todo >= 0) {
- _particles_process(particles, frame_time);
- todo -= frame_time;
+ if (particles->clear) {
+ todo = particles->pre_process_time;
+ while (todo > 0.0) {
+ _particles_process(particles, frame_time > todo ? todo : frame_time);
+ todo -= frame_time;
+ }
+ }
+ if (particles->request_process_time > 0.0) {
+ todo = particles->request_process_time;
+ while (todo > 0.0) {
+ _particles_process(particles, frame_time > todo ? todo : frame_time);
+ todo -= frame_time;
+ }
+ }
+ if (particles->request_process_time_residual > 0.0) {
+ particles->emitting = false;
+ todo = particles->request_process_time_residual;
+ while (todo > 0.0) {
+ _particles_process(particles, frame_time > todo ? todo : frame_time);
+ todo -= frame_time;
+ }
}
- particles->request_process_time = 0.0;
particles->speed_scale = tmp_scale;
}
- double time_scale = MAX(particles->speed_scale, 0.0);
+ particles->request_process_time = 0.0;
+ particles->request_process_time_residual = 0.0;
+ float time_scale = MAX(particles->speed_scale, 0.0);
if (fixed_fps > 0) {
- double frame_time = 1.0 / fixed_fps;
- double delta = RendererCompositorRD::get_singleton()->get_frame_delta_time();
+ float frame_time = 1.0 / fixed_fps;
+ float delta = (float)RendererCompositorRD::get_singleton()->get_frame_delta_time();
if (delta > 0.1) { //avoid recursive stalls if fps goes below 10
delta = 0.1;
} else if (delta <= 0.0) { //unlikely but..
@@ -1603,7 +1622,6 @@ void ParticlesStorage::update_particles() {
} else {
_particles_process(particles, RendererCompositorRD::get_singleton()->get_frame_delta_time() * time_scale);
}
-
// Ensure that memory is initialized (the code above should ensure that _particles_process is always called at least once upon clearing).
DEV_ASSERT(!particles->clear);
diff --git a/servers/rendering/renderer_rd/storage_rd/particles_storage.h b/servers/rendering/renderer_rd/storage_rd/particles_storage.h
index 9b3e2254055..b95b756af52 100644
--- a/servers/rendering/renderer_rd/storage_rd/particles_storage.h
+++ b/servers/rendering/renderer_rd/storage_rd/particles_storage.h
@@ -169,6 +169,7 @@ private:
double lifetime = 1.0;
double pre_process_time = 0.0;
real_t request_process_time = 0.0;
+ real_t request_process_time_residual = 0.0;
real_t explosiveness = 0.0;
real_t randomness = 0.0;
bool restart_request = false;
@@ -455,7 +456,7 @@ public:
virtual void particles_set_lifetime(RID p_particles, double p_lifetime) override;
virtual void particles_set_one_shot(RID p_particles, bool p_one_shot) override;
virtual void particles_set_pre_process_time(RID p_particles, double p_time) override;
- virtual void particles_request_process_time(RID p_particles, real_t p_request_process_time) override;
+ virtual void particles_request_process_time(RID p_particles, real_t p_request_process_time, real_t p_request_process_time_residual) override;
virtual void particles_set_explosiveness_ratio(RID p_particles, real_t p_ratio) override;
virtual void particles_set_randomness_ratio(RID p_particles, real_t p_ratio) override;
virtual void particles_set_custom_aabb(RID p_particles, const AABB &p_aabb) override;
diff --git a/servers/rendering/rendering_server.compat.inc b/servers/rendering/rendering_server.compat.inc
index a37ec227919..bbe5a7d9f66 100644
--- a/servers/rendering/rendering_server.compat.inc
+++ b/servers/rendering/rendering_server.compat.inc
@@ -66,6 +66,10 @@ void RenderingServer::_viewport_set_size_compat_115799(RID p_viewport, int p_wid
viewport_set_size(p_viewport, p_width, p_height, 1);
}
+void RenderingServer::_particles_request_process_time_bind_compat_109142(RID p_particles, real_t p_request_process_time) {
+ particles_request_process_time(p_particles, p_request_process_time, 0.0);
+}
+
void RenderingServer::_bind_compatibility_methods() {
ClassDB::bind_compatibility_method(D_METHOD("multimesh_allocate_data", "multimesh", "instances", "transform_format", "color_format", "custom_data_format"), &RenderingServer::_multimesh_allocate_data_bind_compat_99455, DEFVAL(false), DEFVAL(false));
ClassDB::bind_compatibility_method(D_METHOD("environment_set_fog", "env", "enable", "light_color", "light_energy", "sun_scatter", "density", "height", "height_density", "aerial_perspective", "sky_affect"), &RenderingServer::_environment_set_fog_bind_compat_84792);
@@ -75,6 +79,7 @@ void RenderingServer::_bind_compatibility_methods() {
ClassDB::bind_compatibility_method(D_METHOD("instance_set_interpolated", "instance", "interpolated"), &RenderingServer::_instance_set_interpolated_bind_compat_104269);
ClassDB::bind_compatibility_method(D_METHOD("instance_reset_physics_interpolation", "instance"), &RenderingServer::_instance_reset_physics_interpolation_bind_compat_104269);
ClassDB::bind_compatibility_method(D_METHOD("viewport_set_size", "viewport", "width", "height"), &RenderingServer::_viewport_set_size_compat_115799);
+ ClassDB::bind_compatibility_method(D_METHOD("particles_request_process_time", "particles", "process_time"), &RenderingServer::_particles_request_process_time_bind_compat_109142);
}
#endif // DISABLE_DEPRECATED
diff --git a/servers/rendering/rendering_server.cpp b/servers/rendering/rendering_server.cpp
index 3b251d6a716..d12730b8556 100644
--- a/servers/rendering/rendering_server.cpp
+++ b/servers/rendering/rendering_server.cpp
@@ -2710,7 +2710,7 @@ void RenderingServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("particles_set_lifetime", "particles", "lifetime"), &RenderingServer::particles_set_lifetime);
ClassDB::bind_method(D_METHOD("particles_set_one_shot", "particles", "one_shot"), &RenderingServer::particles_set_one_shot);
ClassDB::bind_method(D_METHOD("particles_set_pre_process_time", "particles", "time"), &RenderingServer::particles_set_pre_process_time);
- ClassDB::bind_method(D_METHOD("particles_request_process_time", "particles", "time"), &RenderingServer::particles_request_process_time);
+ ClassDB::bind_method(D_METHOD("particles_request_process_time", "particles", "process_time", "process_time_residual"), &RenderingServer::particles_request_process_time, DEFVAL(0.0f));
ClassDB::bind_method(D_METHOD("particles_set_explosiveness_ratio", "particles", "ratio"), &RenderingServer::particles_set_explosiveness_ratio);
ClassDB::bind_method(D_METHOD("particles_set_randomness_ratio", "particles", "ratio"), &RenderingServer::particles_set_randomness_ratio);
ClassDB::bind_method(D_METHOD("particles_set_interp_to_end", "particles", "factor"), &RenderingServer::particles_set_interp_to_end);
diff --git a/servers/rendering/rendering_server.h b/servers/rendering/rendering_server.h
index 093338d0bbe..0c81a0fac54 100644
--- a/servers/rendering/rendering_server.h
+++ b/servers/rendering/rendering_server.h
@@ -96,6 +96,7 @@ protected:
void _instance_set_interpolated_bind_compat_104269(RID p_instance, bool p_interpolated);
void _instance_reset_physics_interpolation_bind_compat_104269(RID p_instance);
void _viewport_set_size_compat_115799(RID p_viewport, int p_width, int p_height);
+ void _particles_request_process_time_bind_compat_109142(RID p_particles, real_t p_request_process_time);
static void _bind_compatibility_methods();
#endif
@@ -436,7 +437,7 @@ public:
virtual void particles_set_lifetime(RID p_particles, double p_lifetime) = 0;
virtual void particles_set_one_shot(RID p_particles, bool p_one_shot) = 0;
virtual void particles_set_pre_process_time(RID p_particles, double p_time) = 0;
- virtual void particles_request_process_time(RID p_particles, real_t p_request_process_time) = 0;
+ virtual void particles_request_process_time(RID p_particles, real_t p_request_process_time, real_t p_request_process_time_residual = 0.0) = 0;
virtual void particles_set_explosiveness_ratio(RID p_particles, float p_ratio) = 0;
virtual void particles_set_randomness_ratio(RID p_particles, float p_ratio) = 0;
virtual void particles_set_custom_aabb(RID p_particles, const AABB &p_aabb) = 0;
diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h
index fc0cf25bc24..832b6c79db3 100644
--- a/servers/rendering/rendering_server_default.h
+++ b/servers/rendering/rendering_server_default.h
@@ -617,7 +617,7 @@ public:
FUNC2(particles_set_lifetime, RID, double)
FUNC2(particles_set_one_shot, RID, bool)
FUNC2(particles_set_pre_process_time, RID, double)
- FUNC2(particles_request_process_time, RID, real_t)
+ FUNC3(particles_request_process_time, RID, real_t, real_t)
FUNC2(particles_set_explosiveness_ratio, RID, float)
FUNC2(particles_set_randomness_ratio, RID, float)
FUNC2(particles_set_seed, RID, uint32_t)
diff --git a/servers/rendering/storage/particles_storage.h b/servers/rendering/storage/particles_storage.h
index abb55b662e2..12057e9b75e 100644
--- a/servers/rendering/storage/particles_storage.h
+++ b/servers/rendering/storage/particles_storage.h
@@ -62,7 +62,7 @@ public:
virtual void particles_set_lifetime(RID p_particles, double p_lifetime) = 0;
virtual void particles_set_one_shot(RID p_particles, bool p_one_shot) = 0;
virtual void particles_set_pre_process_time(RID p_particles, double p_time) = 0;
- virtual void particles_request_process_time(RID p_particles, real_t p_request_process_time) = 0;
+ virtual void particles_request_process_time(RID p_particles, real_t p_request_process_time, real_t p_request_process_time_residual = 0.0) = 0;
virtual void particles_set_explosiveness_ratio(RID p_particles, real_t p_ratio) = 0;
virtual void particles_set_randomness_ratio(RID p_particles, real_t p_ratio) = 0;
virtual void particles_set_custom_aabb(RID p_particles, const AABB &p_aabb) = 0;