Skip to content

Commit

Permalink
Add infinite lifetime for Particles
Browse files Browse the repository at this point in the history
This is useful in custom shaders to render permanent elements
such as grass.
  • Loading branch information
Calinou committed Jun 27, 2023
1 parent 29eeb46 commit 23381dc
Show file tree
Hide file tree
Showing 15 changed files with 77 additions and 11 deletions.
6 changes: 5 additions & 1 deletion doc/classes/Particles.xml
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,11 @@
If [code]true[/code], results in fractional delta calculation which has a smoother particles display effect.
</member>
<member name="lifetime" type="float" setter="set_lifetime" getter="get_lifetime" default="1.0">
The amount of time each particle will exist (in seconds).
The amount of time each particle will exist (in seconds). See also [member lifetime_infinite].
</member>
<member name="lifetime_infinite" type="bool" setter="set_lifetime_infinite" getter="is_lifetime_infinite" default="false">
If [code]true[/code], particles will be emitted once and will never despawn. Particles can still be manually restarted, either by changing [member amount] or calling [method restart].
[member lifetime_infinite] is mainly useful for custom particle shaders (such as grass rendering). [member lifetime] is ignored when [member lifetime_infinite] is [code]true[/code].
</member>
<member name="local_coords" type="bool" setter="set_use_local_coordinates" getter="get_use_local_coordinates" default="true">
If [code]true[/code], particles use the parent node's coordinate space. If [code]false[/code], they use global coordinates.
Expand Down
7 changes: 7 additions & 0 deletions doc/classes/VisualServer.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2395,6 +2395,13 @@
Sets the lifetime of each particle in the system. Equivalent to [member Particles.lifetime].
</description>
</method>
<method name="particles_set_lifetime_infinite">
<return type="void" />
<argument index="0" name="particles" type="RID" />
<argument index="1" name="lifetime" type="bool" />
<description>
</description>
</method>
<method name="particles_set_one_shot">
<return type="void" />
<argument index="0" name="particles" type="RID" />
Expand Down
1 change: 1 addition & 0 deletions drivers/dummy/rasterizer_dummy.h
Original file line number Diff line number Diff line change
Expand Up @@ -676,6 +676,7 @@ class RasterizerStorageDummy : public RasterizerStorage {
void particles_set_emitting(RID p_particles, bool p_emitting) {}
void particles_set_amount(RID p_particles, int p_amount) {}
void particles_set_lifetime(RID p_particles, float p_lifetime) {}
void particles_set_lifetime_infinite(RID p_particles, bool p_lifetime_infinite) {}
void particles_set_one_shot(RID p_particles, bool p_one_shot) {}
void particles_set_pre_process_time(RID p_particles, float p_time) {}
void particles_set_explosiveness_ratio(RID p_particles, float p_ratio) {}
Expand Down
3 changes: 3 additions & 0 deletions drivers/gles2/rasterizer_storage_gles2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4840,6 +4840,9 @@ void RasterizerStorageGLES2::particles_set_amount(RID p_particles, int p_amount)
void RasterizerStorageGLES2::particles_set_lifetime(RID p_particles, float p_lifetime) {
}

void RasterizerStorageGLES2::particles_set_lifetime_infinite(RID p_particles, bool p_lifetime_infinite) {
}

void RasterizerStorageGLES2::particles_set_one_shot(RID p_particles, bool p_one_shot) {
}

Expand Down
1 change: 1 addition & 0 deletions drivers/gles2/rasterizer_storage_gles2.h
Original file line number Diff line number Diff line change
Expand Up @@ -1141,6 +1141,7 @@ class RasterizerStorageGLES2 : public RasterizerStorage {

virtual void particles_set_amount(RID p_particles, int p_amount);
virtual void particles_set_lifetime(RID p_particles, float p_lifetime);
virtual void particles_set_lifetime_infinite(RID p_particles, bool p_lifetime_infinite);
virtual void particles_set_one_shot(RID p_particles, bool p_one_shot);
virtual void particles_set_pre_process_time(RID p_particles, float p_time);
virtual void particles_set_explosiveness_ratio(RID p_particles, float p_ratio);
Expand Down
24 changes: 20 additions & 4 deletions drivers/gles3/rasterizer_storage_gles3.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6261,6 +6261,12 @@ void RasterizerStorageGLES3::particles_set_lifetime(RID p_particles, float p_lif
particles->lifetime = p_lifetime;
}

void RasterizerStorageGLES3::particles_set_lifetime_infinite(RID p_particles, bool p_lifetime_infinite) {
Particles *particles = particles_owner.getornull(p_particles);
ERR_FAIL_COND(!particles);
particles->lifetime_infinite = p_lifetime_infinite;
}

void RasterizerStorageGLES3::particles_set_one_shot(RID p_particles, bool p_one_shot) {
Particles *particles = particles_owner.getornull(p_particles);
ERR_FAIL_COND(!particles);
Expand Down Expand Up @@ -6488,12 +6494,17 @@ RID RasterizerStorageGLES3::particles_get_draw_pass_mesh(RID p_particles, int p_
}

void RasterizerStorageGLES3::_particles_process(Particles *p_particles, float p_delta) {
float new_phase = Math::fmod((float)p_particles->phase + (p_delta / p_particles->lifetime) * p_particles->speed_scale, (float)1.0);
float new_phase;
if (p_particles->lifetime_infinite) {
new_phase = 0.5;
} else {
new_phase = Math::fmod((float)p_particles->phase + (p_delta / p_particles->lifetime) * p_particles->speed_scale, (float)1.0);
}

if (p_particles->clear) {
p_particles->cycle_number = 0;
p_particles->random_seed = Math::rand();
} else if (new_phase < p_particles->phase) {
} else if (!p_particles->lifetime_infinite && new_phase < p_particles->phase) {
if (p_particles->one_shot) {
p_particles->emitting = false;
shaders.particles.set_uniform(ParticlesShaderGLES3::EMITTING, false);
Expand All @@ -6505,7 +6516,11 @@ void RasterizerStorageGLES3::_particles_process(Particles *p_particles, float p_
shaders.particles.set_uniform(ParticlesShaderGLES3::PREV_SYSTEM_PHASE, p_particles->phase);
p_particles->phase = new_phase;

shaders.particles.set_uniform(ParticlesShaderGLES3::DELTA, p_delta * p_particles->speed_scale);
if (p_particles->lifetime_infinite) {
shaders.particles.set_uniform(ParticlesShaderGLES3::DELTA, 0.0);
} else {
shaders.particles.set_uniform(ParticlesShaderGLES3::DELTA, p_delta * p_particles->speed_scale);
}
shaders.particles.set_uniform(ParticlesShaderGLES3::CLEAR, p_particles->clear);
glUniform1ui(shaders.particles.get_uniform_location(ParticlesShaderGLES3::RANDOM_SEED), p_particles->random_seed);

Expand Down Expand Up @@ -6594,7 +6609,7 @@ void RasterizerStorageGLES3::update_particles() {
particles->inactive_time = 0;
} else {
particles->inactive_time += particles->speed_scale * frame.delta;
if (particles->inactive_time > particles->lifetime * 1.2) {
if (!particles->lifetime_infinite && particles->inactive_time > particles->lifetime * 1.2) {
particles->inactive = true;
particle_update_list.remove(particle_update_list.first());
continue;
Expand Down Expand Up @@ -6663,6 +6678,7 @@ void RasterizerStorageGLES3::update_particles() {
shaders.particles.set_uniform(ParticlesShaderGLES3::TIME, frame.time[0]);
shaders.particles.set_uniform(ParticlesShaderGLES3::EXPLOSIVENESS, particles->explosiveness);
shaders.particles.set_uniform(ParticlesShaderGLES3::LIFETIME, particles->lifetime);
shaders.particles.set_uniform(ParticlesShaderGLES3::LIFETIME_INFINITE, particles->lifetime_infinite);
shaders.particles.set_uniform(ParticlesShaderGLES3::ATTRACTOR_COUNT, 0);
shaders.particles.set_uniform(ParticlesShaderGLES3::EMITTING, particles->emitting);
shaders.particles.set_uniform(ParticlesShaderGLES3::RANDOMNESS, particles->randomness);
Expand Down
3 changes: 3 additions & 0 deletions drivers/gles3/rasterizer_storage_gles3.h
Original file line number Diff line number Diff line change
Expand Up @@ -1188,6 +1188,7 @@ class RasterizerStorageGLES3 : public RasterizerStorage {
bool one_shot;
int amount;
float lifetime;
bool lifetime_infinite;
float pre_process_time;
float explosiveness;
float randomness;
Expand Down Expand Up @@ -1234,6 +1235,7 @@ class RasterizerStorageGLES3 : public RasterizerStorage {
one_shot(false),
amount(0),
lifetime(1.0),
lifetime_infinite(false),
pre_process_time(0.0),
explosiveness(0.0),
randomness(0.0),
Expand Down Expand Up @@ -1279,6 +1281,7 @@ class RasterizerStorageGLES3 : public RasterizerStorage {
virtual bool particles_get_emitting(RID p_particles);
virtual void particles_set_amount(RID p_particles, int p_amount);
virtual void particles_set_lifetime(RID p_particles, float p_lifetime);
virtual void particles_set_lifetime_infinite(RID p_particles, bool p_lifetime_infinite);
virtual void particles_set_one_shot(RID p_particles, bool p_one_shot);
virtual void particles_set_pre_process_time(RID p_particles, float p_time);
virtual void particles_set_explosiveness_ratio(RID p_particles, float p_ratio);
Expand Down
17 changes: 11 additions & 6 deletions drivers/gles3/shaders/particles.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ uniform Attractor attractors[MAX_ATTRACTORS];
uniform bool clear;
uniform uint cycle;
uniform float lifetime;
uniform bool lifetime_infinite;
uniform mat4 emission_transform;
uniform uint random_seed;

Expand Down Expand Up @@ -103,20 +104,26 @@ void main() {
if (restart_phase >= prev_system_phase && restart_phase < system_phase) {
restart = true;
#ifdef USE_FRACTIONAL_DELTA //ubershader-runtime
local_delta = (system_phase - restart_phase) * lifetime;
if (!lifetime_infinite) {
local_delta = (system_phase - restart_phase) * lifetime;
}
#endif //ubershader-runtime
}

} else if (delta > 0.0) {
if (restart_phase >= prev_system_phase) {
restart = true;
#ifdef USE_FRACTIONAL_DELTA //ubershader-runtime
local_delta = (1.0 - restart_phase + system_phase) * lifetime;
if (!lifetime_infinite) {
local_delta = (1.0 - restart_phase + system_phase) * lifetime;
}
#endif //ubershader-runtime
} else if (restart_phase < system_phase) {
restart = true;
#ifdef USE_FRACTIONAL_DELTA //ubershader-runtime
local_delta = (system_phase - restart_phase) * lifetime;
if (!lifetime_infinite) {
local_delta = (system_phase - restart_phase) * lifetime;
}
#endif //ubershader-runtime
}
}
Expand Down Expand Up @@ -201,9 +208,7 @@ VERTEX_SHADER_CODE

#if !defined(DISABLE_VELOCITY)

if (true) {
xform[3].xyz += out_velocity_active.xyz * local_delta;
}
xform[3].xyz += out_velocity_active.xyz * local_delta;
#endif
} else {
xform = mat4(0.0);
Expand Down
18 changes: 18 additions & 0 deletions scene/3d/particles.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,11 @@ void Particles::set_lifetime(float p_lifetime) {
lifetime = p_lifetime;
VS::get_singleton()->particles_set_lifetime(particles, lifetime);
}
void Particles::set_lifetime_infinite(bool p_lifetime_infinite) {
lifetime_infinite = p_lifetime_infinite;
VS::get_singleton()->particles_set_lifetime_infinite(particles, p_lifetime_infinite);
_change_notify();
}

void Particles::set_one_shot(bool p_one_shot) {
one_shot = p_one_shot;
Expand Down Expand Up @@ -126,6 +131,9 @@ int Particles::get_amount() const {
float Particles::get_lifetime() const {
return lifetime;
}
bool Particles::is_lifetime_infinite() const {
return lifetime_infinite;
}
bool Particles::get_one_shot() const {
return one_shot;
}
Expand Down Expand Up @@ -288,6 +296,12 @@ AABB Particles::capture_aabb() const {
}

void Particles::_validate_property(PropertyInfo &property) const {
if (property.name == "lifetime" && lifetime_infinite) {
// The lifetime property is ignored when lifetime is infinite, so hide it.
property.usage = 0;
return;
}

if (property.name.begins_with("draw_pass_")) {
int index = property.name.get_slicec('_', 2).to_int() - 1;
if (index >= draw_passes.size()) {
Expand Down Expand Up @@ -327,6 +341,7 @@ void Particles::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_emitting", "emitting"), &Particles::set_emitting);
ClassDB::bind_method(D_METHOD("set_amount", "amount"), &Particles::set_amount);
ClassDB::bind_method(D_METHOD("set_lifetime", "secs"), &Particles::set_lifetime);
ClassDB::bind_method(D_METHOD("set_lifetime_infinite", "infinite"), &Particles::set_lifetime_infinite);
ClassDB::bind_method(D_METHOD("set_one_shot", "enable"), &Particles::set_one_shot);
ClassDB::bind_method(D_METHOD("set_pre_process_time", "secs"), &Particles::set_pre_process_time);
ClassDB::bind_method(D_METHOD("set_explosiveness_ratio", "ratio"), &Particles::set_explosiveness_ratio);
Expand All @@ -341,6 +356,7 @@ void Particles::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_emitting"), &Particles::is_emitting);
ClassDB::bind_method(D_METHOD("get_amount"), &Particles::get_amount);
ClassDB::bind_method(D_METHOD("get_lifetime"), &Particles::get_lifetime);
ClassDB::bind_method(D_METHOD("is_lifetime_infinite"), &Particles::is_lifetime_infinite);
ClassDB::bind_method(D_METHOD("get_one_shot"), &Particles::get_one_shot);
ClassDB::bind_method(D_METHOD("get_pre_process_time"), &Particles::get_pre_process_time);
ClassDB::bind_method(D_METHOD("get_explosiveness_ratio"), &Particles::get_explosiveness_ratio);
Expand Down Expand Up @@ -369,6 +385,7 @@ void Particles::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "amount", PROPERTY_HINT_EXP_RANGE, "1,1000000,1"), "set_amount", "get_amount");
ADD_GROUP("Time", "");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "lifetime", PROPERTY_HINT_EXP_RANGE, "0.01,600.0,0.01,or_greater"), "set_lifetime", "get_lifetime");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "lifetime_infinite"), "set_lifetime_infinite", "is_lifetime_infinite");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "one_shot"), "set_one_shot", "get_one_shot");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "preprocess", PROPERTY_HINT_EXP_RANGE, "0.00,600.0,0.01"), "set_pre_process_time", "get_pre_process_time");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "speed_scale", PROPERTY_HINT_RANGE, "0,64,0.01"), "set_speed_scale", "get_speed_scale");
Expand Down Expand Up @@ -403,6 +420,7 @@ Particles::Particles() {
set_one_shot(false);
set_amount(8);
set_lifetime(1);
set_lifetime_infinite(false);
set_fixed_fps(0);
set_fractional_delta(true);
set_pre_process_time(0);
Expand Down
3 changes: 3 additions & 0 deletions scene/3d/particles.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ class Particles : public GeometryInstance {
bool one_shot;
int amount;
float lifetime;
bool lifetime_infinite;
float pre_process_time;
float explosiveness_ratio;
float randomness_ratio;
Expand Down Expand Up @@ -83,6 +84,7 @@ class Particles : public GeometryInstance {
void set_emitting(bool p_emitting);
void set_amount(int p_amount);
void set_lifetime(float p_lifetime);
void set_lifetime_infinite(bool p_lifetime_infinite);
void set_one_shot(bool p_one_shot);
void set_pre_process_time(float p_time);
void set_explosiveness_ratio(float p_ratio);
Expand All @@ -95,6 +97,7 @@ class Particles : public GeometryInstance {
bool is_emitting() const;
int get_amount() const;
float get_lifetime() const;
bool is_lifetime_infinite() const;
bool get_one_shot() const;
float get_pre_process_time() const;
float get_explosiveness_ratio() const;
Expand Down
1 change: 1 addition & 0 deletions servers/visual/rasterizer.h
Original file line number Diff line number Diff line change
Expand Up @@ -608,6 +608,7 @@ class RasterizerStorage {

virtual void particles_set_amount(RID p_particles, int p_amount) = 0;
virtual void particles_set_lifetime(RID p_particles, float p_lifetime) = 0;
virtual void particles_set_lifetime_infinite(RID p_particles, bool p_lifetime_infinite) = 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, float p_time) = 0;
virtual void particles_set_explosiveness_ratio(RID p_particles, float p_ratio) = 0;
Expand Down
1 change: 1 addition & 0 deletions servers/visual/visual_server_raster.h
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,7 @@ class VisualServerRaster : public VisualServer {
BIND1R(bool, particles_get_emitting, RID)
BIND2(particles_set_amount, RID, int)
BIND2(particles_set_lifetime, RID, float)
BIND2(particles_set_lifetime_infinite, RID, bool)
BIND2(particles_set_one_shot, RID, bool)
BIND2(particles_set_pre_process_time, RID, float)
BIND2(particles_set_explosiveness_ratio, RID, float)
Expand Down
1 change: 1 addition & 0 deletions servers/visual/visual_server_wrap_mt.h
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,7 @@ class VisualServerWrapMT : public VisualServer {
FUNC1R(bool, particles_get_emitting, RID)
FUNC2(particles_set_amount, RID, int)
FUNC2(particles_set_lifetime, RID, float)
FUNC2(particles_set_lifetime_infinite, RID, bool)
FUNC2(particles_set_one_shot, RID, bool)
FUNC2(particles_set_pre_process_time, RID, float)
FUNC2(particles_set_explosiveness_ratio, RID, float)
Expand Down
1 change: 1 addition & 0 deletions servers/visual_server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2040,6 +2040,7 @@ void VisualServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("particles_get_emitting", "particles"), &VisualServer::particles_get_emitting);
ClassDB::bind_method(D_METHOD("particles_set_amount", "particles", "amount"), &VisualServer::particles_set_amount);
ClassDB::bind_method(D_METHOD("particles_set_lifetime", "particles", "lifetime"), &VisualServer::particles_set_lifetime);
ClassDB::bind_method(D_METHOD("particles_set_lifetime_infinite", "particles", "lifetime"), &VisualServer::particles_set_lifetime_infinite);
ClassDB::bind_method(D_METHOD("particles_set_one_shot", "particles", "one_shot"), &VisualServer::particles_set_one_shot);
ClassDB::bind_method(D_METHOD("particles_set_pre_process_time", "particles", "time"), &VisualServer::particles_set_pre_process_time);
ClassDB::bind_method(D_METHOD("particles_set_explosiveness_ratio", "particles", "ratio"), &VisualServer::particles_set_explosiveness_ratio);
Expand Down
1 change: 1 addition & 0 deletions servers/visual_server.h
Original file line number Diff line number Diff line change
Expand Up @@ -592,6 +592,7 @@ class VisualServer : public Object {
virtual bool particles_get_emitting(RID p_particles) = 0;
virtual void particles_set_amount(RID p_particles, int p_amount) = 0;
virtual void particles_set_lifetime(RID p_particles, float p_lifetime) = 0;
virtual void particles_set_lifetime_infinite(RID p_particles, bool p_lifetime_infinite) = 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, float p_time) = 0;
virtual void particles_set_explosiveness_ratio(RID p_particles, float p_ratio) = 0;
Expand Down

0 comments on commit 23381dc

Please sign in to comment.