From 9ce57050a5d12776deedd44fcb82dd5841a56686 Mon Sep 17 00:00:00 2001 From: clayjohn Date: Mon, 7 Nov 2022 22:40:03 -0800 Subject: Add GPUParticles to the OpenGL3 renderer. This includes collision (2D SDF, Box, Sphere, Heightmap), attraction (Box, Sphere), and all sorting modes. This does not include 3D SDF collisions, trails, or manual emission. --- drivers/gles3/effects/copy_effects.cpp | 18 +- drivers/gles3/rasterizer_canvas_gles3.cpp | 137 ++- drivers/gles3/rasterizer_canvas_gles3.h | 4 +- drivers/gles3/rasterizer_scene_gles3.cpp | 209 +++- drivers/gles3/rasterizer_scene_gles3.h | 10 - drivers/gles3/shader_gles3.cpp | 22 +- drivers/gles3/shader_gles3.h | 18 +- drivers/gles3/shaders/SCsub | 2 + drivers/gles3/shaders/cubemap_filter.glsl | 4 +- drivers/gles3/shaders/particles.glsl | 501 ++++++++ drivers/gles3/shaders/particles_copy.glsl | 122 ++ drivers/gles3/shaders/scene.glsl | 14 +- drivers/gles3/storage/material_storage.cpp | 269 ++++- drivers/gles3/storage/material_storage.h | 62 + drivers/gles3/storage/mesh_storage.cpp | 5 +- drivers/gles3/storage/mesh_storage.h | 3 + drivers/gles3/storage/particles_storage.cpp | 1211 +++++++++++++++++++- drivers/gles3/storage/particles_storage.h | 315 ++++- drivers/gles3/storage/texture_storage.cpp | 20 +- drivers/gles3/storage/utilities.cpp | 61 +- gles3_builders.py | 49 +- scene/2d/gpu_particles_2d.cpp | 4 - scene/2d/gpu_particles_2d.h | 2 +- scene/3d/gpu_particles_3d.cpp | 4 - scene/3d/gpu_particles_3d.h | 2 +- .../rendering/dummy/storage/particles_storage.h | 3 - .../renderer_rd/storage_rd/particles_storage.cpp | 1 + .../renderer_rd/storage_rd/particles_storage.h | 12 +- servers/rendering/storage/particles_storage.h | 3 - .../gles3/vertex_fragment_expected_full.glsl | 5 +- .../gles3/vertex_fragment_expected_parts.json | 1 + 31 files changed, 2841 insertions(+), 252 deletions(-) create mode 100644 drivers/gles3/shaders/particles.glsl create mode 100644 drivers/gles3/shaders/particles_copy.glsl 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(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(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(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(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(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(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 &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(&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 &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 &p_render_ template 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 &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(&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 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 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 2d6a793c9e..6172114dc7 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 &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 &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 @@ -3586,7 +3594,7 @@ void SceneMaterialData::set_next_pass(RID p_pass) { } void SceneMaterialData::update_parameters(const HashMap &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() { @@ -3619,4 +3627,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 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(); + } + default_texture_params[p_name][p_index] = p_texture; + } +} + +void ParticlesShaderData::get_shader_uniform_list(List *p_param_list) const { + HashMap order; + + for (const KeyValue &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 &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 *p_param_list) const { + for (const KeyValue &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 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 &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(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 24d9a0fee1..c463c3ded1 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 *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 *p_param_list) const; virtual void get_instance_param_list(List *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 *p_param_list) const; virtual void get_instance_param_list(List *p_param_list) const; @@ -337,6 +341,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 *p_param_list) const; virtual void get_instance_param_list(List *p_param_list) const; @@ -368,6 +373,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 uniforms; + Vector texture_uniforms; + + Vector ubo_offsets; + uint32_t ubo_size = 0; + + String path; + String code; + HashMap> 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 *p_param_list) const; + virtual void get_instance_param_list(List *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 &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 { @@ -504,6 +565,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(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 &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 &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 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(material_storage->material_get_data(p_particles->process_material, RS::SHADER_PARTICLES)); + if (!m) { + m = static_cast(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(glMapBufferRange(GL_ARRAY_BUFFER, 0, particles->amount * sizeof(ParticleInstanceData3D), GL_MAP_READ_BIT | GL_MAP_WRITE_BIT)); + ERR_FAIL_NULL(particle_array); +#else + LocalVector 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 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(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(particles); + } else { + _particles_reverse_lifetime_sort(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 +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(glMapBufferRange(GL_ARRAY_BUFFER, 0, buffer_size, GL_MAP_READ_BIT | GL_MAP_WRITE_BIT)); + + ERR_FAIL_NULL(particle_array); +#else + LocalVector 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 +#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 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 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 + 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_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 particles_collision_owner; + + mutable RID_Owner 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 &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 process_material; - DrawOrder draw_order; + DrawOrder draw_order = DRAW_ORDER_LIFETIME; Ref 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 process_material; - DrawOrder draw_order; + DrawOrder draw_order = DRAW_ORDER_INDEX; Vector> draw_passes; Ref 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", -- cgit v1.2.3