From 158d8784c3b55c82ad1c3ed680b924104370c110 Mon Sep 17 00:00:00 2001 From: Qbieshay Date: Wed, 16 Jul 2025 18:49:57 +0200 Subject: [PATCH] Add parameter to request_process_time to have particle process without emission. Co-authored-by: A Thousand Ships <96648715+AThousandShips@users.noreply.github.com> Co-authored-by: Hugo Locurcio --- doc/classes/CPUParticles2D.xml | 3 +- doc/classes/CPUParticles3D.xml | 3 +- doc/classes/GPUParticles2D.xml | 3 +- doc/classes/GPUParticles3D.xml | 3 +- doc/classes/RenderingServer.xml | 6 +- drivers/gles3/storage/particles_storage.cpp | 58 +++++++++++---- drivers/gles3/storage/particles_storage.h | 3 +- .../4.6-stable/GH-109142.txt | 10 +++ scene/2d/cpu_particles_2d.compat.inc | 5 ++ scene/2d/cpu_particles_2d.cpp | 72 +++++++++++++------ scene/2d/cpu_particles_2d.h | 6 +- scene/2d/gpu_particles_2d.compat.inc | 5 ++ scene/2d/gpu_particles_2d.cpp | 13 +++- scene/2d/gpu_particles_2d.h | 3 +- scene/3d/cpu_particles_3d.compat.inc | 5 ++ scene/3d/cpu_particles_3d.cpp | 69 +++++++++++++----- scene/3d/cpu_particles_3d.h | 6 +- scene/3d/gpu_particles_3d.compat.inc | 5 ++ scene/3d/gpu_particles_3d.cpp | 18 ++++- scene/3d/gpu_particles_3d.h | 3 +- .../dummy/storage/particles_storage.h | 2 +- .../storage_rd/particles_storage.cpp | 50 ++++++++----- .../storage_rd/particles_storage.h | 3 +- servers/rendering/rendering_server.compat.inc | 5 ++ servers/rendering/rendering_server.cpp | 2 +- servers/rendering/rendering_server.h | 3 +- servers/rendering/rendering_server_default.h | 2 +- servers/rendering/storage/particles_storage.h | 2 +- 28 files changed, 277 insertions(+), 91 deletions(-) create mode 100644 misc/extension_api_validation/4.6-stable/GH-109142.txt diff --git a/doc/classes/CPUParticles2D.xml b/doc/classes/CPUParticles2D.xml index dcf533d884c..8b48d2bd7bc 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 61b4911dedc..8dff7c5e441 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 e211584f6b4..cfe498be013 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 90fca02cb8f..873314fc0e7 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 89b9c149b88..74f041bd046 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;