Split particle shader entry points

* Particle shaders now have start() and process()
* Particle collision happens between them.
* The RESTART property is kept, so porting an old shader is still possible.

This fixes the problem of particle collisions not functioning on the first particle frame.
This commit is contained in:
reduz 2021-04-17 12:21:03 -03:00
parent 0ee744ff5f
commit 906882ee66
5 changed files with 262 additions and 233 deletions

View file

@ -4781,6 +4781,7 @@ void RendererStorageRD::ParticlesShaderData::set_code(const String &p_code) {
ShaderCompilerRD::GeneratedCode gen_code;
ShaderCompilerRD::IdentifierActions actions;
actions.entry_point_stages["start"] = ShaderCompilerRD::STAGE_COMPUTE;
actions.entry_point_stages["process"] = ShaderCompilerRD::STAGE_COMPUTE;
/*

View file

@ -233,6 +233,7 @@ void ShaderRD::_compile_variant(uint32_t p_variant, Version *p_version) {
_build_variant_code(builder, p_variant, p_version, stage_templates[STAGE_TYPE_COMPUTE]);
current_source = builder.as_string();
RD::ShaderStageData stage;
stage.spir_v = RD::get_singleton()->shader_compile_from_source(RD::SHADER_STAGE_COMPUTE, current_source, RD::SHADER_LANGUAGE_GLSL, &error);
if (stage.spir_v.size() == 0) {

View file

@ -252,6 +252,115 @@ void main() {
/* Process physics if active */
if (params.sub_emitter_mode) {
if (!PARTICLE.is_active) {
int src_index = atomicAdd(src_particles.particle_count, -1) - 1;
if (src_index >= 0) {
PARTICLE.is_active = true;
restart = true;
if (bool(src_particles.data[src_index].flags & EMISSION_FLAG_HAS_POSITION)) {
PARTICLE.xform[3] = src_particles.data[src_index].xform[3];
} else {
PARTICLE.xform[3] = vec4(0, 0, 0, 1);
restart_position = true;
}
if (bool(src_particles.data[src_index].flags & EMISSION_FLAG_HAS_ROTATION_SCALE)) {
PARTICLE.xform[0] = src_particles.data[src_index].xform[0];
PARTICLE.xform[1] = src_particles.data[src_index].xform[1];
PARTICLE.xform[2] = src_particles.data[src_index].xform[2];
} else {
PARTICLE.xform[0] = vec4(1, 0, 0, 0);
PARTICLE.xform[1] = vec4(0, 1, 0, 0);
PARTICLE.xform[2] = vec4(0, 0, 1, 0);
restart_rotation_scale = true;
}
if (bool(src_particles.data[src_index].flags & EMISSION_FLAG_HAS_VELOCITY)) {
PARTICLE.velocity = src_particles.data[src_index].velocity;
} else {
PARTICLE.velocity = vec3(0);
restart_velocity = true;
}
if (bool(src_particles.data[src_index].flags & EMISSION_FLAG_HAS_COLOR)) {
PARTICLE.color = src_particles.data[src_index].color;
} else {
PARTICLE.color = vec4(1);
restart_color = true;
}
if (bool(src_particles.data[src_index].flags & EMISSION_FLAG_HAS_CUSTOM)) {
PARTICLE.custom = src_particles.data[src_index].custom;
} else {
PARTICLE.custom = vec4(0);
restart_custom = true;
}
}
}
} else if (FRAME.emitting) {
float restart_phase = float(index) / float(params.total_particles);
if (FRAME.randomness > 0.0) {
uint seed = FRAME.cycle;
if (restart_phase >= FRAME.system_phase) {
seed -= uint(1);
}
seed *= uint(params.total_particles);
seed += uint(index);
float random = float(hash(seed) % uint(65536)) / 65536.0;
restart_phase += FRAME.randomness * random * 1.0 / float(params.total_particles);
}
restart_phase *= (1.0 - FRAME.explosiveness);
if (FRAME.system_phase > FRAME.prev_system_phase) {
// restart_phase >= prev_system_phase is used so particles emit in the first frame they are processed
if (restart_phase >= FRAME.prev_system_phase && restart_phase < FRAME.system_phase) {
restart = true;
if (params.use_fractional_delta) {
local_delta = (FRAME.system_phase - restart_phase) * params.lifetime;
}
}
} else if (FRAME.delta > 0.0) {
if (restart_phase >= FRAME.prev_system_phase) {
restart = true;
if (params.use_fractional_delta) {
local_delta = (1.0 - restart_phase + FRAME.system_phase) * params.lifetime;
}
} else if (restart_phase < FRAME.system_phase) {
restart = true;
if (params.use_fractional_delta) {
local_delta = (FRAME.system_phase - restart_phase) * params.lifetime;
}
}
}
uint current_cycle = FRAME.cycle;
if (FRAME.system_phase < restart_phase) {
current_cycle -= uint(1);
}
uint particle_number = current_cycle * uint(params.total_particles) + particle;
if (restart) {
PARTICLE.is_active = FRAME.emitting;
restart_position = true;
restart_rotation_scale = true;
restart_velocity = true;
restart_color = true;
restart_custom = true;
}
}
if (restart && PARTICLE.is_active) {
#CODE : START
}
if (PARTICLE.is_active) {
for (uint i = 0; i < FRAME.attractor_count; i++) {
vec3 dir;
@ -430,111 +539,6 @@ void main() {
}
}
if (params.sub_emitter_mode) {
if (!PARTICLE.is_active) {
int src_index = atomicAdd(src_particles.particle_count, -1) - 1;
if (src_index >= 0) {
PARTICLE.is_active = true;
restart = true;
if (bool(src_particles.data[src_index].flags & EMISSION_FLAG_HAS_POSITION)) {
PARTICLE.xform[3] = src_particles.data[src_index].xform[3];
} else {
PARTICLE.xform[3] = vec4(0, 0, 0, 1);
restart_position = true;
}
if (bool(src_particles.data[src_index].flags & EMISSION_FLAG_HAS_ROTATION_SCALE)) {
PARTICLE.xform[0] = src_particles.data[src_index].xform[0];
PARTICLE.xform[1] = src_particles.data[src_index].xform[1];
PARTICLE.xform[2] = src_particles.data[src_index].xform[2];
} else {
PARTICLE.xform[0] = vec4(1, 0, 0, 0);
PARTICLE.xform[1] = vec4(0, 1, 0, 0);
PARTICLE.xform[2] = vec4(0, 0, 1, 0);
restart_rotation_scale = true;
}
if (bool(src_particles.data[src_index].flags & EMISSION_FLAG_HAS_VELOCITY)) {
PARTICLE.velocity = src_particles.data[src_index].velocity;
} else {
PARTICLE.velocity = vec3(0);
restart_velocity = true;
}
if (bool(src_particles.data[src_index].flags & EMISSION_FLAG_HAS_COLOR)) {
PARTICLE.color = src_particles.data[src_index].color;
} else {
PARTICLE.color = vec4(1);
restart_color = true;
}
if (bool(src_particles.data[src_index].flags & EMISSION_FLAG_HAS_CUSTOM)) {
PARTICLE.custom = src_particles.data[src_index].custom;
} else {
PARTICLE.custom = vec4(0);
restart_custom = true;
}
}
}
} else if (FRAME.emitting) {
float restart_phase = float(index) / float(params.total_particles);
if (FRAME.randomness > 0.0) {
uint seed = FRAME.cycle;
if (restart_phase >= FRAME.system_phase) {
seed -= uint(1);
}
seed *= uint(params.total_particles);
seed += uint(index);
float random = float(hash(seed) % uint(65536)) / 65536.0;
restart_phase += FRAME.randomness * random * 1.0 / float(params.total_particles);
}
restart_phase *= (1.0 - FRAME.explosiveness);
if (FRAME.system_phase > FRAME.prev_system_phase) {
// restart_phase >= prev_system_phase is used so particles emit in the first frame they are processed
if (restart_phase >= FRAME.prev_system_phase && restart_phase < FRAME.system_phase) {
restart = true;
if (params.use_fractional_delta) {
local_delta = (FRAME.system_phase - restart_phase) * params.lifetime;
}
}
} else if (FRAME.delta > 0.0) {
if (restart_phase >= FRAME.prev_system_phase) {
restart = true;
if (params.use_fractional_delta) {
local_delta = (1.0 - restart_phase + FRAME.system_phase) * params.lifetime;
}
} else if (restart_phase < FRAME.system_phase) {
restart = true;
if (params.use_fractional_delta) {
local_delta = (FRAME.system_phase - restart_phase) * params.lifetime;
}
}
}
uint current_cycle = FRAME.cycle;
if (FRAME.system_phase < restart_phase) {
current_cycle -= uint(1);
}
uint particle_number = current_cycle * uint(params.total_particles) + particle;
if (restart) {
PARTICLE.is_active = FRAME.emitting;
restart_position = true;
restart_rotation_scale = true;
restart_velocity = true;
restart_color = true;
restart_custom = true;
}
}
if (PARTICLE.is_active) {
#CODE : PROCESS
}