Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ticking "Emitting" On in a GPUParticles3D when One Shot is active with Explosiveness 1 will not always loop despite all Particles having already despawned. #83909

Closed
ArkyonVeil opened this issue Oct 24, 2023 · 3 comments

Comments

@ArkyonVeil
Copy link

ArkyonVeil commented Oct 24, 2023

Godot version

v4.1.1.stable.mono.official [bd6af8e]

System information

Godot v4.1.1.stable.mono - Windows 10.0.22621 - Vulkan (Forward+) - dedicated NVIDIA GeForce RTX 3090 (NVIDIA; 31.0.15.3161) - Intel(R) Core(TM) i7-9700K CPU @ 3.60GHz (8 Threads)

Issue description

Simple Example:
https://github.com/godotengine/godot/assets/42283444/830b0fb3-eba1-4c9c-819c-ee792563591e

Seems like there is a problem when using oneshot particle spawning and looping the emission when mixed with high Explosiveness values.

I'd expect that since it's a oneshot particle emitter, whenever every particle's lifetime reaches 0, activating emission again would loop it. This works perfectly when Explosiveness is 0.

When explosiveness is higher, as more easily seen in explosiveness 1. Ticking emission once all particles have vanished will not cause it to loop reliably. At a glance it appears that it still requires the same amount of time to pass that every particle with explosiveness 0 took for their lifetime to reach 0.

In other words...

  • If explosiveness is 1 and lifetime 1. The system should reset after 1 second passed.
  • Instead, it actually takes at least 2 seconds. 1 for the time every particle needs to spawn as if it were in Explosiveness 0 + their lifetime.

In turn, when making animations this can cause loops to not function reliably. Requiring the animation loop to be much larger so when looping, it can cover the emission's expected full cycle duration at explosiveness 0.

Workflow with Animation loop showcasing the bug:
https://github.com/godotengine/godot/assets/42283444/9e82095c-e246-43d2-ba84-1d96901e46fb

Note: Also tested in Godot 4.2 Beta 2/3, bug still occurs.

Steps to reproduce

  1. Create a GPUParticles3D,
  2. Setup with bare minimum to render. (IE DrawPass Mesh, ParticleProcessMaterial)
  3. Tick One Shot
  4. Slide Explosiveness to 1,
  5. Tick emitting on,
  6. Wait a second for the particles to disappear, and then try ticking back on again the moment every particle has despawned. Notice how even though all particles vanished, the spawner will not restart.

Minimal reproduction project

N/A

@Calinou
Copy link
Member

Calinou commented Oct 24, 2023

Remember that you can use manual particle emission in 4.x using a custom shader. 4.2.beta2 also has an Amount Ratio property in ParticleProcessMaterial to adjust the number of particles emitted without restarting the particle system.

@ArkyonVeil
Copy link
Author

ArkyonVeil commented Oct 25, 2023

Remember that you can use manual particle emission in 4.x using a custom shader. 4.2.beta2 also has an Amount Ratio property in ParticleProcessMaterial to adjust the number of particles emitted without restarting the particle system.

Not sure if related. Anyhow, I seem to have gotten the issue fixed by modifying the gpu_particles_3d.cpp file.

On the following method, triggering a restart of the particles will cause switching on emission in a one shot to work as expected.

void GPUParticles3D::set_emitting(bool p_emitting) {
	// Do not return even if `p_emitting == emitting` because `emitting` is just an approximation.

	if (p_emitting && one_shot) {
		if (!active && !emitting) {
			// Last cycle ended.
			active = true;
			time = 0;
			signal_canceled = false;
			emission_time = lifetime;
			active_time = lifetime * (2 - explosiveness_ratio);
---->		RenderingServer::get_singleton()->particles_restart(particles);
		} else {
			signal_canceled = true;
		}
		set_process_internal(true);
	} else if (!p_emitting) {
		if (one_shot) {
			{
				set_process_internal(true);
			}
		} else {
			set_process_internal(false);
		}
	} else {
		set_process_internal(true);
	}

	emitting = p_emitting;
	RS::get_singleton()->particles_set_emitting(particles, p_emitting);
}

Potential fix? Or is there a better way?

Edit: Also just tried mixing animation with this fix. If the animation matches the lifetime exactly, it loops roughly 50% of the time, though a 0.05s increase will already increase the chance of a successful loop to near 100% (Depending if there's frame drops or not) which will make working with VFX more fluent. A better fix for this workflow should have the Animator trigger the restart manually, but that is in one's opinion best reserved for a proper VFX focused overhaul.

godot.windows.editor.x86_64_7eIcjPHGTc.mp4

@Calinou
Copy link
Member

Calinou commented Oct 25, 2023

Closing in favor of godotengine/godot-proposals#7322, as this is ultimately the same problem that you're trying to address here. Feel free to post your findings in a comment though 🙂

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants