You've already forked godot
mirror of
https://github.com/godotengine/godot.git
synced 2025-11-11 13:10:58 +00:00
Implement manual particle emission and particle sub emitters.
This commit is contained in:
@@ -66,6 +66,38 @@ layout(set = 1, binding = 1, std430) restrict buffer Particles {
|
||||
}
|
||||
particles;
|
||||
|
||||
#define EMISSION_FLAG_HAS_POSITION 1
|
||||
#define EMISSION_FLAG_HAS_ROTATION_SCALE 2
|
||||
#define EMISSION_FLAG_HAS_VELOCITY 4
|
||||
#define EMISSION_FLAG_HAS_COLOR 8
|
||||
#define EMISSION_FLAG_HAS_CUSTOM 16
|
||||
|
||||
struct ParticleEmission {
|
||||
mat4 xform;
|
||||
vec3 velocity;
|
||||
uint flags;
|
||||
vec4 color;
|
||||
vec4 custom;
|
||||
};
|
||||
|
||||
layout(set = 1, binding = 2, std430) restrict volatile coherent buffer SourceEmission {
|
||||
int particle_count;
|
||||
uint pad0;
|
||||
uint pad1;
|
||||
uint pad2;
|
||||
ParticleEmission data[];
|
||||
}
|
||||
src_particles;
|
||||
|
||||
layout(set = 1, binding = 3, std430) restrict volatile coherent buffer DestEmission {
|
||||
int particle_count;
|
||||
int particle_max;
|
||||
uint pad1;
|
||||
uint pad2;
|
||||
ParticleEmission data[];
|
||||
}
|
||||
dst_particles;
|
||||
|
||||
/* SET 2: MATERIAL */
|
||||
|
||||
#ifdef USE_MATERIAL_UNIFORMS
|
||||
@@ -82,7 +114,9 @@ layout(push_constant, binding = 0, std430) uniform Params {
|
||||
uint total_particles;
|
||||
uint trail_size;
|
||||
bool use_fractional_delta;
|
||||
uint pad[3];
|
||||
bool sub_emitter_mode;
|
||||
bool can_emit;
|
||||
uint pad;
|
||||
}
|
||||
params;
|
||||
|
||||
@@ -93,6 +127,51 @@ uint hash(uint x) {
|
||||
return x;
|
||||
}
|
||||
|
||||
bool emit_particle(mat4 p_xform, vec3 p_velocity, vec4 p_color, vec4 p_custom, uint p_flags) {
|
||||
if (!params.can_emit) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool valid = false;
|
||||
|
||||
int dst_index = atomicAdd(dst_particles.particle_count, 1);
|
||||
|
||||
if (dst_index >= dst_particles.particle_max) {
|
||||
atomicAdd(dst_particles.particle_count, -1);
|
||||
return false;
|
||||
}
|
||||
/*
|
||||
valid = true;
|
||||
|
||||
int attempts = 256; // never trust compute
|
||||
while(attempts-- > 0) {
|
||||
dst_index = dst_particles.particle_count;
|
||||
if (dst_index == dst_particles.particle_max) {
|
||||
return false; //cant emit anymore
|
||||
}
|
||||
|
||||
if (atomicCompSwap(dst_particles.particle_count, dst_index, dst_index +1 ) != dst_index) {
|
||||
continue;
|
||||
}
|
||||
valid=true;
|
||||
break;
|
||||
}
|
||||
|
||||
barrier();
|
||||
|
||||
if (!valid) {
|
||||
return false; //gave up (attempts exhausted)
|
||||
}
|
||||
*/
|
||||
dst_particles.data[dst_index].xform = p_xform;
|
||||
dst_particles.data[dst_index].velocity = p_velocity;
|
||||
dst_particles.data[dst_index].color = p_color;
|
||||
dst_particles.data[dst_index].custom = p_custom;
|
||||
dst_particles.data[dst_index].flags = p_flags;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* clang-format off */
|
||||
|
||||
COMPUTE_SHADER_GLOBALS
|
||||
@@ -118,71 +197,19 @@ void main() {
|
||||
|
||||
float mass = 1.0;
|
||||
|
||||
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);
|
||||
|
||||
bool restart = false;
|
||||
|
||||
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
|
||||
bool restart_position = false;
|
||||
bool restart_rotation_scale = false;
|
||||
bool restart_velocity = false;
|
||||
bool restart_color = false;
|
||||
bool restart_custom = false;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_KEEP_DATA
|
||||
if (params.clear) {
|
||||
#else
|
||||
if (params.clear || restart) {
|
||||
#endif
|
||||
PARTICLE.color = vec4(1.0);
|
||||
PARTICLE.custom = vec4(0.0);
|
||||
PARTICLE.velocity = vec3(0.0);
|
||||
if (!restart) {
|
||||
PARTICLE.is_active = false;
|
||||
}
|
||||
PARTICLE.is_active = false;
|
||||
PARTICLE.xform = mat4(
|
||||
vec4(1.0, 0.0, 0.0, 0.0),
|
||||
vec4(0.0, 1.0, 0.0, 0.0),
|
||||
@@ -190,6 +217,111 @@ void main() {
|
||||
vec4(0.0, 0.0, 0.0, 1.0));
|
||||
}
|
||||
|
||||
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) {
|
||||
/* clang-format off */
|
||||
|
||||
|
||||
Reference in New Issue
Block a user