godotengine / godot-proposals

Godot Improvement Proposals (GIPs)
MIT License
1.08k stars 69 forks source link

Particle system: Add emission by distance travelled between emits (instead of time) #780

Open ghsoares opened 4 years ago

ghsoares commented 4 years ago

Describe the project you are working on: A simple top-down shooter where I have high-speed rocket launcher adn the rocket leaves smoke behind.

Describe the problem or limitation you are having in your project: The particles system (2D and 3D) are leaving a "gap" between particles when local coords speed is high or the system is set to global coords and the parent speed is high. In the Unity engine, we have a option to emit particles by distance that creates new particles in between gaps

Describe the feature / enhancement and how it helps to overcome the problem or limitation: To overcome the problem, it would be great if there was an option to emit by distance, like in Unity engine.

Describe how your proposal will work, with code, pseudocode, mockups, and/or diagrams: In my mind, it can have two new properties: "rate by distance" and "minimal distance delta", then if the distance from previous global position and current global position is higher than minimal distance delta, then creates new particles between this gap.

In a custom particles shader, I can't just create new particles because there's pre-defined amount of particles that restarts to "create new ones".

So in pseudocode we could have (thinking in a particles shader):

uniform float min_distance_delta;
uniform int interpolation_emission_step;
uniform int rate_by_distance;

void vertex() {
    vec3 previous_position; //some kind of previous particle system global position read
    vec3 current_position; //some kind of current particle system global position read
    float distance = distance(current_position, previous_position);
    if (distance > min_distance_delta) {
        //calculates every position using interpolation_emission_step
        //and creates new particles in each position
    }
}

I don't know if with current particles system shader API, it's possible to create options to read previous and current position and create new particles besides the already alocated ones.

If this enhancement will not be used often, can it be worked around with a few lines of script?: I think that it can't be made with gdscript or C# for performance reasons, as the only way is to instantiate new particles systems

Is there a reason why this should be core and not an add-on in the asset library?: Because there are many projects that have high-speed gameplay and uses particles with global coords (a racing game where has tires smokes in drifting, for example), so it would be nice to be a main feature

reduz commented 3 years ago

Manual particle emission in 4.0 should allow you to solve this. You can just set the maximum amount of particles processed, then do your own emitter logic.

Calinou commented 2 years ago

In 4.0.alpha, it looks like you can implement GPUParticles manual emission in a script, rather than in a shader. Shaders can only emit subparticles, but scripts can emit standard particles. However, CPUParticles still lacks a way to perform manual particle emission.

Here's an example with emission by distance (try moving the GPUParticles3D node in the editor): test_particle_manual_emission.zip

Edit: Updated example for Godot 4.2+ (tested on 4.3.beta1): test_particle_emission_over_distance.zip

However, custom transform origins seem to be ignored, so I can't actually create a "line" between the old position and new position when the particle moves too fast in a given frame (to create particles on this "line"). Edit: Fixed thanks to https://github.com/godotengine/godot-proposals/issues/780#issuecomment-1542435246 :slightly_smiling_face:

Note that calling emit_particle() will set emitting to false, as you're expected to handle all the emission yourself.

Wiggan commented 1 year ago

@Calinou Hi, I tested your zip-file. I found a way to make it work when changing the last parameter, the int flags to1. That makes it respect the transform sent in. Now i get a line! Cool stuff, thank you for the example!

Xorblax commented 1 month ago

Any plans on this still being implemented? While it can be done through script, you lose a lot of the functionality of the particle shader whic forces you to script that yourself. It's also a very common thing youd want a particle system to do I think, and would be more performant if it were part of the particle system, since otherwise youre doing a lot of interpolation loops in gdscript for what may be a large number of particle systems.

Calinou commented 1 month ago

Any plans on this still being implemented?

It should be possible to implement this in core by porting what the project linked in https://github.com/godotengine/godot-proposals/issues/780#issuecomment-1157789050 does in C++. Note that it has a few caveats, such as requiring the Emitting property to be false (otherwise, the manually emitted particles will be overwritten by the automatically emitted ones).

Also, since particle emission occurs manually in C++ in this case, it won't be able to use particle emission shapes from ParticleProcessMaterial or ShaderMaterial. Instead, new properties will be needed to define the random spread for particle emission by distance (e.g. an enum to choose between point, box, sphere and sphere surface, and a Vector3 to choose the spread on each axis).

Edit: Here's a WIP C++ implementation: https://github.com/Calinou/godot/tree/particles-add-emit-over-distance

https://github.com/godotengine/godot-proposals/assets/180032/4f9d1a72-1ce6-44eb-876d-0f8069662979