1
0
mirror of https://github.com/godotengine/godot.git synced 2025-11-09 12:50:35 +00:00

Implement manual particle emission and particle sub emitters.

This commit is contained in:
reduz
2020-09-06 09:18:10 -03:00
parent ceed524936
commit d0bddf53c5
14 changed files with 871 additions and 317 deletions

View File

@@ -3119,52 +3119,47 @@ bool RasterizerStorageRD::particles_get_emitting(RID p_particles) {
return particles->emitting;
}
void RasterizerStorageRD::_particles_free_data(Particles *particles) {
if (!particles->particle_buffer.is_valid()) {
return;
}
RD::get_singleton()->free(particles->particle_buffer);
RD::get_singleton()->free(particles->frame_params_buffer);
RD::get_singleton()->free(particles->particle_instance_buffer);
particles->particles_transforms_buffer_uniform_set = RID();
particles->particle_buffer = RID();
if (particles->particles_sort_buffer.is_valid()) {
RD::get_singleton()->free(particles->particles_sort_buffer);
particles->particles_sort_buffer = RID();
}
if (particles->emission_buffer != nullptr) {
particles->emission_buffer = nullptr;
particles->emission_buffer_data.clear();
RD::get_singleton()->free(particles->emission_storage_buffer);
particles->emission_storage_buffer = RID();
}
}
void RasterizerStorageRD::particles_set_amount(RID p_particles, int p_amount) {
Particles *particles = particles_owner.getornull(p_particles);
ERR_FAIL_COND(!particles);
particles->amount = p_amount;
if (particles->particle_buffer.is_valid()) {
RD::get_singleton()->free(particles->particle_buffer);
RD::get_singleton()->free(particles->frame_params_buffer);
RD::get_singleton()->free(particles->particle_instance_buffer);
particles->particles_transforms_buffer_uniform_set = RID();
particles->particle_buffer = RID();
if (particles->particles_sort_buffer.is_valid()) {
RD::get_singleton()->free(particles->particles_sort_buffer);
particles->particles_sort_buffer = RID();
}
if (particles->amount == p_amount) {
return;
}
_particles_free_data(particles);
particles->amount = p_amount;
if (particles->amount > 0) {
particles->particle_buffer = RD::get_singleton()->storage_buffer_create(sizeof(ParticleData) * p_amount);
particles->frame_params_buffer = RD::get_singleton()->storage_buffer_create(sizeof(ParticlesFrameParams) * 1);
particles->particle_instance_buffer = RD::get_singleton()->storage_buffer_create(sizeof(float) * 4 * (3 + 1 + 1) * p_amount);
//needs to clear it
{
Vector<RD::Uniform> uniforms;
{
RD::Uniform u;
u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
u.binding = 0;
u.ids.push_back(particles->frame_params_buffer);
uniforms.push_back(u);
}
{
RD::Uniform u;
u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
u.binding = 1;
u.ids.push_back(particles->particle_buffer);
uniforms.push_back(u);
}
particles->particles_material_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, particles_shader.default_shader_rd, 1);
}
{
Vector<RD::Uniform> uniforms;
@@ -3290,6 +3285,79 @@ void RasterizerStorageRD::particles_restart(RID p_particles) {
particles->restart_request = true;
}
void RasterizerStorageRD::_particles_allocate_emission_buffer(Particles *particles) {
ERR_FAIL_COND(particles->emission_buffer != nullptr);
particles->emission_buffer_data.resize(sizeof(ParticleEmissionBuffer::Data) * particles->amount + sizeof(uint32_t) * 4);
zeromem(particles->emission_buffer_data.ptrw(), particles->emission_buffer_data.size());
particles->emission_buffer = (ParticleEmissionBuffer *)particles->emission_buffer_data.ptrw();
particles->emission_buffer->particle_max = particles->amount;
particles->emission_storage_buffer = RD::get_singleton()->storage_buffer_create(particles->emission_buffer_data.size(), particles->emission_buffer_data);
if (RD::get_singleton()->uniform_set_is_valid(particles->particles_material_uniform_set)) {
//will need to be re-created
RD::get_singleton()->free(particles->particles_material_uniform_set);
particles->particles_material_uniform_set = RID();
}
}
void RasterizerStorageRD::particles_set_subemitter(RID p_particles, RID p_subemitter_particles) {
Particles *particles = particles_owner.getornull(p_particles);
ERR_FAIL_COND(!particles);
ERR_FAIL_COND(p_particles == p_subemitter_particles);
particles->sub_emitter = p_subemitter_particles;
if (RD::get_singleton()->uniform_set_is_valid(particles->particles_material_uniform_set)) {
RD::get_singleton()->free(particles->particles_material_uniform_set);
particles->particles_material_uniform_set = RID(); //clear and force to re create sub emitting
}
}
void RasterizerStorageRD::particles_emit(RID p_particles, const Transform &p_transform, const Vector3 &p_velocity, const Color &p_color, const Color &p_custom, uint32_t p_emit_flags) {
Particles *particles = particles_owner.getornull(p_particles);
ERR_FAIL_COND(!particles);
ERR_FAIL_COND(particles->amount == 0);
if (particles->emitting) {
particles->clear = true;
particles->emitting = false;
}
if (particles->emission_buffer == nullptr) {
_particles_allocate_emission_buffer(particles);
}
if (particles->inactive) {
//in case it was inactive, make active again
particles->inactive = false;
particles->inactive_time = 0;
}
int32_t idx = particles->emission_buffer->particle_count;
if (idx < particles->emission_buffer->particle_max) {
store_transform(p_transform, particles->emission_buffer->data[idx].xform);
particles->emission_buffer->data[idx].velocity[0] = p_velocity.x;
particles->emission_buffer->data[idx].velocity[1] = p_velocity.y;
particles->emission_buffer->data[idx].velocity[2] = p_velocity.z;
particles->emission_buffer->data[idx].custom[0] = p_custom.r;
particles->emission_buffer->data[idx].custom[1] = p_custom.g;
particles->emission_buffer->data[idx].custom[2] = p_custom.b;
particles->emission_buffer->data[idx].custom[3] = p_custom.a;
particles->emission_buffer->data[idx].color[0] = p_color.r;
particles->emission_buffer->data[idx].color[1] = p_color.g;
particles->emission_buffer->data[idx].color[2] = p_color.b;
particles->emission_buffer->data[idx].color[3] = p_color.a;
particles->emission_buffer->data[idx].flags = p_emit_flags;
particles->emission_buffer->particle_count++;
}
}
void RasterizerStorageRD::particles_request_process(RID p_particles) {
Particles *particles = particles_owner.getornull(p_particles);
ERR_FAIL_COND(!particles);
@@ -3375,6 +3443,54 @@ RID RasterizerStorageRD::particles_get_draw_pass_mesh(RID p_particles, int p_pas
}
void RasterizerStorageRD::_particles_process(Particles *p_particles, float p_delta) {
if (p_particles->particles_material_uniform_set.is_null() || !RD::get_singleton()->uniform_set_is_valid(p_particles->particles_material_uniform_set)) {
Vector<RD::Uniform> uniforms;
{
RD::Uniform u;
u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
u.binding = 0;
u.ids.push_back(p_particles->frame_params_buffer);
uniforms.push_back(u);
}
{
RD::Uniform u;
u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
u.binding = 1;
u.ids.push_back(p_particles->particle_buffer);
uniforms.push_back(u);
}
{
RD::Uniform u;
u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
u.binding = 2;
if (p_particles->emission_storage_buffer.is_valid()) {
u.ids.push_back(p_particles->emission_storage_buffer);
} else {
u.ids.push_back(default_rd_storage_buffer);
}
uniforms.push_back(u);
}
{
RD::Uniform u;
u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
u.binding = 3;
Particles *sub_emitter = particles_owner.getornull(p_particles->sub_emitter);
if (sub_emitter) {
if (sub_emitter->emission_buffer == nullptr) { //no emission buffer, allocate emission buffer
_particles_allocate_emission_buffer(sub_emitter);
}
u.ids.push_back(sub_emitter->emission_storage_buffer);
} else {
u.ids.push_back(default_rd_storage_buffer);
}
uniforms.push_back(u);
}
p_particles->particles_material_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, particles_shader.default_shader_rd, 1);
}
float new_phase = Math::fmod((float)p_particles->phase + (p_delta / p_particles->lifetime) * p_particles->speed_scale, (float)1.0);
ParticlesFrameParams &frame_params = p_particles->frame_params;
@@ -3416,6 +3532,36 @@ void RasterizerStorageRD::_particles_process(Particles *p_particles, float p_del
push_constant.lifetime = p_particles->lifetime;
push_constant.trail_size = 1;
push_constant.use_fractional_delta = p_particles->fractional_delta;
push_constant.sub_emitter_mode = !p_particles->emitting && p_particles->emission_buffer && (p_particles->emission_buffer->particle_count > 0 || p_particles->force_sub_emit);
p_particles->force_sub_emit = false; //reset
Particles *sub_emitter = particles_owner.getornull(p_particles->sub_emitter);
if (sub_emitter && sub_emitter->emission_storage_buffer.is_valid()) {
// print_line("updating subemitter buffer");
int32_t zero[4] = { 0, sub_emitter->amount, 0, 0 };
RD::get_singleton()->buffer_update(sub_emitter->emission_storage_buffer, 0, sizeof(uint32_t) * 4, zero, true);
push_constant.can_emit = true;
if (sub_emitter->emitting) {
sub_emitter->emitting = false;
sub_emitter->clear = true; //will need to clear if it was emitting, sorry
}
//make sure the sub emitter processes particles too
sub_emitter->inactive = false;
sub_emitter->inactive_time = 0;
sub_emitter->force_sub_emit = true;
} else {
push_constant.can_emit = false;
}
if (p_particles->emission_buffer && p_particles->emission_buffer->particle_count) {
RD::get_singleton()->buffer_update(p_particles->emission_storage_buffer, 0, sizeof(uint32_t) * 4 + sizeof(ParticleEmissionBuffer::Data) * p_particles->emission_buffer->particle_count, p_particles->emission_buffer, true);
p_particles->emission_buffer->particle_count = 0;
}
p_particles->clear = false;
@@ -3617,9 +3763,6 @@ void RasterizerStorageRD::update_particles() {
RD::get_singleton()->compute_list_end();
}
particle_update_list = particles->update_list;
particles->update_list = nullptr;
particles->instance_dependency.instance_notify_changed(true, false); //make sure shadows are updated
}
}
@@ -6519,6 +6662,11 @@ bool RasterizerStorageRD::free(RID p_rid) {
light->instance_dependency.instance_notify_deleted(p_rid);
light_owner.free(p_rid);
} else if (particles_owner.owns(p_rid)) {
Particles *particles = particles_owner.getornull(p_rid);
_particles_free_data(particles);
particles->instance_dependency.instance_notify_deleted(p_rid);
particles_owner.free(p_rid);
} else if (render_target_owner.owns(p_rid)) {
RenderTarget *rt = render_target_owner.getornull(p_rid);
@@ -7013,6 +7161,17 @@ RasterizerStorageRD::RasterizerStorageRD() {
//actions.renames["GRAVITY"] = "current_gravity";
actions.renames["EMISSION_TRANSFORM"] = "FRAME.emission_transform";
actions.renames["RANDOM_SEED"] = "FRAME.random_seed";
actions.renames["FLAG_EMIT_POSITION"] = "EMISSION_FLAG_HAS_POSITION";
actions.renames["FLAG_EMIT_ROT_SCALE"] = "EMISSION_FLAG_HAS_ROTATION_SCALE";
actions.renames["FLAG_EMIT_VELOCITY"] = "EMISSION_FLAG_HAS_VELOCITY";
actions.renames["FLAG_EMIT_COLOR"] = "EMISSION_FLAG_HAS_COLOR";
actions.renames["FLAG_EMIT_CUSTOM"] = "EMISSION_FLAG_HAS_CUSTOM";
actions.renames["RESTART_POSITION"] = "restart_position";
actions.renames["RESTART_ROT_SCALE"] = "restart_rotation_scale";
actions.renames["RESTART_VELOCITY"] = "restart_velocity";
actions.renames["RESTART_COLOR"] = "restart_color";
actions.renames["RESTART_CUSTOM"] = "restart_custom";
actions.renames["emit_particle"] = "emit_particle";
actions.render_mode_defines["disable_force"] = "#define DISABLE_FORCE\n";
actions.render_mode_defines["disable_velocity"] = "#define DISABLE_VELOCITY\n";
@@ -7075,6 +7234,8 @@ RasterizerStorageRD::RasterizerStorageRD() {
particles_shader.base_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, particles_shader.default_shader_rd, 0);
}
default_rd_storage_buffer = RD::get_singleton()->storage_buffer_create(sizeof(uint32_t) * 4);
{
Vector<String> copy_modes;
copy_modes.push_back("\n#define MODE_FILL_INSTANCES\n");
@@ -7115,6 +7276,8 @@ RasterizerStorageRD::~RasterizerStorageRD() {
}
giprobe_sdf_shader.version_free(giprobe_sdf_shader_version);
RD::get_singleton()->free(default_rd_storage_buffer);
if (decal_atlas.textures.size()) {
ERR_PRINT("Decal Atlas: " + itos(decal_atlas.textures.size()) + " textures were not removed from the atlas.");
}