diff options
author | Rémi Verschelde <rverschelde@gmail.com> | 2022-11-15 10:28:44 +0100 |
---|---|---|
committer | Rémi Verschelde <rverschelde@gmail.com> | 2022-11-15 10:28:44 +0100 |
commit | 5f78f24b081b123f480e00fd0cb1dad590ecd964 (patch) | |
tree | 47ab1c946a13557c98f162be0a759efef23b7b9d | |
parent | 1e9a61cd404c9fbaf55d465268ca665c7acb4828 (diff) | |
parent | 9ce57050a5d12776deedd44fcb82dd5841a56686 (diff) |
Merge pull request #68426 from clayjohn/GLES3-particles
Add GPUParticles to the OpenGL3 renderer.
31 files changed, 2841 insertions, 252 deletions
diff --git a/drivers/gles3/effects/copy_effects.cpp b/drivers/gles3/effects/copy_effects.cpp index 3acbcf6b53..b552b52cd5 100644 --- a/drivers/gles3/effects/copy_effects.cpp +++ b/drivers/gles3/effects/copy_effects.cpp @@ -115,13 +115,21 @@ CopyEffects::~CopyEffects() { } void CopyEffects::copy_to_rect(const Rect2 &p_rect) { - copy.shader.version_bind_shader(copy.shader_version, CopyShaderGLES3::MODE_COPY_SECTION); + bool success = copy.shader.version_bind_shader(copy.shader_version, CopyShaderGLES3::MODE_COPY_SECTION); + if (!success) { + return; + } + copy.shader.version_set_uniform(CopyShaderGLES3::COPY_SECTION, p_rect.position.x, p_rect.position.y, p_rect.size.x, p_rect.size.y, copy.shader_version, CopyShaderGLES3::MODE_COPY_SECTION); draw_screen_quad(); } void CopyEffects::copy_screen() { - copy.shader.version_bind_shader(copy.shader_version, CopyShaderGLES3::MODE_DEFAULT); + bool success = copy.shader.version_bind_shader(copy.shader_version, CopyShaderGLES3::MODE_DEFAULT); + if (!success) { + return; + } + draw_screen_triangle(); } @@ -151,7 +159,11 @@ void CopyEffects::bilinear_blur(GLuint p_source_texture, int p_mipmap_count, con } void CopyEffects::set_color(const Color &p_color, const Rect2i &p_region) { - copy.shader.version_bind_shader(copy.shader_version, CopyShaderGLES3::MODE_SIMPLE_COLOR); + bool success = copy.shader.version_bind_shader(copy.shader_version, CopyShaderGLES3::MODE_SIMPLE_COLOR); + if (!success) { + return; + } + copy.shader.version_set_uniform(CopyShaderGLES3::COPY_SECTION, p_region.position.x, p_region.position.y, p_region.size.x, p_region.size.y, copy.shader_version, CopyShaderGLES3::MODE_SIMPLE_COLOR); copy.shader.version_set_uniform(CopyShaderGLES3::COLOR_IN, p_color, copy.shader_version, CopyShaderGLES3::MODE_SIMPLE_COLOR); draw_screen_quad(); diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp index 0a64cda787..0c102bfc1d 100644 --- a/drivers/gles3/rasterizer_canvas_gles3.cpp +++ b/drivers/gles3/rasterizer_canvas_gles3.cpp @@ -41,6 +41,7 @@ #include "storage/config.h" #include "storage/material_storage.h" #include "storage/mesh_storage.h" +#include "storage/particles_storage.h" #include "storage/texture_storage.h" void RasterizerCanvasGLES3::_update_transform_2d_to_mat4(const Transform2D &p_transform, float *p_mat4) { @@ -578,7 +579,7 @@ void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_cou GLES3::CanvasShaderData::BlendMode blend_mode = shader_data_cache ? shader_data_cache->blend_mode : GLES3::CanvasShaderData::BLEND_MODE_MIX; - _record_item_commands(ci, p_canvas_transform_inverse, current_clip, blend_mode, p_lights, index, batch_broken); + _record_item_commands(ci, p_to_render_target, p_canvas_transform_inverse, current_clip, blend_mode, p_lights, index, batch_broken); } if (index == 0) { @@ -623,7 +624,10 @@ void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_cou uint64_t specialization = 0; specialization |= uint64_t(state.canvas_instance_batches[i].lights_disabled); specialization |= uint64_t(!GLES3::Config::get_singleton()->float_texture_supported) << 1; - _bind_material(material_data, variant, specialization); + bool success = _bind_material(material_data, variant, specialization); + if (!success) { + continue; + } GLES3::CanvasShaderData::BlendMode blend_mode = state.canvas_instance_batches[i].blend_mode; @@ -707,7 +711,7 @@ void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_cou r_last_index += index; } -void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, const Transform2D &p_canvas_transform_inverse, Item *¤t_clip, GLES3::CanvasShaderData::BlendMode p_blend_mode, Light *p_lights, uint32_t &r_index, bool &r_batch_broken) { +void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_render_target, const Transform2D &p_canvas_transform_inverse, Item *¤t_clip, GLES3::CanvasShaderData::BlendMode p_blend_mode, Light *p_lights, uint32_t &r_index, bool &r_batch_broken) { RenderingServer::CanvasItemTextureFilter texture_filter = p_item->texture_filter == RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT ? state.default_filter : p_item->texture_filter; if (texture_filter != state.canvas_instance_batches[state.current_batch_index].filter) { @@ -1064,15 +1068,45 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, const Tran state.canvas_instance_batches[state.current_batch_index].tex = m->texture; _update_transform_2d_to_mat2x3(base_transform * draw_transform * m->transform, state.instance_data_array[r_index].world); modulate = m->modulate; + } else if (c->type == Item::Command::TYPE_MULTIMESH) { const Item::CommandMultiMesh *mm = static_cast<const Item::CommandMultiMesh *>(c); state.canvas_instance_batches[state.current_batch_index].tex = mm->texture; - uint32_t instance_count = GLES3::MeshStorage::get_singleton()->multimesh_get_instances_to_draw(mm->multimesh); - if (instance_count > 1) { - state.canvas_instance_batches[state.current_batch_index].shader_variant = CanvasShaderGLES3::MODE_INSTANCED; - } + state.canvas_instance_batches[state.current_batch_index].shader_variant = CanvasShaderGLES3::MODE_INSTANCED; + } else if (c->type == Item::Command::TYPE_PARTICLES) { - WARN_PRINT_ONCE("Particles not supported yet, sorry :("); + GLES3::ParticlesStorage *particles_storage = GLES3::ParticlesStorage::get_singleton(); + GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton(); + + const Item::CommandParticles *pt = static_cast<const Item::CommandParticles *>(c); + RID particles = pt->particles; + state.canvas_instance_batches[state.current_batch_index].tex = pt->texture; + state.canvas_instance_batches[state.current_batch_index].shader_variant = CanvasShaderGLES3::MODE_INSTANCED; + bool local_coords = particles_storage->particles_is_using_local_coords(particles); + + if (particles_storage->particles_has_collision(particles) && texture_storage->render_target_is_sdf_enabled(p_render_target)) { + // Pass collision information. + Transform2D xform; + if (local_coords) { + xform = p_item->final_transform; + } else { + xform = p_canvas_transform_inverse; + } + + GLuint sdf_texture = texture_storage->render_target_get_sdf_texture(p_render_target); + + Rect2 to_screen; + { + Rect2 sdf_rect = texture_storage->render_target_get_sdf_rect(p_render_target); + + to_screen.size = Vector2(1.0 / sdf_rect.size.width, 1.0 / sdf_rect.size.height); + to_screen.position = -sdf_rect.position * to_screen.size; + } + + particles_storage->particles_set_canvas_sdf_collision(pt->particles, true, xform, to_screen, sdf_texture); + } else { + particles_storage->particles_set_canvas_sdf_collision(pt->particles, false, Transform2D(), Rect2(), 0); + } } state.canvas_instance_batches[state.current_batch_index].command = c; @@ -1209,20 +1243,21 @@ void RasterizerCanvasGLES3::_render_batch(Light *p_lights, uint32_t p_index) { case Item::Command::TYPE_MULTIMESH: case Item::Command::TYPE_PARTICLES: { GLES3::MeshStorage *mesh_storage = GLES3::MeshStorage::get_singleton(); + GLES3::ParticlesStorage *particles_storage = GLES3::ParticlesStorage::get_singleton(); RID mesh; RID mesh_instance; - RID texture; uint32_t instance_count = 1; - GLuint multimesh_buffer = 0; - uint32_t multimesh_stride = 0; - uint32_t multimesh_color_offset = 0; - bool multimesh_uses_color = false; - bool multimesh_uses_custom_data = false; + GLuint instance_buffer = 0; + uint32_t instance_stride = 0; + uint32_t instance_color_offset = 0; + bool instance_uses_color = false; + bool instance_uses_custom_data = false; if (state.canvas_instance_batches[p_index].command_type == Item::Command::TYPE_MESH) { const Item::CommandMesh *m = static_cast<const Item::CommandMesh *>(state.canvas_instance_batches[p_index].command); mesh = m->mesh; mesh_instance = m->mesh_instance; + } else if (state.canvas_instance_batches[p_index].command_type == Item::Command::TYPE_MULTIMESH) { const Item::CommandMultiMesh *mm = static_cast<const Item::CommandMultiMesh *>(state.canvas_instance_batches[p_index].command); RID multimesh = mm->multimesh; @@ -1238,13 +1273,41 @@ void RasterizerCanvasGLES3::_render_batch(Light *p_lights, uint32_t p_index) { break; } - multimesh_buffer = mesh_storage->multimesh_get_gl_buffer(multimesh); - multimesh_stride = mesh_storage->multimesh_get_stride(multimesh); - multimesh_color_offset = mesh_storage->multimesh_get_color_offset(multimesh); - multimesh_uses_color = mesh_storage->multimesh_uses_colors(multimesh); - multimesh_uses_custom_data = mesh_storage->multimesh_uses_custom_data(multimesh); + instance_buffer = mesh_storage->multimesh_get_gl_buffer(multimesh); + instance_stride = mesh_storage->multimesh_get_stride(multimesh); + instance_color_offset = mesh_storage->multimesh_get_color_offset(multimesh); + instance_uses_color = mesh_storage->multimesh_uses_colors(multimesh); + instance_uses_custom_data = mesh_storage->multimesh_uses_custom_data(multimesh); + } else if (state.canvas_instance_batches[p_index].command_type == Item::Command::TYPE_PARTICLES) { - // Do nothing for now. + const Item::CommandParticles *pt = static_cast<const Item::CommandParticles *>(state.canvas_instance_batches[p_index].command); + RID particles = pt->particles; + mesh = particles_storage->particles_get_draw_pass_mesh(particles, 0); + + ERR_BREAK(particles_storage->particles_get_mode(particles) != RS::PARTICLES_MODE_2D); + particles_storage->particles_request_process(particles); + + if (particles_storage->particles_is_inactive(particles)) { + break; + } + + RenderingServerDefault::redraw_request(); // Active particles means redraw request. + + int dpc = particles_storage->particles_get_draw_passes(particles); + if (dpc == 0) { + break; // Nothing to draw. + } + + instance_count = particles_storage->particles_get_amount(particles); + instance_buffer = particles_storage->particles_get_gl_buffer(particles); + instance_stride = 12; // 8 bytes for instance transform and 4 bytes for packed color and custom. + instance_color_offset = 8; // 8 bytes for instance transform. + instance_uses_color = true; + instance_uses_custom_data = true; + } + + if (instance_buffer == 0) { + break; } ERR_FAIL_COND(mesh.is_null()); @@ -1277,17 +1340,17 @@ void RasterizerCanvasGLES3::_render_batch(Light *p_lights, uint32_t p_index) { if (instance_count > 1) { // Bind instance buffers. - glBindBuffer(GL_ARRAY_BUFFER, multimesh_buffer); + glBindBuffer(GL_ARRAY_BUFFER, instance_buffer); glEnableVertexAttribArray(1); - glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, multimesh_stride * sizeof(float), CAST_INT_TO_UCHAR_PTR(0)); + glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, instance_stride * sizeof(float), CAST_INT_TO_UCHAR_PTR(0)); glVertexAttribDivisor(1, 1); glEnableVertexAttribArray(2); - glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, multimesh_stride * sizeof(float), CAST_INT_TO_UCHAR_PTR(4 * 4)); + glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, instance_stride * sizeof(float), CAST_INT_TO_UCHAR_PTR(4 * 4)); glVertexAttribDivisor(2, 1); - if (multimesh_uses_color || multimesh_uses_custom_data) { + if (instance_uses_color || instance_uses_custom_data) { glEnableVertexAttribArray(5); - glVertexAttribIPointer(5, 4, GL_UNSIGNED_INT, multimesh_stride * sizeof(float), CAST_INT_TO_UCHAR_PTR(multimesh_color_offset * sizeof(float))); + glVertexAttribIPointer(5, 4, GL_UNSIGNED_INT, instance_stride * sizeof(float), CAST_INT_TO_UCHAR_PTR(instance_color_offset * sizeof(float))); glVertexAttribDivisor(5, 1); } } @@ -1361,17 +1424,17 @@ void RasterizerCanvasGLES3::_new_batch(bool &r_batch_broken, uint32_t &r_index) _align_instance_data_buffer(r_index); } -void RasterizerCanvasGLES3::_bind_material(GLES3::CanvasMaterialData *p_material_data, CanvasShaderGLES3::ShaderVariant p_variant, uint64_t p_specialization) { +bool RasterizerCanvasGLES3::_bind_material(GLES3::CanvasMaterialData *p_material_data, CanvasShaderGLES3::ShaderVariant p_variant, uint64_t p_specialization) { if (p_material_data) { if (p_material_data->shader_data->version.is_valid() && p_material_data->shader_data->valid) { // Bind uniform buffer and textures p_material_data->bind_uniforms(); - GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_bind_shader(p_material_data->shader_data->version, p_variant, p_specialization); + return GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_bind_shader(p_material_data->shader_data->version, p_variant, p_specialization); } else { - GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_bind_shader(data.canvas_shader_default_version, p_variant, p_specialization); + return GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_bind_shader(data.canvas_shader_default_version, p_variant, p_specialization); } } else { - GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_bind_shader(data.canvas_shader_default_version, p_variant, p_specialization); + return GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_bind_shader(data.canvas_shader_default_version, p_variant, p_specialization); } } @@ -1435,7 +1498,10 @@ void RasterizerCanvasGLES3::light_update_shadow(RID p_rid, int p_shadow_index, c RS::CanvasOccluderPolygonCullMode cull_mode = RS::CANVAS_OCCLUDER_POLYGON_CULL_DISABLED; CanvasOcclusionShaderGLES3::ShaderVariant variant = config->float_texture_supported ? CanvasOcclusionShaderGLES3::MODE_SHADOW : CanvasOcclusionShaderGLES3::MODE_SHADOW_RGBA; - shadow_render.shader.version_bind_shader(shadow_render.shader_version, variant); + bool success = shadow_render.shader.version_bind_shader(shadow_render.shader_version, variant); + if (!success) { + return; + } for (int i = 0; i < 4; i++) { glViewport((state.shadow_texture_size / 4) * i, p_shadow_index * 2, (state.shadow_texture_size / 4), 2); @@ -1553,7 +1619,10 @@ void RasterizerCanvasGLES3::light_update_directional_shadow(RID p_rid, int p_sha RS::CanvasOccluderPolygonCullMode cull_mode = RS::CANVAS_OCCLUDER_POLYGON_CULL_DISABLED; CanvasOcclusionShaderGLES3::ShaderVariant variant = config->float_texture_supported ? CanvasOcclusionShaderGLES3::MODE_SHADOW : CanvasOcclusionShaderGLES3::MODE_SHADOW_RGBA; - shadow_render.shader.version_bind_shader(shadow_render.shader_version, variant); + bool success = shadow_render.shader.version_bind_shader(shadow_render.shader_version, variant); + if (!success) { + return; + } Projection projection; projection.set_orthogonal(-half_size, half_size, -0.5, 0.5, 0.0, distance); @@ -1685,7 +1754,10 @@ void RasterizerCanvasGLES3::render_sdf(RID p_render_target, LightOccluderInstanc glClear(GL_COLOR_BUFFER_BIT); CanvasOcclusionShaderGLES3::ShaderVariant variant = CanvasOcclusionShaderGLES3::MODE_SDF; - shadow_render.shader.version_bind_shader(shadow_render.shader_version, variant); + bool success = shadow_render.shader.version_bind_shader(shadow_render.shader_version, variant); + if (!success) { + return; + } shadow_render.shader.version_set_uniform(CanvasOcclusionShaderGLES3::PROJECTION, Projection(), shadow_render.shader_version, variant); shadow_render.shader.version_set_uniform(CanvasOcclusionShaderGLES3::DIRECTION, 0.0, 0.0, shadow_render.shader_version, variant); @@ -2555,7 +2627,6 @@ RasterizerCanvasGLES3::RasterizerCanvasGLES3() { GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.initialize(global_defines); data.canvas_shader_default_version = GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_create(); - GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_bind_shader(data.canvas_shader_default_version, CanvasShaderGLES3::MODE_QUAD); shadow_render.shader.initialize(); shadow_render.shader_version = shadow_render.shader.version_create(); diff --git a/drivers/gles3/rasterizer_canvas_gles3.h b/drivers/gles3/rasterizer_canvas_gles3.h index b7f3a4b801..0a03d43d07 100644 --- a/drivers/gles3/rasterizer_canvas_gles3.h +++ b/drivers/gles3/rasterizer_canvas_gles3.h @@ -352,9 +352,9 @@ public: void canvas_render_items(RID p_to_render_target, Item *p_item_list, const Color &p_modulate, Light *p_light_list, Light *p_directional_list, const Transform2D &p_canvas_transform, RS::CanvasItemTextureFilter p_default_filter, RS::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel, bool &r_sdf_used) override; void _render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, uint32_t &r_last_index, bool p_to_backbuffer = false); - void _record_item_commands(const Item *p_item, const Transform2D &p_canvas_transform_inverse, Item *¤t_clip, GLES3::CanvasShaderData::BlendMode p_blend_mode, Light *p_lights, uint32_t &r_index, bool &r_break_batch); + void _record_item_commands(const Item *p_item, RID p_render_target, const Transform2D &p_canvas_transform_inverse, Item *¤t_clip, GLES3::CanvasShaderData::BlendMode p_blend_mode, Light *p_lights, uint32_t &r_index, bool &r_break_batch); void _render_batch(Light *p_lights, uint32_t p_index); - void _bind_material(GLES3::CanvasMaterialData *p_material_data, CanvasShaderGLES3::ShaderVariant p_variant, uint64_t p_specialization); + bool _bind_material(GLES3::CanvasMaterialData *p_material_data, CanvasShaderGLES3::ShaderVariant p_variant, uint64_t p_specialization); void _new_batch(bool &r_batch_broken, uint32_t &r_index); void _add_to_batch(uint32_t &r_index, bool &r_batch_broken); void _allocate_instance_data_buffer(); diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp index 734911ccdb..026ec85e6b 100644 --- a/drivers/gles3/rasterizer_scene_gles3.cpp +++ b/drivers/gles3/rasterizer_scene_gles3.cpp @@ -35,6 +35,7 @@ #include "servers/rendering/rendering_server_globals.h" #include "storage/config.h" #include "storage/mesh_storage.h" +#include "storage/particles_storage.h" #include "storage/texture_storage.h" #ifdef GLES3_ENABLED @@ -50,6 +51,9 @@ RenderGeometryInstance *RasterizerSceneGLES3::geometry_instance_create(RID p_bas ginstance->data->base = p_base; ginstance->data->base_type = type; + ginstance->data->dependency_tracker.userdata = ginstance; + ginstance->data->dependency_tracker.changed_callback = _geometry_instance_dependency_changed; + ginstance->data->dependency_tracker.deleted_callback = _geometry_instance_dependency_deleted; ginstance->_mark_dirty(); @@ -314,6 +318,8 @@ void RasterizerSceneGLES3::_geometry_instance_add_surface(GeometryInstanceGLES3 void RasterizerSceneGLES3::_geometry_instance_update(RenderGeometryInstance *p_geometry_instance) { GLES3::MeshStorage *mesh_storage = GLES3::MeshStorage::get_singleton(); + GLES3::ParticlesStorage *particles_storage = GLES3::ParticlesStorage::get_singleton(); + GeometryInstanceGLES3 *ginstance = static_cast<GeometryInstanceGLES3 *>(p_geometry_instance); if (ginstance->data->dirty_dependencies) { @@ -361,6 +367,26 @@ void RasterizerSceneGLES3::_geometry_instance_update(RenderGeometryInstance *p_g } break; case RS::INSTANCE_PARTICLES: { + int draw_passes = particles_storage->particles_get_draw_passes(ginstance->data->base); + + for (int j = 0; j < draw_passes; j++) { + RID mesh = particles_storage->particles_get_draw_pass_mesh(ginstance->data->base, j); + if (!mesh.is_valid()) { + continue; + } + + const RID *materials = nullptr; + uint32_t surface_count; + + materials = mesh_storage->mesh_get_surface_count_and_materials(mesh, surface_count); + if (materials) { + for (uint32_t k = 0; k < surface_count; k++) { + _geometry_instance_add_surface(ginstance, k, materials[k], mesh); + } + } + } + + ginstance->instance_count = particles_storage->particles_get_amount(ginstance->data->base); } break; default: { @@ -382,9 +408,17 @@ void RasterizerSceneGLES3::_geometry_instance_update(RenderGeometryInstance *p_g ginstance->base_flags |= INSTANCE_DATA_FLAG_MULTIMESH_HAS_CUSTOM_DATA; } - //ginstance->transforms_uniform_set = mesh_storage->multimesh_get_3d_uniform_set(ginstance->data->base, scene_globals.default_shader_rd, TRANSFORMS_UNIFORM_SET); - } else if (ginstance->data->base_type == RS::INSTANCE_PARTICLES) { + ginstance->base_flags |= INSTANCE_DATA_FLAG_PARTICLES; + ginstance->base_flags |= INSTANCE_DATA_FLAG_MULTIMESH; + + ginstance->base_flags |= INSTANCE_DATA_FLAG_MULTIMESH_HAS_COLOR; + ginstance->base_flags |= INSTANCE_DATA_FLAG_MULTIMESH_HAS_CUSTOM_DATA; + + if (!particles_storage->particles_is_using_local_coords(ginstance->data->base)) { + store_transform = false; + } + } else if (ginstance->data->base_type == RS::INSTANCE_MESH) { } @@ -751,12 +785,16 @@ void RasterizerSceneGLES3::_draw_sky(RID p_env, const Projection &p_projection, sky_transform.invert(); sky_transform = p_transform.basis * sky_transform; - GLES3::MaterialStorage::get_singleton()->shaders.sky_shader.version_bind_shader(shader_data->version, SkyShaderGLES3::MODE_BACKGROUND); - GLES3::MaterialStorage::get_singleton()->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::ORIENTATION, sky_transform, shader_data->version, SkyShaderGLES3::MODE_BACKGROUND); - GLES3::MaterialStorage::get_singleton()->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::PROJECTION, camera.columns[2][0], camera.columns[0][0], camera.columns[2][1], camera.columns[1][1], shader_data->version, SkyShaderGLES3::MODE_BACKGROUND); - GLES3::MaterialStorage::get_singleton()->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::POSITION, p_transform.origin, shader_data->version, SkyShaderGLES3::MODE_BACKGROUND); - GLES3::MaterialStorage::get_singleton()->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::TIME, time, shader_data->version, SkyShaderGLES3::MODE_BACKGROUND); - GLES3::MaterialStorage::get_singleton()->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::LUMINANCE_MULTIPLIER, p_luminance_multiplier, shader_data->version, SkyShaderGLES3::MODE_BACKGROUND); + bool success = material_storage->shaders.sky_shader.version_bind_shader(shader_data->version, SkyShaderGLES3::MODE_BACKGROUND); + if (!success) { + return; + } + + material_storage->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::ORIENTATION, sky_transform, shader_data->version, SkyShaderGLES3::MODE_BACKGROUND); + material_storage->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::PROJECTION, camera.columns[2][0], camera.columns[0][0], camera.columns[2][1], camera.columns[1][1], shader_data->version, SkyShaderGLES3::MODE_BACKGROUND); + material_storage->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::POSITION, p_transform.origin, shader_data->version, SkyShaderGLES3::MODE_BACKGROUND); + material_storage->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::TIME, time, shader_data->version, SkyShaderGLES3::MODE_BACKGROUND); + material_storage->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::LUMINANCE_MULTIPLIER, p_luminance_multiplier, shader_data->version, SkyShaderGLES3::MODE_BACKGROUND); glBindVertexArray(sky_globals.screen_triangle_array); glDrawArrays(GL_TRIANGLES, 0, 3); @@ -850,12 +888,15 @@ void RasterizerSceneGLES3::_update_sky_radiance(RID p_env, const Projection &p_p correction.columns[1][1] = -1.0; cm = correction * cm; - GLES3::MaterialStorage::get_singleton()->shaders.sky_shader.version_bind_shader(shader_data->version, SkyShaderGLES3::MODE_CUBEMAP); + bool success = material_storage->shaders.sky_shader.version_bind_shader(shader_data->version, SkyShaderGLES3::MODE_CUBEMAP); + if (!success) { + return; + } - GLES3::MaterialStorage::get_singleton()->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::POSITION, p_transform.origin, shader_data->version, SkyShaderGLES3::MODE_CUBEMAP); - GLES3::MaterialStorage::get_singleton()->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::TIME, time, shader_data->version, SkyShaderGLES3::MODE_CUBEMAP); - GLES3::MaterialStorage::get_singleton()->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::PROJECTION, cm.columns[2][0], cm.columns[0][0], cm.columns[2][1], cm.columns[1][1], shader_data->version, SkyShaderGLES3::MODE_CUBEMAP); - GLES3::MaterialStorage::get_singleton()->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::LUMINANCE_MULTIPLIER, p_luminance_multiplier, shader_data->version, SkyShaderGLES3::MODE_CUBEMAP); + material_storage->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::POSITION, p_transform.origin, shader_data->version, SkyShaderGLES3::MODE_CUBEMAP); + material_storage->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::TIME, time, shader_data->version, SkyShaderGLES3::MODE_CUBEMAP); + material_storage->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::PROJECTION, cm.columns[2][0], cm.columns[0][0], cm.columns[2][1], cm.columns[1][1], shader_data->version, SkyShaderGLES3::MODE_CUBEMAP); + material_storage->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::LUMINANCE_MULTIPLIER, p_luminance_multiplier, shader_data->version, SkyShaderGLES3::MODE_CUBEMAP); glBindVertexArray(sky_globals.screen_triangle_array); @@ -864,7 +905,7 @@ void RasterizerSceneGLES3::_update_sky_radiance(RID p_env, const Projection &p_p for (int i = 0; i < 6; i++) { Basis local_view = Basis::looking_at(view_normals[i], view_up[i]); - GLES3::MaterialStorage::get_singleton()->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::ORIENTATION, local_view, shader_data->version, SkyShaderGLES3::MODE_CUBEMAP); + material_storage->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::ORIENTATION, local_view, shader_data->version, SkyShaderGLES3::MODE_CUBEMAP); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, sky->raw_radiance, 0); glDrawArrays(GL_TRIANGLES, 0, 3); } @@ -945,7 +986,10 @@ void RasterizerSceneGLES3::_filter_sky_radiance(Sky *p_sky, int p_base_layer) { glViewport(0, 0, size, size); glBindVertexArray(sky_globals.screen_triangle_array); - material_storage->shaders.cubemap_filter_shader.version_bind_shader(scene_globals.cubemap_filter_shader_version, mode); + bool success = material_storage->shaders.cubemap_filter_shader.version_bind_shader(scene_globals.cubemap_filter_shader_version, mode); + if (!success) { + return; + } if (p_base_layer > 0) { const uint32_t sample_counts[4] = { 1, sky_globals.ggx_samples / 4, sky_globals.ggx_samples / 2, sky_globals.ggx_samples }; @@ -1851,8 +1895,11 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_ glColorMask(0, 0, 0, 0); glClearDepth(1.0f); glClear(GL_DEPTH_BUFFER_BIT); + uint32_t spec_constant = SceneShaderGLES3::DISABLE_FOG | SceneShaderGLES3::DISABLE_LIGHT_DIRECTIONAL | + SceneShaderGLES3::DISABLE_LIGHTMAP | SceneShaderGLES3::DISABLE_LIGHT_OMNI | + SceneShaderGLES3::DISABLE_LIGHT_SPOT; - RenderListParameters render_list_params(render_list[RENDER_LIST_OPAQUE].elements.ptr(), render_list[RENDER_LIST_OPAQUE].elements.size(), reverse_cull, 0, use_wireframe); + RenderListParameters render_list_params(render_list[RENDER_LIST_OPAQUE].elements.ptr(), render_list[RENDER_LIST_OPAQUE].elements.size(), reverse_cull, spec_constant, use_wireframe); _render_list_template<PASS_MODE_DEPTH>(&render_list_params, &render_data, 0, render_list[RENDER_LIST_OPAQUE].elements.size()); glColorMask(1, 1, 1, 1); @@ -1894,11 +1941,11 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_ { // Specialization Constants that apply for entire rendering pass. if (render_data.directional_light_count == 0) { - spec_constant_base_flags |= 1 << SPEC_CONSTANT_DISABLE_DIRECTIONAL_LIGHTS; + spec_constant_base_flags |= SceneShaderGLES3::DISABLE_LIGHT_DIRECTIONAL; } if (render_data.environment.is_null() || (render_data.environment.is_valid() && !environment_get_fog_enabled(render_data.environment))) { - spec_constant_base_flags |= 1 << SPEC_CONSTANT_DISABLE_FOG; + spec_constant_base_flags |= SceneShaderGLES3::DISABLE_FOG; } } // Render Opaque Objects. @@ -1947,6 +1994,7 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_ template <PassMode p_pass_mode> void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, const RenderDataGLES3 *p_render_data, uint32_t p_from_element, uint32_t p_to_element, bool p_alpha_pass) { GLES3::MeshStorage *mesh_storage = GLES3::MeshStorage::get_singleton(); + GLES3::ParticlesStorage *particles_storage = GLES3::ParticlesStorage::get_singleton(); GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton(); GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton(); GLES3::Config *config = GLES3::Config::get_singleton(); @@ -1960,11 +2008,10 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, SceneShaderGLES3::ShaderVariant prev_variant = SceneShaderGLES3::ShaderVariant::MODE_COLOR; SceneShaderGLES3::ShaderVariant shader_variant = SceneShaderGLES3::MODE_COLOR; // Assigned to silence wrong -Wmaybe-initialized - // @todo Get this from p_params->spec_constant_base_flags instead of hardcoding it. - uint32_t base_spec_constants = 0; + uint32_t base_spec_constants = p_params->spec_constant_base_flags; if (p_render_data->view_count > 1) { - base_spec_constants |= 1 << SPEC_CONSTANT_USE_MULTIVIEW; + base_spec_constants |= SceneShaderGLES3::USE_MULTIVIEW; } switch (p_pass_mode) { @@ -1987,7 +2034,7 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, Sky *sky = sky_owner.get_or_null(environment_get_sky(p_render_data->environment)); if (sky && sky->radiance != 0) { texture_to_bind = sky->radiance; - // base_spec_constant |= USE_RADIANCE_MAP; + base_spec_constants |= SceneShaderGLES3::USE_RADIANCE_MAP; } glBindTexture(GL_TEXTURE_CUBE_MAP, texture_to_bind); } @@ -2157,7 +2204,7 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, } bool use_index_buffer = index_array_gl != 0; - if (prev_index_array_gl != index_array_gl) { + if (prev_index_array_gl != index_array_gl || prev_vertex_array_gl != vertex_array_gl) { if (index_array_gl != 0) { // Bind index each time so we can use LODs glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_array_gl); @@ -2177,11 +2224,16 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, SceneShaderGLES3::ShaderVariant instance_variant = shader_variant; if (inst->instance_count > 0) { + // Will need to use instancing to draw (either MultiMesh or Particles). instance_variant = SceneShaderGLES3::ShaderVariant(1 + int(shader_variant)); } if (prev_shader != shader || prev_variant != instance_variant) { - material_storage->shaders.scene_shader.version_bind_shader(shader->version, instance_variant, base_spec_constants); + bool success = material_storage->shaders.scene_shader.version_bind_shader(shader->version, instance_variant, base_spec_constants); + if (!success) { + continue; + } + float opaque_prepass_threshold = 0.0; if constexpr (p_pass_mode == PASS_MODE_DEPTH) { opaque_prepass_threshold = 0.99; @@ -2213,25 +2265,42 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::WORLD_TRANSFORM, world_transform, shader->version, instance_variant, base_spec_constants); if (inst->instance_count > 0) { - // Using MultiMesh. + // Using MultiMesh or Particles. // Bind instance buffers. - GLuint multimesh_buffer = mesh_storage->multimesh_get_gl_buffer(inst->data->base); - glBindBuffer(GL_ARRAY_BUFFER, multimesh_buffer); - uint32_t multimesh_stride = mesh_storage->multimesh_get_stride(inst->data->base); + GLuint instance_buffer = 0; + uint32_t stride = 0; + if (inst->flags_cache & INSTANCE_DATA_FLAG_PARTICLES) { + instance_buffer = particles_storage->particles_get_gl_buffer(inst->data->base); + stride = 16; // 12 bytes for instance transform and 4 bytes for packed color and custom. + } else { + instance_buffer = mesh_storage->multimesh_get_gl_buffer(inst->data->base); + stride = mesh_storage->multimesh_get_stride(inst->data->base); + } + + if (instance_buffer == 0) { + // Instance buffer not initialized yet. Skip rendering for now. + continue; + } + + glBindBuffer(GL_ARRAY_BUFFER, instance_buffer); + glEnableVertexAttribArray(12); - glVertexAttribPointer(12, 4, GL_FLOAT, GL_FALSE, multimesh_stride * sizeof(float), CAST_INT_TO_UCHAR_PTR(0)); + glVertexAttribPointer(12, 4, GL_FLOAT, GL_FALSE, stride * sizeof(float), CAST_INT_TO_UCHAR_PTR(0)); glVertexAttribDivisor(12, 1); glEnableVertexAttribArray(13); - glVertexAttribPointer(13, 4, GL_FLOAT, GL_FALSE, multimesh_stride * sizeof(float), CAST_INT_TO_UCHAR_PTR(4 * 4)); + glVertexAttribPointer(13, 4, GL_FLOAT, GL_FALSE, stride * sizeof(float), CAST_INT_TO_UCHAR_PTR(sizeof(float) * 4)); glVertexAttribDivisor(13, 1); - glEnableVertexAttribArray(14); - glVertexAttribPointer(14, 4, GL_FLOAT, GL_FALSE, multimesh_stride * sizeof(float), CAST_INT_TO_UCHAR_PTR(4 * 8)); - glVertexAttribDivisor(14, 1); + if (!(inst->flags_cache & INSTANCE_DATA_FLAG_MULTIMESH_FORMAT_2D)) { + glEnableVertexAttribArray(14); + glVertexAttribPointer(14, 4, GL_FLOAT, GL_FALSE, stride * sizeof(float), CAST_INT_TO_UCHAR_PTR(sizeof(float) * 8)); + glVertexAttribDivisor(14, 1); + } - if (mesh_storage->multimesh_uses_colors(inst->data->base) || mesh_storage->multimesh_uses_custom_data(inst->data->base)) { + if ((inst->flags_cache & INSTANCE_DATA_FLAG_MULTIMESH_HAS_COLOR) || (inst->flags_cache & INSTANCE_DATA_FLAG_MULTIMESH_HAS_CUSTOM_DATA)) { + uint32_t color_custom_offset = inst->flags_cache & INSTANCE_DATA_FLAG_MULTIMESH_FORMAT_2D ? 8 : 12; glEnableVertexAttribArray(15); - glVertexAttribIPointer(15, 4, GL_UNSIGNED_INT, multimesh_stride * sizeof(float), CAST_INT_TO_UCHAR_PTR(mesh_storage->multimesh_get_color_offset(inst->data->base) * sizeof(float))); + glVertexAttribIPointer(15, 4, GL_UNSIGNED_INT, stride * sizeof(float), CAST_INT_TO_UCHAR_PTR(color_custom_offset * sizeof(float))); glVertexAttribDivisor(15, 1); } if (use_index_buffer) { @@ -2265,6 +2334,72 @@ void RasterizerSceneGLES3::render_material(const Transform3D &p_cam_transform, c } void RasterizerSceneGLES3::render_particle_collider_heightfield(RID p_collider, const Transform3D &p_transform, const PagedArray<RenderGeometryInstance *> &p_instances) { + GLES3::ParticlesStorage *particles_storage = GLES3::ParticlesStorage::get_singleton(); + + ERR_FAIL_COND(!particles_storage->particles_collision_is_heightfield(p_collider)); + Vector3 extents = particles_storage->particles_collision_get_extents(p_collider) * p_transform.basis.get_scale(); + Projection cm; + cm.set_orthogonal(-extents.x, extents.x, -extents.z, extents.z, 0, extents.y * 2.0); + + Vector3 cam_pos = p_transform.origin; + cam_pos.y += extents.y; + + Transform3D cam_xform; + cam_xform.set_look_at(cam_pos, cam_pos - p_transform.basis.get_column(Vector3::AXIS_Y), -p_transform.basis.get_column(Vector3::AXIS_Z).normalized()); + + GLuint fb = particles_storage->particles_collision_get_heightfield_framebuffer(p_collider); + Size2i fb_size = particles_storage->particles_collision_get_heightfield_size(p_collider); + + RENDER_TIMESTAMP("Setup GPUParticlesCollisionHeightField3D"); + + RenderDataGLES3 render_data; + + render_data.cam_projection = cm; + render_data.cam_transform = cam_xform; + render_data.view_projection[0] = cm; + render_data.inv_cam_transform = render_data.cam_transform.affine_inverse(); + render_data.cam_orthogonal = true; + render_data.z_near = 0.0; + render_data.z_far = cm.get_z_far(); + + render_data.instances = &p_instances; + + _setup_environment(&render_data, true, Vector2(fb_size), true, Color(), false); + + PassMode pass_mode = PASS_MODE_SHADOW; + + _fill_render_list(RENDER_LIST_SECONDARY, &render_data, pass_mode); + render_list[RENDER_LIST_SECONDARY].sort_by_key(); + + RENDER_TIMESTAMP("Render Collider Heightfield"); + + glBindFramebuffer(GL_FRAMEBUFFER, fb); + glViewport(0, 0, fb_size.width, fb_size.height); + + GLuint global_buffer = GLES3::MaterialStorage::get_singleton()->global_shader_parameters_get_uniform_buffer(); + + glBindBufferBase(GL_UNIFORM_BUFFER, SCENE_GLOBALS_UNIFORM_LOCATION, global_buffer); + glBindBuffer(GL_UNIFORM_BUFFER, 0); + + glDisable(GL_BLEND); + glDepthMask(GL_TRUE); + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LESS); + glDisable(GL_SCISSOR_TEST); + glCullFace(GL_BACK); + glEnable(GL_CULL_FACE); + scene_state.cull_mode = GLES3::SceneShaderData::CULL_BACK; + + glColorMask(0, 0, 0, 0); + glClearDepth(1.0f); + glClear(GL_DEPTH_BUFFER_BIT); + + RenderListParameters render_list_params(render_list[RENDER_LIST_SECONDARY].elements.ptr(), render_list[RENDER_LIST_SECONDARY].elements.size(), false, 31, false); + + _render_list_template<PASS_MODE_SHADOW>(&render_list_params, &render_data, 0, render_list[RENDER_LIST_SECONDARY].elements.size()); + + glColorMask(1, 1, 1, 1); + glBindFramebuffer(GL_FRAMEBUFFER, 0); } void RasterizerSceneGLES3::set_time(double p_time, double p_step) { @@ -2415,7 +2550,7 @@ RasterizerSceneGLES3::RasterizerSceneGLES3() { global_defines += "#define MAX_GLOBAL_SHADER_UNIFORMS 256\n"; // TODO: this is arbitrary for now global_defines += "\n#define MAX_LIGHT_DATA_STRUCTS " + itos(config->max_renderable_lights) + "\n"; global_defines += "\n#define MAX_DIRECTIONAL_LIGHT_DATA_STRUCTS " + itos(MAX_DIRECTIONAL_LIGHTS) + "\n"; - global_defines += "\n#define MAX_FORWARD_LIGHTS " + itos(config->max_lights_per_object) + "\n"; + global_defines += "\n#define MAX_FORWARD_LIGHTS uint(" + itos(config->max_lights_per_object) + ")\n"; material_storage->shaders.scene_shader.initialize(global_defines); scene_globals.shader_default_version = material_storage->shaders.scene_shader.version_create(); material_storage->shaders.scene_shader.version_bind_shader(scene_globals.shader_default_version, SceneShaderGLES3::MODE_COLOR); @@ -2455,7 +2590,6 @@ void fragment() { global_defines += "\n#define MAX_DIRECTIONAL_LIGHT_DATA_STRUCTS " + itos(sky_globals.max_directional_lights) + "\n"; material_storage->shaders.sky_shader.initialize(global_defines); sky_globals.shader_default_version = material_storage->shaders.sky_shader.version_create(); - material_storage->shaders.sky_shader.version_bind_shader(sky_globals.shader_default_version, SkyShaderGLES3::MODE_BACKGROUND); } { @@ -2463,7 +2597,6 @@ void fragment() { global_defines += "\n#define MAX_SAMPLE_COUNT " + itos(sky_globals.ggx_samples) + "\n"; material_storage->shaders.cubemap_filter_shader.initialize(global_defines); scene_globals.cubemap_filter_shader_version = material_storage->shaders.cubemap_filter_shader.version_create(); - material_storage->shaders.cubemap_filter_shader.version_bind_shader(scene_globals.cubemap_filter_shader_version, CubemapFilterShaderGLES3::MODE_DEFAULT); } { diff --git a/drivers/gles3/rasterizer_scene_gles3.h b/drivers/gles3/rasterizer_scene_gles3.h index 4bd0a7188d..6e1f1babf8 100644 --- a/drivers/gles3/rasterizer_scene_gles3.h +++ b/drivers/gles3/rasterizer_scene_gles3.h @@ -85,16 +85,6 @@ enum SkyUniformLocation { SKY_DIRECTIONAL_LIGHT_UNIFORM_LOCATION, }; -enum { - SPEC_CONSTANT_DISABLE_LIGHTMAP = 0, - SPEC_CONSTANT_DISABLE_DIRECTIONAL_LIGHTS = 1, - SPEC_CONSTANT_DISABLE_OMNI_LIGHTS = 2, - SPEC_CONSTANT_DISABLE_SPOT_LIGHTS = 3, - SPEC_CONSTANT_DISABLE_FOG = 4, - SPEC_CONSTANT_USE_RADIANCE_MAP = 5, - SPEC_CONSTANT_USE_MULTIVIEW = 6, -}; - struct RenderDataGLES3 { Ref<RenderSceneBuffersGLES3> render_buffers; bool transparent_bg = false; diff --git a/drivers/gles3/shader_gles3.cpp b/drivers/gles3/shader_gles3.cpp index 2ff7f72180..1dcd17ea0e 100644 --- a/drivers/gles3/shader_gles3.cpp +++ b/drivers/gles3/shader_gles3.cpp @@ -92,7 +92,7 @@ void ShaderGLES3::_add_stage(const char *p_code, StageType p_stage_type) { } } -void ShaderGLES3::_setup(const char *p_vertex_code, const char *p_fragment_code, const char *p_name, int p_uniform_count, const char **p_uniform_names, int p_ubo_count, const UBOPair *p_ubos, int p_texture_count, const TexUnitPair *p_tex_units, int p_specialization_count, const Specialization *p_specializations, int p_variant_count, const char **p_variants) { +void ShaderGLES3::_setup(const char *p_vertex_code, const char *p_fragment_code, const char *p_name, int p_uniform_count, const char **p_uniform_names, int p_ubo_count, const UBOPair *p_ubos, int p_feedback_count, const Feedback *p_feedback, int p_texture_count, const TexUnitPair *p_tex_units, int p_specialization_count, const Specialization *p_specializations, int p_variant_count, const char **p_variants) { name = p_name; if (p_vertex_code) { @@ -118,6 +118,8 @@ void ShaderGLES3::_setup(const char *p_vertex_code, const char *p_fragment_code, } variant_defines = p_variants; variant_count = p_variant_count; + feedbacks = p_feedback; + feedback_count = p_feedback_count; StringBuilder tohash; /* @@ -339,9 +341,21 @@ void ShaderGLES3::_compile_specialization(Version::Specialization &spec, uint32_ glAttachShader(spec.id, spec.frag_id); glAttachShader(spec.id, spec.vert_id); - //for (int i = 0; i < attribute_pair_count; i++) { - // glBindAttribLocation(v.id, attribute_pairs[i].index, attribute_pairs[i].name); - //} + // If feedback exists, set it up. + + if (feedback_count) { + Vector<const char *> feedback; + for (int i = 0; i < feedback_count; i++) { + if (feedbacks[i].specialization == 0 || (feedbacks[i].specialization & p_specialization)) { + // Specialization for this feedback is enabled + feedback.push_back(feedbacks[i].name); + } + } + + if (feedback.size()) { + glTransformFeedbackVaryings(spec.id, feedback.size(), feedback.ptr(), GL_INTERLEAVED_ATTRIBS); + } + } glLinkProgram(spec.id); diff --git a/drivers/gles3/shader_gles3.h b/drivers/gles3/shader_gles3.h index 3ab7642357..83b950ef45 100644 --- a/drivers/gles3/shader_gles3.h +++ b/drivers/gles3/shader_gles3.h @@ -70,6 +70,11 @@ protected: bool default_value = false; }; + struct Feedback { + const char *name; + uint64_t specialization; + }; + private: //versions CharString general_defines; @@ -165,6 +170,8 @@ private: int uniform_count = 0; const UBOPair *ubo_pairs = nullptr; int ubo_count = 0; + const Feedback *feedbacks; + int feedback_count = 0; const TexUnitPair *texunit_pairs = nullptr; int texunit_pair_count = 0; int specialization_count = 0; @@ -178,13 +185,13 @@ private: protected: ShaderGLES3(); - void _setup(const char *p_vertex_code, const char *p_fragment_code, const char *p_name, int p_uniform_count, const char **p_uniform_names, int p_ubo_count, const UBOPair *p_ubos, int p_texture_count, const TexUnitPair *p_tex_units, int p_specialization_count, const Specialization *p_specializations, int p_variant_count, const char **p_variants); + void _setup(const char *p_vertex_code, const char *p_fragment_code, const char *p_name, int p_uniform_count, const char **p_uniform_names, int p_ubo_count, const UBOPair *p_ubos, int p_feedback_count, const Feedback *p_feedback, int p_texture_count, const TexUnitPair *p_tex_units, int p_specialization_count, const Specialization *p_specializations, int p_variant_count, const char **p_variants); - _FORCE_INLINE_ void _version_bind_shader(RID p_version, int p_variant, uint64_t p_specialization) { - ERR_FAIL_INDEX(p_variant, variant_count); + _FORCE_INLINE_ bool _version_bind_shader(RID p_version, int p_variant, uint64_t p_specialization) { + ERR_FAIL_INDEX_V(p_variant, variant_count, false); Version *version = version_owner.get_or_null(p_version); - ERR_FAIL_COND(!version); + ERR_FAIL_COND_V(!version, false); if (version->variants.size() == 0) { _initialize_version(version); //may lack initialization @@ -210,11 +217,12 @@ protected: if (!spec || !spec->ok) { WARN_PRINT_ONCE("shader failed to compile, unable to bind shader."); - return; + return false; } glUseProgram(spec->id); current_shader = spec; + return true; } _FORCE_INLINE_ int _version_get_uniform(int p_which, RID p_version, int p_variant, uint64_t p_specialization) { diff --git a/drivers/gles3/shaders/SCsub b/drivers/gles3/shaders/SCsub index b8bb08ec34..2686b1aa48 100644 --- a/drivers/gles3/shaders/SCsub +++ b/drivers/gles3/shaders/SCsub @@ -19,3 +19,5 @@ if "GLES3_GLSL" in env["BUILDERS"]: env.GLES3_GLSL("cubemap_filter.glsl") env.GLES3_GLSL("canvas_occlusion.glsl") env.GLES3_GLSL("canvas_sdf.glsl") + env.GLES3_GLSL("particles.glsl") + env.GLES3_GLSL("particles_copy.glsl") diff --git a/drivers/gles3/shaders/cubemap_filter.glsl b/drivers/gles3/shaders/cubemap_filter.glsl index 88464876f1..6fcb23204d 100644 --- a/drivers/gles3/shaders/cubemap_filter.glsl +++ b/drivers/gles3/shaders/cubemap_filter.glsl @@ -31,7 +31,7 @@ uniform samplerCube source_cube; //texunit:0 uniform int face_id; #ifndef MODE_DIRECT_WRITE -uniform int sample_count; +uniform uint sample_count; uniform vec4 sample_directions_mip[MAX_SAMPLE_COUNT]; uniform float weight; #endif @@ -105,7 +105,7 @@ void main() { T[1] = cross(N, T[0]); T[2] = N; - for (int sample_num = 0; sample_num < sample_count; sample_num++) { + for (uint sample_num = 0u; sample_num < sample_count; sample_num++) { vec4 sample_direction_mip = sample_directions_mip[sample_num]; vec3 L = T * sample_direction_mip.xyz; vec3 val = textureLod(source_cube, L, sample_direction_mip.w).rgb; diff --git a/drivers/gles3/shaders/particles.glsl b/drivers/gles3/shaders/particles.glsl new file mode 100644 index 0000000000..f8741a22ab --- /dev/null +++ b/drivers/gles3/shaders/particles.glsl @@ -0,0 +1,501 @@ +/* clang-format off */ +#[modes] + +mode_default = + +#[specializations] + +MODE_3D = false +USERDATA1_USED = false +USERDATA2_USED = false +USERDATA3_USED = false +USERDATA4_USED = false +USERDATA5_USED = false +USERDATA6_USED = false + +#[vertex] + +#define SDF_MAX_LENGTH 16384.0 + +layout(std140) uniform GlobalShaderUniformData { //ubo:1 + vec4 global_shader_uniforms[MAX_GLOBAL_SHADER_UNIFORMS]; +}; + +// This needs to be outside clang-format so the ubo comment is in the right place +#ifdef MATERIAL_UNIFORMS_USED +layout(std140) uniform MaterialUniforms{ //ubo:2 + +#MATERIAL_UNIFORMS + +}; +#endif + +/* clang-format on */ + +#define MAX_ATTRACTORS 32 + +#define ATTRACTOR_TYPE_SPHERE uint(0) +#define ATTRACTOR_TYPE_BOX uint(1) +#define ATTRACTOR_TYPE_VECTOR_FIELD uint(2) + +struct Attractor { + mat4 transform; + vec4 extents; // Extents or radius. w-channel is padding. + + uint type; + float strength; + float attenuation; + float directionality; +}; + +#define MAX_COLLIDERS 32 + +#define COLLIDER_TYPE_SPHERE uint(0) +#define COLLIDER_TYPE_BOX uint(1) +#define COLLIDER_TYPE_SDF uint(2) +#define COLLIDER_TYPE_HEIGHT_FIELD uint(3) +#define COLLIDER_TYPE_2D_SDF uint(4) + +struct Collider { + mat4 transform; + vec4 extents; // Extents or radius. w-channel is padding. + + uint type; + float scale; + float pad0; + float pad1; +}; + +layout(std140) uniform FrameData { //ubo:0 + bool emitting; + uint cycle; + float system_phase; + float prev_system_phase; + + float explosiveness; + float randomness; + float time; + float delta; + + float particle_size; + float pad0; + float pad1; + float pad2; + + uint random_seed; + uint attractor_count; + uint collider_count; + uint frame; + + mat4 emission_transform; + + Attractor attractors[MAX_ATTRACTORS]; + Collider colliders[MAX_COLLIDERS]; +}; + +#define PARTICLE_FLAG_ACTIVE uint(1) +#define PARTICLE_FLAG_STARTED uint(2) +#define PARTICLE_FLAG_TRAILED uint(4) +#define PARTICLE_FRAME_MASK uint(0xFFFF) +#define PARTICLE_FRAME_SHIFT uint(16) + +// ParticleData +layout(location = 0) in highp vec4 color; +layout(location = 1) in highp vec4 velocity_flags; +layout(location = 2) in highp vec4 custom; +layout(location = 3) in highp vec4 xform_1; +layout(location = 4) in highp vec4 xform_2; +#ifdef MODE_3D +layout(location = 5) in highp vec4 xform_3; +#endif +#ifdef USERDATA1_USED +layout(location = 6) in highp vec4 userdata1; +#endif +#ifdef USERDATA2_USED +layout(location = 7) in highp vec4 userdata2; +#endif +#ifdef USERDATA3_USED +layout(location = 8) in highp vec4 userdata3; +#endif +#ifdef USERDATA4_USED +layout(location = 9) in highp vec4 userdata4; +#endif +#ifdef USERDATA5_USED +layout(location = 10) in highp vec4 userdata5; +#endif +#ifdef USERDATA6_USED +layout(location = 11) in highp vec4 userdata6; +#endif + +out highp vec4 out_color; //tfb: +out highp vec4 out_velocity_flags; //tfb: +out highp vec4 out_custom; //tfb: +out highp vec4 out_xform_1; //tfb: +out highp vec4 out_xform_2; //tfb: +#ifdef MODE_3D +out highp vec4 out_xform_3; //tfb:MODE_3D +#endif +#ifdef USERDATA1_USED +out highp vec4 out_userdata1; //tfb:USERDATA1_USED +#endif +#ifdef USERDATA2_USED +out highp vec4 out_userdata2; //tfb:USERDATA2_USED +#endif +#ifdef USERDATA3_USED +out highp vec4 out_userdata3; //tfb:USERDATA3_USED +#endif +#ifdef USERDATA4_USED +out highp vec4 out_userdata4; //tfb:USERDATA4_USED +#endif +#ifdef USERDATA5_USED +out highp vec4 out_userdata5; //tfb:USERDATA5_USED +#endif +#ifdef USERDATA6_USED +out highp vec4 out_userdata6; //tfb:USERDATA6_USED +#endif + +uniform sampler2D height_field_texture; //texunit:0 + +uniform float lifetime; +uniform bool clear; +uniform uint total_particles; +uniform bool use_fractional_delta; + +uint hash(uint x) { + x = ((x >> uint(16)) ^ x) * uint(0x45d9f3b); + x = ((x >> uint(16)) ^ x) * uint(0x45d9f3b); + x = (x >> uint(16)) ^ x; + return x; +} + +vec3 safe_normalize(vec3 direction) { + const float EPSILON = 0.001; + if (length(direction) < EPSILON) { + return vec3(0.0); + } + return normalize(direction); +} + +// Needed whenever 2D sdf texture is read from as it is packed in RGBA8. +float vec4_to_float(vec4 p_vec) { + return dot(p_vec, vec4(1.0 / (255.0 * 255.0 * 255.0), 1.0 / (255.0 * 255.0), 1.0 / 255.0, 1.0)) * 2.0 - 1.0; +} + +#GLOBALS + +void main() { + bool apply_forces = true; + bool apply_velocity = true; + float local_delta = delta; + + float mass = 1.0; + + bool restart = false; + + bool restart_position = false; + bool restart_rotation_scale = false; + bool restart_velocity = false; + bool restart_color = false; + bool restart_custom = false; + + mat4 xform = mat4(1.0); + uint flags = 0u; + + if (clear) { + out_color = vec4(1.0); + out_custom = vec4(0.0); + out_velocity_flags = vec4(0.0); + } else { + out_color = color; + out_velocity_flags = velocity_flags; + out_custom = custom; + xform[0] = xform_1; + xform[1] = xform_2; +#ifdef MODE_3D + xform[2] = xform_3; +#endif + xform = transpose(xform); + flags = floatBitsToUint(velocity_flags.w); + } + + //clear started flag if set + flags &= ~PARTICLE_FLAG_STARTED; + + bool collided = false; + vec3 collision_normal = vec3(0.0); + float collision_depth = 0.0; + + vec3 attractor_force = vec3(0.0); + +#if !defined(DISABLE_VELOCITY) + + if (bool(flags & PARTICLE_FLAG_ACTIVE)) { + xform[3].xyz += out_velocity_flags.xyz * local_delta; + } +#endif + uint index = uint(gl_VertexID); + if (emitting) { + float restart_phase = float(index) / float(total_particles); + + if (randomness > 0.0) { + uint seed = cycle; + if (restart_phase >= system_phase) { + seed -= uint(1); + } + seed *= uint(total_particles); + seed += index; + float random = float(hash(seed) % uint(65536)) / 65536.0; + restart_phase += randomness * random * 1.0 / float(total_particles); + } + + restart_phase *= (1.0 - explosiveness); + + if (system_phase > prev_system_phase) { + // restart_phase >= prev_system_phase is used so particles emit in the first frame they are processed + + if (restart_phase >= prev_system_phase && restart_phase < system_phase) { + restart = true; + if (use_fractional_delta) { + local_delta = (system_phase - restart_phase) * lifetime; + } + } + + } else if (delta > 0.0) { + if (restart_phase >= prev_system_phase) { + restart = true; + if (use_fractional_delta) { + local_delta = (1.0 - restart_phase + system_phase) * lifetime; + } + + } else if (restart_phase < system_phase) { + restart = true; + if (use_fractional_delta) { + local_delta = (system_phase - restart_phase) * lifetime; + } + } + } + + if (restart) { + flags = emitting ? (PARTICLE_FLAG_ACTIVE | PARTICLE_FLAG_STARTED | (cycle << PARTICLE_FRAME_SHIFT)) : 0u; + restart_position = true; + restart_rotation_scale = true; + restart_velocity = true; + restart_color = true; + restart_custom = true; + } + } + + bool particle_active = bool(flags & PARTICLE_FLAG_ACTIVE); + + uint particle_number = (flags >> PARTICLE_FRAME_SHIFT) * uint(total_particles) + index; + + if (restart && particle_active) { +#CODE : START + } + + if (particle_active) { + for (uint i = 0u; i < attractor_count; i++) { + vec3 dir; + float amount; + vec3 rel_vec = xform[3].xyz - attractors[i].transform[3].xyz; + vec3 local_pos = rel_vec * mat3(attractors[i].transform); + + switch (attractors[i].type) { + case ATTRACTOR_TYPE_SPHERE: { + dir = safe_normalize(rel_vec); + float d = length(local_pos) / attractors[i].extents.x; + if (d > 1.0) { + continue; + } + amount = max(0.0, 1.0 - d); + } break; + case ATTRACTOR_TYPE_BOX: { + dir = safe_normalize(rel_vec); + + vec3 abs_pos = abs(local_pos / attractors[i].extents.xyz); + float d = max(abs_pos.x, max(abs_pos.y, abs_pos.z)); + if (d > 1.0) { + continue; + } + amount = max(0.0, 1.0 - d); + + } break; + case ATTRACTOR_TYPE_VECTOR_FIELD: { + } break; + } + amount = pow(amount, attractors[i].attenuation); + dir = safe_normalize(mix(dir, attractors[i].transform[2].xyz, attractors[i].directionality)); + attractor_force -= amount * dir * attractors[i].strength; + } + + float particle_size = particle_size; + +#ifdef USE_COLLISION_SCALE + + particle_size *= dot(vec3(length(xform[0].xyz), length(xform[1].xyz), length(xform[2].xyz)), vec3(0.33333333333)); + +#endif + + if (collider_count == 1u && colliders[0].type == COLLIDER_TYPE_2D_SDF) { + //2D collision + + vec2 pos = xform[3].xy; + vec4 to_sdf_x = colliders[0].transform[0]; + vec4 to_sdf_y = colliders[0].transform[1]; + vec2 sdf_pos = vec2(dot(vec4(pos, 0, 1), to_sdf_x), dot(vec4(pos, 0, 1), to_sdf_y)); + + vec4 sdf_to_screen = vec4(colliders[0].extents.xyz, colliders[0].scale); + + vec2 uv_pos = sdf_pos * sdf_to_screen.xy + sdf_to_screen.zw; + + if (all(greaterThan(uv_pos, vec2(0.0))) && all(lessThan(uv_pos, vec2(1.0)))) { + vec2 pos2 = pos + vec2(0, particle_size); + vec2 sdf_pos2 = vec2(dot(vec4(pos2, 0, 1), to_sdf_x), dot(vec4(pos2, 0, 1), to_sdf_y)); + float sdf_particle_size = distance(sdf_pos, sdf_pos2); + + float d = vec4_to_float(texture(height_field_texture, uv_pos)) * SDF_MAX_LENGTH; + + d -= sdf_particle_size; + + if (d < 0.0) { + const float EPSILON = 0.001; + vec2 n = normalize(vec2( + vec4_to_float(texture(height_field_texture, uv_pos + vec2(EPSILON, 0.0))) - vec4_to_float(texture(height_field_texture, uv_pos - vec2(EPSILON, 0.0))), + vec4_to_float(texture(height_field_texture, uv_pos + vec2(0.0, EPSILON))) - vec4_to_float(texture(height_field_texture, uv_pos - vec2(0.0, EPSILON))))); + + collided = true; + sdf_pos2 = sdf_pos + n * d; + pos2 = vec2(dot(vec4(sdf_pos2, 0, 1), colliders[0].transform[2]), dot(vec4(sdf_pos2, 0, 1), colliders[0].transform[3])); + + n = pos - pos2; + + collision_normal = normalize(vec3(n, 0.0)); + collision_depth = length(n); + } + } + + } else { + for (uint i = 0u; i < collider_count; i++) { + vec3 normal; + float depth; + bool col = false; + + vec3 rel_vec = xform[3].xyz - colliders[i].transform[3].xyz; + vec3 local_pos = rel_vec * mat3(colliders[i].transform); + + switch (colliders[i].type) { + case COLLIDER_TYPE_SPHERE: { + float d = length(rel_vec) - (particle_size + colliders[i].extents.x); + + if (d < 0.0) { + col = true; + depth = -d; + normal = normalize(rel_vec); + } + + } break; + case COLLIDER_TYPE_BOX: { + vec3 abs_pos = abs(local_pos); + vec3 sgn_pos = sign(local_pos); + + if (any(greaterThan(abs_pos, colliders[i].extents.xyz))) { + //point outside box + + vec3 closest = min(abs_pos, colliders[i].extents.xyz); + vec3 rel = abs_pos - closest; + depth = length(rel) - particle_size; + if (depth < 0.0) { + col = true; + normal = mat3(colliders[i].transform) * (normalize(rel) * sgn_pos); + depth = -depth; + } + } else { + //point inside box + vec3 axis_len = colliders[i].extents.xyz - abs_pos; + // there has to be a faster way to do this? + if (all(lessThan(axis_len.xx, axis_len.yz))) { + normal = vec3(1, 0, 0); + } else if (all(lessThan(axis_len.yy, axis_len.xz))) { + normal = vec3(0, 1, 0); + } else { + normal = vec3(0, 0, 1); + } + + col = true; + depth = dot(normal * axis_len, vec3(1)) + particle_size; + normal = mat3(colliders[i].transform) * (normal * sgn_pos); + } + + } break; + case COLLIDER_TYPE_SDF: { + } break; + case COLLIDER_TYPE_HEIGHT_FIELD: { + vec3 local_pos_bottom = local_pos; + local_pos_bottom.y -= particle_size; + + if (any(greaterThan(abs(local_pos_bottom), colliders[i].extents.xyz))) { + continue; + } + const float DELTA = 1.0 / 8192.0; + + vec3 uvw_pos = vec3(local_pos_bottom / colliders[i].extents.xyz) * 0.5 + 0.5; + + float y = 1.0 - texture(height_field_texture, uvw_pos.xz).r; + + if (y > uvw_pos.y) { + //inside heightfield + + vec3 pos1 = (vec3(uvw_pos.x, y, uvw_pos.z) * 2.0 - 1.0) * colliders[i].extents.xyz; + vec3 pos2 = (vec3(uvw_pos.x + DELTA, 1.0 - texture(height_field_texture, uvw_pos.xz + vec2(DELTA, 0)).r, uvw_pos.z) * 2.0 - 1.0) * colliders[i].extents.xyz; + vec3 pos3 = (vec3(uvw_pos.x, 1.0 - texture(height_field_texture, uvw_pos.xz + vec2(0, DELTA)).r, uvw_pos.z + DELTA) * 2.0 - 1.0) * colliders[i].extents.xyz; + + normal = normalize(cross(pos1 - pos2, pos1 - pos3)); + float local_y = (vec3(local_pos / colliders[i].extents.xyz) * 0.5 + 0.5).y; + + col = true; + depth = dot(normal, pos1) - dot(normal, local_pos_bottom); + } + + } break; + } + + if (col) { + if (!collided) { + collided = true; + collision_normal = normal; + collision_depth = depth; + } else { + vec3 c = collision_normal * collision_depth; + c += normal * max(0.0, depth - dot(normal, c)); + collision_normal = normalize(c); + collision_depth = length(c); + } + } + } + } + } + + if (particle_active) { +#CODE : PROCESS + } + + flags &= ~PARTICLE_FLAG_ACTIVE; + if (particle_active) { + flags |= PARTICLE_FLAG_ACTIVE; + } + + xform = transpose(xform); + out_xform_1 = xform[0]; + out_xform_2 = xform[1]; +#ifdef MODE_3D + out_xform_3 = xform[2]; +#endif + out_velocity_flags.w = uintBitsToFloat(flags); +} + +/* clang-format off */ +#[fragment] + +void main() { +} +/* clang-format on */ diff --git a/drivers/gles3/shaders/particles_copy.glsl b/drivers/gles3/shaders/particles_copy.glsl new file mode 100644 index 0000000000..f273cb7b64 --- /dev/null +++ b/drivers/gles3/shaders/particles_copy.glsl @@ -0,0 +1,122 @@ +/* clang-format off */ +#[modes] + +mode_default = + +#[specializations] + +MODE_3D = false + +#[vertex] + +#include "stdlib_inc.glsl" + +// ParticleData +layout(location = 0) in highp vec4 color; +layout(location = 1) in highp vec4 velocity_flags; +layout(location = 2) in highp vec4 custom; +layout(location = 3) in highp vec4 xform_1; +layout(location = 4) in highp vec4 xform_2; +#ifdef MODE_3D +layout(location = 5) in highp vec4 xform_3; +#endif + +/* clang-format on */ +out highp vec4 out_xform_1; //tfb: +out highp vec4 out_xform_2; //tfb: +#ifdef MODE_3D +out highp vec4 out_xform_3; //tfb:MODE_3D +#endif +flat out highp uvec4 instance_color_custom_data; //tfb: + +uniform lowp vec3 sort_direction; +uniform highp float frame_remainder; + +uniform highp vec3 align_up; +uniform highp uint align_mode; + +uniform highp mat4 inv_emission_transform; + +#define TRANSFORM_ALIGN_DISABLED uint(0) +#define TRANSFORM_ALIGN_Z_BILLBOARD uint(1) +#define TRANSFORM_ALIGN_Y_TO_VELOCITY uint(2) +#define TRANSFORM_ALIGN_Z_BILLBOARD_Y_TO_VELOCITY uint(3) + +#define PARTICLE_FLAG_ACTIVE uint(1) + +void main() { + mat4 txform = mat4(vec4(0.0), vec4(0.0), vec4(0.0), vec4(0.0)); // zero scale, becomes invisible. + if (bool(floatBitsToUint(velocity_flags.w) & PARTICLE_FLAG_ACTIVE)) { +#ifdef MODE_3D + txform = transpose(mat4(xform_1, xform_2, xform_3, vec4(0.0, 0.0, 0.0, 1.0))); +#else + txform = transpose(mat4(xform_1, xform_2, vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))); +#endif + + switch (align_mode) { + case TRANSFORM_ALIGN_DISABLED: { + } break; //nothing + case TRANSFORM_ALIGN_Z_BILLBOARD: { + mat3 local = mat3(normalize(cross(align_up, sort_direction)), align_up, sort_direction); + local = local * mat3(txform); + txform[0].xyz = local[0]; + txform[1].xyz = local[1]; + txform[2].xyz = local[2]; + + } break; + case TRANSFORM_ALIGN_Y_TO_VELOCITY: { + vec3 v = velocity_flags.xyz; + float s = (length(txform[0]) + length(txform[1]) + length(txform[2])) / 3.0; + if (length(v) > 0.0) { + txform[1].xyz = normalize(v); + } else { + txform[1].xyz = normalize(txform[1].xyz); + } + + txform[0].xyz = normalize(cross(txform[1].xyz, txform[2].xyz)); + txform[2].xyz = vec3(0.0, 0.0, 1.0) * s; + txform[0].xyz *= s; + txform[1].xyz *= s; + } break; + case TRANSFORM_ALIGN_Z_BILLBOARD_Y_TO_VELOCITY: { + vec3 sv = velocity_flags.xyz - sort_direction * dot(sort_direction, velocity_flags.xyz); //screen velocity + float s = (length(txform[0]) + length(txform[1]) + length(txform[2])) / 3.0; + + if (length(sv) == 0.0) { + sv = align_up; + } + + sv = normalize(sv); + + txform[0].xyz = normalize(cross(sv, sort_direction)) * s; + txform[1].xyz = sv * s; + txform[2].xyz = sort_direction * s; + + } break; + } + + txform[3].xyz += velocity_flags.xyz * frame_remainder; + +#ifndef MODE_3D + // In global mode, bring 2D particles to local coordinates + // as they will be drawn with the node position as origin. + txform = inv_emission_transform * txform; +#endif + + txform = transpose(txform); + } + + instance_color_custom_data = uvec4(packHalf2x16(color.xy), packHalf2x16(color.zw), packHalf2x16(custom.xy), packHalf2x16(custom.zw)); + out_xform_1 = txform[0]; + out_xform_2 = txform[1]; +#ifdef MODE_3D + out_xform_3 = txform[2]; +#endif +} + +/* clang-format off */ +#[fragment] + +void main() { +} +/* clang-format on */ diff --git a/drivers/gles3/shaders/scene.glsl b/drivers/gles3/shaders/scene.glsl index ed176c7829..f14ed24965 100644 --- a/drivers/gles3/shaders/scene.glsl +++ b/drivers/gles3/shaders/scene.glsl @@ -197,7 +197,7 @@ out vec3 tangent_interp; out vec3 binormal_interp; #endif -#if defined(MATERIAL_UNIFORMS_USED) +#ifdef MATERIAL_UNIFORMS_USED /* clang-format off */ layout(std140) uniform MaterialUniforms { // ubo:3 @@ -366,7 +366,9 @@ void main() { #endif #endif +#ifndef MODE_RENDER_DEPTH #include "tonemap_inc.glsl" +#endif #include "stdlib_inc.glsl" /* texture unit usage, N is max_texture_unity-N @@ -428,7 +430,7 @@ layout(std140) uniform GlobalShaderUniformData { //ubo:1 /* Material Uniforms */ -#if defined(MATERIAL_UNIFORMS_USED) +#ifdef MATERIAL_UNIFORMS_USED /* clang-format off */ layout(std140) uniform MaterialUniforms { // ubo:3 @@ -535,7 +537,7 @@ layout(std140) uniform OmniLightData { // ubo:5 LightData omni_lights[MAX_LIGHT_DATA_STRUCTS]; }; uniform uint omni_light_indices[MAX_FORWARD_LIGHTS]; -uniform int omni_light_count; +uniform uint omni_light_count; #endif #ifndef DISABLE_LIGHT_SPOT @@ -545,7 +547,7 @@ layout(std140) uniform SpotLightData { // ubo:6 LightData spot_lights[MAX_LIGHT_DATA_STRUCTS]; }; uniform uint spot_light_indices[MAX_FORWARD_LIGHTS]; -uniform int spot_light_count; +uniform uint spot_light_count; #endif #ifdef USE_ADDITIVE_LIGHTING @@ -1188,7 +1190,7 @@ void main() { #endif //!DISABLE_LIGHT_DIRECTIONAL #ifndef DISABLE_LIGHT_OMNI - for (int i = 0; i < MAX_FORWARD_LIGHTS; i++) { + for (uint i = 0u; i < MAX_FORWARD_LIGHTS; i++) { if (i >= omni_light_count) { break; } @@ -1211,7 +1213,7 @@ void main() { #endif // !DISABLE_LIGHT_OMNI #ifndef DISABLE_LIGHT_SPOT - for (int i = 0; i < MAX_FORWARD_LIGHTS; i++) { + for (uint i = 0u; i < MAX_FORWARD_LIGHTS; i++) { if (i >= spot_light_count) { break; } diff --git a/drivers/gles3/storage/material_storage.cpp b/drivers/gles3/storage/material_storage.cpp index aaf223df1d..d413c2b00e 100644 --- a/drivers/gles3/storage/material_storage.cpp +++ b/drivers/gles3/storage/material_storage.cpp @@ -34,6 +34,7 @@ #include "config.h" #include "material_storage.h" +#include "particles_storage.h" #include "texture_storage.h" #include "drivers/gles3/rasterizer_canvas_gles3.h" @@ -1342,13 +1343,13 @@ MaterialStorage::MaterialStorage() { shader_data_request_func[RS::SHADER_SPATIAL] = _create_scene_shader_func; shader_data_request_func[RS::SHADER_CANVAS_ITEM] = _create_canvas_shader_func; - shader_data_request_func[RS::SHADER_PARTICLES] = nullptr; + shader_data_request_func[RS::SHADER_PARTICLES] = _create_particles_shader_func; shader_data_request_func[RS::SHADER_SKY] = _create_sky_shader_func; shader_data_request_func[RS::SHADER_FOG] = nullptr; material_data_request_func[RS::SHADER_SPATIAL] = _create_scene_material_func; material_data_request_func[RS::SHADER_CANVAS_ITEM] = _create_canvas_material_func; - material_data_request_func[RS::SHADER_PARTICLES] = nullptr; + material_data_request_func[RS::SHADER_PARTICLES] = _create_particles_material_func; material_data_request_func[RS::SHADER_SKY] = _create_sky_material_func; material_data_request_func[RS::SHADER_FOG] = nullptr; @@ -1613,32 +1614,32 @@ MaterialStorage::MaterialStorage() { { // Setup Particles compiler - /* -ShaderCompiler::DefaultIdentifierActions actions; - actions.renames["COLOR"] = "PARTICLE.color"; - actions.renames["VELOCITY"] = "PARTICLE.velocity"; + ShaderCompiler::DefaultIdentifierActions actions; + + actions.renames["COLOR"] = "out_color"; + actions.renames["VELOCITY"] = "out_velocity_flags.xyz"; //actions.renames["MASS"] = "mass"; ? actions.renames["ACTIVE"] = "particle_active"; actions.renames["RESTART"] = "restart"; - actions.renames["CUSTOM"] = "PARTICLE.custom"; - for (int i = 0; i < ParticlesShader::MAX_USERDATAS; i++) { + actions.renames["CUSTOM"] = "out_custom"; + for (int i = 0; i < PARTICLES_MAX_USERDATAS; i++) { String udname = "USERDATA" + itos(i + 1); - actions.renames[udname] = "PARTICLE.userdata" + itos(i + 1); + actions.renames[udname] = "out_userdata" + itos(i + 1); actions.usage_defines[udname] = "#define USERDATA" + itos(i + 1) + "_USED\n"; } - actions.renames["TRANSFORM"] = "PARTICLE.xform"; - actions.renames["TIME"] = "frame_history.data[0].time"; + actions.renames["TRANSFORM"] = "xform"; + actions.renames["TIME"] = "time"; actions.renames["PI"] = _MKSTR(Math_PI); actions.renames["TAU"] = _MKSTR(Math_TAU); actions.renames["E"] = _MKSTR(Math_E); - actions.renames["LIFETIME"] = "params.lifetime"; + actions.renames["LIFETIME"] = "lifetime"; actions.renames["DELTA"] = "local_delta"; actions.renames["NUMBER"] = "particle_number"; actions.renames["INDEX"] = "index"; //actions.renames["GRAVITY"] = "current_gravity"; - actions.renames["EMISSION_TRANSFORM"] = "FRAME.emission_transform"; - actions.renames["RANDOM_SEED"] = "FRAME.random_seed"; + actions.renames["EMISSION_TRANSFORM"] = "emission_transform"; + actions.renames["RANDOM_SEED"] = "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"; @@ -1660,18 +1661,10 @@ ShaderCompiler::DefaultIdentifierActions actions; actions.render_mode_defines["keep_data"] = "#define ENABLE_KEEP_DATA\n"; actions.render_mode_defines["collision_use_scale"] = "#define USE_COLLISION_SCALE\n"; - actions.sampler_array_name = "material_samplers"; - actions.base_texture_binding_index = 1; - actions.texture_layout_set = 3; - actions.base_uniform_string = "material."; - actions.base_varying_index = 10; - actions.default_filter = ShaderLanguage::FILTER_LINEAR_MIPMAP; actions.default_repeat = ShaderLanguage::REPEAT_ENABLE; - actions.global_buffer_array_variable = "global_shader_uniforms.data"; - particles_shader.compiler.initialize(actions); - */ + shaders.compiler_particles.initialize(actions); } { @@ -2470,8 +2463,8 @@ void MaterialStorage::shader_set_code(RID p_shader, const String &p_code) { RS::ShaderMode new_mode; if (mode_string == "canvas_item") { new_mode = RS::SHADER_CANVAS_ITEM; - //} else if (mode_string == "particles") { - // new_mode = RS::SHADER_PARTICLES; + } else if (mode_string == "particles") { + new_mode = RS::SHADER_PARTICLES; } else if (mode_string == "spatial") { new_mode = RS::SHADER_SPATIAL; } else if (mode_string == "sky") { @@ -2542,6 +2535,9 @@ void MaterialStorage::shader_set_path_hint(RID p_shader, const String &p_path) { ERR_FAIL_COND(!shader); shader->path_hint = p_path; + if (shader->data) { + shader->data->set_path_hint(p_path); + } } String MaterialStorage::shader_get_code(RID p_shader) const { @@ -2809,6 +2805,10 @@ void MaterialStorage::material_update_dependency(RID p_material, DependencyTrack /* Canvas Shader Data */ +void CanvasShaderData::set_path_hint(const String &p_path) { + path = p_path; +} + void CanvasShaderData::set_code(const String &p_code) { // compile the shader @@ -3007,7 +3007,7 @@ GLES3::ShaderData *GLES3::_create_canvas_shader_func() { } void CanvasMaterialData::update_parameters(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty) { - return update_parameters_internal(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size); + update_parameters_internal(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size); } void CanvasMaterialData::bind_uniforms() { @@ -3043,6 +3043,10 @@ GLES3::MaterialData *GLES3::_create_canvas_material_func(ShaderData *p_shader) { //////////////////////////////////////////////////////////////////////////////// // SKY SHADER +void SkyShaderData::set_path_hint(const String &p_path) { + path = p_path; +} + void SkyShaderData::set_code(const String &p_code) { //compile @@ -3251,7 +3255,7 @@ GLES3::ShaderData *GLES3::_create_sky_shader_func() { void SkyMaterialData::update_parameters(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty) { uniform_set_updated = true; - return update_parameters_internal(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size); + update_parameters_internal(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size); } SkyMaterialData::~SkyMaterialData() { @@ -3286,6 +3290,10 @@ void SkyMaterialData::bind_uniforms() { //////////////////////////////////////////////////////////////////////////////// // Scene SHADER +void SceneShaderData::set_path_hint(const String &p_path) { + path = p_path; +} + void SceneShaderData::set_code(const String &p_code) { //compile @@ -3592,7 +3600,7 @@ void SceneMaterialData::set_next_pass(RID p_pass) { } void SceneMaterialData::update_parameters(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty) { - return update_parameters_internal(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size); + update_parameters_internal(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size); } SceneMaterialData::~SceneMaterialData() { @@ -3625,4 +3633,209 @@ void SceneMaterialData::bind_uniforms() { } } +/* Particles SHADER */ + +void ParticlesShaderData::set_path_hint(const String &p_path) { + path = p_path; +} + +void ParticlesShaderData::set_code(const String &p_code) { + //compile + + code = p_code; + valid = false; + ubo_size = 0; + uniforms.clear(); + uses_collision = false; + + if (code.is_empty()) { + return; //just invalid, but no error + } + + ShaderCompiler::GeneratedCode gen_code; + ShaderCompiler::IdentifierActions actions; + actions.entry_point_stages["start"] = ShaderCompiler::STAGE_VERTEX; + actions.entry_point_stages["process"] = ShaderCompiler::STAGE_VERTEX; + + actions.usage_flag_pointers["COLLIDED"] = &uses_collision; + + userdata_count = 0; + for (uint32_t i = 0; i < PARTICLES_MAX_USERDATAS; i++) { + userdatas_used[i] = false; + actions.usage_flag_pointers["USERDATA" + itos(i + 1)] = &userdatas_used[i]; + } + + actions.uniforms = &uniforms; + + Error err = MaterialStorage::get_singleton()->shaders.compiler_particles.compile(RS::SHADER_PARTICLES, code, &actions, path, gen_code); + ERR_FAIL_COND_MSG(err != OK, "Shader compilation failed."); + + if (version.is_null()) { + version = MaterialStorage::get_singleton()->shaders.particles_process_shader.version_create(); + } + + for (uint32_t i = 0; i < PARTICLES_MAX_USERDATAS; i++) { + if (userdatas_used[i]) { + userdata_count++; + } + } + + Vector<StringName> texture_uniform_names; + for (int i = 0; i < gen_code.texture_uniforms.size(); i++) { + texture_uniform_names.push_back(gen_code.texture_uniforms[i].name); + } + + MaterialStorage::get_singleton()->shaders.particles_process_shader.version_set_code(version, gen_code.code, gen_code.uniforms, gen_code.stage_globals[ShaderCompiler::STAGE_VERTEX], gen_code.stage_globals[ShaderCompiler::STAGE_FRAGMENT], gen_code.defines, texture_uniform_names); + ERR_FAIL_COND(!MaterialStorage::get_singleton()->shaders.particles_process_shader.version_is_valid(version)); + + ubo_size = gen_code.uniform_total_size; + ubo_offsets = gen_code.uniform_offsets; + texture_uniforms = gen_code.texture_uniforms; + + valid = true; +} + +void ParticlesShaderData::set_default_texture_parameter(const StringName &p_name, RID p_texture, int p_index) { + if (!p_texture.is_valid()) { + if (default_texture_params.has(p_name) && default_texture_params[p_name].has(p_index)) { + default_texture_params[p_name].erase(p_index); + + if (default_texture_params[p_name].is_empty()) { + default_texture_params.erase(p_name); + } + } + } else { + if (!default_texture_params.has(p_name)) { + default_texture_params[p_name] = HashMap<int, RID>(); + } + default_texture_params[p_name][p_index] = p_texture; + } +} + +void ParticlesShaderData::get_shader_uniform_list(List<PropertyInfo> *p_param_list) const { + HashMap<int, StringName> order; + + for (const KeyValue<StringName, ShaderLanguage::ShaderNode::Uniform> &E : uniforms) { + if (E.value.scope == ShaderLanguage::ShaderNode::Uniform::SCOPE_GLOBAL || E.value.scope == ShaderLanguage::ShaderNode::Uniform::SCOPE_INSTANCE) { + continue; + } + + if (E.value.texture_order >= 0) { + order[E.value.texture_order + 100000] = E.key; + } else { + order[E.value.order] = E.key; + } + } + + String last_group; + for (const KeyValue<int, StringName> &E : order) { + String group = uniforms[E.value].group; + if (!uniforms[E.value].subgroup.is_empty()) { + group += "::" + uniforms[E.value].subgroup; + } + + if (group != last_group) { + PropertyInfo pi; + pi.usage = PROPERTY_USAGE_GROUP; + pi.name = group; + p_param_list->push_back(pi); + + last_group = group; + } + + PropertyInfo pi = ShaderLanguage::uniform_to_property_info(uniforms[E.value]); + pi.name = E.value; + p_param_list->push_back(pi); + } +} + +void ParticlesShaderData::get_instance_param_list(List<RendererMaterialStorage::InstanceShaderParam> *p_param_list) const { + for (const KeyValue<StringName, ShaderLanguage::ShaderNode::Uniform> &E : uniforms) { + if (E.value.scope != ShaderLanguage::ShaderNode::Uniform::SCOPE_INSTANCE) { + continue; + } + + RendererMaterialStorage::InstanceShaderParam p; + p.info = ShaderLanguage::uniform_to_property_info(E.value); + p.info.name = E.key; //supply name + p.index = E.value.instance_index; + p.default_value = ShaderLanguage::constant_value_to_variant(E.value.default_value, E.value.type, E.value.array_size, E.value.hint); + p_param_list->push_back(p); + } +} + +bool ParticlesShaderData::is_parameter_texture(const StringName &p_param) const { + if (!uniforms.has(p_param)) { + return false; + } + + return uniforms[p_param].texture_order >= 0; +} + +bool ParticlesShaderData::is_animated() const { + return false; +} + +bool ParticlesShaderData::casts_shadows() const { + return false; +} + +Variant ParticlesShaderData::get_default_parameter(const StringName &p_parameter) const { + if (uniforms.has(p_parameter)) { + ShaderLanguage::ShaderNode::Uniform uniform = uniforms[p_parameter]; + Vector<ShaderLanguage::ConstantNode::Value> default_value = uniform.default_value; + return ShaderLanguage::constant_value_to_variant(default_value, uniform.type, uniform.array_size, uniform.hint); + } + return Variant(); +} + +RS::ShaderNativeSourceCode ParticlesShaderData::get_native_source_code() const { + return MaterialStorage::get_singleton()->shaders.particles_process_shader.version_get_native_source_code(version); +} + +ParticlesShaderData::~ParticlesShaderData() { + if (version.is_valid()) { + MaterialStorage::get_singleton()->shaders.particles_process_shader.version_free(version); + } +} + +GLES3::ShaderData *GLES3::_create_particles_shader_func() { + ParticlesShaderData *shader_data = memnew(ParticlesShaderData); + return shader_data; +} + +void ParticleProcessMaterialData::update_parameters(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty) { + update_parameters_internal(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size); +} + +ParticleProcessMaterialData::~ParticleProcessMaterialData() { +} + +GLES3::MaterialData *GLES3::_create_particles_material_func(ShaderData *p_shader) { + ParticleProcessMaterialData *material_data = memnew(ParticleProcessMaterialData); + material_data->shader_data = static_cast<ParticlesShaderData *>(p_shader); + //update will happen later anyway so do nothing. + return material_data; +} + +void ParticleProcessMaterialData::bind_uniforms() { + // Bind Material Uniforms + glBindBufferBase(GL_UNIFORM_BUFFER, GLES3::PARTICLES_MATERIAL_UNIFORM_LOCATION, uniform_buffer); + + RID *textures = texture_cache.ptrw(); + ShaderCompiler::GeneratedCode::Texture *texture_uniforms = shader_data->texture_uniforms.ptrw(); + for (int ti = 0; ti < texture_cache.size(); ti++) { + Texture *texture = TextureStorage::get_singleton()->get_texture(textures[ti]); + glActiveTexture(GL_TEXTURE1 + ti); // Start at GL_TEXTURE1 becuase texture slot 0 is reserved for the heightmap texture. + glBindTexture(target_from_type[texture_uniforms[ti].type], texture->tex_id); + + // Set sampler state here as the same texture can be used in multiple places with different flags + // Need to convert sampler state from ShaderLanguage::Texture* to RS::CanvasItemTexture* + RS::CanvasItemTextureFilter filter = RS::CanvasItemTextureFilter((int(texture_uniforms[ti].filter) + 1) % RS::CANVAS_ITEM_TEXTURE_FILTER_MAX); + RS::CanvasItemTextureRepeat repeat = RS::CanvasItemTextureRepeat((int(texture_uniforms[ti].repeat) + 1) % RS::CANVAS_ITEM_TEXTURE_REPEAT_MIRROR); + texture->gl_set_filter(filter); + texture->gl_set_repeat(repeat); + } +} + #endif // !GLES3_ENABLED diff --git a/drivers/gles3/storage/material_storage.h b/drivers/gles3/storage/material_storage.h index 9ebfc3097c..8ae5e5eb9c 100644 --- a/drivers/gles3/storage/material_storage.h +++ b/drivers/gles3/storage/material_storage.h @@ -44,6 +44,7 @@ #include "../shaders/canvas.glsl.gen.h" #include "../shaders/cubemap_filter.glsl.gen.h" +#include "../shaders/particles.glsl.gen.h" #include "../shaders/scene.glsl.gen.h" #include "../shaders/sky.glsl.gen.h" @@ -53,6 +54,7 @@ namespace GLES3 { struct ShaderData { virtual void set_code(const String &p_Code) = 0; + virtual void set_path_hint(const String &p_hint) = 0; virtual void set_default_texture_parameter(const StringName &p_name, RID p_texture, int p_index) = 0; virtual void get_shader_uniform_list(List<PropertyInfo> *p_param_list) const = 0; @@ -165,6 +167,7 @@ struct CanvasShaderData : public ShaderData { bool uses_time = false; virtual void set_code(const String &p_Code); + virtual void set_path_hint(const String &p_hint); virtual void set_default_texture_parameter(const StringName &p_name, RID p_texture, int p_index); virtual void get_shader_uniform_list(List<PropertyInfo> *p_param_list) const; virtual void get_instance_param_list(List<RendererMaterialStorage::InstanceShaderParam> *p_param_list) const; @@ -216,6 +219,7 @@ struct SkyShaderData : public ShaderData { bool uses_light; virtual void set_code(const String &p_Code); + virtual void set_path_hint(const String &p_hint); virtual void set_default_texture_parameter(const StringName &p_name, RID p_texture, int p_index); virtual void get_shader_uniform_list(List<PropertyInfo> *p_param_list) const; virtual void get_instance_param_list(List<RendererMaterialStorage::InstanceShaderParam> *p_param_list) const; @@ -339,6 +343,7 @@ struct SceneShaderData : public ShaderData { uint32_t index = 0; virtual void set_code(const String &p_Code); + virtual void set_path_hint(const String &p_hint); virtual void set_default_texture_parameter(const StringName &p_name, RID p_texture, int p_index); virtual void get_shader_uniform_list(List<PropertyInfo> *p_param_list) const; virtual void get_instance_param_list(List<RendererMaterialStorage::InstanceShaderParam> *p_param_list) const; @@ -370,6 +375,62 @@ struct SceneMaterialData : public MaterialData { MaterialData *_create_scene_material_func(ShaderData *p_shader); +/* Particle Shader */ + +enum { + PARTICLES_MAX_USERDATAS = 6 +}; + +struct ParticlesShaderData : public ShaderData { + bool valid = false; + RID version; + bool uses_collision = false; + + HashMap<StringName, ShaderLanguage::ShaderNode::Uniform> uniforms; + Vector<ShaderCompiler::GeneratedCode::Texture> texture_uniforms; + + Vector<uint32_t> ubo_offsets; + uint32_t ubo_size = 0; + + String path; + String code; + HashMap<StringName, HashMap<int, RID>> default_texture_params; + + bool uses_time = false; + + bool userdatas_used[PARTICLES_MAX_USERDATAS] = {}; + uint32_t userdata_count = 0; + + virtual void set_code(const String &p_Code); + virtual void set_path_hint(const String &p_hint); + virtual void set_default_texture_parameter(const StringName &p_name, RID p_texture, int p_index); + virtual void get_shader_uniform_list(List<PropertyInfo> *p_param_list) const; + virtual void get_instance_param_list(List<RendererMaterialStorage::InstanceShaderParam> *p_param_list) const; + virtual bool is_parameter_texture(const StringName &p_param) const; + virtual bool is_animated() const; + virtual bool casts_shadows() const; + virtual Variant get_default_parameter(const StringName &p_parameter) const; + virtual RS::ShaderNativeSourceCode get_native_source_code() const; + + ParticlesShaderData() {} + virtual ~ParticlesShaderData(); +}; + +ShaderData *_create_particles_shader_func(); + +struct ParticleProcessMaterialData : public MaterialData { + ParticlesShaderData *shader_data = nullptr; + RID uniform_set; + + virtual void set_render_priority(int p_priority) {} + virtual void set_next_pass(RID p_pass) {} + virtual void update_parameters(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty); + virtual void bind_uniforms(); + virtual ~ParticleProcessMaterialData(); +}; + +MaterialData *_create_particles_material_func(ShaderData *p_shader); + /* Global shader uniform structs */ struct GlobalShaderUniforms { enum { @@ -506,6 +567,7 @@ public: SkyShaderGLES3 sky_shader; SceneShaderGLES3 scene_shader; CubemapFilterShaderGLES3 cubemap_filter_shader; + ParticlesShaderGLES3 particles_process_shader; ShaderCompiler compiler_canvas; ShaderCompiler compiler_scene; diff --git a/drivers/gles3/storage/mesh_storage.cpp b/drivers/gles3/storage/mesh_storage.cpp index 9ec0fc0286..a47df42500 100644 --- a/drivers/gles3/storage/mesh_storage.cpp +++ b/drivers/gles3/storage/mesh_storage.cpp @@ -87,8 +87,11 @@ void MeshStorage::mesh_set_blend_shape_count(RID p_mesh, int p_blend_shape_count ERR_FAIL_COND(!mesh); ERR_FAIL_COND(mesh->surface_count > 0); //surfaces already exist - WARN_PRINT_ONCE("blend shapes not supported by GLES3 renderer yet"); mesh->blend_shape_count = p_blend_shape_count; + + if (p_blend_shape_count > 0) { + WARN_PRINT_ONCE("blend shapes not supported by GLES3 renderer yet"); + } } bool MeshStorage::mesh_needs_instance(RID p_mesh, bool p_has_skeleton) { diff --git a/drivers/gles3/storage/mesh_storage.h b/drivers/gles3/storage/mesh_storage.h index a31db24f2d..1aef3cbf78 100644 --- a/drivers/gles3/storage/mesh_storage.h +++ b/drivers/gles3/storage/mesh_storage.h @@ -327,6 +327,7 @@ public: _FORCE_INLINE_ uint32_t mesh_surface_get_lod(void *p_surface, float p_model_scale, float p_distance_threshold, float p_mesh_lod_threshold, uint32_t &r_index_count) const { Mesh::Surface *s = reinterpret_cast<Mesh::Surface *>(p_surface); + ERR_FAIL_COND_V(!s, 0); int32_t current_lod = -1; r_index_count = s->index_count; @@ -403,6 +404,8 @@ public: virtual void mesh_instance_check_for_update(RID p_mesh_instance) override; virtual void update_mesh_instances() override; + // TODO: considering hashing versions with multimesh buffer RID. + // Doing so would allow us to avoid specifying multimesh buffer pointers every frame and may improve performance. _FORCE_INLINE_ void mesh_instance_surface_get_vertex_arrays_and_format(RID p_mesh_instance, uint32_t p_surface_index, uint32_t p_input_mask, GLuint &r_vertex_array_gl) { MeshInstance *mi = mesh_instance_owner.get_or_null(p_mesh_instance); ERR_FAIL_COND(!mi); diff --git a/drivers/gles3/storage/particles_storage.cpp b/drivers/gles3/storage/particles_storage.cpp index 9ed9fedd5a..1a0d97df01 100644 --- a/drivers/gles3/storage/particles_storage.cpp +++ b/drivers/gles3/storage/particles_storage.cpp @@ -31,6 +31,12 @@ #ifdef GLES3_ENABLED #include "particles_storage.h" +#include "material_storage.h" +#include "mesh_storage.h" +#include "texture_storage.h" +#include "utilities.h" + +#include "servers/rendering/rendering_server_default.h" using namespace GLES3; @@ -42,213 +48,1338 @@ ParticlesStorage *ParticlesStorage::get_singleton() { ParticlesStorage::ParticlesStorage() { singleton = this; + GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton(); + + { + String global_defines; + global_defines += "#define MAX_GLOBAL_SHADER_UNIFORMS 256\n"; // TODO: this is arbitrary for now + material_storage->shaders.particles_process_shader.initialize(global_defines); + } + { + // default material and shader for particles shader + particles_shader.default_shader = material_storage->shader_allocate(); + material_storage->shader_initialize(particles_shader.default_shader); + material_storage->shader_set_code(particles_shader.default_shader, R"( +// Default particles shader. + +shader_type particles; + +void process() { + COLOR = vec4(1.0); +} +)"); + particles_shader.default_material = material_storage->material_allocate(); + material_storage->material_initialize(particles_shader.default_material); + material_storage->material_set_shader(particles_shader.default_material, particles_shader.default_shader); + } + { + particles_shader.copy_shader.initialize(); + particles_shader.copy_shader_version = particles_shader.copy_shader.version_create(); + } } ParticlesStorage::~ParticlesStorage() { singleton = nullptr; + GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton(); + + material_storage->material_free(particles_shader.default_material); + material_storage->shader_free(particles_shader.default_shader); + particles_shader.copy_shader.version_free(particles_shader.copy_shader_version); } /* PARTICLES */ RID ParticlesStorage::particles_allocate() { - return RID(); + return particles_owner.allocate_rid(); } void ParticlesStorage::particles_initialize(RID p_rid) { + particles_owner.initialize_rid(p_rid, Particles()); } void ParticlesStorage::particles_free(RID p_rid) { + update_particles(); + Particles *particles = particles_owner.get_or_null(p_rid); + particles->dependency.deleted_notify(p_rid); + _particles_free_data(particles); + particles_owner.free(p_rid); } void ParticlesStorage::particles_set_mode(RID p_particles, RS::ParticlesMode p_mode) { -} + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND(!particles); + if (particles->mode == p_mode) { + return; + } -void ParticlesStorage::particles_emit(RID p_particles, const Transform3D &p_transform, const Vector3 &p_velocity, const Color &p_color, const Color &p_custom, uint32_t p_emit_flags) { + _particles_free_data(particles); + + particles->mode = p_mode; } void ParticlesStorage::particles_set_emitting(RID p_particles, bool p_emitting) { + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND(!particles); + + particles->emitting = p_emitting; +} + +bool ParticlesStorage::particles_get_emitting(RID p_particles) { + ERR_FAIL_COND_V_MSG(RSG::threaded, false, "This function should never be used with threaded rendering, as it stalls the renderer."); + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND_V(!particles, false); + + return particles->emitting; +} + +void ParticlesStorage::_particles_free_data(Particles *particles) { + particles->userdata_count = 0; + particles->instance_buffer_size_cache = 0; + particles->instance_buffer_stride_cache = 0; + particles->num_attrib_arrays_cache = 0; + particles->process_buffer_stride_cache = 0; + + if (particles->front_process_buffer != 0) { + glDeleteVertexArrays(1, &particles->front_vertex_array); + glDeleteBuffers(1, &particles->front_process_buffer); + glDeleteBuffers(1, &particles->front_instance_buffer); + particles->front_vertex_array = 0; + particles->front_process_buffer = 0; + particles->front_instance_buffer = 0; + + glDeleteVertexArrays(1, &particles->back_vertex_array); + glDeleteBuffers(1, &particles->back_process_buffer); + glDeleteBuffers(1, &particles->back_instance_buffer); + particles->back_vertex_array = 0; + particles->back_process_buffer = 0; + particles->back_instance_buffer = 0; + } + + if (particles->sort_buffer != 0) { + glDeleteBuffers(1, &particles->last_frame_buffer); + glDeleteBuffers(1, &particles->sort_buffer); + particles->last_frame_buffer = 0; + particles->sort_buffer = 0; + particles->sort_buffer_filled = false; + particles->last_frame_buffer_filled = false; + } + + if (particles->frame_params_ubo != 0) { + glDeleteBuffers(1, &particles->frame_params_ubo); + particles->frame_params_ubo = 0; + } } void ParticlesStorage::particles_set_amount(RID p_particles, int p_amount) { + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND(!particles); + + if (particles->amount == p_amount) { + return; + } + + _particles_free_data(particles); + + particles->amount = p_amount; + + particles->prev_ticks = 0; + particles->phase = 0; + particles->prev_phase = 0; + particles->clear = true; + + particles->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_PARTICLES); } void ParticlesStorage::particles_set_lifetime(RID p_particles, double p_lifetime) { + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND(!particles); + particles->lifetime = p_lifetime; } void ParticlesStorage::particles_set_one_shot(RID p_particles, bool p_one_shot) { + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND(!particles); + particles->one_shot = p_one_shot; } void ParticlesStorage::particles_set_pre_process_time(RID p_particles, double p_time) { + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND(!particles); + particles->pre_process_time = p_time; } - void ParticlesStorage::particles_set_explosiveness_ratio(RID p_particles, real_t p_ratio) { + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND(!particles); + particles->explosiveness = p_ratio; } - void ParticlesStorage::particles_set_randomness_ratio(RID p_particles, real_t p_ratio) { + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND(!particles); + particles->randomness = p_ratio; } void ParticlesStorage::particles_set_custom_aabb(RID p_particles, const AABB &p_aabb) { + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND(!particles); + particles->custom_aabb = p_aabb; + particles->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_AABB); } void ParticlesStorage::particles_set_speed_scale(RID p_particles, double p_scale) { -} - -void ParticlesStorage::particles_set_use_local_coordinates(RID p_particles, bool p_enable) { -} + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND(!particles); -void ParticlesStorage::particles_set_process_material(RID p_particles, RID p_material) { + particles->speed_scale = p_scale; } +void ParticlesStorage::particles_set_use_local_coordinates(RID p_particles, bool p_enable) { + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND(!particles); -RID ParticlesStorage::particles_get_process_material(RID p_particles) const { - return RID(); + particles->use_local_coords = p_enable; + particles->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_PARTICLES); } void ParticlesStorage::particles_set_fixed_fps(RID p_particles, int p_fps) { + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND(!particles); + + particles->fixed_fps = p_fps; + + _particles_free_data(particles); + + particles->prev_ticks = 0; + particles->phase = 0; + particles->prev_phase = 0; + particles->clear = true; + + particles->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_PARTICLES); } void ParticlesStorage::particles_set_interpolate(RID p_particles, bool p_enable) { + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND(!particles); + + particles->interpolate = p_enable; } void ParticlesStorage::particles_set_fractional_delta(RID p_particles, bool p_enable) { + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND(!particles); + + particles->fractional_delta = p_enable; } -void ParticlesStorage::particles_set_subemitter(RID p_particles, RID p_subemitter_particles) { +void ParticlesStorage::particles_set_trails(RID p_particles, bool p_enable, double p_length) { + if (p_enable) { + WARN_PRINT_ONCE("The OpenGL 3 renderer does not support particle trails"); + } } -void ParticlesStorage::particles_set_view_axis(RID p_particles, const Vector3 &p_axis, const Vector3 &p_up_axis) { +void ParticlesStorage::particles_set_trail_bind_poses(RID p_particles, const Vector<Transform3D> &p_bind_poses) { + if (p_bind_poses.size() != 0) { + WARN_PRINT_ONCE("The OpenGL 3 renderer does not support particle trails"); + } } void ParticlesStorage::particles_set_collision_base_size(RID p_particles, real_t p_size) { + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND(!particles); + + particles->collision_base_size = p_size; } void ParticlesStorage::particles_set_transform_align(RID p_particles, RS::ParticlesTransformAlign p_transform_align) { -} + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND(!particles); -void ParticlesStorage::particles_set_trails(RID p_particles, bool p_enable, double p_length) { + particles->transform_align = p_transform_align; } -void ParticlesStorage::particles_set_trail_bind_poses(RID p_particles, const Vector<Transform3D> &p_bind_poses) { +void ParticlesStorage::particles_set_process_material(RID p_particles, RID p_material) { + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND(!particles); + + particles->process_material = p_material; + particles->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_PARTICLES); //the instance buffer may have changed } -void ParticlesStorage::particles_restart(RID p_particles) { +RID ParticlesStorage::particles_get_process_material(RID p_particles) const { + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND_V(!particles, RID()); + + return particles->process_material; } void ParticlesStorage::particles_set_draw_order(RID p_particles, RS::ParticlesDrawOrder p_order) { + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND(!particles); + + particles->draw_order = p_order; } -void ParticlesStorage::particles_set_draw_passes(RID p_particles, int p_count) { +void ParticlesStorage::particles_set_draw_passes(RID p_particles, int p_passes) { + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND(!particles); + + particles->draw_passes.resize(p_passes); } void ParticlesStorage::particles_set_draw_pass_mesh(RID p_particles, int p_pass, RID p_mesh) { + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND(!particles); + ERR_FAIL_INDEX(p_pass, particles->draw_passes.size()); + particles->draw_passes.write[p_pass] = p_mesh; + particles->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_PARTICLES); +} + +void ParticlesStorage::particles_restart(RID p_particles) { + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND(!particles); + + particles->restart_request = true; +} + +void ParticlesStorage::particles_set_subemitter(RID p_particles, RID p_subemitter_particles) { + if (p_subemitter_particles.is_valid()) { + WARN_PRINT_ONCE("The OpenGL 3 renderer does not support particle sub emitters"); + } +} + +void ParticlesStorage::particles_emit(RID p_particles, const Transform3D &p_transform, const Vector3 &p_velocity, const Color &p_color, const Color &p_custom, uint32_t p_emit_flags) { + WARN_PRINT_ONCE("The OpenGL 3 renderer does not support manually emitting particles"); } void ParticlesStorage::particles_request_process(RID p_particles) { + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND(!particles); + + if (!particles->dirty) { + particles->dirty = true; + particles->update_list = particle_update_list; + particle_update_list = particles; + } } AABB ParticlesStorage::particles_get_current_aabb(RID p_particles) { - return AABB(); + if (RSG::threaded) { + WARN_PRINT_ONCE("Calling this function with threaded rendering enabled stalls the renderer, use with care."); + } + + const Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND_V(!particles, AABB()); + + int total_amount = particles->amount; + + // If available, read from the sort buffer which should be 2 frames out of date. + // This will help alleviate GPU stalls. + GLuint read_buffer = particles->sort_buffer_filled ? particles->sort_buffer : particles->back_instance_buffer; + + Vector<uint8_t> buffer = Utilities::buffer_get_data(GL_ARRAY_BUFFER, read_buffer, total_amount * sizeof(ParticleInstanceData3D)); + ERR_FAIL_COND_V(buffer.size() != (int)(total_amount * sizeof(ParticleInstanceData3D)), AABB()); + + Transform3D inv = particles->emission_transform.affine_inverse(); + + AABB aabb; + if (buffer.size()) { + bool first = true; + + const uint8_t *data_ptr = (const uint8_t *)buffer.ptr(); + uint32_t particle_data_size = sizeof(ParticleInstanceData3D) + sizeof(float) * particles->userdata_count; + + for (int i = 0; i < total_amount; i++) { + const ParticleInstanceData3D &particle_data = *(const ParticleInstanceData3D *)&data_ptr[particle_data_size * i]; + // If scale is 0.0, we assume the particle is inactive. + if (particle_data.xform[0] > 0.0) { + Vector3 pos = Vector3(particle_data.xform[3], particle_data.xform[7], particle_data.xform[11]); + if (!particles->use_local_coords) { + pos = inv.xform(pos); + } + if (first) { + aabb.position = pos; + first = false; + } else { + aabb.expand_to(pos); + } + } + } + } + + float longest_axis_size = 0; + for (int i = 0; i < particles->draw_passes.size(); i++) { + if (particles->draw_passes[i].is_valid()) { + AABB maabb = MeshStorage::get_singleton()->mesh_get_aabb(particles->draw_passes[i], RID()); + longest_axis_size = MAX(maabb.get_longest_axis_size(), longest_axis_size); + } + } + + aabb.grow_by(longest_axis_size); + + return aabb; } AABB ParticlesStorage::particles_get_aabb(RID p_particles) const { - return AABB(); + const Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND_V(!particles, AABB()); + + return particles->custom_aabb; } void ParticlesStorage::particles_set_emission_transform(RID p_particles, const Transform3D &p_transform) { -} + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND(!particles); -bool ParticlesStorage::particles_get_emitting(RID p_particles) { - return false; + particles->emission_transform = p_transform; } int ParticlesStorage::particles_get_draw_passes(RID p_particles) const { - return 0; -} + const Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND_V(!particles, 0); -RID ParticlesStorage::particles_get_draw_pass_mesh(RID p_particles, int p_pass) const { - return RID(); + return particles->draw_passes.size(); } -void ParticlesStorage::particles_add_collision(RID p_particles, RID p_instance) { +RID ParticlesStorage::particles_get_draw_pass_mesh(RID p_particles, int p_pass) const { + const Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND_V(!particles, RID()); + ERR_FAIL_INDEX_V(p_pass, particles->draw_passes.size(), RID()); + + return particles->draw_passes[p_pass]; } -void ParticlesStorage::particles_remove_collision(RID p_particles, RID p_instance) { +void ParticlesStorage::particles_add_collision(RID p_particles, RID p_particles_collision_instance) { + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND(!particles); + particles->collisions.insert(p_particles_collision_instance); +} + +void ParticlesStorage::particles_remove_collision(RID p_particles, RID p_particles_collision_instance) { + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND(!particles); + particles->collisions.erase(p_particles_collision_instance); +} + +void ParticlesStorage::particles_set_canvas_sdf_collision(RID p_particles, bool p_enable, const Transform2D &p_xform, const Rect2 &p_to_screen, GLuint p_texture) { + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND(!particles); + particles->has_sdf_collision = p_enable; + particles->sdf_collision_transform = p_xform; + particles->sdf_collision_to_screen = p_to_screen; + particles->sdf_collision_texture = p_texture; +} + +// Does one step of processing particles by reading from back_process_buffer and writing to front_process_buffer. +void ParticlesStorage::_particles_process(Particles *p_particles, double p_delta) { + GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton(); + GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton(); + + double new_phase = Math::fmod(p_particles->phase + (p_delta / p_particles->lifetime) * p_particles->speed_scale, 1.0); + + //update current frame + ParticlesFrameParams frame_params; + + if (p_particles->clear) { + p_particles->cycle_number = 0; + p_particles->random_seed = Math::rand(); + } else if (new_phase < p_particles->phase) { + if (p_particles->one_shot) { + p_particles->emitting = false; + } + p_particles->cycle_number++; + } + + frame_params.emitting = p_particles->emitting; + frame_params.system_phase = new_phase; + frame_params.prev_system_phase = p_particles->phase; + + p_particles->phase = new_phase; + + frame_params.time = RSG::rasterizer->get_total_time(); + frame_params.delta = p_delta * p_particles->speed_scale; + frame_params.random_seed = p_particles->random_seed; + frame_params.explosiveness = p_particles->explosiveness; + frame_params.randomness = p_particles->randomness; + + if (p_particles->use_local_coords) { + GLES3::MaterialStorage::store_transform(Transform3D(), frame_params.emission_transform); + } else { + GLES3::MaterialStorage::store_transform(p_particles->emission_transform, frame_params.emission_transform); + } + + frame_params.cycle = p_particles->cycle_number; + frame_params.frame = p_particles->frame_counter++; + frame_params.pad0 = 0; + frame_params.pad1 = 0; + frame_params.pad2 = 0; + + { //collision and attractors + + frame_params.collider_count = 0; + frame_params.attractor_count = 0; + frame_params.particle_size = p_particles->collision_base_size; + + GLuint collision_heightmap_texture = 0; + + Transform3D to_particles; + if (p_particles->use_local_coords) { + to_particles = p_particles->emission_transform.affine_inverse(); + } + + if (p_particles->has_sdf_collision && p_particles->sdf_collision_texture != 0) { + //2D collision + + Transform2D xform = p_particles->sdf_collision_transform; //will use dotproduct manually so invert beforehand + Transform2D revert = xform.affine_inverse(); + frame_params.collider_count = 1; + frame_params.colliders[0].transform[0] = xform.columns[0][0]; + frame_params.colliders[0].transform[1] = xform.columns[0][1]; + frame_params.colliders[0].transform[2] = 0; + frame_params.colliders[0].transform[3] = xform.columns[2][0]; + + frame_params.colliders[0].transform[4] = xform.columns[1][0]; + frame_params.colliders[0].transform[5] = xform.columns[1][1]; + frame_params.colliders[0].transform[6] = 0; + frame_params.colliders[0].transform[7] = xform.columns[2][1]; + + frame_params.colliders[0].transform[8] = revert.columns[0][0]; + frame_params.colliders[0].transform[9] = revert.columns[0][1]; + frame_params.colliders[0].transform[10] = 0; + frame_params.colliders[0].transform[11] = revert.columns[2][0]; + + frame_params.colliders[0].transform[12] = revert.columns[1][0]; + frame_params.colliders[0].transform[13] = revert.columns[1][1]; + frame_params.colliders[0].transform[14] = 0; + frame_params.colliders[0].transform[15] = revert.columns[2][1]; + + frame_params.colliders[0].extents[0] = p_particles->sdf_collision_to_screen.size.x; + frame_params.colliders[0].extents[1] = p_particles->sdf_collision_to_screen.size.y; + frame_params.colliders[0].extents[2] = p_particles->sdf_collision_to_screen.position.x; + frame_params.colliders[0].scale = p_particles->sdf_collision_to_screen.position.y; + frame_params.colliders[0].type = ParticlesFrameParams::COLLISION_TYPE_2D_SDF; + + collision_heightmap_texture = p_particles->sdf_collision_texture; + } + + for (const RID &E : p_particles->collisions) { + ParticlesCollisionInstance *pci = particles_collision_instance_owner.get_or_null(E); + if (!pci || !pci->active) { + continue; + } + ParticlesCollision *pc = particles_collision_owner.get_or_null(pci->collision); + ERR_CONTINUE(!pc); + + Transform3D to_collider = pci->transform; + if (p_particles->use_local_coords) { + to_collider = to_particles * to_collider; + } + Vector3 scale = to_collider.basis.get_scale(); + to_collider.basis.orthonormalize(); + + if (pc->type <= RS::PARTICLES_COLLISION_TYPE_VECTOR_FIELD_ATTRACT) { + //attractor + if (frame_params.attractor_count >= ParticlesFrameParams::MAX_ATTRACTORS) { + continue; + } + + ParticlesFrameParams::Attractor &attr = frame_params.attractors[frame_params.attractor_count]; + + GLES3::MaterialStorage::store_transform(to_collider, attr.transform); + attr.strength = pc->attractor_strength; + attr.attenuation = pc->attractor_attenuation; + attr.directionality = pc->attractor_directionality; + + switch (pc->type) { + case RS::PARTICLES_COLLISION_TYPE_SPHERE_ATTRACT: { + attr.type = ParticlesFrameParams::ATTRACTOR_TYPE_SPHERE; + float radius = pc->radius; + radius *= (scale.x + scale.y + scale.z) / 3.0; + attr.extents[0] = radius; + attr.extents[1] = radius; + attr.extents[2] = radius; + } break; + case RS::PARTICLES_COLLISION_TYPE_BOX_ATTRACT: { + attr.type = ParticlesFrameParams::ATTRACTOR_TYPE_BOX; + Vector3 extents = pc->extents * scale; + attr.extents[0] = extents.x; + attr.extents[1] = extents.y; + attr.extents[2] = extents.z; + } break; + case RS::PARTICLES_COLLISION_TYPE_VECTOR_FIELD_ATTRACT: { + WARN_PRINT_ONCE("Vector field particle attractors are not available in the OpenGL2 renderer."); + } break; + default: { + } + } + + frame_params.attractor_count++; + } else { + //collider + if (frame_params.collider_count >= ParticlesFrameParams::MAX_COLLIDERS) { + continue; + } + + ParticlesFrameParams::Collider &col = frame_params.colliders[frame_params.collider_count]; + + GLES3::MaterialStorage::store_transform(to_collider, col.transform); + switch (pc->type) { + case RS::PARTICLES_COLLISION_TYPE_SPHERE_COLLIDE: { + col.type = ParticlesFrameParams::COLLISION_TYPE_SPHERE; + float radius = pc->radius; + radius *= (scale.x + scale.y + scale.z) / 3.0; + col.extents[0] = radius; + col.extents[1] = radius; + col.extents[2] = radius; + } break; + case RS::PARTICLES_COLLISION_TYPE_BOX_COLLIDE: { + col.type = ParticlesFrameParams::COLLISION_TYPE_BOX; + Vector3 extents = pc->extents * scale; + col.extents[0] = extents.x; + col.extents[1] = extents.y; + col.extents[2] = extents.z; + } break; + case RS::PARTICLES_COLLISION_TYPE_SDF_COLLIDE: { + WARN_PRINT_ONCE("SDF Particle Colliders are not available in the OpenGL 3 renderer."); + } break; + case RS::PARTICLES_COLLISION_TYPE_HEIGHTFIELD_COLLIDE: { + if (collision_heightmap_texture != 0) { //already taken + continue; + } + + col.type = ParticlesFrameParams::COLLISION_TYPE_HEIGHT_FIELD; + Vector3 extents = pc->extents * scale; + col.extents[0] = extents.x; + col.extents[1] = extents.y; + col.extents[2] = extents.z; + collision_heightmap_texture = pc->heightfield_texture; + } break; + default: { + } + } + + frame_params.collider_count++; + } + } + + // Bind heightmap or SDF texture. + GLuint heightmap = collision_heightmap_texture; + if (heightmap == 0) { + GLES3::Texture *tex = texture_storage->get_texture(texture_storage->texture_gl_get_default(GLES3::DEFAULT_GL_TEXTURE_BLACK)); + heightmap = tex->tex_id; + } + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, heightmap); + } + + if (p_particles->frame_params_ubo == 0) { + glGenBuffers(1, &p_particles->frame_params_ubo); + } + // Update per-frame UBO. + glBindBufferBase(GL_UNIFORM_BUFFER, PARTICLES_FRAME_UNIFORM_LOCATION, p_particles->frame_params_ubo); + glBufferData(GL_UNIFORM_BUFFER, sizeof(ParticlesFrameParams), &frame_params, GL_STREAM_DRAW); + + // Get shader and set shader uniforms; + ParticleProcessMaterialData *m = static_cast<ParticleProcessMaterialData *>(material_storage->material_get_data(p_particles->process_material, RS::SHADER_PARTICLES)); + if (!m) { + m = static_cast<ParticleProcessMaterialData *>(material_storage->material_get_data(particles_shader.default_material, RS::SHADER_PARTICLES)); + } + + ERR_FAIL_COND(!m); + + ParticlesShaderGLES3::ShaderVariant variant = ParticlesShaderGLES3::MODE_DEFAULT; + + uint32_t specialization = 0; + for (uint32_t i = 0; i < p_particles->userdata_count; i++) { + specialization |= (1 << i); + } + + if (p_particles->mode == RS::ParticlesMode::PARTICLES_MODE_3D) { + specialization |= ParticlesShaderGLES3::MODE_3D; + } + + RID version = particles_shader.default_shader_version; + if (m->shader_data->version.is_valid() && m->shader_data->valid) { + // Bind material uniform buffer and textures. + m->bind_uniforms(); + version = m->shader_data->version; + } + + bool success = material_storage->shaders.particles_process_shader.version_bind_shader(version, variant, specialization); + if (!success) { + return; + } + + material_storage->shaders.particles_process_shader.version_set_uniform(ParticlesShaderGLES3::LIFETIME, p_particles->lifetime, version, variant, specialization); + material_storage->shaders.particles_process_shader.version_set_uniform(ParticlesShaderGLES3::CLEAR, p_particles->clear, version, variant, specialization); + material_storage->shaders.particles_process_shader.version_set_uniform(ParticlesShaderGLES3::TOTAL_PARTICLES, uint32_t(p_particles->amount), version, variant, specialization); + material_storage->shaders.particles_process_shader.version_set_uniform(ParticlesShaderGLES3::USE_FRACTIONAL_DELTA, p_particles->fractional_delta, version, variant, specialization); + + p_particles->clear = false; + + p_particles->has_collision_cache = m->shader_data->uses_collision; + + glBindVertexArray(p_particles->back_vertex_array); + + glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, p_particles->front_process_buffer); + + glBeginTransformFeedback(GL_POINTS); + glDrawArrays(GL_POINTS, 0, p_particles->amount); + glEndTransformFeedback(); + + glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, 0); + glBindVertexArray(0); + + SWAP(p_particles->front_process_buffer, p_particles->back_process_buffer); + SWAP(p_particles->front_vertex_array, p_particles->back_vertex_array); } -void ParticlesStorage::particles_set_canvas_sdf_collision(RID p_particles, bool p_enable, const Transform2D &p_xform, const Rect2 &p_to_screen, RID p_texture) { +void ParticlesStorage::particles_set_view_axis(RID p_particles, const Vector3 &p_axis, const Vector3 &p_up_axis) { + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND(!particles); + + if (particles->draw_order != RS::PARTICLES_DRAW_ORDER_VIEW_DEPTH && particles->transform_align != RS::PARTICLES_TRANSFORM_ALIGN_Z_BILLBOARD && particles->transform_align != RS::PARTICLES_TRANSFORM_ALIGN_Z_BILLBOARD_Y_TO_VELOCITY) { + return; + } + + if (particles->front_process_buffer == 0) { + return; //particles have not processed yet + } + + Vector3 axis = -p_axis; // cameras look to z negative + + if (particles->use_local_coords) { + axis = particles->emission_transform.basis.xform_inv(axis).normalized(); + } + + // Sort will be done on CPU since we don't have compute shaders. + // If the sort_buffer has valid data + // Use a buffer that is 2 frames out of date to avoid stalls. + if (particles->draw_order == RS::PARTICLES_DRAW_ORDER_VIEW_DEPTH && particles->sort_buffer_filled) { + glBindBuffer(GL_ARRAY_BUFFER, particles->sort_buffer); + + ParticleInstanceData3D *particle_array; +#ifndef __EMSCRIPTEN__ + particle_array = static_cast<ParticleInstanceData3D *>(glMapBufferRange(GL_ARRAY_BUFFER, 0, particles->amount * sizeof(ParticleInstanceData3D), GL_MAP_READ_BIT | GL_MAP_WRITE_BIT)); + ERR_FAIL_NULL(particle_array); +#else + LocalVector<ParticleInstanceData3D> particle_vector; + particle_vector.resize(particles->amount); + particle_array = particle_vector.ptr(); + glGetBufferSubData(GL_ARRAY_BUFFER, 0, particles->amount * sizeof(ParticleInstanceData3D), particle_array); +#endif + SortArray<ParticleInstanceData3D, ParticlesViewSort> sorter; + sorter.compare.z_dir = axis; + sorter.sort(particle_array, particles->amount); + +#ifndef __EMSCRIPTEN__ + glUnmapBuffer(GL_ARRAY_BUFFER); +#else + glBufferSubData(GL_ARRAY_BUFFER, 0, particles->amount * sizeof(ParticleInstanceData3D), particle_vector.ptr()); +#endif + } + + glEnable(GL_RASTERIZER_DISCARD); + _particles_update_instance_buffer(particles, axis, p_up_axis); + glDisable(GL_RASTERIZER_DISCARD); +} + +void ParticlesStorage::_particles_update_buffers(Particles *particles) { + GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton(); + uint32_t userdata_count = 0; + + if (particles->process_material.is_valid()) { + GLES3::ParticleProcessMaterialData *material_data = static_cast<GLES3::ParticleProcessMaterialData *>(material_storage->material_get_data(particles->process_material, RS::SHADER_PARTICLES)); + if (material_data && material_data->shader_data->version.is_valid() && material_data->shader_data->valid) { + userdata_count = material_data->shader_data->userdata_count; + } + } + + if (userdata_count != particles->userdata_count) { + // Mismatch userdata, re-create buffers. + _particles_free_data(particles); + } + + if (particles->amount > 0 && particles->front_process_buffer == 0) { + int total_amount = particles->amount; + + particles->userdata_count = userdata_count; + + uint32_t xform_size = particles->mode == RS::PARTICLES_MODE_2D ? 2 : 3; + particles->instance_buffer_stride_cache = sizeof(float) * 4 * (xform_size + 1); + particles->instance_buffer_size_cache = particles->instance_buffer_stride_cache * total_amount; + particles->num_attrib_arrays_cache = 5 + userdata_count + (xform_size - 2); + particles->process_buffer_stride_cache = sizeof(float) * 4 * particles->num_attrib_arrays_cache; + + int process_data_amount = 4 * particles->num_attrib_arrays_cache * total_amount; + float *data = memnew_arr(float, process_data_amount); + + for (int i = 0; i < process_data_amount; i++) { + data[i] = 0; + } + + { + glGenVertexArrays(1, &particles->front_vertex_array); + glBindVertexArray(particles->front_vertex_array); + glGenBuffers(1, &particles->front_process_buffer); + glGenBuffers(1, &particles->front_instance_buffer); + + glBindBuffer(GL_ARRAY_BUFFER, particles->front_process_buffer); + glBufferData(GL_ARRAY_BUFFER, particles->process_buffer_stride_cache * total_amount, data, GL_DYNAMIC_COPY); + + for (uint32_t j = 0; j < particles->num_attrib_arrays_cache; j++) { + glEnableVertexAttribArray(j); + glVertexAttribPointer(j, 4, GL_FLOAT, GL_FALSE, particles->process_buffer_stride_cache, CAST_INT_TO_UCHAR_PTR(sizeof(float) * 4 * j)); + } + glBindVertexArray(0); + + glBindBuffer(GL_ARRAY_BUFFER, particles->front_instance_buffer); + glBufferData(GL_ARRAY_BUFFER, particles->instance_buffer_size_cache, nullptr, GL_DYNAMIC_COPY); + } + + { + glGenVertexArrays(1, &particles->back_vertex_array); + glBindVertexArray(particles->back_vertex_array); + glGenBuffers(1, &particles->back_process_buffer); + glGenBuffers(1, &particles->back_instance_buffer); + + glBindBuffer(GL_ARRAY_BUFFER, particles->back_process_buffer); + glBufferData(GL_ARRAY_BUFFER, particles->process_buffer_stride_cache * total_amount, data, GL_DYNAMIC_COPY); + + for (uint32_t j = 0; j < particles->num_attrib_arrays_cache; j++) { + glEnableVertexAttribArray(j); + glVertexAttribPointer(j, 4, GL_FLOAT, GL_FALSE, particles->process_buffer_stride_cache, CAST_INT_TO_UCHAR_PTR(sizeof(float) * 4 * j)); + } + glBindVertexArray(0); + + glBindBuffer(GL_ARRAY_BUFFER, particles->back_instance_buffer); + glBufferData(GL_ARRAY_BUFFER, particles->instance_buffer_size_cache, nullptr, GL_DYNAMIC_COPY); + } + glBindBuffer(GL_ARRAY_BUFFER, 0); + + memdelete_arr(data); + } +} + +void ParticlesStorage::_particles_allocate_history_buffers(Particles *particles) { + if (particles->sort_buffer == 0) { + glGenBuffers(1, &particles->last_frame_buffer); + glBindBuffer(GL_ARRAY_BUFFER, particles->last_frame_buffer); + glBufferData(GL_ARRAY_BUFFER, particles->instance_buffer_size_cache, nullptr, GL_DYNAMIC_READ); + + glGenBuffers(1, &particles->sort_buffer); + glBindBuffer(GL_ARRAY_BUFFER, particles->sort_buffer); + glBufferData(GL_ARRAY_BUFFER, particles->instance_buffer_size_cache, nullptr, GL_DYNAMIC_READ); + particles->sort_buffer_filled = false; + particles->last_frame_buffer_filled = false; + glBindBuffer(GL_ARRAY_BUFFER, 0); + } +} +void ParticlesStorage::_particles_update_instance_buffer(Particles *particles, const Vector3 &p_axis, const Vector3 &p_up_axis) { + ParticlesCopyShaderGLES3::ShaderVariant variant = ParticlesCopyShaderGLES3::MODE_DEFAULT; + + uint64_t specialization = 0; + if (particles->mode == RS::ParticlesMode::PARTICLES_MODE_3D) { + specialization |= ParticlesCopyShaderGLES3::MODE_3D; + } + + bool success = particles_shader.copy_shader.version_bind_shader(particles_shader.copy_shader_version, variant, specialization); + if (!success) { + return; + } + + // Affect 2D only. + if (particles->use_local_coords) { + // In local mode, particle positions are calculated locally (relative to the node position) + // and they're also drawn locally. + // It works as expected, so we just pass an identity transform. + particles_shader.copy_shader.version_set_uniform(ParticlesCopyShaderGLES3::INV_EMISSION_TRANSFORM, Transform3D(), particles_shader.copy_shader_version, variant, specialization); + } else { + // In global mode, particle positions are calculated globally (relative to the canvas origin) + // but they're drawn locally. + // So, we need to pass the inverse of the emission transform to bring the + // particles to local coordinates before drawing. + Transform3D inv = particles->emission_transform.affine_inverse(); + particles_shader.copy_shader.version_set_uniform(ParticlesCopyShaderGLES3::INV_EMISSION_TRANSFORM, inv, particles_shader.copy_shader_version, variant, specialization); + } + + particles_shader.copy_shader.version_set_uniform(ParticlesCopyShaderGLES3::FRAME_REMAINDER, particles->interpolate ? particles->frame_remainder : 0.0, particles_shader.copy_shader_version, variant, specialization); + particles_shader.copy_shader.version_set_uniform(ParticlesCopyShaderGLES3::ALIGN_MODE, uint32_t(particles->transform_align), particles_shader.copy_shader_version, variant, specialization); + particles_shader.copy_shader.version_set_uniform(ParticlesCopyShaderGLES3::ALIGN_UP, p_up_axis, particles_shader.copy_shader_version, variant, specialization); + particles_shader.copy_shader.version_set_uniform(ParticlesCopyShaderGLES3::SORT_DIRECTION, p_axis, particles_shader.copy_shader_version, variant, specialization); + + glBindVertexArray(particles->back_vertex_array); + glBindBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, particles->front_instance_buffer, 0, particles->instance_buffer_size_cache); + glBeginTransformFeedback(GL_POINTS); + + if (particles->draw_order == RS::PARTICLES_DRAW_ORDER_LIFETIME) { + uint32_t lifetime_split = MIN(particles->amount * particles->phase, particles->amount - 1); + uint32_t stride = particles->process_buffer_stride_cache; + + glBindBuffer(GL_ARRAY_BUFFER, particles->back_process_buffer); + + // Offset VBO so you render starting at the newest particle. + if (particles->amount - lifetime_split > 0) { + glEnableVertexAttribArray(0); // Color. + glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(stride * lifetime_split + sizeof(float) * 4 * 0)); + glEnableVertexAttribArray(1); // .xyz: velocity. .z: flags. + glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(stride * lifetime_split + sizeof(float) * 4 * 1)); + glEnableVertexAttribArray(2); // Custom. + glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(stride * lifetime_split + sizeof(float) * 4 * 2)); + glEnableVertexAttribArray(3); // Xform1. + glVertexAttribPointer(3, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(stride * lifetime_split + sizeof(float) * 4 * 3)); + glEnableVertexAttribArray(4); // Xform2. + glVertexAttribPointer(4, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(stride * lifetime_split + sizeof(float) * 4 * 4)); + if (particles->mode == RS::PARTICLES_MODE_3D) { + glEnableVertexAttribArray(5); // Xform3. + glVertexAttribPointer(5, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(stride * lifetime_split + sizeof(float) * 4 * 5)); + } + + uint32_t to_draw = particles->amount - lifetime_split; + glDrawArrays(GL_POINTS, 0, to_draw); + } + + // Then render from index 0 up intil the newest particle. + if (lifetime_split > 0) { + glEndTransformFeedback(); + // Now output to the second portion of the instance buffer. + glBindBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, particles->front_instance_buffer, particles->instance_buffer_stride_cache * (particles->amount - lifetime_split), particles->instance_buffer_stride_cache * (lifetime_split)); + glBeginTransformFeedback(GL_POINTS); + // Reset back to normal. + for (uint32_t j = 0; j < particles->num_attrib_arrays_cache; j++) { + glEnableVertexAttribArray(j); + glVertexAttribPointer(j, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(sizeof(float) * 4 * j)); + } + + glDrawArrays(GL_POINTS, 0, lifetime_split); + } + } else { + glDrawArrays(GL_POINTS, 0, particles->amount); + } + + glEndTransformFeedback(); + glBindBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, 0, 0, 0); + glBindVertexArray(0); + glBindBuffer(GL_ARRAY_BUFFER, 0); } void ParticlesStorage::update_particles() { + glEnable(GL_RASTERIZER_DISCARD); + + GLuint global_buffer = GLES3::MaterialStorage::get_singleton()->global_shader_parameters_get_uniform_buffer(); + + glBindBufferBase(GL_UNIFORM_BUFFER, PARTICLES_GLOBALS_UNIFORM_LOCATION, global_buffer); + glBindBuffer(GL_UNIFORM_BUFFER, 0); + + while (particle_update_list) { + // Use transform feedback to process particles. + + Particles *particles = particle_update_list; + + particle_update_list = particles->update_list; + particles->update_list = nullptr; + particles->dirty = false; + + _particles_update_buffers(particles); + + if (particles->restart_request) { + particles->prev_ticks = 0; + particles->phase = 0; + particles->prev_phase = 0; + particles->clear = true; + particles->restart_request = false; + } + + if (particles->inactive && !particles->emitting) { + //go next + continue; + } + + if (particles->emitting) { + if (particles->inactive) { + //restart system from scratch + particles->prev_ticks = 0; + particles->phase = 0; + particles->prev_phase = 0; + particles->clear = true; + } + particles->inactive = false; + particles->inactive_time = 0; + } else { + particles->inactive_time += particles->speed_scale * RSG::rasterizer->get_frame_delta_time(); + if (particles->inactive_time > particles->lifetime * 1.2) { + particles->inactive = true; + continue; + } + } + + // Copy the instance buffer that was last used into the last_frame buffer. + // sort_buffer should now be 2 frames out of date. + if (particles->draw_order == RS::PARTICLES_DRAW_ORDER_VIEW_DEPTH || particles->draw_order == RS::PARTICLES_DRAW_ORDER_REVERSE_LIFETIME) { + _particles_allocate_history_buffers(particles); + SWAP(particles->last_frame_buffer, particles->sort_buffer); + + glBindBuffer(GL_COPY_READ_BUFFER, particles->back_instance_buffer); + glBindBuffer(GL_COPY_WRITE_BUFFER, particles->last_frame_buffer); + glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, 0, 0, particles->instance_buffer_size_cache); + + // Last frame's last_frame turned into this frame's sort buffer. + particles->sort_buffer_filled = particles->last_frame_buffer_filled; + particles->sort_buffer_phase = particles->last_frame_phase; + particles->last_frame_buffer_filled = true; + particles->last_frame_phase = particles->phase; + glBindBuffer(GL_COPY_READ_BUFFER, 0); + glBindBuffer(GL_COPY_WRITE_BUFFER, 0); + } + + int fixed_fps = 0; + if (particles->fixed_fps > 0) { + fixed_fps = particles->fixed_fps; + } + + bool zero_time_scale = Engine::get_singleton()->get_time_scale() <= 0.0; + + if (particles->clear && particles->pre_process_time > 0.0) { + double frame_time; + if (fixed_fps > 0) { + frame_time = 1.0 / fixed_fps; + } else { + frame_time = 1.0 / 30.0; + } + + double todo = particles->pre_process_time; + + while (todo >= 0) { + _particles_process(particles, frame_time); + todo -= frame_time; + } + } + + if (fixed_fps > 0) { + double frame_time; + double decr; + if (zero_time_scale) { + frame_time = 0.0; + decr = 1.0 / fixed_fps; + } else { + frame_time = 1.0 / fixed_fps; + decr = frame_time; + } + double delta = RSG::rasterizer->get_frame_delta_time(); + if (delta > 0.1) { //avoid recursive stalls if fps goes below 10 + delta = 0.1; + } else if (delta <= 0.0) { //unlikely but.. + delta = 0.001; + } + double todo = particles->frame_remainder + delta; + + while (todo >= frame_time) { + _particles_process(particles, frame_time); + todo -= decr; + } + + particles->frame_remainder = todo; + + } else { + if (zero_time_scale) { + _particles_process(particles, 0.0); + } else { + _particles_process(particles, RSG::rasterizer->get_frame_delta_time()); + } + } + + // Copy particles to instance buffer and pack Color/Custom. + // We don't have camera information here, so don't copy here if we need camera information for view depth or align mode. + if (particles->draw_order != RS::PARTICLES_DRAW_ORDER_VIEW_DEPTH && particles->transform_align != RS::PARTICLES_TRANSFORM_ALIGN_Z_BILLBOARD && particles->transform_align != RS::PARTICLES_TRANSFORM_ALIGN_Z_BILLBOARD_Y_TO_VELOCITY) { + _particles_update_instance_buffer(particles, Vector3(0.0, 0.0, 0.0), Vector3(0.0, 0.0, 0.0)); + + if (particles->draw_order == RS::PARTICLES_DRAW_ORDER_REVERSE_LIFETIME && particles->sort_buffer_filled) { + if (particles->mode == RS::ParticlesMode::PARTICLES_MODE_2D) { + _particles_reverse_lifetime_sort<ParticleInstanceData2D>(particles); + } else { + _particles_reverse_lifetime_sort<ParticleInstanceData3D>(particles); + } + } + } + + SWAP(particles->front_instance_buffer, particles->back_instance_buffer); + + // At the end of update, the back_buffer contains the most up-to-date-information to read from. + + particles->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_AABB); + } + + glDisable(GL_RASTERIZER_DISCARD); +} + +template <typename ParticleInstanceData> +void ParticlesStorage::_particles_reverse_lifetime_sort(Particles *particles) { + glBindBuffer(GL_ARRAY_BUFFER, particles->sort_buffer); + + ParticleInstanceData *particle_array; + uint32_t buffer_size = particles->amount * sizeof(ParticleInstanceData); +#ifndef __EMSCRIPTEN__ + particle_array = static_cast<ParticleInstanceData *>(glMapBufferRange(GL_ARRAY_BUFFER, 0, buffer_size, GL_MAP_READ_BIT | GL_MAP_WRITE_BIT)); + + ERR_FAIL_NULL(particle_array); +#else + LocalVector<ParticleInstanceData> particle_vector; + particle_vector.resize(particles->amount); + particle_array = particle_vector.ptr(); + glGetBufferSubData(GL_ARRAY_BUFFER, 0, buffer_size, particle_array); +#endif + + uint32_t lifetime_split = MIN(particles->amount * particles->sort_buffer_phase, particles->amount - 1); + + for (uint32_t i = 0; i < lifetime_split / 2; i++) { + SWAP(particle_array[i], particle_array[lifetime_split - i]); + } + + for (uint32_t i = 0; i < (particles->amount - lifetime_split) / 2; i++) { + SWAP(particle_array[lifetime_split + i + 1], particle_array[particles->amount - 1 - i]); + } + +#ifndef __EMSCRIPTEN__ + glUnmapBuffer(GL_ARRAY_BUFFER); +#else + glBufferSubData(GL_ARRAY_BUFFER, 0, buffer_size, particle_vector.ptr()); +#endif + glBindBuffer(GL_ARRAY_BUFFER, 0); +} + +Dependency *ParticlesStorage::particles_get_dependency(RID p_particles) const { + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_NULL_V(particles, nullptr); + + return &particles->dependency; } bool ParticlesStorage::particles_is_inactive(RID p_particles) const { - return false; + ERR_FAIL_COND_V_MSG(RSG::threaded, false, "This function should never be used with threaded rendering, as it stalls the renderer."); + const Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND_V(!particles, false); + return !particles->emitting && particles->inactive; } -/* PARTICLES COLLISION */ +/* PARTICLES COLLISION API */ RID ParticlesStorage::particles_collision_allocate() { - return RID(); + return particles_collision_owner.allocate_rid(); } - void ParticlesStorage::particles_collision_initialize(RID p_rid) { + particles_collision_owner.initialize_rid(p_rid, ParticlesCollision()); } void ParticlesStorage::particles_collision_free(RID p_rid) { + ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_rid); + + if (particles_collision->heightfield_texture != 0) { + glDeleteTextures(1, &particles_collision->heightfield_texture); + particles_collision->heightfield_texture = 0; + glDeleteFramebuffers(1, &particles_collision->heightfield_fb); + particles_collision->heightfield_fb = 0; + } + particles_collision->dependency.deleted_notify(p_rid); + particles_collision_owner.free(p_rid); +} + +GLuint ParticlesStorage::particles_collision_get_heightfield_framebuffer(RID p_particles_collision) const { + ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_particles_collision); + ERR_FAIL_COND_V(!particles_collision, 0); + ERR_FAIL_COND_V(particles_collision->type != RS::PARTICLES_COLLISION_TYPE_HEIGHTFIELD_COLLIDE, 0); + + if (particles_collision->heightfield_texture == 0) { + //create + const int resolutions[RS::PARTICLES_COLLISION_HEIGHTFIELD_RESOLUTION_MAX] = { 256, 512, 1024, 2048, 4096, 8192 }; + Size2i size; + if (particles_collision->extents.x > particles_collision->extents.z) { + size.x = resolutions[particles_collision->heightfield_resolution]; + size.y = int32_t(particles_collision->extents.z / particles_collision->extents.x * size.x); + } else { + size.y = resolutions[particles_collision->heightfield_resolution]; + size.x = int32_t(particles_collision->extents.x / particles_collision->extents.z * size.y); + } + + glGenTextures(1, &particles_collision->heightfield_texture); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, particles_collision->heightfield_texture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32F, size.x, size.y, 0, GL_DEPTH_COMPONENT, GL_FLOAT, nullptr); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + + glGenFramebuffers(1, &particles_collision->heightfield_fb); + glBindFramebuffer(GL_FRAMEBUFFER, particles_collision->heightfield_fb); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, particles_collision->heightfield_texture, 0); +#ifdef DEBUG_ENABLED + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) { + WARN_PRINT("Could create heightmap texture status: " + GLES3::TextureStorage::get_singleton()->get_framebuffer_error(status)); + } +#endif + particles_collision->heightfield_fb_size = size; + + glBindTexture(GL_TEXTURE_2D, 0); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + } + + return particles_collision->heightfield_fb; } void ParticlesStorage::particles_collision_set_collision_type(RID p_particles_collision, RS::ParticlesCollisionType p_type) { + ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_particles_collision); + ERR_FAIL_COND(!particles_collision); + + if (p_type == particles_collision->type) { + return; + } + + if (particles_collision->heightfield_texture != 0) { + glDeleteTextures(1, &particles_collision->heightfield_texture); + particles_collision->heightfield_texture = 0; + glDeleteFramebuffers(1, &particles_collision->heightfield_fb); + particles_collision->heightfield_fb = 0; + } + + particles_collision->type = p_type; + particles_collision->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_AABB); } void ParticlesStorage::particles_collision_set_cull_mask(RID p_particles_collision, uint32_t p_cull_mask) { + ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_particles_collision); + ERR_FAIL_COND(!particles_collision); + particles_collision->cull_mask = p_cull_mask; } void ParticlesStorage::particles_collision_set_sphere_radius(RID p_particles_collision, real_t p_radius) { + ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_particles_collision); + ERR_FAIL_COND(!particles_collision); + + particles_collision->radius = p_radius; + particles_collision->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_AABB); } void ParticlesStorage::particles_collision_set_box_extents(RID p_particles_collision, const Vector3 &p_extents) { + ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_particles_collision); + ERR_FAIL_COND(!particles_collision); + + particles_collision->extents = p_extents; + particles_collision->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_AABB); } void ParticlesStorage::particles_collision_set_attractor_strength(RID p_particles_collision, real_t p_strength) { + ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_particles_collision); + ERR_FAIL_COND(!particles_collision); + + particles_collision->attractor_strength = p_strength; } void ParticlesStorage::particles_collision_set_attractor_directionality(RID p_particles_collision, real_t p_directionality) { + ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_particles_collision); + ERR_FAIL_COND(!particles_collision); + + particles_collision->attractor_directionality = p_directionality; } void ParticlesStorage::particles_collision_set_attractor_attenuation(RID p_particles_collision, real_t p_curve) { + ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_particles_collision); + ERR_FAIL_COND(!particles_collision); + + particles_collision->attractor_attenuation = p_curve; } void ParticlesStorage::particles_collision_set_field_texture(RID p_particles_collision, RID p_texture) { + WARN_PRINT_ONCE("The OpenGL 3 renderer does not support SDF collisions in 3D particle shaders"); } void ParticlesStorage::particles_collision_height_field_update(RID p_particles_collision) { + ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_particles_collision); + ERR_FAIL_COND(!particles_collision); + particles_collision->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_AABB); } void ParticlesStorage::particles_collision_set_height_field_resolution(RID p_particles_collision, RS::ParticlesCollisionHeightfieldResolution p_resolution) { + ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_particles_collision); + ERR_FAIL_COND(!particles_collision); + ERR_FAIL_INDEX(p_resolution, RS::PARTICLES_COLLISION_HEIGHTFIELD_RESOLUTION_MAX); + + if (particles_collision->heightfield_resolution == p_resolution) { + return; + } + + particles_collision->heightfield_resolution = p_resolution; + + if (particles_collision->heightfield_texture != 0) { + glDeleteTextures(1, &particles_collision->heightfield_texture); + particles_collision->heightfield_texture = 0; + glDeleteFramebuffers(1, &particles_collision->heightfield_fb); + particles_collision->heightfield_fb = 0; + } } AABB ParticlesStorage::particles_collision_get_aabb(RID p_particles_collision) const { - return AABB(); + ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_particles_collision); + ERR_FAIL_COND_V(!particles_collision, AABB()); + + switch (particles_collision->type) { + case RS::PARTICLES_COLLISION_TYPE_SPHERE_ATTRACT: + case RS::PARTICLES_COLLISION_TYPE_SPHERE_COLLIDE: { + AABB aabb; + aabb.position = -Vector3(1, 1, 1) * particles_collision->radius; + aabb.size = Vector3(2, 2, 2) * particles_collision->radius; + return aabb; + } + default: { + AABB aabb; + aabb.position = -particles_collision->extents; + aabb.size = particles_collision->extents * 2; + return aabb; + } + } +} + +Vector3 ParticlesStorage::particles_collision_get_extents(RID p_particles_collision) const { + const ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_particles_collision); + ERR_FAIL_COND_V(!particles_collision, Vector3()); + return particles_collision->extents; } bool ParticlesStorage::particles_collision_is_heightfield(RID p_particles_collision) const { - return false; + const ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_particles_collision); + ERR_FAIL_COND_V(!particles_collision, false); + return particles_collision->type == RS::PARTICLES_COLLISION_TYPE_HEIGHTFIELD_COLLIDE; } -RID ParticlesStorage::particles_collision_get_heightfield_framebuffer(RID p_particles_collision) const { - return RID(); +Dependency *ParticlesStorage::particles_collision_get_dependency(RID p_particles_collision) const { + ParticlesCollision *pc = particles_collision_owner.get_or_null(p_particles_collision); + ERR_FAIL_NULL_V(pc, nullptr); + + return &pc->dependency; } +/* Particles collision instance */ + RID ParticlesStorage::particles_collision_instance_create(RID p_collision) { - return RID(); + ParticlesCollisionInstance pci; + pci.collision = p_collision; + return particles_collision_instance_owner.make_rid(pci); } void ParticlesStorage::particles_collision_instance_free(RID p_rid) { + particles_collision_instance_owner.free(p_rid); } void ParticlesStorage::particles_collision_instance_set_transform(RID p_collision_instance, const Transform3D &p_transform) { + ParticlesCollisionInstance *pci = particles_collision_instance_owner.get_or_null(p_collision_instance); + ERR_FAIL_COND(!pci); + pci->transform = p_transform; } void ParticlesStorage::particles_collision_instance_set_active(RID p_collision_instance, bool p_active) { + ParticlesCollisionInstance *pci = particles_collision_instance_owner.get_or_null(p_collision_instance); + ERR_FAIL_COND(!pci); + pci->active = p_active; } #endif // GLES3_ENABLED diff --git a/drivers/gles3/storage/particles_storage.h b/drivers/gles3/storage/particles_storage.h index 84d1f94d8c..434718006e 100644 --- a/drivers/gles3/storage/particles_storage.h +++ b/drivers/gles3/storage/particles_storage.h @@ -33,25 +33,283 @@ #ifdef GLES3_ENABLED +#include "../shaders/particles_copy.glsl.gen.h" #include "core/templates/local_vector.h" #include "core/templates/rid_owner.h" #include "core/templates/self_list.h" #include "servers/rendering/storage/particles_storage.h" +#include "servers/rendering/storage/utilities.h" + +#include "platform_config.h" +#ifndef OPENGL_INCLUDE_H +#include <GLES3/gl3.h> +#else +#include OPENGL_INCLUDE_H +#endif namespace GLES3 { +enum ParticlesUniformLocation { + PARTICLES_FRAME_UNIFORM_LOCATION, + PARTICLES_GLOBALS_UNIFORM_LOCATION, + PARTICLES_MATERIAL_UNIFORM_LOCATION, +}; + class ParticlesStorage : public RendererParticlesStorage { private: static ParticlesStorage *singleton; + /* PARTICLES */ + + struct ParticleInstanceData3D { + float xform[12]; + float color[2]; // Color and custom are packed together into one vec4; + float custom[2]; + }; + + struct ParticleInstanceData2D { + float xform[8]; + float color[2]; // Color and custom are packed together into one vec4; + float custom[2]; + }; + + struct ParticlesViewSort { + Vector3 z_dir; + bool operator()(const ParticleInstanceData3D &p_a, const ParticleInstanceData3D &p_b) const { + return z_dir.dot(Vector3(p_a.xform[3], p_a.xform[7], p_a.xform[11])) < z_dir.dot(Vector3(p_b.xform[3], p_b.xform[7], p_b.xform[11])); + } + }; + + struct ParticlesFrameParams { + enum { + MAX_ATTRACTORS = 32, + MAX_COLLIDERS = 32, + MAX_3D_TEXTURES = 0 // GLES3 renderer doesn't support using 3D textures for flow field or collisions. + }; + + enum AttractorType { + ATTRACTOR_TYPE_SPHERE, + ATTRACTOR_TYPE_BOX, + ATTRACTOR_TYPE_VECTOR_FIELD, + }; + + struct Attractor { + float transform[16]; + float extents[4]; // Extents or radius. w-channel is padding. + + uint32_t type; + float strength; + float attenuation; + float directionality; + }; + + enum CollisionType { + COLLISION_TYPE_SPHERE, + COLLISION_TYPE_BOX, + COLLISION_TYPE_SDF, + COLLISION_TYPE_HEIGHT_FIELD, + COLLISION_TYPE_2D_SDF, + + }; + + struct Collider { + float transform[16]; + float extents[4]; // Extents or radius. w-channel is padding. + + uint32_t type; + float scale; + float pad0; + float pad1; + }; + + uint32_t emitting; + uint32_t cycle; + float system_phase; + float prev_system_phase; + + float explosiveness; + float randomness; + float time; + float delta; + + float particle_size; + float pad0; + float pad1; + float pad2; + + uint32_t random_seed; + uint32_t attractor_count; + uint32_t collider_count; + uint32_t frame; + + float emission_transform[16]; + + Attractor attractors[MAX_ATTRACTORS]; + Collider colliders[MAX_COLLIDERS]; + }; + + struct Particles { + RS::ParticlesMode mode = RS::PARTICLES_MODE_3D; + bool inactive = true; + double inactive_time = 0.0; + bool emitting = false; + bool one_shot = false; + int amount = 0; + double lifetime = 1.0; + double pre_process_time = 0.0; + real_t explosiveness = 0.0; + real_t randomness = 0.0; + bool restart_request = false; + AABB custom_aabb = AABB(Vector3(-4, -4, -4), Vector3(8, 8, 8)); + bool use_local_coords = false; + bool has_collision_cache = false; + + bool has_sdf_collision = false; + Transform2D sdf_collision_transform; + Rect2 sdf_collision_to_screen; + GLuint sdf_collision_texture = 0; + + RID process_material; + uint32_t frame_counter = 0; + RS::ParticlesTransformAlign transform_align = RS::PARTICLES_TRANSFORM_ALIGN_DISABLED; + + RS::ParticlesDrawOrder draw_order = RS::PARTICLES_DRAW_ORDER_INDEX; + + Vector<RID> draw_passes; + + GLuint frame_params_ubo = 0; + + // We may process particles multiple times each frame (if they have a fixed FPS higher than the game FPS). + // Unfortunately, this means we can't just use a round-robin system of 3 buffers. + // To ensure the sort buffer is accurate, we copy the last frame instance buffer just before processing. + + // Transform Feedback buffer and VAO for rendering. + // Each frame we render to this one. + GLuint front_vertex_array = 0; // Binds process buffer. Used for processing. + GLuint front_process_buffer = 0; // Transform + color + custom data + userdata + velocity + flags. Only needed for processing. + GLuint front_instance_buffer = 0; // Transform + color + custom data. In packed format needed for rendering. + + // VAO for transform feedback, contains last frame's data. + // Read from this one for particles process and then copy to last frame buffer. + GLuint back_vertex_array = 0; // Binds process buffer. Used for processing. + GLuint back_process_buffer = 0; // Transform + color + custom data + userdata + velocity + flags. Only needed for processing. + GLuint back_instance_buffer = 0; // Transform + color + custom data. In packed format needed for rendering. + + uint32_t instance_buffer_size_cache = 0; + uint32_t instance_buffer_stride_cache = 0; + uint32_t num_attrib_arrays_cache = 0; + uint32_t process_buffer_stride_cache = 0; + + // Only ever copied to, holds last frame's instance data, then swaps with sort_buffer. + GLuint last_frame_buffer = 0; + bool last_frame_buffer_filled = false; + float last_frame_phase = 0.0; + + // The frame-before-last's instance buffer. + // Use this to copy data back for sorting or computing AABB. + GLuint sort_buffer = 0; + bool sort_buffer_filled = false; + float sort_buffer_phase = 0.0; + + uint32_t userdata_count = 0; + + bool dirty = false; + Particles *update_list = nullptr; + + double phase = 0.0; + double prev_phase = 0.0; + uint64_t prev_ticks = 0; + uint32_t random_seed = 0; + + uint32_t cycle_number = 0; + + double speed_scale = 1.0; + + int fixed_fps = 30; + bool interpolate = true; + bool fractional_delta = false; + double frame_remainder = 0; + real_t collision_base_size = 0.01; + + bool clear = true; + + Transform3D emission_transform; + + HashSet<RID> collisions; + + Dependency dependency; + + double trail_length = 1.0; + bool trails_enabled = false; + + Particles() { + } + }; + + void _particles_process(Particles *p_particles, double p_delta); + void _particles_free_data(Particles *particles); + void _particles_update_buffers(Particles *particles); + void _particles_allocate_history_buffers(Particles *particles); + void _particles_update_instance_buffer(Particles *particles, const Vector3 &p_axis, const Vector3 &p_up_axis); + + template <typename T> + void _particles_reverse_lifetime_sort(Particles *particles); + + struct ParticlesShader { + RID default_shader; + RID default_material; + RID default_shader_version; + + ParticlesCopyShaderGLES3 copy_shader; + RID copy_shader_version; + } particles_shader; + + Particles *particle_update_list = nullptr; + + mutable RID_Owner<Particles, true> particles_owner; + + /* Particles Collision */ + + struct ParticlesCollision { + RS::ParticlesCollisionType type = RS::PARTICLES_COLLISION_TYPE_SPHERE_ATTRACT; + uint32_t cull_mask = 0xFFFFFFFF; + float radius = 1.0; + Vector3 extents = Vector3(1, 1, 1); + float attractor_strength = 1.0; + float attractor_attenuation = 1.0; + float attractor_directionality = 0.0; + GLuint field_texture = 0; + GLuint heightfield_texture = 0; + GLuint heightfield_fb = 0; + Size2i heightfield_fb_size; + + RS::ParticlesCollisionHeightfieldResolution heightfield_resolution = RS::PARTICLES_COLLISION_HEIGHTFIELD_RESOLUTION_1024; + + Dependency dependency; + }; + + struct ParticlesCollisionInstance { + RID collision; + Transform3D transform; + bool active = false; + }; + + mutable RID_Owner<ParticlesCollision, true> particles_collision_owner; + + mutable RID_Owner<ParticlesCollisionInstance> particles_collision_instance_owner; + public: static ParticlesStorage *get_singleton(); ParticlesStorage(); virtual ~ParticlesStorage(); + bool free(RID p_rid); + /* PARTICLES */ + bool owns_particles(RID p_rid) { return particles_owner.owns(p_rid); } + virtual RID particles_allocate() override; virtual void particles_initialize(RID p_rid) override; virtual void particles_free(RID p_rid) override; @@ -102,12 +360,51 @@ public: virtual void particles_add_collision(RID p_particles, RID p_instance) override; virtual void particles_remove_collision(RID p_particles, RID p_instance) override; - virtual void particles_set_canvas_sdf_collision(RID p_particles, bool p_enable, const Transform2D &p_xform, const Rect2 &p_to_screen, RID p_texture) override; + void particles_set_canvas_sdf_collision(RID p_particles, bool p_enable, const Transform2D &p_xform, const Rect2 &p_to_screen, GLuint p_texture); virtual void update_particles() override; virtual bool particles_is_inactive(RID p_particles) const override; + _FORCE_INLINE_ RS::ParticlesMode particles_get_mode(RID p_particles) { + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND_V(!particles, RS::PARTICLES_MODE_2D); + return particles->mode; + } + + _FORCE_INLINE_ uint32_t particles_get_amount(RID p_particles) { + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND_V(!particles, 0); + + return particles->amount; + } + + _FORCE_INLINE_ GLuint particles_get_gl_buffer(RID p_particles) { + Particles *particles = particles_owner.get_or_null(p_particles); + + if ((particles->draw_order == RS::PARTICLES_DRAW_ORDER_VIEW_DEPTH || particles->draw_order == RS::PARTICLES_DRAW_ORDER_REVERSE_LIFETIME) && particles->sort_buffer_filled) { + return particles->sort_buffer; + } + return particles->back_instance_buffer; + } + + _FORCE_INLINE_ bool particles_has_collision(RID p_particles) { + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND_V(!particles, 0); + + return particles->has_collision_cache; + } + + _FORCE_INLINE_ uint32_t particles_is_using_local_coords(RID p_particles) { + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND_V(!particles, false); + + return particles->use_local_coords; + } + + Dependency *particles_get_dependency(RID p_particles) const; + /* PARTICLES COLLISION */ + bool owns_particles_collision(RID p_rid) { return particles_collision_owner.owns(p_rid); } virtual RID particles_collision_allocate() override; virtual void particles_collision_initialize(RID p_rid) override; @@ -124,8 +421,22 @@ public: virtual void particles_collision_height_field_update(RID p_particles_collision) override; virtual void particles_collision_set_height_field_resolution(RID p_particles_collision, RS::ParticlesCollisionHeightfieldResolution p_resolution) override; virtual AABB particles_collision_get_aabb(RID p_particles_collision) const override; + Vector3 particles_collision_get_extents(RID p_particles_collision) const; virtual bool particles_collision_is_heightfield(RID p_particles_collision) const override; - virtual RID particles_collision_get_heightfield_framebuffer(RID p_particles_collision) const override; + GLuint particles_collision_get_heightfield_framebuffer(RID p_particles_collision) const; + + _FORCE_INLINE_ Size2i particles_collision_get_heightfield_size(RID p_particles_collision) const { + ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_particles_collision); + ERR_FAIL_COND_V(!particles_collision, Size2i()); + ERR_FAIL_COND_V(particles_collision->type != RS::PARTICLES_COLLISION_TYPE_HEIGHTFIELD_COLLIDE, Size2i()); + + return particles_collision->heightfield_fb_size; + } + + Dependency *particles_collision_get_dependency(RID p_particles) const; + + /* PARTICLES COLLISION INSTANCE*/ + bool owns_particles_collision_instance(RID p_rid) { return particles_collision_instance_owner.owns(p_rid); } virtual RID particles_collision_instance_create(RID p_collision) override; virtual void particles_collision_instance_free(RID p_rid) override; diff --git a/drivers/gles3/storage/texture_storage.cpp b/drivers/gles3/storage/texture_storage.cpp index 51e22fe779..767ac2bf1a 100644 --- a/drivers/gles3/storage/texture_storage.cpp +++ b/drivers/gles3/storage/texture_storage.cpp @@ -647,7 +647,7 @@ void TextureStorage::texture_2d_initialize(RID p_texture, const Ref<Image> &p_im texture.height = p_image->get_height(); texture.alloc_width = texture.width; texture.alloc_height = texture.height; - texture.mipmaps = p_image->get_mipmap_count(); + texture.mipmaps = p_image->get_mipmap_count() + 1; texture.format = p_image->get_format(); texture.type = Texture::TYPE_2D; texture.target = GL_TEXTURE_2D; @@ -2215,7 +2215,11 @@ void TextureStorage::render_target_sdf_process(RID p_render_target) { // Load CanvasSdfShaderGLES3::ShaderVariant variant = shrink ? CanvasSdfShaderGLES3::MODE_LOAD_SHRINK : CanvasSdfShaderGLES3::MODE_LOAD; - sdf_shader.shader.version_bind_shader(sdf_shader.shader_version, variant); + bool success = sdf_shader.shader.version_bind_shader(sdf_shader.shader_version, variant); + if (!success) { + return; + } + sdf_shader.shader.version_set_uniform(CanvasSdfShaderGLES3::BASE_SIZE, r.size, sdf_shader.shader_version, variant); sdf_shader.shader.version_set_uniform(CanvasSdfShaderGLES3::SIZE, size, sdf_shader.shader_version, variant); sdf_shader.shader.version_set_uniform(CanvasSdfShaderGLES3::STRIDE, 0, sdf_shader.shader_version, variant); @@ -2236,7 +2240,11 @@ void TextureStorage::render_target_sdf_process(RID p_render_target) { int stride = nearest_power_of_2_templated(MAX(size.width, size.height) / 2); variant = CanvasSdfShaderGLES3::MODE_PROCESS; - sdf_shader.shader.version_bind_shader(sdf_shader.shader_version, variant); + success = sdf_shader.shader.version_bind_shader(sdf_shader.shader_version, variant); + if (!success) { + return; + } + sdf_shader.shader.version_set_uniform(CanvasSdfShaderGLES3::BASE_SIZE, r.size, sdf_shader.shader_version, variant); sdf_shader.shader.version_set_uniform(CanvasSdfShaderGLES3::SIZE, size, sdf_shader.shader_version, variant); sdf_shader.shader.version_set_uniform(CanvasSdfShaderGLES3::STRIDE, stride, sdf_shader.shader_version, variant); @@ -2260,7 +2268,11 @@ void TextureStorage::render_target_sdf_process(RID p_render_target) { // Store variant = shrink ? CanvasSdfShaderGLES3::MODE_STORE_SHRINK : CanvasSdfShaderGLES3::MODE_STORE; - sdf_shader.shader.version_bind_shader(sdf_shader.shader_version, variant); + success = sdf_shader.shader.version_bind_shader(sdf_shader.shader_version, variant); + if (!success) { + return; + } + sdf_shader.shader.version_set_uniform(CanvasSdfShaderGLES3::BASE_SIZE, r.size, sdf_shader.shader_version, variant); sdf_shader.shader.version_set_uniform(CanvasSdfShaderGLES3::SIZE, size, sdf_shader.shader_version, variant); sdf_shader.shader.version_set_uniform(CanvasSdfShaderGLES3::STRIDE, stride, sdf_shader.shader_version, variant); diff --git a/drivers/gles3/storage/utilities.cpp b/drivers/gles3/storage/utilities.cpp index 8e7e218bb9..393093c2a7 100644 --- a/drivers/gles3/storage/utilities.cpp +++ b/drivers/gles3/storage/utilities.cpp @@ -108,6 +108,10 @@ RS::InstanceType Utilities::get_base_type(RID p_rid) const { return RS::INSTANCE_LIGHT; } else if (GLES3::LightStorage::get_singleton()->owns_lightmap(p_rid)) { return RS::INSTANCE_LIGHTMAP; + } else if (GLES3::ParticlesStorage::get_singleton()->owns_particles(p_rid)) { + return RS::INSTANCE_PARTICLES; + } else if (GLES3::ParticlesStorage::get_singleton()->owns_particles_collision(p_rid)) { + return RS::INSTANCE_PARTICLES_COLLISION; } return RS::INSTANCE_NONE; } @@ -143,53 +147,18 @@ bool Utilities::free(RID p_rid) { } else if (GLES3::LightStorage::get_singleton()->owns_lightmap(p_rid)) { GLES3::LightStorage::get_singleton()->lightmap_free(p_rid); return true; - } else { - return false; - } - /* - else if (reflection_probe_owner.owns(p_rid)) { - // delete the texture - ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_rid); - reflection_probe->instance_remove_deps(); - - reflection_probe_owner.free(p_rid); - memdelete(reflection_probe); - - return true; - } else if (lightmap_capture_data_owner.owns(p_rid)) { - // delete the texture - LightmapCapture *lightmap_capture = lightmap_capture_data_owner.get_or_null(p_rid); - lightmap_capture->instance_remove_deps(); - - lightmap_capture_data_owner.free(p_rid); - memdelete(lightmap_capture); + } else if (GLES3::ParticlesStorage::get_singleton()->owns_particles(p_rid)) { + GLES3::ParticlesStorage::get_singleton()->particles_free(p_rid); return true; - - } else if (canvas_occluder_owner.owns(p_rid)) { - CanvasOccluder *co = canvas_occluder_owner.get_or_null(p_rid); - if (co->index_id) { - glDeleteBuffers(1, &co->index_id); - } - if (co->vertex_id) { - glDeleteBuffers(1, &co->vertex_id); - } - - canvas_occluder_owner.free(p_rid); - memdelete(co); - + } else if (GLES3::ParticlesStorage::get_singleton()->owns_particles_collision(p_rid)) { + GLES3::ParticlesStorage::get_singleton()->particles_collision_free(p_rid); return true; - - } else if (canvas_light_shadow_owner.owns(p_rid)) { - CanvasLightShadow *cls = canvas_light_shadow_owner.get_or_null(p_rid); - glDeleteFramebuffers(1, &cls->fbo); - glDeleteRenderbuffers(1, &cls->depth); - glDeleteTextures(1, &cls->distance); - canvas_light_shadow_owner.free(p_rid); - memdelete(cls); - + } else if (GLES3::ParticlesStorage::get_singleton()->owns_particles_collision_instance(p_rid)) { + GLES3::ParticlesStorage::get_singleton()->particles_collision_instance_free(p_rid); return true; + } else { + return false; } - */ } /* DEPENDENCIES */ @@ -207,6 +176,12 @@ void Utilities::base_update_dependency(RID p_base, DependencyTracker *p_instance } else if (LightStorage::get_singleton()->owns_light(p_base)) { Light *l = LightStorage::get_singleton()->get_light(p_base); p_instance->update_dependency(&l->dependency); + } else if (ParticlesStorage::get_singleton()->owns_particles(p_base)) { + Dependency *dependency = ParticlesStorage::get_singleton()->particles_get_dependency(p_base); + p_instance->update_dependency(dependency); + } else if (ParticlesStorage::get_singleton()->owns_particles_collision(p_base)) { + Dependency *dependency = ParticlesStorage::get_singleton()->particles_collision_get_dependency(p_base); + p_instance->update_dependency(dependency); } } diff --git a/gles3_builders.py b/gles3_builders.py index 46ab1bbc03..0d0037dea1 100644 --- a/gles3_builders.py +++ b/gles3_builders.py @@ -20,6 +20,7 @@ class GLES3HeaderStruct: self.texunit_names = [] self.ubos = [] self.ubo_names = [] + self.feedbacks = [] self.vertex_included_files = [] self.fragment_included_files = [] @@ -168,6 +169,20 @@ def include_file_in_gles3_header(filename: str, header_data: GLES3HeaderStruct, if not x in header_data.uniforms: header_data.uniforms += [x] + if (line.strip().find("out ") == 0 or line.strip().find("flat ") == 0) and line.find("tfb:") != -1: + uline = line.replace("flat ", "") + uline = uline.replace("out ", "") + uline = uline.replace("highp ", "") + uline = uline.replace(";", "") + uline = uline[uline.find(" ") :].strip() + + if uline.find("//") != -1: + name, bind = uline.split("//") + if bind.find("tfb:") != -1: + name = name.strip() + bind = bind.replace("tfb:", "").strip() + header_data.feedbacks += [(name, bind)] + line = line.replace("\r", "") line = line.replace("\n", "") @@ -240,11 +255,11 @@ def build_gles3_header(filename: str, include: str, class_suffix: str, header_da defspec |= 1 << i fd.write( - "\t_FORCE_INLINE_ void version_bind_shader(RID p_version,ShaderVariant p_variant" + "\t_FORCE_INLINE_ bool version_bind_shader(RID p_version,ShaderVariant p_variant" + defvariant + ",uint64_t p_specialization=" + str(defspec) - + ") { _version_bind_shader(p_version,p_variant,p_specialization); }\n\n" + + ") { return _version_bind_shader(p_version,p_variant,p_specialization); }\n\n" ) if header_data.uniforms: @@ -278,7 +293,7 @@ def build_gles3_header(filename: str, include: str, class_suffix: str, header_da + defvariant + ",uint64_t p_specialization=" + str(defspec) - + ") { _FU glUniform1i(version_get_uniform(p_uniform,p_version,p_variant,p_specialization),p_value); }\n\n" + + ") { _FU glUniform1ui(version_get_uniform(p_uniform,p_version,p_variant,p_specialization),p_value); }\n\n" ) fd.write( "\t_FORCE_INLINE_ void version_set_uniform(Uniforms p_uniform, int8_t p_value,RID p_version,ShaderVariant p_variant" @@ -292,7 +307,7 @@ def build_gles3_header(filename: str, include: str, class_suffix: str, header_da + defvariant + ",uint64_t p_specialization=" + str(defspec) - + ") { _FU glUniform1i(version_get_uniform(p_uniform,p_version,p_variant,p_specialization),p_value); }\n\n" + + ") { _FU glUniform1ui(version_get_uniform(p_uniform,p_version,p_variant,p_specialization),p_value); }\n\n" ) fd.write( "\t_FORCE_INLINE_ void version_set_uniform(Uniforms p_uniform, int16_t p_value,RID p_version,ShaderVariant p_variant" @@ -306,7 +321,7 @@ def build_gles3_header(filename: str, include: str, class_suffix: str, header_da + defvariant + ",uint64_t p_specialization=" + str(defspec) - + ") { _FU glUniform1i(version_get_uniform(p_uniform,p_version,p_variant,p_specialization),p_value); }\n\n" + + ") { _FU glUniform1ui(version_get_uniform(p_uniform,p_version,p_variant,p_specialization),p_value); }\n\n" ) fd.write( "\t_FORCE_INLINE_ void version_set_uniform(Uniforms p_uniform, int32_t p_value,RID p_version,ShaderVariant p_variant" @@ -497,6 +512,8 @@ def build_gles3_header(filename: str, include: str, class_suffix: str, header_da else: fd.write("\t\tstatic UBOPair *_ubo_pairs=nullptr;\n") + specializations_found = [] + if header_data.specialization_names: fd.write("\t\tstatic Specialization _spec_pairs[]={\n") for i in range(len(header_data.specialization_names)): @@ -507,10 +524,30 @@ def build_gles3_header(filename: str, include: str, class_suffix: str, header_da defval = "false" fd.write('\t\t\t{"' + header_data.specialization_names[i] + '",' + defval + "},\n") + specializations_found.append(header_data.specialization_names[i]) fd.write("\t\t};\n\n") else: fd.write("\t\tstatic Specialization *_spec_pairs=nullptr;\n") + feedback_count = 0 + + if header_data.feedbacks: + + fd.write("\t\tstatic const Feedback _feedbacks[]={\n") + for x in header_data.feedbacks: + name = x[0] + spec = x[1] + if spec in specializations_found: + fd.write('\t\t\t{"' + name + '",' + str(1 << specializations_found.index(spec)) + "},\n") + else: + fd.write('\t\t\t{"' + name + '",0},\n') + + feedback_count += 1 + + fd.write("\t\t};\n\n") + else: + fd.write("\t\tstatic const Feedback* _feedbacks=nullptr;\n") + fd.write("\t\tstatic const char _vertex_code[]={\n") for x in header_data.vertex_lines: for c in x: @@ -535,6 +572,8 @@ def build_gles3_header(filename: str, include: str, class_suffix: str, header_da + ",_uniform_strings," + str(len(header_data.ubos)) + ",_ubo_pairs," + + str(feedback_count) + + ",_feedbacks," + str(len(header_data.texunits)) + ",_texunit_pairs," + str(len(header_data.specialization_names)) diff --git a/scene/2d/gpu_particles_2d.cpp b/scene/2d/gpu_particles_2d.cpp index e18f2bd1a1..c8f5d7f5a6 100644 --- a/scene/2d/gpu_particles_2d.cpp +++ b/scene/2d/gpu_particles_2d.cpp @@ -299,10 +299,6 @@ bool GPUParticles2D::get_interpolate() const { PackedStringArray GPUParticles2D::get_configuration_warnings() const { PackedStringArray warnings = Node2D::get_configuration_warnings(); - if (RenderingServer::get_singleton()->is_low_end()) { - warnings.push_back(RTR("GPU-based particles are not supported by the OpenGL video driver.\nUse the CPUParticles2D node instead. You can use the \"Convert to CPUParticles2D\" option for this purpose.")); - } - if (process_material.is_null()) { warnings.push_back(RTR("A material to process the particles is not assigned, so no behavior is imprinted.")); } else { diff --git a/scene/2d/gpu_particles_2d.h b/scene/2d/gpu_particles_2d.h index 7fba174357..94555e0bb0 100644 --- a/scene/2d/gpu_particles_2d.h +++ b/scene/2d/gpu_particles_2d.h @@ -64,7 +64,7 @@ private: #endif Ref<Material> process_material; - DrawOrder draw_order; + DrawOrder draw_order = DRAW_ORDER_LIFETIME; Ref<Texture2D> texture; diff --git a/scene/3d/gpu_particles_3d.cpp b/scene/3d/gpu_particles_3d.cpp index 9d04202c2d..17dfe2610e 100644 --- a/scene/3d/gpu_particles_3d.cpp +++ b/scene/3d/gpu_particles_3d.cpp @@ -272,10 +272,6 @@ bool GPUParticles3D::get_interpolate() const { PackedStringArray GPUParticles3D::get_configuration_warnings() const { PackedStringArray warnings = GeometryInstance3D::get_configuration_warnings(); - if (RenderingServer::get_singleton()->is_low_end()) { - warnings.push_back(RTR("GPU-based particles are not supported by the OpenGL video driver.\nUse the CPUParticles3D node instead. You can use the \"Convert to CPUParticles3D\" option for this purpose.")); - } - bool meshes_found = false; bool anim_material_found = false; diff --git a/scene/3d/gpu_particles_3d.h b/scene/3d/gpu_particles_3d.h index 835d71862a..d1768436e5 100644 --- a/scene/3d/gpu_particles_3d.h +++ b/scene/3d/gpu_particles_3d.h @@ -82,7 +82,7 @@ private: Ref<Material> process_material; - DrawOrder draw_order; + DrawOrder draw_order = DRAW_ORDER_INDEX; Vector<Ref<Mesh>> draw_passes; Ref<Skin> skin; diff --git a/servers/rendering/dummy/storage/particles_storage.h b/servers/rendering/dummy/storage/particles_storage.h index 7cee55922d..beedff0a76 100644 --- a/servers/rendering/dummy/storage/particles_storage.h +++ b/servers/rendering/dummy/storage/particles_storage.h @@ -89,8 +89,6 @@ public: virtual void particles_add_collision(RID p_particles, RID p_instance) override {} virtual void particles_remove_collision(RID p_particles, RID p_instance) override {} - virtual void particles_set_canvas_sdf_collision(RID p_particles, bool p_enable, const Transform2D &p_xform, const Rect2 &p_to_screen, RID p_texture) override {} - virtual void update_particles() override {} /* PARTICLES COLLISION */ @@ -111,7 +109,6 @@ public: virtual void particles_collision_set_height_field_resolution(RID p_particles_collision, RS::ParticlesCollisionHeightfieldResolution p_resolution) override {} virtual AABB particles_collision_get_aabb(RID p_particles_collision) const override { return AABB(); } virtual bool particles_collision_is_heightfield(RID p_particles_collision) const override { return false; } - virtual RID particles_collision_get_heightfield_framebuffer(RID p_particles_collision) const override { return RID(); } virtual RID particles_collision_instance_create(RID p_collision) override { return RID(); } virtual void particles_collision_instance_free(RID p_rid) override {} diff --git a/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp b/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp index 854976692e..51aa81745b 100644 --- a/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp @@ -1027,6 +1027,7 @@ void ParticlesStorage::_particles_process(Particles *p_particles, double p_delta uniforms.push_back(u); } p_particles->collision_textures_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, particles_shader.default_shader_rd, 2); + p_particles->collision_heightmap_texture = collision_heightmap_texture; } } diff --git a/servers/rendering/renderer_rd/storage_rd/particles_storage.h b/servers/rendering/renderer_rd/storage_rd/particles_storage.h index a9cc98abb9..49a8444e2f 100644 --- a/servers/rendering/renderer_rd/storage_rd/particles_storage.h +++ b/servers/rendering/renderer_rd/storage_rd/particles_storage.h @@ -54,8 +54,7 @@ private: float velocity[3]; uint32_t active; float color[4]; - float custom[3]; - float lifetime; + float custom[4]; }; struct ParticlesFrameParams { @@ -127,9 +126,6 @@ private: Collider colliders[MAX_COLLIDERS]; }; - struct ParticleEmissionBufferData { - }; - struct ParticleEmissionBuffer { struct Data { float xform[16]; @@ -412,7 +408,7 @@ public: bool owns_particles(RID p_rid) { return particles_owner.owns(p_rid); } virtual RID particles_allocate() override; - virtual void particles_initialize(RID p_particles_collision) override; + virtual void particles_initialize(RID p_rid) override; virtual void particles_free(RID p_rid) override; virtual void particles_set_mode(RID p_particles, RS::ParticlesMode p_mode) override; @@ -519,7 +515,7 @@ public: virtual void particles_add_collision(RID p_particles, RID p_particles_collision_instance) override; virtual void particles_remove_collision(RID p_particles, RID p_particles_collision_instance) override; - virtual void particles_set_canvas_sdf_collision(RID p_particles, bool p_enable, const Transform2D &p_xform, const Rect2 &p_to_screen, RID p_texture) override; + void particles_set_canvas_sdf_collision(RID p_particles, bool p_enable, const Transform2D &p_xform, const Rect2 &p_to_screen, RID p_texture); virtual void update_particles() override; @@ -546,7 +542,7 @@ public: virtual AABB particles_collision_get_aabb(RID p_particles_collision) const override; Vector3 particles_collision_get_extents(RID p_particles_collision) const; virtual bool particles_collision_is_heightfield(RID p_particles_collision) const override; - virtual RID particles_collision_get_heightfield_framebuffer(RID p_particles_collision) const override; + RID particles_collision_get_heightfield_framebuffer(RID p_particles_collision) const; Dependency *particles_collision_get_dependency(RID p_particles) const; diff --git a/servers/rendering/storage/particles_storage.h b/servers/rendering/storage/particles_storage.h index ee4b8679b3..5ef54346f2 100644 --- a/servers/rendering/storage/particles_storage.h +++ b/servers/rendering/storage/particles_storage.h @@ -94,8 +94,6 @@ public: virtual void particles_add_collision(RID p_particles, RID p_particles_collision_instance) = 0; virtual void particles_remove_collision(RID p_particles, RID p_particles_collision_instance) = 0; - virtual void particles_set_canvas_sdf_collision(RID p_particles, bool p_enable, const Transform2D &p_xform, const Rect2 &p_to_screen, RID p_texture) = 0; - virtual void update_particles() = 0; /* PARTICLES COLLISION */ @@ -116,7 +114,6 @@ public: virtual void particles_collision_set_height_field_resolution(RID p_particles_collision, RS::ParticlesCollisionHeightfieldResolution p_resolution) = 0; //for SDF and vector field virtual AABB particles_collision_get_aabb(RID p_particles_collision) const = 0; virtual bool particles_collision_is_heightfield(RID p_particles_collision) const = 0; - virtual RID particles_collision_get_heightfield_framebuffer(RID p_particles_collision) const = 0; //used from 2D and 3D virtual RID particles_collision_instance_create(RID p_collision) = 0; diff --git a/tests/python_build/fixtures/gles3/vertex_fragment_expected_full.glsl b/tests/python_build/fixtures/gles3/vertex_fragment_expected_full.glsl index 7bf56e73cd..58ddd08cdd 100644 --- a/tests/python_build/fixtures/gles3/vertex_fragment_expected_full.glsl +++ b/tests/python_build/fixtures/gles3/vertex_fragment_expected_full.glsl @@ -18,7 +18,7 @@ public: DISABLE_LIGHTING=1, }; - _FORCE_INLINE_ void version_bind_shader(RID p_version,ShaderVariant p_variant,uint64_t p_specialization=0) { _version_bind_shader(p_version,p_variant,p_specialization); } + _FORCE_INLINE_ bool version_bind_shader(RID p_version,ShaderVariant p_variant,uint64_t p_specialization=0) { return _version_bind_shader(p_version,p_variant,p_specialization); } protected: @@ -35,13 +35,14 @@ protected: {"DISABLE_LIGHTING",false}, }; + static const Feedback* _feedbacks=nullptr; static const char _vertex_code[]={ 10,112,114,101,99,105,115,105,111,110,32,104,105,103,104,112,32,102,108,111,97,116,59,10,112,114,101,99,105,115,105,111,110,32,104,105,103,104,112,32,105,110,116,59,10,10,108,97,121,111,117,116,40,108,111,99,97,116,105,111,110,32,61,32,48,41,32,105,110,32,104,105,103,104,112,32,118,101,99,51,32,118,101,114,116,101,120,59,10,10,111,117,116,32,104,105,103,104,112,32,118,101,99,52,32,112,111,115,105,116,105,111,110,95,105,110,116,101,114,112,59,10,10,118,111,105,100,32,109,97,105,110,40,41,32,123,10,9,112,111,115,105,116,105,111,110,95,105,110,116,101,114,112,32,61,32,118,101,99,52,40,118,101,114,116,101,120,46,120,44,49,44,48,44,49,41,59,10,125,10,10, 0}; static const char _fragment_code[]={ 10,112,114,101,99,105,115,105,111,110,32,104,105,103,104,112,32,102,108,111,97,116,59,10,112,114,101,99,105,115,105,111,110,32,104,105,103,104,112,32,105,110,116,59,10,10,105,110,32,104,105,103,104,112,32,118,101,99,52,32,112,111,115,105,116,105,111,110,95,105,110,116,101,114,112,59,10,10,118,111,105,100,32,109,97,105,110,40,41,32,123,10,9,104,105,103,104,112,32,102,108,111,97,116,32,100,101,112,116,104,32,61,32,40,40,112,111,115,105,116,105,111,110,95,105,110,116,101,114,112,46,122,32,47,32,112,111,115,105,116,105,111,110,95,105,110,116,101,114,112,46,119,41,32,43,32,49,46,48,41,59,10,9,102,114,97,103,95,99,111,108,111,114,32,61,32,118,101,99,52,40,100,101,112,116,104,41,59,10,125,10, 0}; - _setup(_vertex_code,_fragment_code,"VertexFragmentShaderGLES3",0,_uniform_strings,0,_ubo_pairs,0,_texunit_pairs,1,_spec_pairs,1,_variant_defines); + _setup(_vertex_code,_fragment_code,"VertexFragmentShaderGLES3",0,_uniform_strings,0,_ubo_pairs,0,_feedbacks,0,_texunit_pairs,1,_spec_pairs,1,_variant_defines); } }; diff --git a/tests/python_build/fixtures/gles3/vertex_fragment_expected_parts.json b/tests/python_build/fixtures/gles3/vertex_fragment_expected_parts.json index eaeb5981c0..5ac8092ad0 100644 --- a/tests/python_build/fixtures/gles3/vertex_fragment_expected_parts.json +++ b/tests/python_build/fixtures/gles3/vertex_fragment_expected_parts.json @@ -31,6 +31,7 @@ "texunit_names": [], "ubos": [], "ubo_names": [], + "feedbacks": [], "vertex_included_files": [], "fragment_included_files": [], "reading": "fragment", |