diff options
Diffstat (limited to 'servers/rendering')
-rw-r--r-- | servers/rendering/rasterizer.h | 4 | ||||
-rw-r--r-- | servers/rendering/rasterizer_rd/rasterizer_scene_rd.h | 2 | ||||
-rw-r--r-- | servers/rendering/rasterizer_rd/rasterizer_storage_rd.cpp | 235 | ||||
-rw-r--r-- | servers/rendering/rasterizer_rd/rasterizer_storage_rd.h | 38 | ||||
-rw-r--r-- | servers/rendering/rasterizer_rd/shaders/particles.glsl | 238 | ||||
-rw-r--r-- | servers/rendering/rendering_server_canvas.cpp | 13 | ||||
-rw-r--r-- | servers/rendering/rendering_server_raster.h | 2 | ||||
-rw-r--r-- | servers/rendering/rendering_server_wrap_mt.h | 3 | ||||
-rw-r--r-- | servers/rendering/shader_language.cpp | 232 | ||||
-rw-r--r-- | servers/rendering/shader_language.h | 31 | ||||
-rw-r--r-- | servers/rendering/shader_types.cpp | 21 |
11 files changed, 634 insertions, 185 deletions
diff --git a/servers/rendering/rasterizer.h b/servers/rendering/rasterizer.h index 41dcd387de..9cb611f3b4 100644 --- a/servers/rendering/rasterizer.h +++ b/servers/rendering/rasterizer.h @@ -88,7 +88,7 @@ public: virtual void environment_glow_set_use_bicubic_upscale(bool p_enable) = 0; virtual void environment_glow_set_use_high_quality(bool p_enable) = 0; - virtual void environment_set_volumetric_fog(RID p_env, bool p_enable, float p_density, const Color &p_light, float p_light_energy, float p_lenght, float p_detail_spread, float p_gi_inject, RS::EnvVolumetricFogShadowFilter p_shadow_filter) = 0; + virtual void environment_set_volumetric_fog(RID p_env, bool p_enable, float p_density, const Color &p_light, float p_light_energy, float p_length, float p_detail_spread, float p_gi_inject, RS::EnvVolumetricFogShadowFilter p_shadow_filter) = 0; virtual void environment_set_volumetric_fog_volume_size(int p_size, int p_depth) = 0; virtual void environment_set_volumetric_fog_filter_active(bool p_enable) = 0; @@ -661,6 +661,8 @@ public: virtual void particles_set_fixed_fps(RID p_particles, int p_fps) = 0; virtual void particles_set_fractional_delta(RID p_particles, bool p_enable) = 0; virtual void particles_restart(RID p_particles) = 0; + virtual void 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) = 0; + virtual void particles_set_subemitter(RID p_particles, RID p_subemitter_particles) = 0; virtual bool particles_is_inactive(RID p_particles) const = 0; diff --git a/servers/rendering/rasterizer_rd/rasterizer_scene_rd.h b/servers/rendering/rasterizer_rd/rasterizer_scene_rd.h index eb49233b98..fe31d2f76b 100644 --- a/servers/rendering/rasterizer_rd/rasterizer_scene_rd.h +++ b/servers/rendering/rasterizer_rd/rasterizer_scene_rd.h @@ -1542,7 +1542,7 @@ public: float environment_get_fog_height(RID p_env) const; float environment_get_fog_height_density(RID p_env) const; - void environment_set_volumetric_fog(RID p_env, bool p_enable, float p_density, const Color &p_light, float p_light_energy, float p_lenght, float p_detail_spread, float p_gi_inject, RS::EnvVolumetricFogShadowFilter p_shadow_filter); + void environment_set_volumetric_fog(RID p_env, bool p_enable, float p_density, const Color &p_light, float p_light_energy, float p_length, float p_detail_spread, float p_gi_inject, RS::EnvVolumetricFogShadowFilter p_shadow_filter); virtual void environment_set_volumetric_fog_volume_size(int p_size, int p_depth); virtual void environment_set_volumetric_fog_filter_active(bool p_enable); diff --git a/servers/rendering/rasterizer_rd/rasterizer_storage_rd.cpp b/servers/rendering/rasterizer_rd/rasterizer_storage_rd.cpp index e09926c97e..d751f474cd 100644 --- a/servers/rendering/rasterizer_rd/rasterizer_storage_rd.cpp +++ b/servers/rendering/rasterizer_rd/rasterizer_storage_rd.cpp @@ -3119,24 +3119,40 @@ 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->amount == p_amount) { + return; + } - 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(); + _particles_free_data(particles); - if (particles->particles_sort_buffer.is_valid()) { - RD::get_singleton()->free(particles->particles_sort_buffer); - particles->particles_sort_buffer = RID(); - } - } + particles->amount = p_amount; if (particles->amount > 0) { particles->particle_buffer = RD::get_singleton()->storage_buffer_create(sizeof(ParticleData) * p_amount); @@ -3150,27 +3166,6 @@ void RasterizerStorageRD::particles_set_amount(RID p_particles, int p_amount) { { 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; - - { - RD::Uniform u; - u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER; u.binding = 1; u.ids.push_back(particles->particle_buffer); uniforms.push_back(u); @@ -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."); } diff --git a/servers/rendering/rasterizer_rd/rasterizer_storage_rd.h b/servers/rendering/rasterizer_rd/rasterizer_storage_rd.h index e09b4a52eb..cecae6bbb2 100644 --- a/servers/rendering/rasterizer_rd/rasterizer_storage_rd.h +++ b/servers/rendering/rasterizer_rd/rasterizer_storage_rd.h @@ -249,6 +249,7 @@ private: RID default_rd_textures[DEFAULT_RD_TEXTURE_MAX]; RID default_rd_samplers[RS::CANVAS_ITEM_TEXTURE_FILTER_MAX][RS::CANVAS_ITEM_TEXTURE_REPEAT_MAX]; + RID default_rd_storage_buffer; /* DECAL ATLAS */ @@ -482,6 +483,25 @@ private: float emission_transform[16]; }; + struct ParticleEmissionBufferData { + }; + + struct ParticleEmissionBuffer { + struct Data { + float xform[16]; + float velocity[3]; + uint32_t flags; + float color[4]; + float custom[4]; + }; + + int32_t particle_count; + int32_t particle_max; + uint32_t pad1; + uint32_t pad2; + Data data[1]; //its 2020 and empty arrays are still non standard in C++ + }; + struct Particles { bool inactive; float inactive_time; @@ -515,6 +535,8 @@ private: bool dirty = false; Particles *update_list = nullptr; + RID sub_emitter; + float phase; float prev_phase; uint64_t prev_ticks; @@ -530,8 +552,15 @@ private: bool clear; + bool force_sub_emit = false; + Transform emission_transform; + Vector<uint8_t> emission_buffer_data; + + ParticleEmissionBuffer *emission_buffer = nullptr; + RID emission_storage_buffer; + Particles() : inactive(true), inactive_time(0.0), @@ -562,6 +591,8 @@ private: }; void _particles_process(Particles *p_particles, float p_delta); + void _particles_allocate_emission_buffer(Particles *particles); + void _particles_free_data(Particles *particles); struct ParticlesShader { struct PushConstant { @@ -569,8 +600,11 @@ private: uint32_t clear; uint32_t total_particles; uint32_t trail_size; + uint32_t use_fractional_delta; - uint32_t pad[3]; + uint32_t sub_emitter_mode; + uint32_t can_emit; + uint32_t pad; }; ParticlesShaderRD shader; @@ -1650,6 +1684,8 @@ public: void particles_set_fixed_fps(RID p_particles, int p_fps); void particles_set_fractional_delta(RID p_particles, bool p_enable); void particles_restart(RID p_particles); + void 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); + void particles_set_subemitter(RID p_particles, RID p_subemitter_particles); void particles_set_draw_order(RID p_particles, RS::ParticlesDrawOrder p_order); diff --git a/servers/rendering/rasterizer_rd/shaders/particles.glsl b/servers/rendering/rasterizer_rd/shaders/particles.glsl index 7cdedfcbfe..3de807b57c 100644 --- a/servers/rendering/rasterizer_rd/shaders/particles.glsl +++ b/servers/rendering/rasterizer_rd/shaders/particles.glsl @@ -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,76 +197,129 @@ void main() { float mass = 1.0; - float restart_phase = float(index) / float(params.total_particles); + bool restart = false; - 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); + bool restart_position = false; + bool restart_rotation_scale = false; + bool restart_velocity = false; + bool restart_color = false; + bool restart_custom = false; + + if (params.clear) { + PARTICLE.color = vec4(1.0); + PARTICLE.custom = vec4(0.0); + PARTICLE.velocity = vec3(0.0); + 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), + vec4(0.0, 0.0, 1.0, 0.0), + vec4(0.0, 0.0, 0.0, 1.0)); } - restart_phase *= (1.0 - FRAME.explosiveness); + if (params.sub_emitter_mode) { + if (!PARTICLE.is_active) { + int src_index = atomicAdd(src_particles.particle_count, -1) - 1; - bool restart = false; + if (src_index >= 0) { + PARTICLE.is_active = true; + restart = true; - 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 (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 (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; + 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.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 (FRAME.emitting) { + float restart_phase = float(index) / float(params.total_particles); - } else if (restart_phase < FRAME.system_phase) { - restart = true; - if (params.use_fractional_delta) { - local_delta = (FRAME.system_phase - restart_phase) * params.lifetime; + 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); } - } - uint current_cycle = FRAME.cycle; + restart_phase *= (1.0 - FRAME.explosiveness); - if (FRAME.system_phase < restart_phase) { - current_cycle -= uint(1); - } + 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 - uint particle_number = current_cycle * uint(params.total_particles) + particle; + 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; + } + } - if (restart) { - PARTICLE.is_active = FRAME.emitting; - } + } 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; + } -#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; + } 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; } - PARTICLE.xform = mat4( - vec4(1.0, 0.0, 0.0, 0.0), - vec4(0.0, 1.0, 0.0, 0.0), - vec4(0.0, 0.0, 1.0, 0.0), - vec4(0.0, 0.0, 0.0, 1.0)); } if (PARTICLE.is_active) { diff --git a/servers/rendering/rendering_server_canvas.cpp b/servers/rendering/rendering_server_canvas.cpp index 5c0741bb3b..07eabfd430 100644 --- a/servers/rendering/rendering_server_canvas.cpp +++ b/servers/rendering/rendering_server_canvas.cpp @@ -900,13 +900,12 @@ void RenderingServerCanvas::canvas_item_attach_skeleton(RID p_item, RID p_skelet void RenderingServerCanvas::canvas_item_set_copy_to_backbuffer(RID p_item, bool p_enable, const Rect2 &p_rect) { Item *canvas_item = canvas_item_owner.getornull(p_item); ERR_FAIL_COND(!canvas_item); - if (bool(canvas_item->copy_back_buffer != nullptr) != p_enable) { - if (p_enable) { - canvas_item->copy_back_buffer = memnew(RasterizerCanvas::Item::CopyBackBuffer); - } else { - memdelete(canvas_item->copy_back_buffer); - canvas_item->copy_back_buffer = nullptr; - } + if (p_enable && (canvas_item->copy_back_buffer == nullptr)) { + canvas_item->copy_back_buffer = memnew(RasterizerCanvas::Item::CopyBackBuffer); + } + if (!p_enable && (canvas_item->copy_back_buffer != nullptr)) { + memdelete(canvas_item->copy_back_buffer); + canvas_item->copy_back_buffer = nullptr; } if (p_enable) { diff --git a/servers/rendering/rendering_server_raster.h b/servers/rendering/rendering_server_raster.h index 547595f9b0..ceefcfa1fc 100644 --- a/servers/rendering/rendering_server_raster.h +++ b/servers/rendering/rendering_server_raster.h @@ -442,6 +442,8 @@ public: BIND1R(bool, particles_is_inactive, RID) BIND1(particles_request_process, RID) BIND1(particles_restart, RID) + BIND6(particles_emit, RID, const Transform &, const Vector3 &, const Color &, const Color &, uint32_t) + BIND2(particles_set_subemitter, RID, RID) BIND2(particles_set_draw_order, RID, RS::ParticlesDrawOrder) diff --git a/servers/rendering/rendering_server_wrap_mt.h b/servers/rendering/rendering_server_wrap_mt.h index 40aff46a3c..a8a56e7d56 100644 --- a/servers/rendering/rendering_server_wrap_mt.h +++ b/servers/rendering/rendering_server_wrap_mt.h @@ -360,11 +360,14 @@ public: FUNC1(particles_request_process, RID) FUNC1(particles_restart, RID) + FUNC6(particles_emit, RID, const Transform &, const Vector3 &, const Color &, const Color &, uint32_t) + FUNC2(particles_set_draw_order, RID, RS::ParticlesDrawOrder) FUNC2(particles_set_draw_passes, RID, int) FUNC3(particles_set_draw_pass_mesh, RID, int, RID) FUNC2(particles_set_emission_transform, RID, const Transform &) + FUNC2(particles_set_subemitter, RID, RID) FUNC1R(AABB, particles_get_current_aabb, RID) diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp index e60f79f3a0..28c41fb2dc 100644 --- a/servers/rendering/shader_language.cpp +++ b/servers/rendering/shader_language.cpp @@ -920,13 +920,13 @@ void ShaderLanguage::clear() { } } -bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_reassign, const Map<StringName, BuiltInInfo> &p_builtin_types, const StringName &p_identifier, DataType *r_data_type, IdentifierType *r_type, bool *r_is_const, int *r_array_size, StringName *r_struct_name) { - if (p_builtin_types.has(p_identifier)) { +bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_reassign, const FunctionInfo &p_function_info, const StringName &p_identifier, DataType *r_data_type, IdentifierType *r_type, bool *r_is_const, int *r_array_size, StringName *r_struct_name) { + if (p_function_info.built_ins.has(p_identifier)) { if (r_data_type) { - *r_data_type = p_builtin_types[p_identifier].type; + *r_data_type = p_function_info.built_ins[p_identifier].type; } if (r_is_const) { - *r_is_const = p_builtin_types[p_identifier].constant; + *r_is_const = p_function_info.built_ins[p_identifier].constant; } if (r_type) { *r_type = IDENTIFIER_BUILTIN_VAR; @@ -935,6 +935,20 @@ bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_rea return true; } + if (p_function_info.stage_functions.has(p_identifier)) { + if (r_data_type) { + *r_data_type = p_function_info.stage_functions[p_identifier].return_type; + } + if (r_is_const) { + *r_is_const = true; + } + if (r_type) { + *r_type = IDENTIFIER_FUNCTION; + } + + return true; + } + FunctionNode *function = nullptr; while (p_block) { @@ -2152,7 +2166,7 @@ const ShaderLanguage::BuiltinFuncOutArgs ShaderLanguage::builtin_func_out_args[] { nullptr, 0 } }; -bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const Map<StringName, BuiltInInfo> &p_builtin_types, OperatorNode *p_func, DataType *r_ret_type, StringName *r_ret_type_str) { +bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionInfo &p_function_info, OperatorNode *p_func, DataType *r_ret_type, StringName *r_ret_type_str) { ERR_FAIL_COND_V(p_func->op != OP_CALL && p_func->op != OP_CONSTRUCT, false); Vector<DataType> args; @@ -2169,6 +2183,30 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const Map<Strin int argcount = args.size(); + if (p_function_info.stage_functions.has(name)) { + //stage based function + const StageFunctionInfo &sf = p_function_info.stage_functions[name]; + if (argcount != sf.arguments.size()) { + _set_error(vformat("Invalid number of arguments when calling stage function '%s', which expects %d arguments.", String(name), sf.arguments.size())); + return false; + } + //validate arguments + for (int i = 0; i < argcount; i++) { + if (args[i] != sf.arguments[i].type) { + _set_error(vformat("Invalid argument type when calling stage function '%s', type expected is '%s'.", String(name), String(get_datatype_name(sf.arguments[i].type)))); + return false; + } + } + + if (r_ret_type) { + *r_ret_type = sf.return_type; + } + if (r_ret_type_str) { + *r_ret_type_str = ""; + } + return true; + } + bool failed_builtin = false; bool unsupported_builtin = false; int builtin_idx = 0; @@ -2241,8 +2279,8 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const Map<Strin if (shader->uniforms.has(varname)) { fail = true; } else { - if (p_builtin_types.has(varname)) { - BuiltInInfo info = p_builtin_types[varname]; + if (p_function_info.built_ins.has(varname)) { + BuiltInInfo info = p_function_info.built_ins[varname]; if (info.constant) { fail = true; } @@ -2278,7 +2316,7 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const Map<Strin const BlockNode *b = p_block; bool valid = false; while (b) { - if (b->variables.has(var_name) || p_builtin_types.has(var_name)) { + if (b->variables.has(var_name) || p_function_info.built_ins.has(var_name)) { valid = true; break; } @@ -2353,10 +2391,13 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const Map<Strin err += ","; } - if (p_func->arguments[i + 1]->type == Node::TYPE_CONSTANT && p_func->arguments[i + 1]->get_datatype() == TYPE_INT && static_cast<ConstantNode *>(p_func->arguments[i + 1])->values[0].sint < 0) { - err += "-"; + String arg_name; + if (args[i] == TYPE_STRUCT) { + arg_name = args2[i]; + } else { + arg_name = get_datatype_name(args[i]); } - err += get_datatype_name(args[i]); + err += arg_name; } err += ")"; _set_error(err); @@ -2380,6 +2421,9 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const Map<Strin return false; } + int last_arg_count = 0; + String arg_list = ""; + for (int i = 0; i < shader->functions.size(); i++) { if (name != shader->functions[i].name) { continue; @@ -2391,21 +2435,45 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const Map<Strin } FunctionNode *pfunc = shader->functions[i].function; + if (arg_list == "") { + for (int j = 0; j < pfunc->arguments.size(); j++) { + if (j > 0) { + arg_list += ", "; + } + String func_arg_name; + if (pfunc->arguments[j].type == TYPE_STRUCT) { + func_arg_name = pfunc->arguments[j].type_str; + } else { + func_arg_name = get_datatype_name(pfunc->arguments[j].type); + } + arg_list += func_arg_name; + } + } if (pfunc->arguments.size() != args.size()) { + last_arg_count = pfunc->arguments.size(); continue; } bool fail = false; for (int j = 0; j < args.size(); j++) { - if (args[j] == TYPE_STRUCT && args2[j] != pfunc->arguments[j].type_str) { - fail = true; - break; - } if (get_scalar_type(args[j]) == args[j] && p_func->arguments[j + 1]->type == Node::TYPE_CONSTANT && convert_constant(static_cast<ConstantNode *>(p_func->arguments[j + 1]), pfunc->arguments[j].type)) { //all good, but it needs implicit conversion later - } else if (args[j] != pfunc->arguments[j].type) { + } else if (args[j] != pfunc->arguments[j].type || (args[j] == TYPE_STRUCT && args2[j] != pfunc->arguments[j].type_str)) { + String func_arg_name; + if (pfunc->arguments[j].type == TYPE_STRUCT) { + func_arg_name = pfunc->arguments[j].type_str; + } else { + func_arg_name = get_datatype_name(pfunc->arguments[j].type); + } + String arg_name; + if (args[j] == TYPE_STRUCT) { + arg_name = args2[j]; + } else { + arg_name = get_datatype_name(args[j]); + } + _set_error(vformat("Invalid argument for \"%s(%s)\" function: argument %s should be %s but is %s.", String(name), arg_list, j + 1, func_arg_name, arg_name)); fail = true; break; } @@ -2441,6 +2509,12 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const Map<Strin } } + if (last_arg_count > args.size()) { + _set_error(vformat("Too few arguments for \"%s(%s)\" call. Expected at least %s but received %s.", String(name), arg_list, last_arg_count, args.size())); + } else if (last_arg_count < args.size()) { + _set_error(vformat("Too many arguments for \"%s(%s)\" call. Expected at most %s but received %s.", String(name), arg_list, last_arg_count, args.size())); + } + return false; } @@ -2456,7 +2530,7 @@ bool ShaderLanguage::_compare_datatypes_in_nodes(Node *a, Node *b) const { return true; } -bool ShaderLanguage::_parse_function_arguments(BlockNode *p_block, const Map<StringName, BuiltInInfo> &p_builtin_types, OperatorNode *p_func, int *r_complete_arg) { +bool ShaderLanguage::_parse_function_arguments(BlockNode *p_block, const FunctionInfo &p_function_info, OperatorNode *p_func, int *r_complete_arg) { TkPos pos = _get_tkpos(); Token tk = _get_token(); @@ -2478,7 +2552,7 @@ bool ShaderLanguage::_parse_function_arguments(BlockNode *p_block, const Map<Str } } - Node *arg = _parse_and_reduce_expression(p_block, p_builtin_types); + Node *arg = _parse_and_reduce_expression(p_block, p_function_info); if (!arg) { return false; @@ -3017,16 +3091,16 @@ bool ShaderLanguage::_is_operator_assign(Operator p_op) const { return false; } -bool ShaderLanguage::_validate_assign(Node *p_node, const Map<StringName, BuiltInInfo> &p_builtin_types, String *r_message) { +bool ShaderLanguage::_validate_assign(Node *p_node, const FunctionInfo &p_function_info, String *r_message) { if (p_node->type == Node::TYPE_OPERATOR) { OperatorNode *op = static_cast<OperatorNode *>(p_node); if (op->op == OP_INDEX) { - return _validate_assign(op->arguments[0], p_builtin_types, r_message); + return _validate_assign(op->arguments[0], p_function_info, r_message); } else if (_is_operator_assign(op->op)) { //chained assignment - return _validate_assign(op->arguments[1], p_builtin_types, r_message); + return _validate_assign(op->arguments[1], p_function_info, r_message); } else if (op->op == OP_CALL) { if (r_message) { @@ -3045,7 +3119,7 @@ bool ShaderLanguage::_validate_assign(Node *p_node, const Map<StringName, BuiltI return false; } - return _validate_assign(member->owner, p_builtin_types, r_message); + return _validate_assign(member->owner, p_function_info, r_message); } else if (p_node->type == Node::TYPE_VARIABLE) { VariableNode *var = static_cast<VariableNode *>(p_node); @@ -3071,7 +3145,7 @@ bool ShaderLanguage::_validate_assign(Node *p_node, const Map<StringName, BuiltI return false; } - if (!(p_builtin_types.has(var->name) && p_builtin_types[var->name].constant)) { + if (!(p_function_info.built_ins.has(var->name) && p_function_info.built_ins[var->name].constant)) { return true; } } else if (p_node->type == Node::TYPE_ARRAY) { @@ -3168,7 +3242,7 @@ bool ShaderLanguage::_propagate_function_call_sampler_builtin_reference(StringNa ERR_FAIL_V(false); //bug? function not found } -ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, const Map<StringName, BuiltInInfo> &p_builtin_types) { +ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, const FunctionInfo &p_function_info) { Vector<Expression> expression; //Vector<TokenType> operators; @@ -3184,7 +3258,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons if (tk.type == TK_PARENTHESIS_OPEN) { //handle subexpression - expr = _parse_and_reduce_expression(p_block, p_builtin_types); + expr = _parse_and_reduce_expression(p_block, p_function_info); if (!expr) { return nullptr; } @@ -3257,7 +3331,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons int carg = -1; - bool ok = _parse_function_arguments(p_block, p_builtin_types, func, &carg); + bool ok = _parse_function_arguments(p_block, p_function_info, func, &carg); if (carg >= 0) { completion_type = COMPLETION_CALL_ARGUMENTS; @@ -3271,7 +3345,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons return nullptr; } - if (!_validate_function_call(p_block, p_builtin_types, func, &func->return_cache, &func->struct_name)) { + if (!_validate_function_call(p_block, p_function_info, func, &func->return_cache, &func->struct_name)) { _set_error("No matching constructor found for: '" + String(funcname->name) + "'"); return nullptr; } @@ -3347,7 +3421,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons } else { _set_tkpos(pos2); - Node *n = _parse_and_reduce_expression(p_block, p_builtin_types); + Node *n = _parse_and_reduce_expression(p_block, p_function_info); if (!n || n->type != Node::TYPE_CONSTANT || n->get_datatype() != TYPE_INT) { _set_error("Expected single integer constant > 0"); return nullptr; @@ -3408,7 +3482,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons if (tk.type == TK_PARENTHESIS_OPEN || auto_size) { // initialization while (true) { - Node *n = _parse_and_reduce_expression(p_block, p_builtin_types); + Node *n = _parse_and_reduce_expression(p_block, p_function_info); if (!n) { return nullptr; } @@ -3448,7 +3522,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons nexpr = an; } else { - nexpr = _parse_and_reduce_expression(p_block, p_builtin_types); + nexpr = _parse_and_reduce_expression(p_block, p_function_info); if (!nexpr) { return nullptr; } @@ -3490,7 +3564,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons int carg = -1; - bool ok = _parse_function_arguments(p_block, p_builtin_types, func, &carg); + bool ok = _parse_function_arguments(p_block, p_function_info, func, &carg); // Check if block has a variable with the same name as function to prevent shader crash. ShaderLanguage::BlockNode *bnode = p_block; @@ -3532,7 +3606,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons return nullptr; } - if (!_validate_function_call(p_block, p_builtin_types, func, &func->return_cache, &func->struct_name)) { + if (!_validate_function_call(p_block, p_function_info, func, &func->return_cache, &func->struct_name)) { _set_error("No matching function found for: '" + String(funcname->name) + "'"); return nullptr; } @@ -3584,8 +3658,8 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons } else if (shader->uniforms.has(varname)) { error = true; } else { - if (p_builtin_types.has(varname)) { - BuiltInInfo info = p_builtin_types[varname]; + if (p_function_info.built_ins.has(varname)) { + BuiltInInfo info = p_function_info.built_ins[varname]; if (info.constant) { error = true; } @@ -3617,7 +3691,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons if (!_propagate_function_call_sampler_uniform_settings(name, i, u->filter, u->repeat)) { return nullptr; } - } else if (p_builtin_types.has(varname)) { + } else if (p_function_info.built_ins.has(varname)) { //a built-in if (!_propagate_function_call_sampler_builtin_reference(name, i, varname)) { return nullptr; @@ -3672,7 +3746,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons return nullptr; } } else { - if (!_find_identifier(p_block, false, p_builtin_types, identifier, &data_type, &ident_type, &is_const, &array_size, &struct_name)) { + if (!_find_identifier(p_block, false, p_function_info, identifier, &data_type, &ident_type, &is_const, &array_size, &struct_name)) { _set_error("Unknown identifier in expression: " + String(identifier)); return nullptr; } @@ -3697,7 +3771,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons if (tk.type == TK_PERIOD) { completion_class = TAG_ARRAY; p_block->block_tag = SubClassTag::TAG_ARRAY; - call_expression = _parse_and_reduce_expression(p_block, p_builtin_types); + call_expression = _parse_and_reduce_expression(p_block, p_function_info); p_block->block_tag = SubClassTag::TAG_GLOBAL; if (!call_expression) { return nullptr; @@ -3705,7 +3779,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons data_type = call_expression->get_datatype(); } else { // indexing - index_expression = _parse_and_reduce_expression(p_block, p_builtin_types); + index_expression = _parse_and_reduce_expression(p_block, p_function_info); if (!index_expression) { return nullptr; } @@ -4085,7 +4159,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons _set_error("Nested array length() is not yet implemented"); return nullptr; } else if (tk.type == TK_BRACKET_OPEN) { - Node *index_expression = _parse_and_reduce_expression(p_block, p_builtin_types); + Node *index_expression = _parse_and_reduce_expression(p_block, p_function_info); if (!index_expression) { return nullptr; } @@ -4134,7 +4208,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons */ } else if (tk.type == TK_BRACKET_OPEN) { - Node *index = _parse_and_reduce_expression(p_block, p_builtin_types); + Node *index = _parse_and_reduce_expression(p_block, p_function_info); if (!index) { return nullptr; } @@ -4276,7 +4350,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons return nullptr; } - if (!_validate_assign(expr, p_builtin_types)) { + if (!_validate_assign(expr, p_function_info)) { _set_error("Invalid use of increment/decrement operator in constant expression."); return nullptr; } @@ -4574,7 +4648,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons for (int i = expr_pos - 1; i >= next_op; i--) { OperatorNode *op = alloc_node<OperatorNode>(); op->op = expression[i].op; - if ((op->op == OP_INCREMENT || op->op == OP_DECREMENT) && !_validate_assign(expression[i + 1].node, p_builtin_types)) { + if ((op->op == OP_INCREMENT || op->op == OP_DECREMENT) && !_validate_assign(expression[i + 1].node, p_function_info)) { _set_error("Can't use increment/decrement operator in constant expression."); return nullptr; } @@ -4648,7 +4722,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons if (_is_operator_assign(op->op)) { String assign_message; - if (!_validate_assign(expression[next_op - 1].node, p_builtin_types, &assign_message)) { + if (!_validate_assign(expression[next_op - 1].node, p_function_info, &assign_message)) { _set_error(assign_message); return nullptr; } @@ -4802,8 +4876,8 @@ ShaderLanguage::Node *ShaderLanguage::_reduce_expression(BlockNode *p_block, Sha return p_node; } -ShaderLanguage::Node *ShaderLanguage::_parse_and_reduce_expression(BlockNode *p_block, const Map<StringName, BuiltInInfo> &p_builtin_types) { - ShaderLanguage::Node *expr = _parse_expression(p_block, p_builtin_types); +ShaderLanguage::Node *ShaderLanguage::_parse_and_reduce_expression(BlockNode *p_block, const FunctionInfo &p_function_info) { + ShaderLanguage::Node *expr = _parse_expression(p_block, p_function_info); if (!expr) { //errored return nullptr; } @@ -4813,7 +4887,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_and_reduce_expression(BlockNode *p_ return expr; } -Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, BuiltInInfo> &p_builtin_types, bool p_just_one, bool p_can_break, bool p_can_continue) { +Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_function_info, bool p_just_one, bool p_can_break, bool p_can_continue) { while (true) { TkPos pos = _get_tkpos(); @@ -4897,7 +4971,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui StringName name = tk.text; ShaderLanguage::IdentifierType itype; - if (_find_identifier(p_block, true, p_builtin_types, name, (ShaderLanguage::DataType *)nullptr, &itype)) { + if (_find_identifier(p_block, true, p_function_info, name, (ShaderLanguage::DataType *)nullptr, &itype)) { if (itype != IDENTIFIER_FUNCTION) { _set_error("Redefinition of '" + String(name) + "'"); return ERR_PARSE_ERROR; @@ -5016,7 +5090,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui } else { _set_tkpos(pos2); - Node *n = _parse_and_reduce_expression(p_block, p_builtin_types); + Node *n = _parse_and_reduce_expression(p_block, p_function_info); if (!n || n->type != Node::TYPE_CONSTANT || n->get_datatype() != TYPE_INT) { _set_error("Expected single integer constant > 0"); return ERR_PARSE_ERROR; @@ -5097,7 +5171,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui if (tk.type == TK_PARENTHESIS_OPEN || curly) { // initialization while (true) { - Node *n = _parse_and_reduce_expression(p_block, p_builtin_types); + Node *n = _parse_and_reduce_expression(p_block, p_function_info); if (!n) { return ERR_PARSE_ERROR; } @@ -5169,7 +5243,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui decl.initializer = nullptr; //variable created with assignment! must parse an expression - Node *n = _parse_and_reduce_expression(p_block, p_builtin_types); + Node *n = _parse_and_reduce_expression(p_block, p_function_info); if (!n) { return ERR_PARSE_ERROR; } @@ -5229,7 +5303,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui //a sub block, just because.. BlockNode *block = alloc_node<BlockNode>(); block->parent_block = p_block; - if (_parse_block(block, p_builtin_types, false, p_can_break, p_can_continue) != OK) { + if (_parse_block(block, p_function_info, false, p_can_break, p_can_continue) != OK) { return ERR_PARSE_ERROR; } p_block->statements.push_back(block); @@ -5243,7 +5317,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui ControlFlowNode *cf = alloc_node<ControlFlowNode>(); cf->flow_op = FLOW_OP_IF; - Node *n = _parse_and_reduce_expression(p_block, p_builtin_types); + Node *n = _parse_and_reduce_expression(p_block, p_function_info); if (!n) { return ERR_PARSE_ERROR; } @@ -5265,7 +5339,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui cf->blocks.push_back(block); p_block->statements.push_back(cf); - Error err = _parse_block(block, p_builtin_types, true, p_can_break, p_can_continue); + Error err = _parse_block(block, p_function_info, true, p_can_break, p_can_continue); if (err) { return err; } @@ -5276,7 +5350,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui block = alloc_node<BlockNode>(); block->parent_block = p_block; cf->blocks.push_back(block); - err = _parse_block(block, p_builtin_types, true, p_can_break, p_can_continue); + err = _parse_block(block, p_function_info, true, p_can_break, p_can_continue); } else { _set_tkpos(pos); //rollback @@ -5295,7 +5369,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui } ControlFlowNode *cf = alloc_node<ControlFlowNode>(); cf->flow_op = FLOW_OP_SWITCH; - Node *n = _parse_and_reduce_expression(p_block, p_builtin_types); + Node *n = _parse_and_reduce_expression(p_block, p_function_info); if (!n) { return ERR_PARSE_ERROR; } @@ -5323,7 +5397,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui int prev_type = TK_CF_CASE; while (true) { // Go-through multiple cases. - if (_parse_block(switch_block, p_builtin_types, true, true, false) != OK) { + if (_parse_block(switch_block, p_function_info, true, true, false) != OK) { return ERR_PARSE_ERROR; } pos = _get_tkpos(); @@ -5424,7 +5498,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui cf->blocks.push_back(case_block); p_block->statements.push_back(cf); - Error err = _parse_block(case_block, p_builtin_types, false, true, false); + Error err = _parse_block(case_block, p_function_info, false, true, false); if (err) { return err; } @@ -5458,7 +5532,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui cf->blocks.push_back(default_block); p_block->statements.push_back(cf); - Error err = _parse_block(default_block, p_builtin_types, false, true, false); + Error err = _parse_block(default_block, p_function_info, false, true, false); if (err) { return err; } @@ -5475,7 +5549,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui do_block = alloc_node<BlockNode>(); do_block->parent_block = p_block; - Error err = _parse_block(do_block, p_builtin_types, true, true, true); + Error err = _parse_block(do_block, p_function_info, true, true, true); if (err) { return err; } @@ -5499,7 +5573,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui } else { cf->flow_op = FLOW_OP_WHILE; } - Node *n = _parse_and_reduce_expression(p_block, p_builtin_types); + Node *n = _parse_and_reduce_expression(p_block, p_function_info); if (!n) { return ERR_PARSE_ERROR; } @@ -5516,7 +5590,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui cf->blocks.push_back(block); p_block->statements.push_back(cf); - Error err = _parse_block(block, p_builtin_types, true, true, true); + Error err = _parse_block(block, p_function_info, true, true, true); if (err) { return err; } @@ -5547,11 +5621,11 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui init_block->parent_block = p_block; init_block->single_statement = true; cf->blocks.push_back(init_block); - if (_parse_block(init_block, p_builtin_types, true, false, false) != OK) { + if (_parse_block(init_block, p_function_info, true, false, false) != OK) { return ERR_PARSE_ERROR; } - Node *n = _parse_and_reduce_expression(init_block, p_builtin_types); + Node *n = _parse_and_reduce_expression(init_block, p_function_info); if (!n) { return ERR_PARSE_ERROR; } @@ -5569,7 +5643,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui cf->expressions.push_back(n); - n = _parse_and_reduce_expression(init_block, p_builtin_types); + n = _parse_and_reduce_expression(init_block, p_function_info); if (!n) { return ERR_PARSE_ERROR; } @@ -5587,7 +5661,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui cf->blocks.push_back(block); p_block->statements.push_back(cf); - Error err = _parse_block(block, p_builtin_types, true, true, true); + Error err = _parse_block(block, p_function_info, true, true, true); if (err) { return err; } @@ -5623,7 +5697,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui } } else { _set_tkpos(pos); //rollback, wants expression - Node *expr = _parse_and_reduce_expression(p_block, p_builtin_types); + Node *expr = _parse_and_reduce_expression(p_block, p_function_info); if (!expr) { return ERR_PARSE_ERROR; } @@ -5725,7 +5799,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui } else { //nothing else, so expression _set_tkpos(pos); //rollback - Node *expr = _parse_and_reduce_expression(p_block, p_builtin_types); + Node *expr = _parse_and_reduce_expression(p_block, p_function_info); if (!expr) { return ERR_PARSE_ERROR; } @@ -6066,7 +6140,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct name = tk.text; - if (_find_identifier(nullptr, false, Map<StringName, BuiltInInfo>(), name)) { + if (_find_identifier(nullptr, false, FunctionInfo(), name)) { _set_error("Redefinition of '" + String(name) + "'"); return ERR_PARSE_ERROR; } @@ -6315,7 +6389,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct //reset scope for next uniform if (tk.type == TK_OP_ASSIGN) { - Node *expr = _parse_and_reduce_expression(nullptr, Map<StringName, BuiltInInfo>()); + Node *expr = _parse_and_reduce_expression(nullptr, FunctionInfo()); if (!expr) { return ERR_PARSE_ERROR; } @@ -6440,7 +6514,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct return ERR_PARSE_ERROR; } - if (_find_identifier(nullptr, false, Map<StringName, BuiltInInfo>(), name)) { + if (_find_identifier(nullptr, false, FunctionInfo(), name)) { _set_error("Redefinition of '" + String(name) + "'"); return ERR_PARSE_ERROR; } @@ -6553,7 +6627,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct } else { _set_tkpos(pos2); - Node *n = _parse_and_reduce_expression(NULL, Map<StringName, BuiltInInfo>()); + Node *n = _parse_and_reduce_expression(NULL, FunctionInfo()); if (!n || n->type != Node::TYPE_CONSTANT || n->get_datatype() != TYPE_INT) { _set_error("Expected single integer constant > 0"); return ERR_PARSE_ERROR; @@ -6634,7 +6708,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct if (tk.type == TK_PARENTHESIS_OPEN || curly) { // initialization while (true) { - Node *n = _parse_and_reduce_expression(NULL, Map<StringName, BuiltInInfo>()); + Node *n = _parse_and_reduce_expression(NULL, FunctionInfo()); if (!n) { return ERR_PARSE_ERROR; } @@ -6689,7 +6763,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct constant.initializer = static_cast<ConstantNode *>(expr); } else { //variable created with assignment! must parse an expression - Node *expr = _parse_and_reduce_expression(NULL, Map<StringName, BuiltInInfo>()); + Node *expr = _parse_and_reduce_expression(NULL, FunctionInfo()); if (!expr) return ERR_PARSE_ERROR; if (expr->type == Node::TYPE_OPERATOR && ((OperatorNode *)expr)->op == OP_CALL) { @@ -6726,7 +6800,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct } name = tk.text; - if (_find_identifier(nullptr, false, Map<StringName, BuiltInInfo>(), name)) { + if (_find_identifier(nullptr, false, FunctionInfo(), name)) { _set_error("Redefinition of '" + String(name) + "'"); return ERR_PARSE_ERROR; } @@ -6749,14 +6823,14 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct break; } - Map<StringName, BuiltInInfo> builtin_types; + FunctionInfo builtins; if (p_functions.has(name)) { - builtin_types = p_functions[name].built_ins; + builtins = p_functions[name]; } if (p_functions.has("global")) { // Adds global variables: 'TIME' for (Map<StringName, BuiltInInfo>::Element *E = p_functions["global"].built_ins.front(); E; E = E->next()) { - builtin_types.insert(E->key(), E->value()); + builtins.built_ins.insert(E->key(), E->value()); } } @@ -6879,7 +6953,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct pname = tk.text; ShaderLanguage::IdentifierType itype; - if (_find_identifier(func_node->body, false, builtin_types, pname, (ShaderLanguage::DataType *)nullptr, &itype)) { + if (_find_identifier(func_node->body, false, builtins, pname, (ShaderLanguage::DataType *)nullptr, &itype)) { if (itype != IDENTIFIER_FUNCTION) { _set_error("Redefinition of '" + String(pname) + "'"); return ERR_PARSE_ERROR; @@ -6941,7 +7015,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct current_function = name; - Error err = _parse_block(func_node->body, builtin_types); + Error err = _parse_block(func_node->body, builtins); if (err) { return err; } diff --git a/servers/rendering/shader_language.h b/servers/rendering/shader_language.h index faf06a9a85..0d044a21c7 100644 --- a/servers/rendering/shader_language.h +++ b/servers/rendering/shader_language.h @@ -730,8 +730,25 @@ public: constant(p_constant) {} }; + struct StageFunctionInfo { + struct Argument { + StringName name; + DataType type; + + Argument(const StringName &p_name = StringName(), DataType p_type = TYPE_VOID) { + name = p_name; + type = p_type; + } + }; + + Vector<Argument> arguments; + DataType return_type = TYPE_VOID; + }; + struct FunctionInfo { Map<StringName, BuiltInInfo> built_ins; + Map<StringName, StageFunctionInfo> stage_functions; + bool can_discard; }; static bool has_builtin(const Map<StringName, ShaderLanguage::FunctionInfo> &p_functions, const StringName &p_name); @@ -802,9 +819,9 @@ private: IDENTIFIER_CONSTANT, }; - bool _find_identifier(const BlockNode *p_block, bool p_allow_reassign, const Map<StringName, BuiltInInfo> &p_builtin_types, const StringName &p_identifier, DataType *r_data_type = nullptr, IdentifierType *r_type = nullptr, bool *r_is_const = nullptr, int *r_array_size = nullptr, StringName *r_struct_name = nullptr); + bool _find_identifier(const BlockNode *p_block, bool p_allow_reassign, const FunctionInfo &p_function_info, const StringName &p_identifier, DataType *r_data_type = nullptr, IdentifierType *r_type = nullptr, bool *r_is_const = nullptr, int *r_array_size = nullptr, StringName *r_struct_name = nullptr); bool _is_operator_assign(Operator p_op) const; - bool _validate_assign(Node *p_node, const Map<StringName, BuiltInInfo> &p_builtin_types, String *r_message = nullptr); + bool _validate_assign(Node *p_node, const FunctionInfo &p_function_info, String *r_message = nullptr); bool _validate_operator(OperatorNode *p_op, DataType *r_ret_type = nullptr); struct BuiltinFuncDef { @@ -837,16 +854,16 @@ private: Error _validate_datatype(DataType p_type); bool _compare_datatypes_in_nodes(Node *a, Node *b) const; - bool _validate_function_call(BlockNode *p_block, const Map<StringName, BuiltInInfo> &p_builtin_types, OperatorNode *p_func, DataType *r_ret_type, StringName *r_ret_type_str); - bool _parse_function_arguments(BlockNode *p_block, const Map<StringName, BuiltInInfo> &p_builtin_types, OperatorNode *p_func, int *r_complete_arg = nullptr); + bool _validate_function_call(BlockNode *p_block, const FunctionInfo &p_function_info, OperatorNode *p_func, DataType *r_ret_type, StringName *r_ret_type_str); + bool _parse_function_arguments(BlockNode *p_block, const FunctionInfo &p_function_info, OperatorNode *p_func, int *r_complete_arg = nullptr); bool _propagate_function_call_sampler_uniform_settings(StringName p_name, int p_argument, TextureFilter p_filter, TextureRepeat p_repeat); bool _propagate_function_call_sampler_builtin_reference(StringName p_name, int p_argument, const StringName &p_builtin); - Node *_parse_expression(BlockNode *p_block, const Map<StringName, BuiltInInfo> &p_builtin_types); + Node *_parse_expression(BlockNode *p_block, const FunctionInfo &p_function_info); ShaderLanguage::Node *_reduce_expression(BlockNode *p_block, ShaderLanguage::Node *p_node); - Node *_parse_and_reduce_expression(BlockNode *p_block, const Map<StringName, BuiltInInfo> &p_builtin_types); - Error _parse_block(BlockNode *p_block, const Map<StringName, BuiltInInfo> &p_builtin_types, bool p_just_one = false, bool p_can_break = false, bool p_can_continue = false); + Node *_parse_and_reduce_expression(BlockNode *p_block, const FunctionInfo &p_function_info); + Error _parse_block(BlockNode *p_block, const FunctionInfo &p_function_info, bool p_just_one = false, bool p_can_break = false, bool p_can_continue = false); String _get_shader_type_list(const Set<String> &p_shader_types) const; String _get_qualifier_str(ArgumentQualifier p_qualifier) const; diff --git a/servers/rendering/shader_types.cpp b/servers/rendering/shader_types.cpp index 9c4f44b928..f3ef322ad7 100644 --- a/servers/rendering/shader_types.cpp +++ b/servers/rendering/shader_types.cpp @@ -283,8 +283,29 @@ ShaderTypes::ShaderTypes() { shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["INDEX"] = constt(ShaderLanguage::TYPE_INT); shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["EMISSION_TRANSFORM"] = constt(ShaderLanguage::TYPE_MAT4); shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["RANDOM_SEED"] = constt(ShaderLanguage::TYPE_UINT); + shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["FLAG_EMIT_POSITION"] = constt(ShaderLanguage::TYPE_UINT); + shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["FLAG_EMIT_ROT_SCALE"] = constt(ShaderLanguage::TYPE_UINT); + shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["FLAG_EMIT_VELOCITY"] = constt(ShaderLanguage::TYPE_UINT); + shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["FLAG_EMIT_COLOR"] = constt(ShaderLanguage::TYPE_UINT); + shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["FLAG_EMIT_CUSTOM"] = constt(ShaderLanguage::TYPE_UINT); + shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["RESTART_POSITION"] = constt(ShaderLanguage::TYPE_BOOL); + shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["RESTART_ROT_SCALE"] = constt(ShaderLanguage::TYPE_BOOL); + shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["RESTART_VELOCITY"] = constt(ShaderLanguage::TYPE_BOOL); + shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["RESTART_COLOR"] = constt(ShaderLanguage::TYPE_BOOL); + shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["RESTART_CUSTOM"] = constt(ShaderLanguage::TYPE_BOOL); shader_modes[RS::SHADER_PARTICLES].functions["compute"].can_discard = false; + { + ShaderLanguage::StageFunctionInfo emit_vertex_func; + emit_vertex_func.arguments.push_back(ShaderLanguage::StageFunctionInfo::Argument("xform", ShaderLanguage::TYPE_MAT4)); + emit_vertex_func.arguments.push_back(ShaderLanguage::StageFunctionInfo::Argument("velocity", ShaderLanguage::TYPE_VEC3)); + emit_vertex_func.arguments.push_back(ShaderLanguage::StageFunctionInfo::Argument("color", ShaderLanguage::TYPE_VEC4)); + emit_vertex_func.arguments.push_back(ShaderLanguage::StageFunctionInfo::Argument("custom", ShaderLanguage::TYPE_VEC4)); + emit_vertex_func.arguments.push_back(ShaderLanguage::StageFunctionInfo::Argument("flags", ShaderLanguage::TYPE_UINT)); + emit_vertex_func.return_type = ShaderLanguage::TYPE_BOOL; //whether it could emit + shader_modes[RS::SHADER_PARTICLES].functions["compute"].stage_functions["emit_particle"] = emit_vertex_func; + } + shader_modes[RS::SHADER_PARTICLES].modes.push_back("disable_force"); shader_modes[RS::SHADER_PARTICLES].modes.push_back("disable_velocity"); shader_modes[RS::SHADER_PARTICLES].modes.push_back("keep_data"); |