diff options
Diffstat (limited to 'drivers/gles3/storage')
-rw-r--r-- | drivers/gles3/storage/config.cpp | 60 | ||||
-rw-r--r-- | drivers/gles3/storage/config.h | 60 | ||||
-rw-r--r-- | drivers/gles3/storage/light_storage.cpp | 62 | ||||
-rw-r--r-- | drivers/gles3/storage/light_storage.h | 59 | ||||
-rw-r--r-- | drivers/gles3/storage/material_storage.cpp | 672 | ||||
-rw-r--r-- | drivers/gles3/storage/material_storage.h | 155 | ||||
-rw-r--r-- | drivers/gles3/storage/mesh_storage.cpp | 652 | ||||
-rw-r--r-- | drivers/gles3/storage/mesh_storage.h | 102 | ||||
-rw-r--r-- | drivers/gles3/storage/particles_storage.cpp | 1278 | ||||
-rw-r--r-- | drivers/gles3/storage/particles_storage.h | 373 | ||||
-rw-r--r-- | drivers/gles3/storage/render_scene_buffers_gles3.cpp | 123 | ||||
-rw-r--r-- | drivers/gles3/storage/render_scene_buffers_gles3.h | 63 | ||||
-rw-r--r-- | drivers/gles3/storage/texture_storage.cpp | 503 | ||||
-rw-r--r-- | drivers/gles3/storage/texture_storage.h | 100 | ||||
-rw-r--r-- | drivers/gles3/storage/utilities.cpp | 122 | ||||
-rw-r--r-- | drivers/gles3/storage/utilities.h | 58 |
16 files changed, 3308 insertions, 1134 deletions
diff --git a/drivers/gles3/storage/config.cpp b/drivers/gles3/storage/config.cpp index 9b496c0999..d1a83e6851 100644 --- a/drivers/gles3/storage/config.cpp +++ b/drivers/gles3/storage/config.cpp @@ -1,32 +1,32 @@ -/*************************************************************************/ -/* config.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ +/**************************************************************************/ +/* config.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ #ifdef GLES3_ENABLED @@ -90,8 +90,6 @@ Config::Config() { glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE, &max_uniform_buffer_size); glGetIntegerv(GL_MAX_VIEWPORT_DIMS, max_viewport_size); - glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &uniform_buffer_offset_alignment); - support_anisotropic_filter = extensions.has("GL_EXT_texture_filter_anisotropic"); if (support_anisotropic_filter) { glGetFloatv(_GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &anisotropic_level); diff --git a/drivers/gles3/storage/config.h b/drivers/gles3/storage/config.h index 87202fde84..8399502675 100644 --- a/drivers/gles3/storage/config.h +++ b/drivers/gles3/storage/config.h @@ -1,32 +1,32 @@ -/*************************************************************************/ -/* config.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ +/**************************************************************************/ +/* config.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ #ifndef CONFIG_GLES3_H #define CONFIG_GLES3_H @@ -67,8 +67,6 @@ public: int max_renderable_lights = 0; int max_lights_per_object = 0; - int uniform_buffer_offset_alignment = 0; - // TODO implement wireframe in OpenGL // bool generate_wireframes; diff --git a/drivers/gles3/storage/light_storage.cpp b/drivers/gles3/storage/light_storage.cpp index b6bd4a2760..a325f79d8c 100644 --- a/drivers/gles3/storage/light_storage.cpp +++ b/drivers/gles3/storage/light_storage.cpp @@ -1,32 +1,32 @@ -/*************************************************************************/ -/* light_storage.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ +/**************************************************************************/ +/* light_storage.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ #ifdef GLES3_ENABLED @@ -502,6 +502,10 @@ bool LightStorage::reflection_probe_instance_begin_render(RID p_instance, RID p_ return false; } +Ref<RenderSceneBuffers> LightStorage::reflection_probe_atlas_get_render_buffers(RID p_reflection_atlas) { + return Ref<RenderSceneBuffers>(); +} + bool LightStorage::reflection_probe_instance_postprocess_step(RID p_instance) { return true; } diff --git a/drivers/gles3/storage/light_storage.h b/drivers/gles3/storage/light_storage.h index 12cb2c4393..a304b6a265 100644 --- a/drivers/gles3/storage/light_storage.h +++ b/drivers/gles3/storage/light_storage.h @@ -1,32 +1,32 @@ -/*************************************************************************/ -/* light_storage.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ +/**************************************************************************/ +/* light_storage.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ #ifndef LIGHT_STORAGE_GLES3_H #define LIGHT_STORAGE_GLES3_H @@ -366,6 +366,7 @@ public: virtual bool reflection_probe_instance_needs_redraw(RID p_instance) override; virtual bool reflection_probe_instance_has_reflection(RID p_instance) override; virtual bool reflection_probe_instance_begin_render(RID p_instance, RID p_reflection_atlas) override; + virtual Ref<RenderSceneBuffers> reflection_probe_atlas_get_render_buffers(RID p_reflection_atlas) override; virtual bool reflection_probe_instance_postprocess_step(RID p_instance) override; /* LIGHTMAP CAPTURE */ diff --git a/drivers/gles3/storage/material_storage.cpp b/drivers/gles3/storage/material_storage.cpp index 6748eb3676..50e5c868d3 100644 --- a/drivers/gles3/storage/material_storage.cpp +++ b/drivers/gles3/storage/material_storage.cpp @@ -1,32 +1,32 @@ -/*************************************************************************/ -/* material_storage.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ +/**************************************************************************/ +/* material_storage.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ #ifdef GLES3_ENABLED @@ -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" @@ -922,6 +923,104 @@ _FORCE_INLINE_ static void _fill_std140_ubo_empty(ShaderLanguage::DataType type, } /////////////////////////////////////////////////////////////////////////// +// ShaderData + +void ShaderData::set_path_hint(const String &p_hint) { + path = p_hint; +} + +void ShaderData::set_default_texture_parameter(const StringName &p_name, RID p_texture, int p_index) { + if (!p_texture.is_valid()) { + if (default_texture_params.has(p_name) && default_texture_params[p_name].has(p_index)) { + default_texture_params[p_name].erase(p_index); + + if (default_texture_params[p_name].is_empty()) { + default_texture_params.erase(p_name); + } + } + } else { + if (!default_texture_params.has(p_name)) { + default_texture_params[p_name] = HashMap<int, RID>(); + } + default_texture_params[p_name][p_index] = p_texture; + } +} + +Variant ShaderData::get_default_parameter(const StringName &p_parameter) const { + if (uniforms.has(p_parameter)) { + ShaderLanguage::ShaderNode::Uniform uniform = uniforms[p_parameter]; + Vector<ShaderLanguage::ConstantNode::Value> default_value = uniform.default_value; + return ShaderLanguage::constant_value_to_variant(default_value, uniform.type, uniform.array_size, uniform.hint); + } + return Variant(); +} + +void ShaderData::get_shader_uniform_list(List<PropertyInfo> *p_param_list) const { + SortArray<Pair<StringName, int>, ShaderLanguage::UniformOrderComparator> sorter; + LocalVector<Pair<StringName, int>> filtered_uniforms; + + for (const KeyValue<StringName, ShaderLanguage::ShaderNode::Uniform> &E : uniforms) { + if (E.value.scope != ShaderLanguage::ShaderNode::Uniform::SCOPE_LOCAL) { + continue; + } + if (E.value.texture_order >= 0) { + filtered_uniforms.push_back(Pair<StringName, int>(E.key, E.value.texture_order + 100000)); + } else { + filtered_uniforms.push_back(Pair<StringName, int>(E.key, E.value.order)); + } + } + int uniform_count = filtered_uniforms.size(); + sorter.sort(filtered_uniforms.ptr(), uniform_count); + + String last_group; + for (int i = 0; i < uniform_count; i++) { + const StringName &uniform_name = filtered_uniforms[i].first; + const ShaderLanguage::ShaderNode::Uniform &uniform = uniforms[uniform_name]; + + String group = uniform.group; + if (!uniform.subgroup.is_empty()) { + group += "::" + uniform.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(uniform); + pi.name = uniform_name; + p_param_list->push_back(pi); + } +} + +void ShaderData::get_instance_param_list(List<RendererMaterialStorage::InstanceShaderParam> *p_param_list) const { + for (const KeyValue<StringName, ShaderLanguage::ShaderNode::Uniform> &E : uniforms) { + if (E.value.scope != ShaderLanguage::ShaderNode::Uniform::SCOPE_INSTANCE) { + continue; + } + + RendererMaterialStorage::InstanceShaderParam p; + p.info = ShaderLanguage::uniform_to_property_info(E.value); + p.info.name = E.key; //supply name + p.index = E.value.instance_index; + p.default_value = ShaderLanguage::constant_value_to_variant(E.value.default_value, E.value.type, E.value.array_size, E.value.hint); + p_param_list->push_back(p); + } +} + +bool ShaderData::is_parameter_texture(const StringName &p_param) const { + if (!uniforms.has(p_param)) { + return false; + } + + return uniforms[p_param].texture_order >= 0; +} + +/////////////////////////////////////////////////////////////////////////// // MaterialData // Look up table to translate ShaderLanguage::DataType to GL_TEXTURE_* @@ -960,6 +1059,22 @@ static const GLenum target_from_type[ShaderLanguage::TYPE_MAX] = { GL_TEXTURE_2D, // TYPE_STRUCT }; +static const RS::CanvasItemTextureRepeat repeat_from_uniform[ShaderLanguage::REPEAT_DEFAULT + 1] = { + RS::CanvasItemTextureRepeat::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED, // ShaderLanguage::TextureRepeat::REPEAT_DISABLE, + RS::CanvasItemTextureRepeat::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED, // ShaderLanguage::TextureRepeat::REPEAT_ENABLE, + RS::CanvasItemTextureRepeat::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED, // ShaderLanguage::TextureRepeat::REPEAT_DEFAULT, +}; + +static const RS::CanvasItemTextureFilter filter_from_uniform[ShaderLanguage::FILTER_DEFAULT + 1] = { + RS::CanvasItemTextureFilter::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, // ShaderLanguage::TextureFilter::FILTER_NEAREST, + RS::CanvasItemTextureFilter::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, // ShaderLanguage::TextureFilter::FILTER_LINEAR, + RS::CanvasItemTextureFilter::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, // ShaderLanguage::TextureFilter::FILTER_NEAREST_MIPMAP, + RS::CanvasItemTextureFilter::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, // ShaderLanguage::TextureFilter::FILTER_LINEAR_MIPMAP, + RS::CanvasItemTextureFilter::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC, // ShaderLanguage::TextureFilter::FILTER_NEAREST_MIPMAP_ANISOTROPIC, + RS::CanvasItemTextureFilter::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC, // ShaderLanguage::TextureFilter::FILTER_LINEAR_MIPMAP_ANISOTROPIC, + RS::CanvasItemTextureFilter::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, // ShaderLanguage::TextureFilter::FILTER_DEFAULT, +}; + void MaterialData::update_uniform_buffer(const HashMap<StringName, ShaderLanguage::ShaderNode::Uniform> &p_uniforms, const uint32_t *p_uniform_offsets, const HashMap<StringName, Variant> &p_parameters, uint8_t *p_buffer, uint32_t p_buffer_size, bool p_use_linear_color) { MaterialStorage *material_storage = MaterialStorage::get_singleton(); bool uses_global_buffer = false; @@ -1342,13 +1457,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; @@ -1400,7 +1515,6 @@ MaterialStorage::MaterialStorage() { actions.renames["SPECULAR_SHININESS_TEXTURE"] = "specular_texture"; actions.renames["SPECULAR_SHININESS"] = "specular_shininess"; actions.renames["SCREEN_UV"] = "screen_uv"; - actions.renames["SCREEN_TEXTURE"] = "screen_texture"; actions.renames["SCREEN_PIXEL_SIZE"] = "screen_pixel_size"; actions.renames["FRAGCOORD"] = "gl_FragCoord"; actions.renames["POINT_COORD"] = "gl_PointCoord"; @@ -1421,7 +1535,6 @@ MaterialStorage::MaterialStorage() { actions.renames["screen_uv_to_sdf"] = "screen_uv_to_sdf"; actions.usage_defines["COLOR"] = "#define COLOR_USED\n"; - actions.usage_defines["SCREEN_TEXTURE"] = "#define SCREEN_TEXTURE_USED\n"; actions.usage_defines["SCREEN_UV"] = "#define SCREEN_UV_USED\n"; actions.usage_defines["SCREEN_PIXEL_SIZE"] = "@SCREEN_UV"; actions.usage_defines["NORMAL"] = "#define NORMAL_USED\n"; @@ -1502,9 +1615,6 @@ MaterialStorage::MaterialStorage() { actions.renames["POINT_COORD"] = "gl_PointCoord"; actions.renames["INSTANCE_CUSTOM"] = "instance_custom"; actions.renames["SCREEN_UV"] = "screen_uv"; - //actions.renames["SCREEN_TEXTURE"] = "color_buffer"; //Not implemented in 3D yet. - //actions.renames["DEPTH_TEXTURE"] = "depth_buffer"; // Not implemented in 3D yet. - //actions.renames["NORMAL_ROUGHNESS_TEXTURE"] = "normal_roughness_buffer"; // Not implemented in 3D yet actions.renames["DEPTH"] = "gl_FragDepth"; actions.renames["OUTPUT_IS_SRGB"] = "true"; actions.renames["FOG"] = "fog"; @@ -1521,6 +1631,7 @@ MaterialStorage::MaterialStorage() { actions.renames["NODE_POSITION_WORLD"] = "model_matrix[3].xyz"; actions.renames["CAMERA_POSITION_WORLD"] = "scene_data.inv_view_matrix[3].xyz"; actions.renames["CAMERA_DIRECTION_WORLD"] = "scene_data.view_matrix[3].xyz"; + actions.renames["CAMERA_VISIBLE_LAYERS"] = "scene_data.camera_visible_layers"; actions.renames["NODE_POSITION_VIEW"] = "(model_matrix * scene_data.view_matrix)[3].xyz"; actions.renames["VIEW_INDEX"] = "ViewIndex"; @@ -1568,7 +1679,6 @@ MaterialStorage::MaterialStorage() { actions.usage_defines["SSS_STRENGTH"] = "#define ENABLE_SSS\n"; actions.usage_defines["SSS_TRANSMITTANCE_DEPTH"] = "#define ENABLE_TRANSMITTANCE\n"; actions.usage_defines["BACKLIGHT"] = "#define LIGHT_BACKLIGHT_USED\n"; - actions.usage_defines["SCREEN_TEXTURE"] = "#define SCREEN_TEXTURE_USED\n"; actions.usage_defines["SCREEN_UV"] = "#define SCREEN_UV_USED\n"; actions.usage_defines["DIFFUSE_LIGHT"] = "#define USE_LIGHT_SHADER_CODE\n"; @@ -1613,32 +1723,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 +1770,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 +2572,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 +2644,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 { @@ -2654,6 +2759,14 @@ void MaterialStorage::material_free(RID p_rid) { Material *material = material_owner.get_or_null(p_rid); ERR_FAIL_COND(!material); + // Need to clear texture arrays to prevent spin locking of their RID's. + // This happens when the app is being closed. + for (KeyValue<StringName, Variant> &E : material->params) { + if (E.value.get_type() == Variant::ARRAY) { + Array(E.value).clear(); + } + } + material_set_shader(p_rid, RID()); //clean up shader material->dependency.deleted_notify(p_rid); @@ -2841,7 +2954,6 @@ void CanvasShaderData::set_code(const String &p_code) { actions.render_mode_values["blend_premul_alpha"] = Pair<int *, int>(&blend_modei, BLEND_MODE_PMALPHA); actions.render_mode_values["blend_disabled"] = Pair<int *, int>(&blend_modei, BLEND_MODE_DISABLED); - actions.usage_flag_pointers["SCREEN_TEXTURE"] = &uses_screen_texture; actions.usage_flag_pointers["texture_sdf"] = &uses_sdf; actions.usage_flag_pointers["TIME"] = &uses_time; @@ -2855,6 +2967,7 @@ void CanvasShaderData::set_code(const String &p_code) { blend_mode = BlendMode(blend_modei); uses_screen_texture_mipmaps = gen_code.uses_screen_texture_mipmaps; + uses_screen_texture = gen_code.uses_screen_texture; #if 0 print_line("**compiling shader:"); @@ -2889,85 +3002,6 @@ void CanvasShaderData::set_code(const String &p_code) { valid = true; } -void CanvasShaderData::set_default_texture_parameter(const StringName &p_name, RID p_texture, int p_index) { - if (!p_texture.is_valid()) { - if (default_texture_params.has(p_name) && default_texture_params[p_name].has(p_index)) { - default_texture_params[p_name].erase(p_index); - - if (default_texture_params[p_name].is_empty()) { - default_texture_params.erase(p_name); - } - } - } else { - if (!default_texture_params.has(p_name)) { - default_texture_params[p_name] = HashMap<int, RID>(); - } - default_texture_params[p_name][p_index] = p_texture; - } -} - -void CanvasShaderData::get_shader_uniform_list(List<PropertyInfo> *p_param_list) const { - HashMap<int, StringName> order; - - for (const KeyValue<StringName, ShaderLanguage::ShaderNode::Uniform> &E : uniforms) { - if (E.value.scope != ShaderLanguage::ShaderNode::Uniform::SCOPE_LOCAL || - E.value.hint == ShaderLanguage::ShaderNode::Uniform::HINT_SCREEN_TEXTURE || - E.value.hint == ShaderLanguage::ShaderNode::Uniform::HINT_NORMAL_ROUGHNESS_TEXTURE || - E.value.hint == ShaderLanguage::ShaderNode::Uniform::HINT_DEPTH_TEXTURE) { - continue; - } - if (E.value.texture_order >= 0) { - order[E.value.texture_order + 100000] = E.key; - } else { - order[E.value.order] = E.key; - } - } - - String last_group; - for (const KeyValue<int, StringName> &E : order) { - String group = uniforms[E.value].group; - if (!uniforms[E.value].subgroup.is_empty()) { - group += "::" + uniforms[E.value].subgroup; - } - - if (group != last_group) { - PropertyInfo pi; - pi.usage = PROPERTY_USAGE_GROUP; - pi.name = group; - p_param_list->push_back(pi); - - last_group = group; - } - - PropertyInfo pi = ShaderLanguage::uniform_to_property_info(uniforms[E.value]); - pi.name = E.value; - p_param_list->push_back(pi); - } -} - -void CanvasShaderData::get_instance_param_list(List<RendererMaterialStorage::InstanceShaderParam> *p_param_list) const { - for (const KeyValue<StringName, ShaderLanguage::ShaderNode::Uniform> &E : uniforms) { - if (E.value.scope != ShaderLanguage::ShaderNode::Uniform::SCOPE_INSTANCE) { - continue; - } - - RendererMaterialStorage::InstanceShaderParam p; - p.info = ShaderLanguage::uniform_to_property_info(E.value); - p.info.name = E.key; //supply name - p.index = E.value.instance_index; - p.default_value = ShaderLanguage::constant_value_to_variant(E.value.default_value, E.value.type, E.value.array_size, E.value.hint); - p_param_list->push_back(p); - } -} - -bool CanvasShaderData::is_parameter_texture(const StringName &p_param) const { - if (!uniforms.has(p_param)) { - return false; - } - - return uniforms[p_param].texture_order >= 0; -} - bool CanvasShaderData::is_animated() const { return false; } @@ -2976,15 +3010,6 @@ bool CanvasShaderData::casts_shadows() const { return false; } -Variant CanvasShaderData::get_default_parameter(const StringName &p_parameter) const { - if (uniforms.has(p_parameter)) { - ShaderLanguage::ShaderNode::Uniform uniform = uniforms[p_parameter]; - Vector<ShaderLanguage::ConstantNode::Value> default_value = uniform.default_value; - return ShaderLanguage::constant_value_to_variant(default_value, uniform.type, uniform.array_size, uniform.hint); - } - return Variant(); -} - RS::ShaderNativeSourceCode CanvasShaderData::get_native_source_code() const { return MaterialStorage::get_singleton()->shaders.canvas_shader.version_get_native_source_code(version); } @@ -3007,7 +3032,7 @@ GLES3::ShaderData *GLES3::_create_canvas_shader_func() { } void CanvasMaterialData::update_parameters(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty) { - return update_parameters_internal(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size); + update_parameters_internal(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size); } void CanvasMaterialData::bind_uniforms() { @@ -3020,13 +3045,12 @@ void CanvasMaterialData::bind_uniforms() { Texture *texture = TextureStorage::get_singleton()->get_texture(textures[ti]); glActiveTexture(GL_TEXTURE1 + ti); // Start at GL_TEXTURE1 because texture slot 0 is used by the base texture glBindTexture(target_from_type[texture_uniforms[ti].type], texture->tex_id); + if (texture->render_target) { + texture->render_target->used_in_frame = true; + } - // 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); + texture->gl_set_filter(filter_from_uniform[int(texture_uniforms[ti].filter)]); + texture->gl_set_repeat(repeat_from_uniform[int(texture_uniforms[ti].repeat)]); } } @@ -3133,83 +3157,6 @@ void SkyShaderData::set_code(const String &p_code) { valid = true; } -void SkyShaderData::set_default_texture_parameter(const StringName &p_name, RID p_texture, int p_index) { - if (!p_texture.is_valid()) { - if (default_texture_params.has(p_name) && default_texture_params[p_name].has(p_index)) { - default_texture_params[p_name].erase(p_index); - - if (default_texture_params[p_name].is_empty()) { - default_texture_params.erase(p_name); - } - } - } else { - if (!default_texture_params.has(p_name)) { - default_texture_params[p_name] = HashMap<int, RID>(); - } - default_texture_params[p_name][p_index] = p_texture; - } -} - -void SkyShaderData::get_shader_uniform_list(List<PropertyInfo> *p_param_list) const { - RBMap<int, StringName> order; - - for (const KeyValue<StringName, ShaderLanguage::ShaderNode::Uniform> &E : uniforms) { - if (E.value.scope == ShaderLanguage::ShaderNode::Uniform::SCOPE_GLOBAL || E.value.scope == ShaderLanguage::ShaderNode::Uniform::SCOPE_INSTANCE) { - continue; - } - - if (E.value.texture_order >= 0) { - order[E.value.texture_order + 100000] = E.key; - } else { - order[E.value.order] = E.key; - } - } - - String last_group; - for (const KeyValue<int, StringName> &E : order) { - String group = uniforms[E.value].group; - if (!uniforms[E.value].subgroup.is_empty()) { - group += "::" + uniforms[E.value].subgroup; - } - - if (group != last_group) { - PropertyInfo pi; - pi.usage = PROPERTY_USAGE_GROUP; - pi.name = group; - p_param_list->push_back(pi); - - last_group = group; - } - - PropertyInfo pi = ShaderLanguage::uniform_to_property_info(uniforms[E.value]); - pi.name = E.value; - p_param_list->push_back(pi); - } -} - -void SkyShaderData::get_instance_param_list(List<RendererMaterialStorage::InstanceShaderParam> *p_param_list) const { - for (const KeyValue<StringName, ShaderLanguage::ShaderNode::Uniform> &E : uniforms) { - if (E.value.scope != ShaderLanguage::ShaderNode::Uniform::SCOPE_INSTANCE) { - continue; - } - - RendererMaterialStorage::InstanceShaderParam p; - p.info = ShaderLanguage::uniform_to_property_info(E.value); - p.info.name = E.key; //supply name - p.index = E.value.instance_index; - p.default_value = ShaderLanguage::constant_value_to_variant(E.value.default_value, E.value.type, E.value.array_size, E.value.hint); - p_param_list->push_back(p); - } -} - -bool SkyShaderData::is_parameter_texture(const StringName &p_param) const { - if (!uniforms.has(p_param)) { - return false; - } - - return uniforms[p_param].texture_order >= 0; -} - bool SkyShaderData::is_animated() const { return false; } @@ -3218,15 +3165,6 @@ bool SkyShaderData::casts_shadows() const { return false; } -Variant SkyShaderData::get_default_parameter(const StringName &p_parameter) const { - if (uniforms.has(p_parameter)) { - ShaderLanguage::ShaderNode::Uniform uniform = uniforms[p_parameter]; - Vector<ShaderLanguage::ConstantNode::Value> default_value = uniform.default_value; - return ShaderLanguage::constant_value_to_variant(default_value, uniform.type, uniform.array_size, uniform.hint); - } - return Variant(); -} - RS::ShaderNativeSourceCode SkyShaderData::get_native_source_code() const { return MaterialStorage::get_singleton()->shaders.sky_shader.version_get_native_source_code(version); } @@ -3251,7 +3189,7 @@ GLES3::ShaderData *GLES3::_create_sky_shader_func() { void SkyMaterialData::update_parameters(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty) { uniform_set_updated = true; - return update_parameters_internal(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size); + update_parameters_internal(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size); } SkyMaterialData::~SkyMaterialData() { @@ -3273,13 +3211,12 @@ void SkyMaterialData::bind_uniforms() { Texture *texture = TextureStorage::get_singleton()->get_texture(textures[ti]); glActiveTexture(GL_TEXTURE0 + ti); glBindTexture(target_from_type[texture_uniforms[ti].type], texture->tex_id); + if (texture->render_target) { + texture->render_target->used_in_frame = true; + } - // 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); + texture->gl_set_filter(filter_from_uniform[int(texture_uniforms[ti].filter)]); + texture->gl_set_repeat(repeat_from_uniform[int(texture_uniforms[ti].repeat)]); } } @@ -3310,7 +3247,7 @@ void SceneShaderData::set_code(const String &p_code) { uses_alpha = false; uses_alpha_clip = false; uses_blend_alpha = false; - uses_depth_pre_pass = false; + uses_depth_prepass_alpha = false; uses_discard = false; uses_roughness = false; uses_normal = false; @@ -3358,14 +3295,14 @@ void SceneShaderData::set_code(const String &p_code) { actions.usage_flag_pointers["ALPHA"] = &uses_alpha; actions.usage_flag_pointers["ALPHA_SCISSOR_THRESHOLD"] = &uses_alpha_clip; - actions.render_mode_flags["depth_prepass_alpha"] = &uses_depth_pre_pass; + // Use alpha clip pipeline for alpha hash/dither. + // This prevents sorting issues inherent to alpha blending and allows such materials to cast shadows. + actions.usage_flag_pointers["ALPHA_HASH_SCALE"] = &uses_alpha_clip; + actions.render_mode_flags["depth_prepass_alpha"] = &uses_depth_prepass_alpha; actions.usage_flag_pointers["SSS_STRENGTH"] = &uses_sss; actions.usage_flag_pointers["SSS_TRANSMITTANCE_DEPTH"] = &uses_transmittance; - actions.usage_flag_pointers["SCREEN_TEXTURE"] = &uses_screen_texture; - actions.usage_flag_pointers["DEPTH_TEXTURE"] = &uses_depth_texture; - actions.usage_flag_pointers["NORMAL_TEXTURE"] = &uses_normal_texture; actions.usage_flag_pointers["DISCARD"] = &uses_discard; actions.usage_flag_pointers["TIME"] = &uses_time; actions.usage_flag_pointers["ROUGHNESS"] = &uses_roughness; @@ -3418,6 +3355,11 @@ void SceneShaderData::set_code(const String &p_code) { vertex_input_mask |= uses_bones << 9; vertex_input_mask |= uses_weights << 10; uses_screen_texture_mipmaps = gen_code.uses_screen_texture_mipmaps; + uses_screen_texture = gen_code.uses_screen_texture; + uses_depth_texture = gen_code.uses_depth_texture; + uses_normal_texture = gen_code.uses_normal_roughness_texture; + uses_vertex_time = gen_code.uses_vertex_time; + uses_fragment_time = gen_code.uses_fragment_time; #if 0 print_line("**compiling shader:"); @@ -3457,101 +3399,16 @@ void SceneShaderData::set_code(const String &p_code) { valid = true; } -void SceneShaderData::set_default_texture_parameter(const StringName &p_name, RID p_texture, int p_index) { - if (!p_texture.is_valid()) { - if (default_texture_params.has(p_name) && default_texture_params[p_name].has(p_index)) { - default_texture_params[p_name].erase(p_index); - - if (default_texture_params[p_name].is_empty()) { - default_texture_params.erase(p_name); - } - } - } else { - if (!default_texture_params.has(p_name)) { - default_texture_params[p_name] = HashMap<int, RID>(); - } - default_texture_params[p_name][p_index] = p_texture; - } -} - -void SceneShaderData::get_shader_uniform_list(List<PropertyInfo> *p_param_list) const { - RBMap<int, StringName> order; - - for (const KeyValue<StringName, ShaderLanguage::ShaderNode::Uniform> &E : uniforms) { - if (E.value.scope != ShaderLanguage::ShaderNode::Uniform::SCOPE_LOCAL || - E.value.hint == ShaderLanguage::ShaderNode::Uniform::HINT_SCREEN_TEXTURE || - E.value.hint == ShaderLanguage::ShaderNode::Uniform::HINT_NORMAL_ROUGHNESS_TEXTURE || - E.value.hint == ShaderLanguage::ShaderNode::Uniform::HINT_DEPTH_TEXTURE) { - continue; - } - - if (E.value.texture_order >= 0) { - order[E.value.texture_order + 100000] = E.key; - } else { - order[E.value.order] = E.key; - } - } - - String last_group; - for (const KeyValue<int, StringName> &E : order) { - String group = uniforms[E.value].group; - if (!uniforms[E.value].subgroup.is_empty()) { - group += "::" + uniforms[E.value].subgroup; - } - - if (group != last_group) { - PropertyInfo pi; - pi.usage = PROPERTY_USAGE_GROUP; - pi.name = group; - p_param_list->push_back(pi); - - last_group = group; - } - - PropertyInfo pi = ShaderLanguage::uniform_to_property_info(uniforms[E.value]); - pi.name = E.value; - p_param_list->push_back(pi); - } -} - -void SceneShaderData::get_instance_param_list(List<RendererMaterialStorage::InstanceShaderParam> *p_param_list) const { - for (const KeyValue<StringName, ShaderLanguage::ShaderNode::Uniform> &E : uniforms) { - if (E.value.scope != ShaderLanguage::ShaderNode::Uniform::SCOPE_INSTANCE) { - continue; - } - - RendererMaterialStorage::InstanceShaderParam p; - p.info = ShaderLanguage::uniform_to_property_info(E.value); - p.info.name = E.key; //supply name - p.index = E.value.instance_index; - p.default_value = ShaderLanguage::constant_value_to_variant(E.value.default_value, E.value.type, E.value.array_size, E.value.hint); - p_param_list->push_back(p); - } -} - -bool SceneShaderData::is_parameter_texture(const StringName &p_param) const { - if (!uniforms.has(p_param)) { - return false; - } - - return uniforms[p_param].texture_order >= 0; -} - bool SceneShaderData::is_animated() const { - return false; + return (uses_fragment_time && uses_discard) || (uses_vertex_time && uses_vertex); } bool SceneShaderData::casts_shadows() const { - return false; -} + bool has_read_screen_alpha = uses_screen_texture || uses_depth_texture || uses_normal_texture; + bool has_base_alpha = (uses_alpha && !uses_alpha_clip) || has_read_screen_alpha; + bool has_alpha = has_base_alpha || uses_blend_alpha; -Variant SceneShaderData::get_default_parameter(const StringName &p_parameter) const { - if (uniforms.has(p_parameter)) { - ShaderLanguage::ShaderNode::Uniform uniform = uniforms[p_parameter]; - Vector<ShaderLanguage::ConstantNode::Value> default_value = uniform.default_value; - return ShaderLanguage::constant_value_to_variant(default_value, uniform.type, uniform.array_size, uniform.hint); - } - return Variant(); + return !has_alpha || (uses_depth_prepass_alpha && !(depth_draw == DEPTH_DRAW_DISABLED || depth_test == DEPTH_TEST_DISABLED)); } RS::ShaderNativeSourceCode SceneShaderData::get_native_source_code() const { @@ -3583,7 +3440,7 @@ void SceneMaterialData::set_next_pass(RID p_pass) { } void SceneMaterialData::update_parameters(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty) { - return update_parameters_internal(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size); + update_parameters_internal(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size); } SceneMaterialData::~SceneMaterialData() { @@ -3606,13 +3463,126 @@ void SceneMaterialData::bind_uniforms() { Texture *texture = TextureStorage::get_singleton()->get_texture(textures[ti]); glActiveTexture(GL_TEXTURE0 + ti); glBindTexture(target_from_type[texture_uniforms[ti].type], texture->tex_id); + if (texture->render_target) { + texture->render_target->used_in_frame = true; + } + + texture->gl_set_filter(filter_from_uniform[int(texture_uniforms[ti].filter)]); + texture->gl_set_repeat(repeat_from_uniform[int(texture_uniforms[ti].repeat)]); + } +} + +/* Particles SHADER */ + +void ParticlesShaderData::set_code(const String &p_code) { + //compile + + code = p_code; + valid = false; + ubo_size = 0; + uniforms.clear(); + uses_collision = false; + + if (code.is_empty()) { + return; //just invalid, but no error + } + + ShaderCompiler::GeneratedCode gen_code; + ShaderCompiler::IdentifierActions actions; + actions.entry_point_stages["start"] = ShaderCompiler::STAGE_VERTEX; + actions.entry_point_stages["process"] = ShaderCompiler::STAGE_VERTEX; + + actions.usage_flag_pointers["COLLIDED"] = &uses_collision; + + userdata_count = 0; + for (uint32_t i = 0; i < PARTICLES_MAX_USERDATAS; i++) { + userdatas_used[i] = false; + actions.usage_flag_pointers["USERDATA" + itos(i + 1)] = &userdatas_used[i]; + } + + actions.uniforms = &uniforms; + + Error err = MaterialStorage::get_singleton()->shaders.compiler_particles.compile(RS::SHADER_PARTICLES, code, &actions, path, gen_code); + ERR_FAIL_COND_MSG(err != OK, "Shader compilation failed."); + + if (version.is_null()) { + version = MaterialStorage::get_singleton()->shaders.particles_process_shader.version_create(); + } + + for (uint32_t i = 0; i < PARTICLES_MAX_USERDATAS; i++) { + if (userdatas_used[i]) { + userdata_count++; + } + } + + Vector<StringName> texture_uniform_names; + for (int i = 0; i < gen_code.texture_uniforms.size(); i++) { + texture_uniform_names.push_back(gen_code.texture_uniforms[i].name); + } + + MaterialStorage::get_singleton()->shaders.particles_process_shader.version_set_code(version, gen_code.code, gen_code.uniforms, gen_code.stage_globals[ShaderCompiler::STAGE_VERTEX], gen_code.stage_globals[ShaderCompiler::STAGE_FRAGMENT], gen_code.defines, texture_uniform_names); + ERR_FAIL_COND(!MaterialStorage::get_singleton()->shaders.particles_process_shader.version_is_valid(version)); + + ubo_size = gen_code.uniform_total_size; + ubo_offsets = gen_code.uniform_offsets; + texture_uniforms = gen_code.texture_uniforms; + + valid = true; +} + +bool ParticlesShaderData::is_animated() const { + return false; +} + +bool ParticlesShaderData::casts_shadows() const { + return false; +} + +RS::ShaderNativeSourceCode ParticlesShaderData::get_native_source_code() const { + return MaterialStorage::get_singleton()->shaders.particles_process_shader.version_get_native_source_code(version); +} + +ParticlesShaderData::~ParticlesShaderData() { + if (version.is_valid()) { + MaterialStorage::get_singleton()->shaders.particles_process_shader.version_free(version); + } +} + +GLES3::ShaderData *GLES3::_create_particles_shader_func() { + ParticlesShaderData *shader_data = memnew(ParticlesShaderData); + return shader_data; +} + +void ParticleProcessMaterialData::update_parameters(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty) { + update_parameters_internal(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size); +} + +ParticleProcessMaterialData::~ParticleProcessMaterialData() { +} + +GLES3::MaterialData *GLES3::_create_particles_material_func(ShaderData *p_shader) { + ParticleProcessMaterialData *material_data = memnew(ParticleProcessMaterialData); + material_data->shader_data = static_cast<ParticlesShaderData *>(p_shader); + //update will happen later anyway so do nothing. + return material_data; +} + +void ParticleProcessMaterialData::bind_uniforms() { + // Bind Material Uniforms + glBindBufferBase(GL_UNIFORM_BUFFER, GLES3::PARTICLES_MATERIAL_UNIFORM_LOCATION, uniform_buffer); + + RID *textures = texture_cache.ptrw(); + ShaderCompiler::GeneratedCode::Texture *texture_uniforms = shader_data->texture_uniforms.ptrw(); + for (int ti = 0; ti < texture_cache.size(); ti++) { + Texture *texture = TextureStorage::get_singleton()->get_texture(textures[ti]); + glActiveTexture(GL_TEXTURE1 + ti); // Start at GL_TEXTURE1 because texture slot 0 is reserved for the heightmap texture. + glBindTexture(target_from_type[texture_uniforms[ti].type], texture->tex_id); + if (texture->render_target) { + texture->render_target->used_in_frame = true; + } - // 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); + texture->gl_set_filter(filter_from_uniform[int(texture_uniforms[ti].filter)]); + texture->gl_set_repeat(repeat_from_uniform[int(texture_uniforms[ti].repeat)]); } } diff --git a/drivers/gles3/storage/material_storage.h b/drivers/gles3/storage/material_storage.h index 24d9a0fee1..4c5861b017 100644 --- a/drivers/gles3/storage/material_storage.h +++ b/drivers/gles3/storage/material_storage.h @@ -1,32 +1,32 @@ -/*************************************************************************/ -/* material_storage.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ +/**************************************************************************/ +/* material_storage.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ #ifndef MATERIAL_STORAGE_GLES3_H #define MATERIAL_STORAGE_GLES3_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" @@ -52,15 +53,20 @@ namespace GLES3 { /* Shader Structs */ struct ShaderData { - virtual void set_code(const String &p_Code) = 0; - virtual void set_default_texture_parameter(const StringName &p_name, RID p_texture, int p_index) = 0; - virtual void get_shader_uniform_list(List<PropertyInfo> *p_param_list) const = 0; + String path; + HashMap<StringName, ShaderLanguage::ShaderNode::Uniform> uniforms; + HashMap<StringName, HashMap<int, RID>> default_texture_params; - virtual void get_instance_param_list(List<RendererMaterialStorage::InstanceShaderParam> *p_param_list) const = 0; - virtual bool is_parameter_texture(const StringName &p_param) const = 0; + 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 Variant get_default_parameter(const StringName &p_parameter) const; + virtual void get_shader_uniform_list(List<PropertyInfo> *p_param_list) const; + virtual void get_instance_param_list(List<RendererMaterialStorage::InstanceShaderParam> *p_param_list) const; + virtual bool is_parameter_texture(const StringName &p_param) const; + + virtual void set_code(const String &p_Code) = 0; virtual bool is_animated() const = 0; virtual bool casts_shadows() const = 0; - virtual Variant get_default_parameter(const StringName &p_parameter) const = 0; virtual RS::ShaderNativeSourceCode get_native_source_code() const { return RS::ShaderNativeSourceCode(); } virtual ~ShaderData() {} @@ -147,17 +153,14 @@ struct CanvasShaderData : public ShaderData { bool valid; RID version; - String path; BlendMode blend_mode = BLEND_MODE_MIX; - HashMap<StringName, ShaderLanguage::ShaderNode::Uniform> uniforms; Vector<ShaderCompiler::GeneratedCode::Texture> texture_uniforms; Vector<uint32_t> ubo_offsets; uint32_t ubo_size; String code; - HashMap<StringName, HashMap<int, RID>> default_texture_params; bool uses_screen_texture = false; bool uses_screen_texture_mipmaps = false; @@ -165,14 +168,8 @@ struct CanvasShaderData : public ShaderData { bool uses_time = false; virtual void set_code(const String &p_Code); - virtual void set_default_texture_parameter(const StringName &p_name, RID p_texture, int p_index); - virtual void get_shader_uniform_list(List<PropertyInfo> *p_param_list) const; - virtual void get_instance_param_list(List<RendererMaterialStorage::InstanceShaderParam> *p_param_list) const; - - virtual bool is_parameter_texture(const StringName &p_param) const; virtual bool is_animated() const; virtual bool casts_shadows() const; - virtual Variant get_default_parameter(const StringName &p_parameter) const; virtual RS::ShaderNativeSourceCode get_native_source_code() const; CanvasShaderData(); @@ -199,15 +196,12 @@ struct SkyShaderData : public ShaderData { bool valid; RID version; - HashMap<StringName, ShaderLanguage::ShaderNode::Uniform> uniforms; Vector<ShaderCompiler::GeneratedCode::Texture> texture_uniforms; Vector<uint32_t> ubo_offsets; uint32_t ubo_size; - String path; String code; - HashMap<StringName, HashMap<int, RID>> default_texture_params; bool uses_time; bool uses_position; @@ -216,13 +210,8 @@ struct SkyShaderData : public ShaderData { bool uses_light; virtual void set_code(const String &p_Code); - virtual void set_default_texture_parameter(const StringName &p_name, RID p_texture, int p_index); - virtual void get_shader_uniform_list(List<PropertyInfo> *p_param_list) const; - virtual void get_instance_param_list(List<RendererMaterialStorage::InstanceShaderParam> *p_param_list) const; - virtual bool is_parameter_texture(const StringName &p_param) const; virtual bool is_animated() const; virtual bool casts_shadows() const; - virtual Variant get_default_parameter(const StringName &p_parameter) const; virtual RS::ShaderNativeSourceCode get_native_source_code() const; SkyShaderData(); virtual ~SkyShaderData(); @@ -280,16 +269,12 @@ struct SceneShaderData : public ShaderData { bool valid; RID version; - String path; - - HashMap<StringName, ShaderLanguage::ShaderNode::Uniform> uniforms; Vector<ShaderCompiler::GeneratedCode::Texture> texture_uniforms; Vector<uint32_t> ubo_offsets; uint32_t ubo_size; String code; - HashMap<StringName, HashMap<int, RID>> default_texture_params; BlendMode blend_mode; AlphaAntiAliasing alpha_antialiasing_mode; @@ -301,7 +286,7 @@ struct SceneShaderData : public ShaderData { bool uses_alpha; bool uses_blend_alpha; bool uses_alpha_clip; - bool uses_depth_pre_pass; + bool uses_depth_prepass_alpha; bool uses_discard; bool uses_roughness; bool uses_normal; @@ -318,6 +303,8 @@ struct SceneShaderData : public ShaderData { bool uses_depth_texture; bool uses_normal_texture; bool uses_time; + bool uses_vertex_time; + bool uses_fragment_time; bool writes_modelview_or_projection; bool uses_world_coordinates; bool uses_tangent; @@ -337,14 +324,8 @@ struct SceneShaderData : public ShaderData { uint32_t index = 0; virtual void set_code(const String &p_Code); - virtual void set_default_texture_parameter(const StringName &p_name, RID p_texture, int p_index); - virtual void get_shader_uniform_list(List<PropertyInfo> *p_param_list) const; - virtual void get_instance_param_list(List<RendererMaterialStorage::InstanceShaderParam> *p_param_list) const; - - virtual bool is_parameter_texture(const StringName &p_param) const; virtual bool is_animated() const; virtual bool casts_shadows() const; - virtual Variant get_default_parameter(const StringName &p_parameter) const; virtual RS::ShaderNativeSourceCode get_native_source_code() const; SceneShaderData(); @@ -368,6 +349,53 @@ 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; + + Vector<ShaderCompiler::GeneratedCode::Texture> texture_uniforms; + + Vector<uint32_t> ubo_offsets; + uint32_t ubo_size = 0; + + String code; + + bool uses_time = false; + + bool userdatas_used[PARTICLES_MAX_USERDATAS] = {}; + uint32_t userdata_count = 0; + + virtual void set_code(const String &p_Code); + virtual bool is_animated() const; + virtual bool casts_shadows() const; + virtual RS::ShaderNativeSourceCode get_native_source_code() const; + + ParticlesShaderData() {} + virtual ~ParticlesShaderData(); +}; + +ShaderData *_create_particles_shader_func(); + +struct ParticleProcessMaterialData : public MaterialData { + ParticlesShaderData *shader_data = nullptr; + RID uniform_set; + + virtual void set_render_priority(int p_priority) {} + virtual void set_next_pass(RID p_pass) {} + virtual void update_parameters(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty); + virtual void bind_uniforms(); + virtual ~ParticleProcessMaterialData(); +}; + +MaterialData *_create_particles_material_func(ShaderData *p_shader); + /* Global shader uniform structs */ struct GlobalShaderUniforms { enum { @@ -504,6 +532,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..71f262af20 100644 --- a/drivers/gles3/storage/mesh_storage.cpp +++ b/drivers/gles3/storage/mesh_storage.cpp @@ -1,32 +1,32 @@ -/*************************************************************************/ -/* mesh_storage.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ +/**************************************************************************/ +/* mesh_storage.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ #ifdef GLES3_ENABLED @@ -44,10 +44,16 @@ MeshStorage *MeshStorage::get_singleton() { MeshStorage::MeshStorage() { singleton = this; + + { + skeleton_shader.shader.initialize(); + skeleton_shader.shader_version = skeleton_shader.shader.version_create(); + } } MeshStorage::~MeshStorage() { singleton = nullptr; + skeleton_shader.shader.version_free(skeleton_shader.shader_version); } /* MESH API */ @@ -87,7 +93,6 @@ 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; } @@ -111,7 +116,6 @@ void MeshStorage::mesh_add_surface(RID p_mesh, const RS::SurfaceData &p_surface) uint32_t attrib_stride = 0; uint32_t skin_stride = 0; - // TODO: I think this should be <=, but it is copied from RendererRD, will have to verify later for (int i = 0; i < RS::ARRAY_WEIGHTS; i++) { if ((p_surface.format & (1 << i))) { switch (i) { @@ -245,25 +249,82 @@ void MeshStorage::mesh_add_surface(RID p_mesh, const RS::SurfaceData &p_surface) s->aabb = p_surface.aabb; s->bone_aabbs = p_surface.bone_aabbs; //only really useful for returning them. - if (mesh->blend_shape_count > 0) { - //s->blend_shape_buffer = RD::get_singleton()->storage_buffer_create(p_surface.blend_shape_data.size(), p_surface.blend_shape_data); + if (p_surface.skin_data.size() || mesh->blend_shape_count > 0) { + // Size must match the size of the vertex array. + int size = p_surface.vertex_data.size(); + int vertex_size = 0; + int stride = 0; + int normal_offset = 0; + int tangent_offset = 0; + if ((p_surface.format & (1 << RS::ARRAY_VERTEX))) { + if (p_surface.format & RS::ARRAY_FLAG_USE_2D_VERTICES) { + vertex_size = 2; + } else { + vertex_size = 3; + } + stride = sizeof(float) * vertex_size; + } + if ((p_surface.format & (1 << RS::ARRAY_NORMAL))) { + normal_offset = stride; + stride += sizeof(uint16_t) * 2; + } + if ((p_surface.format & (1 << RS::ARRAY_TANGENT))) { + tangent_offset = stride; + stride += sizeof(uint16_t) * 2; + } + + if (mesh->blend_shape_count > 0) { + // Blend shapes are passed as one large array, for OpenGL, we need to split each of them into their own buffer + s->blend_shapes = memnew_arr(Mesh::Surface::BlendShape, mesh->blend_shape_count); + + for (uint32_t i = 0; i < mesh->blend_shape_count; i++) { + glGenVertexArrays(1, &s->blend_shapes[i].vertex_array); + glBindVertexArray(s->blend_shapes[i].vertex_array); + glGenBuffers(1, &s->blend_shapes[i].vertex_buffer); + glBindBuffer(GL_ARRAY_BUFFER, s->blend_shapes[i].vertex_buffer); + glBufferData(GL_ARRAY_BUFFER, size, p_surface.blend_shape_data.ptr() + i * size, (s->format & RS::ARRAY_FLAG_USE_DYNAMIC_UPDATE) ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW); + + if ((p_surface.format & (1 << RS::ARRAY_VERTEX))) { + glEnableVertexAttribArray(RS::ARRAY_VERTEX + 3); + glVertexAttribPointer(RS::ARRAY_VERTEX + 3, vertex_size, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(0)); + } + if ((p_surface.format & (1 << RS::ARRAY_NORMAL))) { + glEnableVertexAttribArray(RS::ARRAY_NORMAL + 3); + glVertexAttribPointer(RS::ARRAY_NORMAL + 3, 2, GL_UNSIGNED_SHORT, GL_TRUE, stride, CAST_INT_TO_UCHAR_PTR(normal_offset)); + } + if ((p_surface.format & (1 << RS::ARRAY_TANGENT))) { + glEnableVertexAttribArray(RS::ARRAY_TANGENT + 3); + glVertexAttribPointer(RS::ARRAY_TANGENT + 3, 2, GL_UNSIGNED_SHORT, GL_TRUE, stride, CAST_INT_TO_UCHAR_PTR(tangent_offset)); + } + } + glBindVertexArray(0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + } + + // Create a vertex array to use for skeleton/blend shapes. + glGenVertexArrays(1, &s->skeleton_vertex_array); + glBindVertexArray(s->skeleton_vertex_array); + glBindBuffer(GL_ARRAY_BUFFER, s->vertex_buffer); + + if ((p_surface.format & (1 << RS::ARRAY_VERTEX))) { + glEnableVertexAttribArray(RS::ARRAY_VERTEX); + glVertexAttribPointer(RS::ARRAY_VERTEX, vertex_size, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(0)); + } + if ((p_surface.format & (1 << RS::ARRAY_NORMAL))) { + glEnableVertexAttribArray(RS::ARRAY_NORMAL); + glVertexAttribPointer(RS::ARRAY_NORMAL, 2, GL_UNSIGNED_SHORT, GL_TRUE, stride, CAST_INT_TO_UCHAR_PTR(normal_offset)); + } + if ((p_surface.format & (1 << RS::ARRAY_TANGENT))) { + glEnableVertexAttribArray(RS::ARRAY_TANGENT); + glVertexAttribPointer(RS::ARRAY_TANGENT, 2, GL_UNSIGNED_SHORT, GL_TRUE, stride, CAST_INT_TO_UCHAR_PTR(tangent_offset)); + } + glBindVertexArray(0); + glBindBuffer(GL_ARRAY_BUFFER, 0); } if (mesh->surface_count == 0) { - mesh->bone_aabbs = p_surface.bone_aabbs; mesh->aabb = p_surface.aabb; } else { - if (mesh->bone_aabbs.size() < p_surface.bone_aabbs.size()) { - // ArrayMesh::_surface_set_data only allocates bone_aabbs up to max_bone - // Each surface may affect different numbers of bones. - mesh->bone_aabbs.resize(p_surface.bone_aabbs.size()); - } - for (int i = 0; i < p_surface.bone_aabbs.size(); i++) { - const AABB &bone = p_surface.bone_aabbs[i]; - if (bone.has_volume()) { - mesh->bone_aabbs.write[i].merge_with(bone); - } - } mesh->aabb.merge_with(p_surface.aabb); } @@ -409,7 +470,13 @@ RS::SurfaceData MeshStorage::mesh_get_surface(RID p_mesh, int p_surface) const { } sd.bone_aabbs = s.bone_aabbs; - glBindBuffer(GL_ARRAY_BUFFER, 0); + + if (mesh->blend_shape_count) { + sd.blend_shape_data = Vector<uint8_t>(); + for (uint32_t i = 0; i < mesh->blend_shape_count; i++) { + sd.blend_shape_data.append_array(Utilities::buffer_get_data(GL_ARRAY_BUFFER, s.blend_shapes[i].vertex_buffer, s.vertex_buffer_size)); + } + } return sd; } @@ -442,7 +509,7 @@ AABB MeshStorage::mesh_get_aabb(RID p_mesh, RID p_skeleton) { Skeleton *skeleton = skeleton_owner.get_or_null(p_skeleton); - if (!skeleton || skeleton->size == 0) { + if (!skeleton || skeleton->size == 0 || mesh->skeleton_aabb_version == skeleton->version) { return mesh->aabb; } @@ -536,6 +603,8 @@ AABB MeshStorage::mesh_get_aabb(RID p_mesh, RID p_skeleton) { } } + mesh->aabb = aabb; + mesh->skeleton_aabb_version = skeleton->version; return aabb; } @@ -605,6 +674,24 @@ void MeshStorage::mesh_clear(RID p_mesh) { memdelete_arr(s.lods); } + if (mesh->blend_shape_count) { + for (uint32_t j = 0; j < mesh->blend_shape_count; j++) { + if (s.blend_shapes[j].vertex_buffer != 0) { + glDeleteBuffers(1, &s.blend_shapes[j].vertex_buffer); + s.blend_shapes[j].vertex_buffer = 0; + } + if (s.blend_shapes[j].vertex_array != 0) { + glDeleteVertexArrays(1, &s.blend_shapes[j].vertex_array); + s.blend_shapes[j].vertex_array = 0; + } + } + memdelete_arr(s.blend_shapes); + } + if (s.skeleton_vertex_array != 0) { + glDeleteVertexArrays(1, &s.skeleton_vertex_array); + s.skeleton_vertex_array = 0; + } + memdelete(mesh->surfaces[i]); } if (mesh->surfaces) { @@ -660,15 +747,15 @@ void MeshStorage::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::V case RS::ARRAY_NORMAL: { attribs[i].offset = vertex_stride; attribs[i].size = 2; - attribs[i].type = GL_UNSIGNED_SHORT; - vertex_stride += sizeof(uint16_t) * 2; + attribs[i].type = (mis ? GL_FLOAT : GL_UNSIGNED_SHORT); + vertex_stride += sizeof(uint16_t) * 2 * (mis ? 2 : 1); attribs[i].normalized = GL_TRUE; } break; case RS::ARRAY_TANGENT: { attribs[i].offset = vertex_stride; attribs[i].size = 2; - attribs[i].type = GL_UNSIGNED_SHORT; - vertex_stride += sizeof(uint16_t) * 2; + attribs[i].type = (mis ? GL_FLOAT : GL_UNSIGNED_SHORT); + vertex_stride += sizeof(uint16_t) * 2 * (mis ? 2 : 1); attribs[i].normalized = GL_TRUE; } break; case RS::ARRAY_COLOR: { @@ -713,7 +800,7 @@ void MeshStorage::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::V attribs[i].offset = skin_stride; attribs[i].size = 4; attribs[i].type = GL_UNSIGNED_SHORT; - attributes_stride += 4 * sizeof(uint16_t); + skin_stride += 4 * sizeof(uint16_t); attribs[i].normalized = GL_FALSE; attribs[i].integer = true; } break; @@ -721,7 +808,7 @@ void MeshStorage::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::V attribs[i].offset = skin_stride; attribs[i].size = 4; attribs[i].type = GL_UNSIGNED_SHORT; - attributes_stride += 4 * sizeof(uint16_t); + skin_stride += 4 * sizeof(uint16_t); attribs[i].normalized = GL_TRUE; } break; } @@ -812,7 +899,7 @@ void MeshStorage::mesh_instance_set_blend_shape_weight(RID p_mesh_instance, int ERR_FAIL_COND(!mi); ERR_FAIL_INDEX(p_shape, (int)mi->blend_weights.size()); mi->blend_weights[p_shape] = p_weight; - mi->weights_dirty = true; + mi->dirty = true; } void MeshStorage::_mesh_instance_clear(MeshInstance *mi) { @@ -824,38 +911,65 @@ void MeshStorage::_mesh_instance_clear(MeshInstance *mi) { } memfree(mi->surfaces[i].versions); } + + if (mi->surfaces[i].vertex_buffers[0] != 0) { + glDeleteBuffers(2, mi->surfaces[i].vertex_buffers); + mi->surfaces[i].vertex_buffers[0] = 0; + mi->surfaces[i].vertex_buffers[1] = 0; + } + if (mi->surfaces[i].vertex_buffer != 0) { glDeleteBuffers(1, &mi->surfaces[i].vertex_buffer); mi->surfaces[i].vertex_buffer = 0; } } mi->surfaces.clear(); - - if (mi->blend_weights_buffer != 0) { - glDeleteBuffers(1, &mi->blend_weights_buffer); - mi->blend_weights_buffer = 0; - } mi->blend_weights.clear(); - mi->weights_dirty = false; mi->skeleton_version = 0; } void MeshStorage::_mesh_instance_add_surface(MeshInstance *mi, Mesh *mesh, uint32_t p_surface) { - if (mesh->blend_shape_count > 0 && mi->blend_weights_buffer == 0) { + if (mesh->blend_shape_count > 0) { mi->blend_weights.resize(mesh->blend_shape_count); for (uint32_t i = 0; i < mi->blend_weights.size(); i++) { - mi->blend_weights[i] = 0; + mi->blend_weights[i] = 0.0; } - // Todo allocate buffer for blend_weights and copy data to it - //mi->blend_weights_buffer = RD::get_singleton()->storage_buffer_create(sizeof(float) * mi->blend_weights.size(), mi->blend_weights.to_byte_array()); - - mi->weights_dirty = true; } MeshInstance::Surface s; - if (mesh->blend_shape_count > 0 || (mesh->surfaces[p_surface]->format & RS::ARRAY_FORMAT_BONES)) { - //surface warrants transform - //s.vertex_buffer = RD::get_singleton()->vertex_buffer_create(mesh->surfaces[p_surface]->vertex_buffer_size, Vector<uint8_t>(), true); + if ((mesh->blend_shape_count > 0 || (mesh->surfaces[p_surface]->format & RS::ARRAY_FORMAT_BONES)) && mesh->surfaces[p_surface]->vertex_buffer_size > 0) { + // Cache surface properties + s.format_cache = mesh->surfaces[p_surface]->format; + if ((s.format_cache & (1 << RS::ARRAY_VERTEX))) { + if (s.format_cache & RS::ARRAY_FLAG_USE_2D_VERTICES) { + s.vertex_size_cache = 2; + } else { + s.vertex_size_cache = 3; + } + s.vertex_stride_cache = sizeof(float) * s.vertex_size_cache; + } + if ((s.format_cache & (1 << RS::ARRAY_NORMAL))) { + s.vertex_normal_offset_cache = s.vertex_stride_cache; + s.vertex_stride_cache += sizeof(uint32_t) * 2; + } + if ((s.format_cache & (1 << RS::ARRAY_TANGENT))) { + s.vertex_tangent_offset_cache = s.vertex_stride_cache; + s.vertex_stride_cache += sizeof(uint32_t) * 2; + } + + // Buffer to be used for rendering. Final output of skeleton and blend shapes. + glGenBuffers(1, &s.vertex_buffer); + glBindBuffer(GL_ARRAY_BUFFER, s.vertex_buffer); + glBufferData(GL_ARRAY_BUFFER, s.vertex_stride_cache * mesh->surfaces[p_surface]->vertex_count, nullptr, GL_DYNAMIC_DRAW); + if (mesh->blend_shape_count > 0) { + // Ping-Pong buffers for processing blendshapes. + glGenBuffers(2, s.vertex_buffers); + for (uint32_t i = 0; i < 2; i++) { + glBindBuffer(GL_ARRAY_BUFFER, s.vertex_buffers[i]); + glBufferData(GL_ARRAY_BUFFER, s.vertex_stride_cache * mesh->surfaces[p_surface]->vertex_count, nullptr, GL_DYNAMIC_DRAW); + } + } + glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind } mi->surfaces.push_back(s); @@ -867,11 +981,6 @@ void MeshStorage::mesh_instance_check_for_update(RID p_mesh_instance) { bool needs_update = mi->dirty; - if (mi->weights_dirty && !mi->weight_update_list.in_list()) { - dirty_mesh_instance_weights.add(&mi->weight_update_list); - needs_update = true; - } - if (mi->array_update_list.in_list()) { return; } @@ -888,22 +997,223 @@ void MeshStorage::mesh_instance_check_for_update(RID p_mesh_instance) { } } -void MeshStorage::update_mesh_instances() { - while (dirty_mesh_instance_weights.first()) { - MeshInstance *mi = dirty_mesh_instance_weights.first()->self(); +void MeshStorage::_blend_shape_bind_mesh_instance_buffer(MeshInstance *p_mi, uint32_t p_surface) { + glBindBuffer(GL_ARRAY_BUFFER, p_mi->surfaces[p_surface].vertex_buffers[0]); - if (mi->blend_weights_buffer != 0) { - //RD::get_singleton()->buffer_update(mi->blend_weights_buffer, 0, mi->blend_weights.size() * sizeof(float), mi->blend_weights.ptr()); - } - dirty_mesh_instance_weights.remove(&mi->weight_update_list); - mi->weights_dirty = false; + if ((p_mi->surfaces[p_surface].format_cache & (1 << RS::ARRAY_VERTEX))) { + glEnableVertexAttribArray(RS::ARRAY_VERTEX); + glVertexAttribPointer(RS::ARRAY_VERTEX, p_mi->surfaces[p_surface].vertex_size_cache, GL_FLOAT, GL_FALSE, p_mi->surfaces[p_surface].vertex_stride_cache, CAST_INT_TO_UCHAR_PTR(0)); + } else { + glDisableVertexAttribArray(RS::ARRAY_VERTEX); + } + if ((p_mi->surfaces[p_surface].format_cache & (1 << RS::ARRAY_NORMAL))) { + glEnableVertexAttribArray(RS::ARRAY_NORMAL); + glVertexAttribIPointer(RS::ARRAY_NORMAL, 2, GL_UNSIGNED_INT, p_mi->surfaces[p_surface].vertex_stride_cache, CAST_INT_TO_UCHAR_PTR(p_mi->surfaces[p_surface].vertex_normal_offset_cache)); + } else { + glDisableVertexAttribArray(RS::ARRAY_NORMAL); } + if ((p_mi->surfaces[p_surface].format_cache & (1 << RS::ARRAY_TANGENT))) { + glEnableVertexAttribArray(RS::ARRAY_TANGENT); + glVertexAttribIPointer(RS::ARRAY_TANGENT, 2, GL_UNSIGNED_INT, p_mi->surfaces[p_surface].vertex_stride_cache, CAST_INT_TO_UCHAR_PTR(p_mi->surfaces[p_surface].vertex_tangent_offset_cache)); + } else { + glDisableVertexAttribArray(RS::ARRAY_TANGENT); + } +} + +void MeshStorage::_compute_skeleton(MeshInstance *p_mi, Skeleton *p_sk, uint32_t p_surface) { + glBindBuffer(GL_ARRAY_BUFFER, 0); + + // Add in the bones and weights. + glBindBuffer(GL_ARRAY_BUFFER, p_mi->mesh->surfaces[p_surface]->skin_buffer); + + bool use_8_weights = p_mi->surfaces[p_surface].format_cache & RS::ARRAY_FLAG_USE_8_BONE_WEIGHTS; + int skin_stride = sizeof(int16_t) * (use_8_weights ? 16 : 8); + glEnableVertexAttribArray(RS::ARRAY_BONES); + glVertexAttribIPointer(RS::ARRAY_BONES, 4, GL_UNSIGNED_SHORT, skin_stride, CAST_INT_TO_UCHAR_PTR(0)); + if (use_8_weights) { + glEnableVertexAttribArray(11); + glVertexAttribIPointer(11, 4, GL_UNSIGNED_SHORT, skin_stride, CAST_INT_TO_UCHAR_PTR(4 * sizeof(uint16_t))); + glEnableVertexAttribArray(12); + glVertexAttribPointer(12, 4, GL_UNSIGNED_SHORT, GL_TRUE, skin_stride, CAST_INT_TO_UCHAR_PTR(8 * sizeof(uint16_t))); + glEnableVertexAttribArray(13); + glVertexAttribPointer(13, 4, GL_UNSIGNED_SHORT, GL_TRUE, skin_stride, CAST_INT_TO_UCHAR_PTR(12 * sizeof(uint16_t))); + } else { + glEnableVertexAttribArray(RS::ARRAY_WEIGHTS); + glVertexAttribPointer(RS::ARRAY_WEIGHTS, 4, GL_UNSIGNED_SHORT, GL_TRUE, skin_stride, CAST_INT_TO_UCHAR_PTR(4 * sizeof(uint16_t))); + } + + glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, p_mi->surfaces[p_surface].vertex_buffer); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, p_sk->transforms_texture); + + glBeginTransformFeedback(GL_POINTS); + glDrawArrays(GL_POINTS, 0, p_mi->mesh->surfaces[p_surface]->vertex_count); + glEndTransformFeedback(); + + glDisableVertexAttribArray(RS::ARRAY_BONES); + glDisableVertexAttribArray(RS::ARRAY_WEIGHTS); + glDisableVertexAttribArray(RS::ARRAY_BONES + 2); + glDisableVertexAttribArray(RS::ARRAY_WEIGHTS + 2); + glBindVertexArray(0); + glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, 0); +} + +void MeshStorage::update_mesh_instances() { if (dirty_mesh_instance_arrays.first() == nullptr) { return; //nothing to do } + glEnable(GL_RASTERIZER_DISCARD); // Process skeletons and blend shapes using transform feedback - // TODO: Implement when working on skeletons and blend shapes + while (dirty_mesh_instance_arrays.first()) { + MeshInstance *mi = dirty_mesh_instance_arrays.first()->self(); + + Skeleton *sk = skeleton_owner.get_or_null(mi->skeleton); + + // Precompute base weight if using blend shapes. + float base_weight = 1.0; + if (mi->mesh->blend_shape_count && mi->mesh->blend_shape_mode == RS::BLEND_SHAPE_MODE_NORMALIZED) { + for (uint32_t i = 0; i < mi->mesh->blend_shape_count; i++) { + base_weight -= mi->blend_weights[i]; + } + } + + for (uint32_t i = 0; i < mi->surfaces.size(); i++) { + if (mi->surfaces[i].vertex_buffer == 0 || mi->mesh->surfaces[i]->skeleton_vertex_array == 0) { + continue; + } + + bool array_is_2d = mi->surfaces[i].format_cache & RS::ARRAY_FLAG_USE_2D_VERTICES; + bool can_use_skeleton = sk != nullptr && sk->use_2d == array_is_2d && (mi->surfaces[i].format_cache & RS::ARRAY_FORMAT_BONES); + bool use_8_weights = mi->surfaces[i].format_cache & RS::ARRAY_FLAG_USE_8_BONE_WEIGHTS; + + // Always process blend shapes first. + if (mi->mesh->blend_shape_count) { + SkeletonShaderGLES3::ShaderVariant variant = SkeletonShaderGLES3::MODE_BASE_PASS; + uint64_t specialization = 0; + specialization |= array_is_2d ? SkeletonShaderGLES3::MODE_2D : 0; + specialization |= SkeletonShaderGLES3::USE_BLEND_SHAPES; + if (!array_is_2d) { + if ((mi->surfaces[i].format_cache & (1 << RS::ARRAY_NORMAL))) { + specialization |= SkeletonShaderGLES3::USE_NORMAL; + } + if ((mi->surfaces[i].format_cache & (1 << RS::ARRAY_TANGENT))) { + specialization |= SkeletonShaderGLES3::USE_TANGENT; + } + } + + bool success = skeleton_shader.shader.version_bind_shader(skeleton_shader.shader_version, variant, specialization); + if (!success) { + continue; + } + + skeleton_shader.shader.version_set_uniform(SkeletonShaderGLES3::BLEND_WEIGHT, base_weight, skeleton_shader.shader_version, variant, specialization); + skeleton_shader.shader.version_set_uniform(SkeletonShaderGLES3::BLEND_SHAPE_COUNT, float(mi->mesh->blend_shape_count), skeleton_shader.shader_version, variant, specialization); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(mi->mesh->surfaces[i]->skeleton_vertex_array); + glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, mi->surfaces[i].vertex_buffers[0]); + glBeginTransformFeedback(GL_POINTS); + glDrawArrays(GL_POINTS, 0, mi->mesh->surfaces[i]->vertex_count); + glEndTransformFeedback(); + + variant = SkeletonShaderGLES3::MODE_BLEND_PASS; + success = skeleton_shader.shader.version_bind_shader(skeleton_shader.shader_version, variant, specialization); + if (!success) { + continue; + } + + //Do the last blend shape separately, as it can be combined with the skeleton pass. + for (uint32_t bs = 0; bs < mi->mesh->blend_shape_count - 1; bs++) { + float weight = mi->blend_weights[bs]; + + if (Math::is_zero_approx(weight)) { + //not bother with this one + continue; + } + skeleton_shader.shader.version_set_uniform(SkeletonShaderGLES3::BLEND_WEIGHT, weight, skeleton_shader.shader_version, variant, specialization); + skeleton_shader.shader.version_set_uniform(SkeletonShaderGLES3::BLEND_SHAPE_COUNT, float(mi->mesh->blend_shape_count), skeleton_shader.shader_version, variant, specialization); + + glBindVertexArray(mi->mesh->surfaces[i]->blend_shapes[bs].vertex_array); + _blend_shape_bind_mesh_instance_buffer(mi, i); + glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, mi->surfaces[i].vertex_buffers[1]); + + glBeginTransformFeedback(GL_POINTS); + glDrawArrays(GL_POINTS, 0, mi->mesh->surfaces[i]->vertex_count); + glEndTransformFeedback(); + + SWAP(mi->surfaces[i].vertex_buffers[0], mi->surfaces[i].vertex_buffers[1]); + } + uint32_t bs = mi->mesh->blend_shape_count - 1; + + float weight = mi->blend_weights[bs]; + + glBindVertexArray(mi->mesh->surfaces[i]->blend_shapes[bs].vertex_array); + _blend_shape_bind_mesh_instance_buffer(mi, i); + + specialization |= can_use_skeleton ? SkeletonShaderGLES3::USE_SKELETON : 0; + specialization |= (can_use_skeleton && use_8_weights) ? SkeletonShaderGLES3::USE_EIGHT_WEIGHTS : 0; + specialization |= SkeletonShaderGLES3::FINAL_PASS; + success = skeleton_shader.shader.version_bind_shader(skeleton_shader.shader_version, variant, specialization); + if (!success) { + continue; + } + + skeleton_shader.shader.version_set_uniform(SkeletonShaderGLES3::BLEND_WEIGHT, weight, skeleton_shader.shader_version, variant, specialization); + skeleton_shader.shader.version_set_uniform(SkeletonShaderGLES3::BLEND_SHAPE_COUNT, float(mi->mesh->blend_shape_count), skeleton_shader.shader_version, variant, specialization); + + if (can_use_skeleton) { + // Do last blendshape in the same pass as the Skeleton. + _compute_skeleton(mi, sk, i); + can_use_skeleton = false; + } else { + // Do last blendshape by itself and prepare vertex data for use by the renderer. + glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, mi->surfaces[i].vertex_buffer); + + glBeginTransformFeedback(GL_POINTS); + glDrawArrays(GL_POINTS, 0, mi->mesh->surfaces[i]->vertex_count); + glEndTransformFeedback(); + } + + glBindVertexArray(0); + glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, 0); + } + + // This branch should only execute when Skeleton is run by itself. + if (can_use_skeleton) { + SkeletonShaderGLES3::ShaderVariant variant = SkeletonShaderGLES3::MODE_BASE_PASS; + uint64_t specialization = 0; + specialization |= array_is_2d ? SkeletonShaderGLES3::MODE_2D : 0; + specialization |= SkeletonShaderGLES3::USE_SKELETON; + specialization |= SkeletonShaderGLES3::FINAL_PASS; + specialization |= use_8_weights ? SkeletonShaderGLES3::USE_EIGHT_WEIGHTS : 0; + if (!array_is_2d) { + if ((mi->surfaces[i].format_cache & (1 << RS::ARRAY_NORMAL))) { + specialization |= SkeletonShaderGLES3::USE_NORMAL; + } + if ((mi->surfaces[i].format_cache & (1 << RS::ARRAY_TANGENT))) { + specialization |= SkeletonShaderGLES3::USE_TANGENT; + } + } + + bool success = skeleton_shader.shader.version_bind_shader(skeleton_shader.shader_version, variant, specialization); + if (!success) { + continue; + } + + glBindVertexArray(mi->mesh->surfaces[i]->skeleton_vertex_array); + _compute_skeleton(mi, sk, i); + } + } + mi->dirty = false; + if (sk) { + mi->skeleton_version = sk->version; + } + dirty_mesh_instance_arrays.remove(&mi->array_update_list); + } + glDisable(GL_RASTERIZER_DISCARD); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, 0); } /* MULTIMESH API */ @@ -1574,45 +1884,207 @@ void MeshStorage::_update_dirty_multimeshes() { /* SKELETON API */ RID MeshStorage::skeleton_allocate() { - return RID(); + return skeleton_owner.allocate_rid(); } void MeshStorage::skeleton_initialize(RID p_rid) { + skeleton_owner.initialize_rid(p_rid, Skeleton()); } void MeshStorage::skeleton_free(RID p_rid) { + _update_dirty_skeletons(); + skeleton_allocate_data(p_rid, 0); + Skeleton *skeleton = skeleton_owner.get_or_null(p_rid); + skeleton->dependency.deleted_notify(p_rid); + skeleton_owner.free(p_rid); +} + +void MeshStorage::_skeleton_make_dirty(Skeleton *skeleton) { + if (!skeleton->dirty) { + skeleton->dirty = true; + skeleton->dirty_list = skeleton_dirty_list; + skeleton_dirty_list = skeleton; + } } void MeshStorage::skeleton_allocate_data(RID p_skeleton, int p_bones, bool p_2d_skeleton) { + Skeleton *skeleton = skeleton_owner.get_or_null(p_skeleton); + ERR_FAIL_COND(!skeleton); + ERR_FAIL_COND(p_bones < 0); + + if (skeleton->size == p_bones && skeleton->use_2d == p_2d_skeleton) { + return; + } + + skeleton->size = p_bones; + skeleton->use_2d = p_2d_skeleton; + skeleton->height = (p_bones * (p_2d_skeleton ? 2 : 3)) / 256; + if ((p_bones * (p_2d_skeleton ? 2 : 3)) % 256) { + skeleton->height++; + } + + if (skeleton->transforms_texture != 0) { + glDeleteTextures(1, &skeleton->transforms_texture); + skeleton->transforms_texture = 0; + skeleton->data.clear(); + } + + if (skeleton->size) { + skeleton->data.resize(256 * skeleton->height * 4); + glGenTextures(1, &skeleton->transforms_texture); + glBindTexture(GL_TEXTURE_2D, skeleton->transforms_texture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, 256, skeleton->height, 0, GL_RGBA, GL_FLOAT, nullptr); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glBindTexture(GL_TEXTURE_2D, 0); + + memset(skeleton->data.ptrw(), 0, skeleton->data.size() * sizeof(float)); + + _skeleton_make_dirty(skeleton); + } + + skeleton->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_SKELETON_DATA); } void MeshStorage::skeleton_set_base_transform_2d(RID p_skeleton, const Transform2D &p_base_transform) { + Skeleton *skeleton = skeleton_owner.get_or_null(p_skeleton); + + ERR_FAIL_NULL(skeleton); + ERR_FAIL_COND(!skeleton->use_2d); + + skeleton->base_transform_2d = p_base_transform; } int MeshStorage::skeleton_get_bone_count(RID p_skeleton) const { - return 0; + Skeleton *skeleton = skeleton_owner.get_or_null(p_skeleton); + ERR_FAIL_COND_V(!skeleton, 0); + + return skeleton->size; } void MeshStorage::skeleton_bone_set_transform(RID p_skeleton, int p_bone, const Transform3D &p_transform) { + Skeleton *skeleton = skeleton_owner.get_or_null(p_skeleton); + + ERR_FAIL_COND(!skeleton); + ERR_FAIL_INDEX(p_bone, skeleton->size); + ERR_FAIL_COND(skeleton->use_2d); + + float *dataptr = skeleton->data.ptrw() + p_bone * 12; + + dataptr[0] = p_transform.basis.rows[0][0]; + dataptr[1] = p_transform.basis.rows[0][1]; + dataptr[2] = p_transform.basis.rows[0][2]; + dataptr[3] = p_transform.origin.x; + dataptr[4] = p_transform.basis.rows[1][0]; + dataptr[5] = p_transform.basis.rows[1][1]; + dataptr[6] = p_transform.basis.rows[1][2]; + dataptr[7] = p_transform.origin.y; + dataptr[8] = p_transform.basis.rows[2][0]; + dataptr[9] = p_transform.basis.rows[2][1]; + dataptr[10] = p_transform.basis.rows[2][2]; + dataptr[11] = p_transform.origin.z; + + _skeleton_make_dirty(skeleton); } Transform3D MeshStorage::skeleton_bone_get_transform(RID p_skeleton, int p_bone) const { - return Transform3D(); + Skeleton *skeleton = skeleton_owner.get_or_null(p_skeleton); + + ERR_FAIL_COND_V(!skeleton, Transform3D()); + ERR_FAIL_INDEX_V(p_bone, skeleton->size, Transform3D()); + ERR_FAIL_COND_V(skeleton->use_2d, Transform3D()); + + const float *dataptr = skeleton->data.ptr() + p_bone * 12; + + Transform3D t; + + t.basis.rows[0][0] = dataptr[0]; + t.basis.rows[0][1] = dataptr[1]; + t.basis.rows[0][2] = dataptr[2]; + t.origin.x = dataptr[3]; + t.basis.rows[1][0] = dataptr[4]; + t.basis.rows[1][1] = dataptr[5]; + t.basis.rows[1][2] = dataptr[6]; + t.origin.y = dataptr[7]; + t.basis.rows[2][0] = dataptr[8]; + t.basis.rows[2][1] = dataptr[9]; + t.basis.rows[2][2] = dataptr[10]; + t.origin.z = dataptr[11]; + + return t; } void MeshStorage::skeleton_bone_set_transform_2d(RID p_skeleton, int p_bone, const Transform2D &p_transform) { + Skeleton *skeleton = skeleton_owner.get_or_null(p_skeleton); + + ERR_FAIL_COND(!skeleton); + ERR_FAIL_INDEX(p_bone, skeleton->size); + ERR_FAIL_COND(!skeleton->use_2d); + + float *dataptr = skeleton->data.ptrw() + p_bone * 8; + + dataptr[0] = p_transform.columns[0][0]; + dataptr[1] = p_transform.columns[1][0]; + dataptr[2] = 0; + dataptr[3] = p_transform.columns[2][0]; + dataptr[4] = p_transform.columns[0][1]; + dataptr[5] = p_transform.columns[1][1]; + dataptr[6] = 0; + dataptr[7] = p_transform.columns[2][1]; + + _skeleton_make_dirty(skeleton); } Transform2D MeshStorage::skeleton_bone_get_transform_2d(RID p_skeleton, int p_bone) const { - return Transform2D(); + Skeleton *skeleton = skeleton_owner.get_or_null(p_skeleton); + + ERR_FAIL_COND_V(!skeleton, Transform2D()); + ERR_FAIL_INDEX_V(p_bone, skeleton->size, Transform2D()); + ERR_FAIL_COND_V(!skeleton->use_2d, Transform2D()); + + const float *dataptr = skeleton->data.ptr() + p_bone * 8; + + Transform2D t; + t.columns[0][0] = dataptr[0]; + t.columns[1][0] = dataptr[1]; + t.columns[2][0] = dataptr[3]; + t.columns[0][1] = dataptr[4]; + t.columns[1][1] = dataptr[5]; + t.columns[2][1] = dataptr[7]; + + return t; } -void MeshStorage::skeleton_update_dependency(RID p_base, DependencyTracker *p_instance) { +void MeshStorage::_update_dirty_skeletons() { + while (skeleton_dirty_list) { + Skeleton *skeleton = skeleton_dirty_list; + + if (skeleton->size) { + glBindTexture(GL_TEXTURE_2D, skeleton->transforms_texture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, 256, skeleton->height, 0, GL_RGBA, GL_FLOAT, skeleton->data.ptr()); + glBindTexture(GL_TEXTURE_2D, 0); + } + + skeleton_dirty_list = skeleton->dirty_list; + + skeleton->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_SKELETON_BONES); + + skeleton->version++; + + skeleton->dirty = false; + skeleton->dirty_list = nullptr; + } + + skeleton_dirty_list = nullptr; } -/* OCCLUDER */ +void MeshStorage::skeleton_update_dependency(RID p_skeleton, DependencyTracker *p_instance) { + Skeleton *skeleton = skeleton_owner.get_or_null(p_skeleton); + ERR_FAIL_COND(!skeleton); -void MeshStorage::occluder_set_mesh(RID p_occluder, const PackedVector3Array &p_vertices, const PackedInt32Array &p_indices) { + p_instance->update_dependency(&skeleton->dependency); } #endif // GLES3_ENABLED diff --git a/drivers/gles3/storage/mesh_storage.h b/drivers/gles3/storage/mesh_storage.h index a31db24f2d..2efc57462b 100644 --- a/drivers/gles3/storage/mesh_storage.h +++ b/drivers/gles3/storage/mesh_storage.h @@ -1,38 +1,39 @@ -/*************************************************************************/ -/* mesh_storage.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ +/**************************************************************************/ +/* mesh_storage.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ #ifndef MESH_STORAGE_GLES3_H #define MESH_STORAGE_GLES3_H #ifdef GLES3_ENABLED +#include "../shaders/skeleton.glsl.gen.h" #include "core/templates/local_vector.h" #include "core/templates/rid_owner.h" #include "core/templates/self_list.h" @@ -102,7 +103,13 @@ struct Mesh { Vector<AABB> bone_aabbs; - GLuint blend_shape_buffer = 0; + struct BlendShape { + GLuint vertex_buffer = 0; + GLuint vertex_array = 0; + }; + + BlendShape *blend_shapes = nullptr; + GLuint skeleton_vertex_array = 0; RID material; }; @@ -113,12 +120,11 @@ struct Mesh { Surface **surfaces = nullptr; uint32_t surface_count = 0; - Vector<AABB> bone_aabbs; - bool has_bone_weights = false; AABB aabb; AABB custom_aabb; + uint64_t skeleton_aabb_version = 0; Vector<RID> material_cache; @@ -136,7 +142,14 @@ struct MeshInstance { Mesh *mesh = nullptr; RID skeleton; struct Surface { + GLuint vertex_buffers[2] = { 0, 0 }; + GLuint vertex_arrays[2] = { 0, 0 }; GLuint vertex_buffer = 0; + int vertex_stride_cache = 0; + int vertex_size_cache = 0; + int vertex_normal_offset_cache = 0; + int vertex_tangent_offset_cache = 0; + uint32_t format_cache = 0; Mesh::Surface::Version *versions = nullptr; //allocated on demand uint32_t version_count = 0; @@ -144,7 +157,6 @@ struct MeshInstance { LocalVector<Surface> surfaces; LocalVector<float> blend_weights; - GLuint blend_weights_buffer = 0; List<MeshInstance *>::Element *I = nullptr; //used to erase itself uint64_t skeleton_version = 0; bool dirty = false; @@ -186,13 +198,15 @@ struct MultiMesh { struct Skeleton { bool use_2d = false; int size = 0; + int height = 0; Vector<float> data; - GLuint buffer = 0; bool dirty = false; Skeleton *dirty_list = nullptr; Transform2D base_transform_2d; + GLuint transforms_texture = 0; + uint64_t version = 1; Dependency dependency; @@ -202,6 +216,11 @@ class MeshStorage : public RendererMeshStorage { private: static MeshStorage *singleton; + struct { + SkeletonShaderGLES3 shader; + RID shader_version; + } skeleton_shader; + /* Mesh */ mutable RID_Owner<Mesh, true> mesh_owner; @@ -214,6 +233,7 @@ private: void _mesh_instance_clear(MeshInstance *mi); void _mesh_instance_add_surface(MeshInstance *mi, Mesh *mesh, uint32_t p_surface); + void _blend_shape_bind_mesh_instance_buffer(MeshInstance *p_mi, uint32_t p_surface); SelfList<MeshInstance>::List dirty_mesh_instance_weights; SelfList<MeshInstance>::List dirty_mesh_instance_arrays; @@ -232,9 +252,10 @@ private: mutable RID_Owner<Skeleton, true> skeleton_owner; - Skeleton *skeleton_dirty_list = nullptr; - _FORCE_INLINE_ void _skeleton_make_dirty(Skeleton *skeleton); + void _compute_skeleton(MeshInstance *p_mi, Skeleton *p_sk, uint32_t p_surface); + + Skeleton *skeleton_dirty_list = nullptr; public: static MeshStorage *get_singleton(); @@ -327,6 +348,7 @@ public: _FORCE_INLINE_ uint32_t mesh_surface_get_lod(void *p_surface, float p_model_scale, float p_distance_threshold, float p_mesh_lod_threshold, uint32_t &r_index_count) const { Mesh::Surface *s = reinterpret_cast<Mesh::Surface *>(p_surface); + ERR_FAIL_COND_V(!s, 0); int32_t current_lod = -1; r_index_count = s->index_count; @@ -403,6 +425,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); @@ -531,9 +555,11 @@ public: virtual void skeleton_update_dependency(RID p_base, DependencyTracker *p_instance) override; - /* OCCLUDER */ + void _update_dirty_skeletons(); - void occluder_set_mesh(RID p_occluder, const PackedVector3Array &p_vertices, const PackedInt32Array &p_indices); + _FORCE_INLINE_ bool skeleton_is_valid(RID p_skeleton) { + return skeleton_owner.get_or_null(p_skeleton) != nullptr; + } }; } // namespace GLES3 diff --git a/drivers/gles3/storage/particles_storage.cpp b/drivers/gles3/storage/particles_storage.cpp index 9ed9fedd5a..2b47271408 100644 --- a/drivers/gles3/storage/particles_storage.cpp +++ b/drivers/gles3/storage/particles_storage.cpp @@ -1,36 +1,42 @@ -/*************************************************************************/ -/* particles_storage.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ +/**************************************************************************/ +/* particles_storage.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ #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,1347 @@ 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, 1); + } + { + // default material and shader for particles shader + particles_shader.default_shader = material_storage->shader_allocate(); + material_storage->shader_initialize(particles_shader.default_shader); + material_storage->shader_set_code(particles_shader.default_shader, R"( +// Default particles shader. + +shader_type particles; + +void process() { + COLOR = vec4(1.0); +} +)"); + particles_shader.default_material = material_storage->material_allocate(); + material_storage->material_initialize(particles_shader.default_material); + material_storage->material_set_shader(particles_shader.default_material, particles_shader.default_shader); + } + { + particles_shader.copy_shader.initialize(); + particles_shader.copy_shader_version = particles_shader.copy_shader.version_create(); + } } ParticlesStorage::~ParticlesStorage() { singleton = nullptr; + GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton(); + + material_storage->material_free(particles_shader.default_material); + material_storage->shader_free(particles_shader.default_shader); + particles_shader.copy_shader.version_free(particles_shader.copy_shader_version); } /* PARTICLES */ RID ParticlesStorage::particles_allocate() { - return RID(); + return particles_owner.allocate_rid(); } void ParticlesStorage::particles_initialize(RID p_rid) { + particles_owner.initialize_rid(p_rid, Particles()); } void ParticlesStorage::particles_free(RID p_rid) { + update_particles(); + Particles *particles = particles_owner.get_or_null(p_rid); + particles->dependency.deleted_notify(p_rid); + _particles_free_data(particles); + particles_owner.free(p_rid); } void ParticlesStorage::particles_set_mode(RID p_particles, RS::ParticlesMode p_mode) { -} + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND(!particles); + if (particles->mode == p_mode) { + return; + } -void ParticlesStorage::particles_emit(RID p_particles, const Transform3D &p_transform, const Vector3 &p_velocity, const Color &p_color, const Color &p_custom, uint32_t p_emit_flags) { + _particles_free_data(particles); + + particles->mode = p_mode; } void ParticlesStorage::particles_set_emitting(RID p_particles, bool p_emitting) { + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND(!particles); + + particles->emitting = p_emitting; +} + +bool ParticlesStorage::particles_get_emitting(RID p_particles) { + ERR_FAIL_COND_V_MSG(RSG::threaded, false, "This function should never be used with threaded rendering, as it stalls the renderer."); + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND_V(!particles, false); + + return particles->emitting; +} + +void ParticlesStorage::_particles_free_data(Particles *particles) { + particles->userdata_count = 0; + particles->instance_buffer_size_cache = 0; + particles->instance_buffer_stride_cache = 0; + particles->num_attrib_arrays_cache = 0; + particles->process_buffer_stride_cache = 0; + + if (particles->front_process_buffer != 0) { + glDeleteVertexArrays(1, &particles->front_vertex_array); + glDeleteBuffers(1, &particles->front_process_buffer); + glDeleteBuffers(1, &particles->front_instance_buffer); + particles->front_vertex_array = 0; + particles->front_process_buffer = 0; + particles->front_instance_buffer = 0; + + glDeleteVertexArrays(1, &particles->back_vertex_array); + glDeleteBuffers(1, &particles->back_process_buffer); + glDeleteBuffers(1, &particles->back_instance_buffer); + particles->back_vertex_array = 0; + particles->back_process_buffer = 0; + particles->back_instance_buffer = 0; + } + + if (particles->sort_buffer != 0) { + glDeleteBuffers(1, &particles->last_frame_buffer); + glDeleteBuffers(1, &particles->sort_buffer); + particles->last_frame_buffer = 0; + particles->sort_buffer = 0; + particles->sort_buffer_filled = false; + particles->last_frame_buffer_filled = false; + } + + if (particles->frame_params_ubo != 0) { + glDeleteBuffers(1, &particles->frame_params_ubo); + particles->frame_params_ubo = 0; + } } void ParticlesStorage::particles_set_amount(RID p_particles, int p_amount) { + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND(!particles); + + if (particles->amount == p_amount) { + return; + } + + _particles_free_data(particles); + + particles->amount = p_amount; + + particles->prev_ticks = 0; + particles->phase = 0; + particles->prev_phase = 0; + particles->clear = true; + + particles->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_PARTICLES); } void ParticlesStorage::particles_set_lifetime(RID p_particles, double p_lifetime) { + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND(!particles); + particles->lifetime = p_lifetime; } void ParticlesStorage::particles_set_one_shot(RID p_particles, bool p_one_shot) { + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND(!particles); + particles->one_shot = p_one_shot; } void ParticlesStorage::particles_set_pre_process_time(RID p_particles, double p_time) { + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND(!particles); + particles->pre_process_time = p_time; } - void ParticlesStorage::particles_set_explosiveness_ratio(RID p_particles, real_t p_ratio) { + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND(!particles); + particles->explosiveness = p_ratio; } - void ParticlesStorage::particles_set_randomness_ratio(RID p_particles, real_t p_ratio) { + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND(!particles); + particles->randomness = p_ratio; } void ParticlesStorage::particles_set_custom_aabb(RID p_particles, const AABB &p_aabb) { + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND(!particles); + particles->custom_aabb = p_aabb; + particles->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_AABB); } void ParticlesStorage::particles_set_speed_scale(RID p_particles, double p_scale) { -} - -void ParticlesStorage::particles_set_use_local_coordinates(RID p_particles, bool p_enable) { -} + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND(!particles); -void ParticlesStorage::particles_set_process_material(RID p_particles, RID p_material) { + particles->speed_scale = p_scale; } +void ParticlesStorage::particles_set_use_local_coordinates(RID p_particles, bool p_enable) { + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND(!particles); -RID ParticlesStorage::particles_get_process_material(RID p_particles) const { - return RID(); + particles->use_local_coords = p_enable; + particles->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_PARTICLES); } void ParticlesStorage::particles_set_fixed_fps(RID p_particles, int p_fps) { + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND(!particles); + + particles->fixed_fps = p_fps; + + _particles_free_data(particles); + + particles->prev_ticks = 0; + particles->phase = 0; + particles->prev_phase = 0; + particles->clear = true; + + particles->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_PARTICLES); } void ParticlesStorage::particles_set_interpolate(RID p_particles, bool p_enable) { + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND(!particles); + + particles->interpolate = p_enable; } void ParticlesStorage::particles_set_fractional_delta(RID p_particles, bool p_enable) { + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND(!particles); + + particles->fractional_delta = p_enable; } -void ParticlesStorage::particles_set_subemitter(RID p_particles, RID p_subemitter_particles) { +void ParticlesStorage::particles_set_trails(RID p_particles, bool p_enable, double p_length) { + if (p_enable) { + WARN_PRINT_ONCE("The OpenGL 3 renderer does not support particle trails"); + } } -void ParticlesStorage::particles_set_view_axis(RID p_particles, const Vector3 &p_axis, const Vector3 &p_up_axis) { +void ParticlesStorage::particles_set_trail_bind_poses(RID p_particles, const Vector<Transform3D> &p_bind_poses) { + if (p_bind_poses.size() != 0) { + WARN_PRINT_ONCE("The OpenGL 3 renderer does not support particle trails"); + } } void ParticlesStorage::particles_set_collision_base_size(RID p_particles, real_t p_size) { + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND(!particles); + + particles->collision_base_size = p_size; } void ParticlesStorage::particles_set_transform_align(RID p_particles, RS::ParticlesTransformAlign p_transform_align) { -} + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND(!particles); -void ParticlesStorage::particles_set_trails(RID p_particles, bool p_enable, double p_length) { + particles->transform_align = p_transform_align; } -void ParticlesStorage::particles_set_trail_bind_poses(RID p_particles, const Vector<Transform3D> &p_bind_poses) { +void ParticlesStorage::particles_set_process_material(RID p_particles, RID p_material) { + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND(!particles); + + particles->process_material = p_material; + particles->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_PARTICLES); //the instance buffer may have changed } -void ParticlesStorage::particles_restart(RID p_particles) { +RID ParticlesStorage::particles_get_process_material(RID p_particles) const { + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND_V(!particles, RID()); + + return particles->process_material; } void ParticlesStorage::particles_set_draw_order(RID p_particles, RS::ParticlesDrawOrder p_order) { + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND(!particles); + + particles->draw_order = p_order; } -void ParticlesStorage::particles_set_draw_passes(RID p_particles, int p_count) { +void ParticlesStorage::particles_set_draw_passes(RID p_particles, int p_passes) { + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND(!particles); + + particles->draw_passes.resize(p_passes); } void ParticlesStorage::particles_set_draw_pass_mesh(RID p_particles, int p_pass, RID p_mesh) { + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND(!particles); + ERR_FAIL_INDEX(p_pass, particles->draw_passes.size()); + particles->draw_passes.write[p_pass] = p_mesh; + particles->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_PARTICLES); +} + +void ParticlesStorage::particles_restart(RID p_particles) { + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND(!particles); + + particles->restart_request = true; +} + +void ParticlesStorage::particles_set_subemitter(RID p_particles, RID p_subemitter_particles) { + if (p_subemitter_particles.is_valid()) { + WARN_PRINT_ONCE("The OpenGL 3 renderer does not support particle sub emitters"); + } +} + +void ParticlesStorage::particles_emit(RID p_particles, const Transform3D &p_transform, const Vector3 &p_velocity, const Color &p_color, const Color &p_custom, uint32_t p_emit_flags) { + WARN_PRINT_ONCE("The OpenGL 3 renderer does not support manually emitting particles"); } void ParticlesStorage::particles_request_process(RID p_particles) { + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND(!particles); + + if (!particles->dirty) { + particles->dirty = true; + particles->update_list = particle_update_list; + particle_update_list = particles; + } } AABB ParticlesStorage::particles_get_current_aabb(RID p_particles) { - return AABB(); + if (RSG::threaded) { + WARN_PRINT_ONCE("Calling this function with threaded rendering enabled stalls the renderer, use with care."); + } + + const Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND_V(!particles, AABB()); + + int total_amount = particles->amount; + + // If available, read from the sort buffer which should be 2 frames out of date. + // This will help alleviate GPU stalls. + GLuint read_buffer = particles->sort_buffer_filled ? particles->sort_buffer : particles->back_instance_buffer; + + Vector<uint8_t> buffer = Utilities::buffer_get_data(GL_ARRAY_BUFFER, read_buffer, total_amount * sizeof(ParticleInstanceData3D)); + ERR_FAIL_COND_V(buffer.size() != (int)(total_amount * sizeof(ParticleInstanceData3D)), AABB()); + + Transform3D inv = particles->emission_transform.affine_inverse(); + + AABB aabb; + if (buffer.size()) { + bool first = true; + + const uint8_t *data_ptr = (const uint8_t *)buffer.ptr(); + uint32_t particle_data_size = sizeof(ParticleInstanceData3D) + sizeof(float) * particles->userdata_count; + + for (int i = 0; i < total_amount; i++) { + const ParticleInstanceData3D &particle_data = *(const ParticleInstanceData3D *)&data_ptr[particle_data_size * i]; + // If scale is 0.0, we assume the particle is inactive. + if (particle_data.xform[0] > 0.0) { + Vector3 pos = Vector3(particle_data.xform[3], particle_data.xform[7], particle_data.xform[11]); + if (!particles->use_local_coords) { + pos = inv.xform(pos); + } + if (first) { + aabb.position = pos; + first = false; + } else { + aabb.expand_to(pos); + } + } + } + } + + float longest_axis_size = 0; + for (int i = 0; i < particles->draw_passes.size(); i++) { + if (particles->draw_passes[i].is_valid()) { + AABB maabb = MeshStorage::get_singleton()->mesh_get_aabb(particles->draw_passes[i], RID()); + longest_axis_size = MAX(maabb.get_longest_axis_size(), longest_axis_size); + } + } + + aabb.grow_by(longest_axis_size); + + return aabb; } AABB ParticlesStorage::particles_get_aabb(RID p_particles) const { - return AABB(); + const Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND_V(!particles, AABB()); + + return particles->custom_aabb; } void ParticlesStorage::particles_set_emission_transform(RID p_particles, const Transform3D &p_transform) { -} + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND(!particles); -bool ParticlesStorage::particles_get_emitting(RID p_particles) { - return false; + particles->emission_transform = p_transform; } int ParticlesStorage::particles_get_draw_passes(RID p_particles) const { - return 0; -} + const Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND_V(!particles, 0); -RID ParticlesStorage::particles_get_draw_pass_mesh(RID p_particles, int p_pass) const { - return RID(); + return particles->draw_passes.size(); } -void ParticlesStorage::particles_add_collision(RID p_particles, RID p_instance) { +RID ParticlesStorage::particles_get_draw_pass_mesh(RID p_particles, int p_pass) const { + const Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND_V(!particles, RID()); + ERR_FAIL_INDEX_V(p_pass, particles->draw_passes.size(), RID()); + + return particles->draw_passes[p_pass]; } -void ParticlesStorage::particles_remove_collision(RID p_particles, RID p_instance) { +void ParticlesStorage::particles_add_collision(RID p_particles, RID p_particles_collision_instance) { + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND(!particles); + particles->collisions.insert(p_particles_collision_instance); +} + +void ParticlesStorage::particles_remove_collision(RID p_particles, RID p_particles_collision_instance) { + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND(!particles); + particles->collisions.erase(p_particles_collision_instance); +} + +void ParticlesStorage::particles_set_canvas_sdf_collision(RID p_particles, bool p_enable, const Transform2D &p_xform, const Rect2 &p_to_screen, GLuint p_texture) { + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND(!particles); + particles->has_sdf_collision = p_enable; + particles->sdf_collision_transform = p_xform; + particles->sdf_collision_to_screen = p_to_screen; + particles->sdf_collision_texture = p_texture; +} + +// Does one step of processing particles by reading from back_process_buffer and writing to front_process_buffer. +void ParticlesStorage::_particles_process(Particles *p_particles, double p_delta) { + GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton(); + GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton(); + + double new_phase = Math::fmod(p_particles->phase + (p_delta / p_particles->lifetime) * p_particles->speed_scale, 1.0); + + //update current frame + ParticlesFrameParams frame_params; + + if (p_particles->clear) { + p_particles->cycle_number = 0; + p_particles->random_seed = Math::rand(); + } else if (new_phase < p_particles->phase) { + if (p_particles->one_shot) { + p_particles->emitting = false; + } + p_particles->cycle_number++; + } + + frame_params.emitting = p_particles->emitting; + frame_params.system_phase = new_phase; + frame_params.prev_system_phase = p_particles->phase; + + p_particles->phase = new_phase; + + frame_params.time = RSG::rasterizer->get_total_time(); + frame_params.delta = p_delta * p_particles->speed_scale; + frame_params.random_seed = p_particles->random_seed; + frame_params.explosiveness = p_particles->explosiveness; + frame_params.randomness = p_particles->randomness; + + if (p_particles->use_local_coords) { + GLES3::MaterialStorage::store_transform(Transform3D(), frame_params.emission_transform); + } else { + GLES3::MaterialStorage::store_transform(p_particles->emission_transform, frame_params.emission_transform); + } + + frame_params.cycle = p_particles->cycle_number; + frame_params.frame = p_particles->frame_counter++; + frame_params.pad0 = 0; + frame_params.pad1 = 0; + frame_params.pad2 = 0; + + { //collision and attractors + + frame_params.collider_count = 0; + frame_params.attractor_count = 0; + frame_params.particle_size = p_particles->collision_base_size; + + GLuint collision_heightmap_texture = 0; + + Transform3D to_particles; + if (p_particles->use_local_coords) { + to_particles = p_particles->emission_transform.affine_inverse(); + } + + if (p_particles->has_sdf_collision && p_particles->sdf_collision_texture != 0) { + //2D collision + + Transform2D xform = p_particles->sdf_collision_transform; //will use dotproduct manually so invert beforehand + + if (!p_particles->use_local_coords) { + Transform2D emission; + emission.columns[0] = Vector2(p_particles->emission_transform.basis.get_column(0).x, p_particles->emission_transform.basis.get_column(0).y); + emission.columns[1] = Vector2(p_particles->emission_transform.basis.get_column(1).x, p_particles->emission_transform.basis.get_column(1).y); + emission.set_origin(Vector2(p_particles->emission_transform.origin.x, p_particles->emission_transform.origin.y)); + xform = xform * emission.affine_inverse(); + } + + Transform2D revert = xform.affine_inverse(); + frame_params.collider_count = 1; + frame_params.colliders[0].transform[0] = xform.columns[0][0]; + frame_params.colliders[0].transform[1] = xform.columns[0][1]; + frame_params.colliders[0].transform[2] = 0; + frame_params.colliders[0].transform[3] = xform.columns[2][0]; + + frame_params.colliders[0].transform[4] = xform.columns[1][0]; + frame_params.colliders[0].transform[5] = xform.columns[1][1]; + frame_params.colliders[0].transform[6] = 0; + frame_params.colliders[0].transform[7] = xform.columns[2][1]; + + frame_params.colliders[0].transform[8] = revert.columns[0][0]; + frame_params.colliders[0].transform[9] = revert.columns[0][1]; + frame_params.colliders[0].transform[10] = 0; + frame_params.colliders[0].transform[11] = revert.columns[2][0]; + + frame_params.colliders[0].transform[12] = revert.columns[1][0]; + frame_params.colliders[0].transform[13] = revert.columns[1][1]; + frame_params.colliders[0].transform[14] = 0; + frame_params.colliders[0].transform[15] = revert.columns[2][1]; + + frame_params.colliders[0].extents[0] = p_particles->sdf_collision_to_screen.size.x; + frame_params.colliders[0].extents[1] = p_particles->sdf_collision_to_screen.size.y; + frame_params.colliders[0].extents[2] = p_particles->sdf_collision_to_screen.position.x; + frame_params.colliders[0].scale = p_particles->sdf_collision_to_screen.position.y; + frame_params.colliders[0].type = ParticlesFrameParams::COLLISION_TYPE_2D_SDF; + + collision_heightmap_texture = p_particles->sdf_collision_texture; + } + + for (const RID &E : p_particles->collisions) { + ParticlesCollisionInstance *pci = particles_collision_instance_owner.get_or_null(E); + if (!pci || !pci->active) { + continue; + } + ParticlesCollision *pc = particles_collision_owner.get_or_null(pci->collision); + ERR_CONTINUE(!pc); + + Transform3D to_collider = pci->transform; + if (p_particles->use_local_coords) { + to_collider = to_particles * to_collider; + } + Vector3 scale = to_collider.basis.get_scale(); + to_collider.basis.orthonormalize(); + + if (pc->type <= RS::PARTICLES_COLLISION_TYPE_VECTOR_FIELD_ATTRACT) { + //attractor + if (frame_params.attractor_count >= ParticlesFrameParams::MAX_ATTRACTORS) { + continue; + } + + ParticlesFrameParams::Attractor &attr = frame_params.attractors[frame_params.attractor_count]; + + GLES3::MaterialStorage::store_transform(to_collider, attr.transform); + attr.strength = pc->attractor_strength; + attr.attenuation = pc->attractor_attenuation; + attr.directionality = pc->attractor_directionality; + + switch (pc->type) { + case RS::PARTICLES_COLLISION_TYPE_SPHERE_ATTRACT: { + attr.type = ParticlesFrameParams::ATTRACTOR_TYPE_SPHERE; + float radius = pc->radius; + radius *= (scale.x + scale.y + scale.z) / 3.0; + attr.extents[0] = radius; + attr.extents[1] = radius; + attr.extents[2] = radius; + } break; + case RS::PARTICLES_COLLISION_TYPE_BOX_ATTRACT: { + attr.type = ParticlesFrameParams::ATTRACTOR_TYPE_BOX; + Vector3 extents = pc->extents * scale; + attr.extents[0] = extents.x; + attr.extents[1] = extents.y; + attr.extents[2] = extents.z; + } break; + case RS::PARTICLES_COLLISION_TYPE_VECTOR_FIELD_ATTRACT: { + WARN_PRINT_ONCE("Vector field particle attractors are not available in the OpenGL2 renderer."); + } break; + default: { + } + } + + frame_params.attractor_count++; + } else { + //collider + if (frame_params.collider_count >= ParticlesFrameParams::MAX_COLLIDERS) { + continue; + } + + ParticlesFrameParams::Collider &col = frame_params.colliders[frame_params.collider_count]; + + GLES3::MaterialStorage::store_transform(to_collider, col.transform); + switch (pc->type) { + case RS::PARTICLES_COLLISION_TYPE_SPHERE_COLLIDE: { + col.type = ParticlesFrameParams::COLLISION_TYPE_SPHERE; + float radius = pc->radius; + radius *= (scale.x + scale.y + scale.z) / 3.0; + col.extents[0] = radius; + col.extents[1] = radius; + col.extents[2] = radius; + } break; + case RS::PARTICLES_COLLISION_TYPE_BOX_COLLIDE: { + col.type = ParticlesFrameParams::COLLISION_TYPE_BOX; + Vector3 extents = pc->extents * scale; + col.extents[0] = extents.x; + col.extents[1] = extents.y; + col.extents[2] = extents.z; + } break; + case RS::PARTICLES_COLLISION_TYPE_SDF_COLLIDE: { + WARN_PRINT_ONCE("SDF Particle Colliders are not available in the OpenGL 3 renderer."); + } break; + case RS::PARTICLES_COLLISION_TYPE_HEIGHTFIELD_COLLIDE: { + if (collision_heightmap_texture != 0) { //already taken + continue; + } + + col.type = ParticlesFrameParams::COLLISION_TYPE_HEIGHT_FIELD; + Vector3 extents = pc->extents * scale; + col.extents[0] = extents.x; + col.extents[1] = extents.y; + col.extents[2] = extents.z; + collision_heightmap_texture = pc->heightfield_texture; + } break; + default: { + } + } + + frame_params.collider_count++; + } + } + + // Bind heightmap or SDF texture. + GLuint heightmap = collision_heightmap_texture; + if (heightmap == 0) { + GLES3::Texture *tex = texture_storage->get_texture(texture_storage->texture_gl_get_default(GLES3::DEFAULT_GL_TEXTURE_BLACK)); + heightmap = tex->tex_id; + } + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, heightmap); + } + + if (p_particles->frame_params_ubo == 0) { + glGenBuffers(1, &p_particles->frame_params_ubo); + } + // Update per-frame UBO. + glBindBufferBase(GL_UNIFORM_BUFFER, PARTICLES_FRAME_UNIFORM_LOCATION, p_particles->frame_params_ubo); + glBufferData(GL_UNIFORM_BUFFER, sizeof(ParticlesFrameParams), &frame_params, GL_STREAM_DRAW); + + // Get shader and set shader uniforms; + ParticleProcessMaterialData *m = static_cast<ParticleProcessMaterialData *>(material_storage->material_get_data(p_particles->process_material, RS::SHADER_PARTICLES)); + if (!m) { + m = static_cast<ParticleProcessMaterialData *>(material_storage->material_get_data(particles_shader.default_material, RS::SHADER_PARTICLES)); + } + + ERR_FAIL_COND(!m); + + ParticlesShaderGLES3::ShaderVariant variant = ParticlesShaderGLES3::MODE_DEFAULT; + + uint32_t specialization = 0; + for (uint32_t i = 0; i < p_particles->userdata_count; i++) { + specialization |= (1 << i); + } + + if (p_particles->mode == RS::ParticlesMode::PARTICLES_MODE_3D) { + specialization |= ParticlesShaderGLES3::MODE_3D; + } + + RID version = particles_shader.default_shader_version; + if (m->shader_data->version.is_valid() && m->shader_data->valid) { + // Bind material uniform buffer and textures. + m->bind_uniforms(); + version = m->shader_data->version; + } + + bool success = material_storage->shaders.particles_process_shader.version_bind_shader(version, variant, specialization); + if (!success) { + return; + } + + material_storage->shaders.particles_process_shader.version_set_uniform(ParticlesShaderGLES3::LIFETIME, p_particles->lifetime, version, variant, specialization); + material_storage->shaders.particles_process_shader.version_set_uniform(ParticlesShaderGLES3::CLEAR, p_particles->clear, version, variant, specialization); + material_storage->shaders.particles_process_shader.version_set_uniform(ParticlesShaderGLES3::TOTAL_PARTICLES, uint32_t(p_particles->amount), version, variant, specialization); + material_storage->shaders.particles_process_shader.version_set_uniform(ParticlesShaderGLES3::USE_FRACTIONAL_DELTA, p_particles->fractional_delta, version, variant, specialization); + + p_particles->clear = false; + + p_particles->has_collision_cache = m->shader_data->uses_collision; + + glBindVertexArray(p_particles->back_vertex_array); + + glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, p_particles->front_process_buffer); + + glBeginTransformFeedback(GL_POINTS); + glDrawArrays(GL_POINTS, 0, p_particles->amount); + glEndTransformFeedback(); + + glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, 0); + glBindVertexArray(0); + + SWAP(p_particles->front_process_buffer, p_particles->back_process_buffer); + SWAP(p_particles->front_vertex_array, p_particles->back_vertex_array); } -void ParticlesStorage::particles_set_canvas_sdf_collision(RID p_particles, bool p_enable, const Transform2D &p_xform, const Rect2 &p_to_screen, RID p_texture) { +void ParticlesStorage::particles_set_view_axis(RID p_particles, const Vector3 &p_axis, const Vector3 &p_up_axis) { + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND(!particles); + + if (particles->draw_order != RS::PARTICLES_DRAW_ORDER_VIEW_DEPTH && particles->transform_align != RS::PARTICLES_TRANSFORM_ALIGN_Z_BILLBOARD && particles->transform_align != RS::PARTICLES_TRANSFORM_ALIGN_Z_BILLBOARD_Y_TO_VELOCITY) { + return; + } + + if (particles->front_process_buffer == 0) { + return; //particles have not processed yet + } + + Vector3 axis = -p_axis; // cameras look to z negative + + if (particles->use_local_coords) { + axis = particles->emission_transform.basis.xform_inv(axis).normalized(); + } + + // Sort will be done on CPU since we don't have compute shaders. + // If the sort_buffer has valid data + // Use a buffer that is 2 frames out of date to avoid stalls. + if (particles->draw_order == RS::PARTICLES_DRAW_ORDER_VIEW_DEPTH && particles->sort_buffer_filled) { + glBindBuffer(GL_ARRAY_BUFFER, particles->sort_buffer); + + ParticleInstanceData3D *particle_array; +#ifndef __EMSCRIPTEN__ + particle_array = static_cast<ParticleInstanceData3D *>(glMapBufferRange(GL_ARRAY_BUFFER, 0, particles->amount * sizeof(ParticleInstanceData3D), GL_MAP_READ_BIT | GL_MAP_WRITE_BIT)); + ERR_FAIL_NULL(particle_array); +#else + LocalVector<ParticleInstanceData3D> particle_vector; + particle_vector.resize(particles->amount); + particle_array = particle_vector.ptr(); + glGetBufferSubData(GL_ARRAY_BUFFER, 0, particles->amount * sizeof(ParticleInstanceData3D), particle_array); +#endif + SortArray<ParticleInstanceData3D, ParticlesViewSort> sorter; + sorter.compare.z_dir = axis; + sorter.sort(particle_array, particles->amount); + +#ifndef __EMSCRIPTEN__ + glUnmapBuffer(GL_ARRAY_BUFFER); +#else + glBufferSubData(GL_ARRAY_BUFFER, 0, particles->amount * sizeof(ParticleInstanceData3D), particle_vector.ptr()); +#endif + } + + glEnable(GL_RASTERIZER_DISCARD); + _particles_update_instance_buffer(particles, axis, p_up_axis); + glDisable(GL_RASTERIZER_DISCARD); +} + +void ParticlesStorage::_particles_update_buffers(Particles *particles) { + GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton(); + uint32_t userdata_count = 0; + + if (particles->process_material.is_valid()) { + GLES3::ParticleProcessMaterialData *material_data = static_cast<GLES3::ParticleProcessMaterialData *>(material_storage->material_get_data(particles->process_material, RS::SHADER_PARTICLES)); + if (material_data && material_data->shader_data->version.is_valid() && material_data->shader_data->valid) { + userdata_count = material_data->shader_data->userdata_count; + } + } + + if (userdata_count != particles->userdata_count) { + // Mismatch userdata, re-create buffers. + _particles_free_data(particles); + } + + if (particles->amount > 0 && particles->front_process_buffer == 0) { + int total_amount = particles->amount; + + particles->userdata_count = userdata_count; + + uint32_t xform_size = particles->mode == RS::PARTICLES_MODE_2D ? 2 : 3; + particles->instance_buffer_stride_cache = sizeof(float) * 4 * (xform_size + 1); + particles->instance_buffer_size_cache = particles->instance_buffer_stride_cache * total_amount; + particles->num_attrib_arrays_cache = 5 + userdata_count + (xform_size - 2); + particles->process_buffer_stride_cache = sizeof(float) * 4 * particles->num_attrib_arrays_cache; + + int process_data_amount = 4 * particles->num_attrib_arrays_cache * total_amount; + float *data = memnew_arr(float, process_data_amount); + + for (int i = 0; i < process_data_amount; i++) { + data[i] = 0; + } + + { + glGenVertexArrays(1, &particles->front_vertex_array); + glBindVertexArray(particles->front_vertex_array); + glGenBuffers(1, &particles->front_process_buffer); + glGenBuffers(1, &particles->front_instance_buffer); + + glBindBuffer(GL_ARRAY_BUFFER, particles->front_process_buffer); + glBufferData(GL_ARRAY_BUFFER, particles->process_buffer_stride_cache * total_amount, data, GL_DYNAMIC_COPY); + + for (uint32_t j = 0; j < particles->num_attrib_arrays_cache; j++) { + glEnableVertexAttribArray(j); + glVertexAttribPointer(j, 4, GL_FLOAT, GL_FALSE, particles->process_buffer_stride_cache, CAST_INT_TO_UCHAR_PTR(sizeof(float) * 4 * j)); + } + glBindVertexArray(0); + + glBindBuffer(GL_ARRAY_BUFFER, particles->front_instance_buffer); + glBufferData(GL_ARRAY_BUFFER, particles->instance_buffer_size_cache, nullptr, GL_DYNAMIC_COPY); + } + + { + glGenVertexArrays(1, &particles->back_vertex_array); + glBindVertexArray(particles->back_vertex_array); + glGenBuffers(1, &particles->back_process_buffer); + glGenBuffers(1, &particles->back_instance_buffer); + + glBindBuffer(GL_ARRAY_BUFFER, particles->back_process_buffer); + glBufferData(GL_ARRAY_BUFFER, particles->process_buffer_stride_cache * total_amount, data, GL_DYNAMIC_COPY); + + for (uint32_t j = 0; j < particles->num_attrib_arrays_cache; j++) { + glEnableVertexAttribArray(j); + glVertexAttribPointer(j, 4, GL_FLOAT, GL_FALSE, particles->process_buffer_stride_cache, CAST_INT_TO_UCHAR_PTR(sizeof(float) * 4 * j)); + } + glBindVertexArray(0); + + glBindBuffer(GL_ARRAY_BUFFER, particles->back_instance_buffer); + glBufferData(GL_ARRAY_BUFFER, particles->instance_buffer_size_cache, nullptr, GL_DYNAMIC_COPY); + } + glBindBuffer(GL_ARRAY_BUFFER, 0); + + memdelete_arr(data); + } +} + +void ParticlesStorage::_particles_allocate_history_buffers(Particles *particles) { + if (particles->sort_buffer == 0) { + glGenBuffers(1, &particles->last_frame_buffer); + glBindBuffer(GL_ARRAY_BUFFER, particles->last_frame_buffer); + glBufferData(GL_ARRAY_BUFFER, particles->instance_buffer_size_cache, nullptr, GL_DYNAMIC_READ); + + glGenBuffers(1, &particles->sort_buffer); + glBindBuffer(GL_ARRAY_BUFFER, particles->sort_buffer); + glBufferData(GL_ARRAY_BUFFER, particles->instance_buffer_size_cache, nullptr, GL_DYNAMIC_READ); + particles->sort_buffer_filled = false; + particles->last_frame_buffer_filled = false; + glBindBuffer(GL_ARRAY_BUFFER, 0); + } +} +void ParticlesStorage::_particles_update_instance_buffer(Particles *particles, const Vector3 &p_axis, const Vector3 &p_up_axis) { + ParticlesCopyShaderGLES3::ShaderVariant variant = ParticlesCopyShaderGLES3::MODE_DEFAULT; + + uint64_t specialization = 0; + if (particles->mode == RS::ParticlesMode::PARTICLES_MODE_3D) { + specialization |= ParticlesCopyShaderGLES3::MODE_3D; + } + + bool success = particles_shader.copy_shader.version_bind_shader(particles_shader.copy_shader_version, variant, specialization); + if (!success) { + return; + } + + // Affect 2D only. + if (particles->use_local_coords) { + // In local mode, particle positions are calculated locally (relative to the node position) + // and they're also drawn locally. + // It works as expected, so we just pass an identity transform. + particles_shader.copy_shader.version_set_uniform(ParticlesCopyShaderGLES3::INV_EMISSION_TRANSFORM, Transform3D(), particles_shader.copy_shader_version, variant, specialization); + } else { + // In global mode, particle positions are calculated globally (relative to the canvas origin) + // but they're drawn locally. + // So, we need to pass the inverse of the emission transform to bring the + // particles to local coordinates before drawing. + Transform3D inv = particles->emission_transform.affine_inverse(); + particles_shader.copy_shader.version_set_uniform(ParticlesCopyShaderGLES3::INV_EMISSION_TRANSFORM, inv, particles_shader.copy_shader_version, variant, specialization); + } + + particles_shader.copy_shader.version_set_uniform(ParticlesCopyShaderGLES3::FRAME_REMAINDER, particles->interpolate ? particles->frame_remainder : 0.0, particles_shader.copy_shader_version, variant, specialization); + particles_shader.copy_shader.version_set_uniform(ParticlesCopyShaderGLES3::ALIGN_MODE, uint32_t(particles->transform_align), particles_shader.copy_shader_version, variant, specialization); + particles_shader.copy_shader.version_set_uniform(ParticlesCopyShaderGLES3::ALIGN_UP, p_up_axis, particles_shader.copy_shader_version, variant, specialization); + particles_shader.copy_shader.version_set_uniform(ParticlesCopyShaderGLES3::SORT_DIRECTION, p_axis, particles_shader.copy_shader_version, variant, specialization); + + glBindVertexArray(particles->back_vertex_array); + glBindBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, particles->front_instance_buffer, 0, particles->instance_buffer_size_cache); + glBeginTransformFeedback(GL_POINTS); + + if (particles->draw_order == RS::PARTICLES_DRAW_ORDER_LIFETIME) { + uint32_t lifetime_split = MIN(particles->amount * particles->phase, particles->amount - 1); + uint32_t stride = particles->process_buffer_stride_cache; + + glBindBuffer(GL_ARRAY_BUFFER, particles->back_process_buffer); + + // Offset VBO so you render starting at the newest particle. + if (particles->amount - lifetime_split > 0) { + glEnableVertexAttribArray(0); // Color. + glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(stride * lifetime_split + sizeof(float) * 4 * 0)); + glEnableVertexAttribArray(1); // .xyz: velocity. .z: flags. + glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(stride * lifetime_split + sizeof(float) * 4 * 1)); + glEnableVertexAttribArray(2); // Custom. + glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(stride * lifetime_split + sizeof(float) * 4 * 2)); + glEnableVertexAttribArray(3); // Xform1. + glVertexAttribPointer(3, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(stride * lifetime_split + sizeof(float) * 4 * 3)); + glEnableVertexAttribArray(4); // Xform2. + glVertexAttribPointer(4, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(stride * lifetime_split + sizeof(float) * 4 * 4)); + if (particles->mode == RS::PARTICLES_MODE_3D) { + glEnableVertexAttribArray(5); // Xform3. + glVertexAttribPointer(5, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(stride * lifetime_split + sizeof(float) * 4 * 5)); + } + + uint32_t to_draw = particles->amount - lifetime_split; + glDrawArrays(GL_POINTS, 0, to_draw); + } + + // Then render from index 0 up intil the newest particle. + if (lifetime_split > 0) { + glEndTransformFeedback(); + // Now output to the second portion of the instance buffer. + glBindBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, particles->front_instance_buffer, particles->instance_buffer_stride_cache * (particles->amount - lifetime_split), particles->instance_buffer_stride_cache * (lifetime_split)); + glBeginTransformFeedback(GL_POINTS); + // Reset back to normal. + for (uint32_t j = 0; j < particles->num_attrib_arrays_cache; j++) { + glEnableVertexAttribArray(j); + glVertexAttribPointer(j, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(sizeof(float) * 4 * j)); + } + + glDrawArrays(GL_POINTS, 0, lifetime_split); + } + } else { + glDrawArrays(GL_POINTS, 0, particles->amount); + } + + glEndTransformFeedback(); + glBindBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, 0, 0, 0); + glBindVertexArray(0); + glBindBuffer(GL_ARRAY_BUFFER, 0); } void ParticlesStorage::update_particles() { + glEnable(GL_RASTERIZER_DISCARD); + + GLuint global_buffer = GLES3::MaterialStorage::get_singleton()->global_shader_parameters_get_uniform_buffer(); + + glBindBufferBase(GL_UNIFORM_BUFFER, PARTICLES_GLOBALS_UNIFORM_LOCATION, global_buffer); + glBindBuffer(GL_UNIFORM_BUFFER, 0); + + while (particle_update_list) { + // Use transform feedback to process particles. + + Particles *particles = particle_update_list; + + particle_update_list = particles->update_list; + particles->update_list = nullptr; + particles->dirty = false; + + _particles_update_buffers(particles); + + if (particles->restart_request) { + particles->prev_ticks = 0; + particles->phase = 0; + particles->prev_phase = 0; + particles->clear = true; + particles->restart_request = false; + } + + if (particles->inactive && !particles->emitting) { + //go next + continue; + } + + if (particles->emitting) { + if (particles->inactive) { + //restart system from scratch + particles->prev_ticks = 0; + particles->phase = 0; + particles->prev_phase = 0; + particles->clear = true; + } + particles->inactive = false; + particles->inactive_time = 0; + } else { + particles->inactive_time += particles->speed_scale * RSG::rasterizer->get_frame_delta_time(); + if (particles->inactive_time > particles->lifetime * 1.2) { + particles->inactive = true; + continue; + } + } + + // Copy the instance buffer that was last used into the last_frame buffer. + // sort_buffer should now be 2 frames out of date. + if (particles->draw_order == RS::PARTICLES_DRAW_ORDER_VIEW_DEPTH || particles->draw_order == RS::PARTICLES_DRAW_ORDER_REVERSE_LIFETIME) { + _particles_allocate_history_buffers(particles); + SWAP(particles->last_frame_buffer, particles->sort_buffer); + + glBindBuffer(GL_COPY_READ_BUFFER, particles->back_instance_buffer); + glBindBuffer(GL_COPY_WRITE_BUFFER, particles->last_frame_buffer); + glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, 0, 0, particles->instance_buffer_size_cache); + + // Last frame's last_frame turned into this frame's sort buffer. + particles->sort_buffer_filled = particles->last_frame_buffer_filled; + particles->sort_buffer_phase = particles->last_frame_phase; + particles->last_frame_buffer_filled = true; + particles->last_frame_phase = particles->phase; + glBindBuffer(GL_COPY_READ_BUFFER, 0); + glBindBuffer(GL_COPY_WRITE_BUFFER, 0); + } + + int fixed_fps = 0; + if (particles->fixed_fps > 0) { + fixed_fps = particles->fixed_fps; + } + + bool zero_time_scale = Engine::get_singleton()->get_time_scale() <= 0.0; + + if (particles->clear && particles->pre_process_time > 0.0) { + double frame_time; + if (fixed_fps > 0) { + frame_time = 1.0 / fixed_fps; + } else { + frame_time = 1.0 / 30.0; + } + + double todo = particles->pre_process_time; + + while (todo >= 0) { + _particles_process(particles, frame_time); + todo -= frame_time; + } + } + + if (fixed_fps > 0) { + double frame_time; + double decr; + if (zero_time_scale) { + frame_time = 0.0; + decr = 1.0 / fixed_fps; + } else { + frame_time = 1.0 / fixed_fps; + decr = frame_time; + } + double delta = RSG::rasterizer->get_frame_delta_time(); + if (delta > 0.1) { //avoid recursive stalls if fps goes below 10 + delta = 0.1; + } else if (delta <= 0.0) { //unlikely but.. + delta = 0.001; + } + double todo = particles->frame_remainder + delta; + + while (todo >= frame_time) { + _particles_process(particles, frame_time); + todo -= decr; + } + + particles->frame_remainder = todo; + + } else { + if (zero_time_scale) { + _particles_process(particles, 0.0); + } else { + _particles_process(particles, RSG::rasterizer->get_frame_delta_time()); + } + } + + // Copy particles to instance buffer and pack Color/Custom. + // We don't have camera information here, so don't copy here if we need camera information for view depth or align mode. + if (particles->draw_order != RS::PARTICLES_DRAW_ORDER_VIEW_DEPTH && particles->transform_align != RS::PARTICLES_TRANSFORM_ALIGN_Z_BILLBOARD && particles->transform_align != RS::PARTICLES_TRANSFORM_ALIGN_Z_BILLBOARD_Y_TO_VELOCITY) { + _particles_update_instance_buffer(particles, Vector3(0.0, 0.0, 0.0), Vector3(0.0, 0.0, 0.0)); + + if (particles->draw_order == RS::PARTICLES_DRAW_ORDER_REVERSE_LIFETIME && particles->sort_buffer_filled) { + if (particles->mode == RS::ParticlesMode::PARTICLES_MODE_2D) { + _particles_reverse_lifetime_sort<ParticleInstanceData2D>(particles); + } else { + _particles_reverse_lifetime_sort<ParticleInstanceData3D>(particles); + } + } + } + + SWAP(particles->front_instance_buffer, particles->back_instance_buffer); + + // At the end of update, the back_buffer contains the most up-to-date-information to read from. + + particles->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_AABB); + } + + glDisable(GL_RASTERIZER_DISCARD); +} + +template <typename ParticleInstanceData> +void ParticlesStorage::_particles_reverse_lifetime_sort(Particles *particles) { + glBindBuffer(GL_ARRAY_BUFFER, particles->sort_buffer); + + ParticleInstanceData *particle_array; + uint32_t buffer_size = particles->amount * sizeof(ParticleInstanceData); +#ifndef __EMSCRIPTEN__ + particle_array = static_cast<ParticleInstanceData *>(glMapBufferRange(GL_ARRAY_BUFFER, 0, buffer_size, GL_MAP_READ_BIT | GL_MAP_WRITE_BIT)); + + ERR_FAIL_NULL(particle_array); +#else + LocalVector<ParticleInstanceData> particle_vector; + particle_vector.resize(particles->amount); + particle_array = particle_vector.ptr(); + glGetBufferSubData(GL_ARRAY_BUFFER, 0, buffer_size, particle_array); +#endif + + uint32_t lifetime_split = MIN(particles->amount * particles->sort_buffer_phase, particles->amount - 1); + + for (uint32_t i = 0; i < lifetime_split / 2; i++) { + SWAP(particle_array[i], particle_array[lifetime_split - i]); + } + + for (uint32_t i = 0; i < (particles->amount - lifetime_split) / 2; i++) { + SWAP(particle_array[lifetime_split + i + 1], particle_array[particles->amount - 1 - i]); + } + +#ifndef __EMSCRIPTEN__ + glUnmapBuffer(GL_ARRAY_BUFFER); +#else + glBufferSubData(GL_ARRAY_BUFFER, 0, buffer_size, particle_vector.ptr()); +#endif + glBindBuffer(GL_ARRAY_BUFFER, 0); +} + +Dependency *ParticlesStorage::particles_get_dependency(RID p_particles) const { + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_NULL_V(particles, nullptr); + + return &particles->dependency; } bool ParticlesStorage::particles_is_inactive(RID p_particles) const { - return false; + ERR_FAIL_COND_V_MSG(RSG::threaded, false, "This function should never be used with threaded rendering, as it stalls the renderer."); + const Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND_V(!particles, false); + return !particles->emitting && particles->inactive; } -/* PARTICLES COLLISION */ +/* PARTICLES COLLISION API */ RID ParticlesStorage::particles_collision_allocate() { - return RID(); + return particles_collision_owner.allocate_rid(); } - void ParticlesStorage::particles_collision_initialize(RID p_rid) { + particles_collision_owner.initialize_rid(p_rid, ParticlesCollision()); } void ParticlesStorage::particles_collision_free(RID p_rid) { + ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_rid); + + if (particles_collision->heightfield_texture != 0) { + glDeleteTextures(1, &particles_collision->heightfield_texture); + particles_collision->heightfield_texture = 0; + glDeleteFramebuffers(1, &particles_collision->heightfield_fb); + particles_collision->heightfield_fb = 0; + } + particles_collision->dependency.deleted_notify(p_rid); + particles_collision_owner.free(p_rid); +} + +GLuint ParticlesStorage::particles_collision_get_heightfield_framebuffer(RID p_particles_collision) const { + ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_particles_collision); + ERR_FAIL_COND_V(!particles_collision, 0); + ERR_FAIL_COND_V(particles_collision->type != RS::PARTICLES_COLLISION_TYPE_HEIGHTFIELD_COLLIDE, 0); + + if (particles_collision->heightfield_texture == 0) { + //create + const int resolutions[RS::PARTICLES_COLLISION_HEIGHTFIELD_RESOLUTION_MAX] = { 256, 512, 1024, 2048, 4096, 8192 }; + Size2i size; + if (particles_collision->extents.x > particles_collision->extents.z) { + size.x = resolutions[particles_collision->heightfield_resolution]; + size.y = int32_t(particles_collision->extents.z / particles_collision->extents.x * size.x); + } else { + size.y = resolutions[particles_collision->heightfield_resolution]; + size.x = int32_t(particles_collision->extents.x / particles_collision->extents.z * size.y); + } + + glGenTextures(1, &particles_collision->heightfield_texture); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, particles_collision->heightfield_texture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32F, size.x, size.y, 0, GL_DEPTH_COMPONENT, GL_FLOAT, nullptr); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + + glGenFramebuffers(1, &particles_collision->heightfield_fb); + glBindFramebuffer(GL_FRAMEBUFFER, particles_collision->heightfield_fb); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, particles_collision->heightfield_texture, 0); +#ifdef DEBUG_ENABLED + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) { + WARN_PRINT("Could create heightmap texture status: " + GLES3::TextureStorage::get_singleton()->get_framebuffer_error(status)); + } +#endif + particles_collision->heightfield_fb_size = size; + + glBindTexture(GL_TEXTURE_2D, 0); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + } + + return particles_collision->heightfield_fb; } void ParticlesStorage::particles_collision_set_collision_type(RID p_particles_collision, RS::ParticlesCollisionType p_type) { + ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_particles_collision); + ERR_FAIL_COND(!particles_collision); + + if (p_type == particles_collision->type) { + return; + } + + if (particles_collision->heightfield_texture != 0) { + glDeleteTextures(1, &particles_collision->heightfield_texture); + particles_collision->heightfield_texture = 0; + glDeleteFramebuffers(1, &particles_collision->heightfield_fb); + particles_collision->heightfield_fb = 0; + } + + particles_collision->type = p_type; + particles_collision->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_AABB); } void ParticlesStorage::particles_collision_set_cull_mask(RID p_particles_collision, uint32_t p_cull_mask) { + ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_particles_collision); + ERR_FAIL_COND(!particles_collision); + particles_collision->cull_mask = p_cull_mask; } void ParticlesStorage::particles_collision_set_sphere_radius(RID p_particles_collision, real_t p_radius) { + ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_particles_collision); + ERR_FAIL_COND(!particles_collision); + + particles_collision->radius = p_radius; + particles_collision->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_AABB); } void ParticlesStorage::particles_collision_set_box_extents(RID p_particles_collision, const Vector3 &p_extents) { + ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_particles_collision); + ERR_FAIL_COND(!particles_collision); + + particles_collision->extents = p_extents; + particles_collision->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_AABB); } void ParticlesStorage::particles_collision_set_attractor_strength(RID p_particles_collision, real_t p_strength) { + ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_particles_collision); + ERR_FAIL_COND(!particles_collision); + + particles_collision->attractor_strength = p_strength; } void ParticlesStorage::particles_collision_set_attractor_directionality(RID p_particles_collision, real_t p_directionality) { + ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_particles_collision); + ERR_FAIL_COND(!particles_collision); + + particles_collision->attractor_directionality = p_directionality; } void ParticlesStorage::particles_collision_set_attractor_attenuation(RID p_particles_collision, real_t p_curve) { + ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_particles_collision); + ERR_FAIL_COND(!particles_collision); + + particles_collision->attractor_attenuation = p_curve; } void ParticlesStorage::particles_collision_set_field_texture(RID p_particles_collision, RID p_texture) { + WARN_PRINT_ONCE("The OpenGL 3 renderer does not support SDF collisions in 3D particle shaders"); } void ParticlesStorage::particles_collision_height_field_update(RID p_particles_collision) { + ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_particles_collision); + ERR_FAIL_COND(!particles_collision); + particles_collision->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_AABB); } void ParticlesStorage::particles_collision_set_height_field_resolution(RID p_particles_collision, RS::ParticlesCollisionHeightfieldResolution p_resolution) { + ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_particles_collision); + ERR_FAIL_COND(!particles_collision); + ERR_FAIL_INDEX(p_resolution, RS::PARTICLES_COLLISION_HEIGHTFIELD_RESOLUTION_MAX); + + if (particles_collision->heightfield_resolution == p_resolution) { + return; + } + + particles_collision->heightfield_resolution = p_resolution; + + if (particles_collision->heightfield_texture != 0) { + glDeleteTextures(1, &particles_collision->heightfield_texture); + particles_collision->heightfield_texture = 0; + glDeleteFramebuffers(1, &particles_collision->heightfield_fb); + particles_collision->heightfield_fb = 0; + } } AABB ParticlesStorage::particles_collision_get_aabb(RID p_particles_collision) const { - return AABB(); + ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_particles_collision); + ERR_FAIL_COND_V(!particles_collision, AABB()); + + switch (particles_collision->type) { + case RS::PARTICLES_COLLISION_TYPE_SPHERE_ATTRACT: + case RS::PARTICLES_COLLISION_TYPE_SPHERE_COLLIDE: { + AABB aabb; + aabb.position = -Vector3(1, 1, 1) * particles_collision->radius; + aabb.size = Vector3(2, 2, 2) * particles_collision->radius; + return aabb; + } + default: { + AABB aabb; + aabb.position = -particles_collision->extents; + aabb.size = particles_collision->extents * 2; + return aabb; + } + } +} + +Vector3 ParticlesStorage::particles_collision_get_extents(RID p_particles_collision) const { + const ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_particles_collision); + ERR_FAIL_COND_V(!particles_collision, Vector3()); + return particles_collision->extents; } bool ParticlesStorage::particles_collision_is_heightfield(RID p_particles_collision) const { - return false; + const ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_particles_collision); + ERR_FAIL_COND_V(!particles_collision, false); + return particles_collision->type == RS::PARTICLES_COLLISION_TYPE_HEIGHTFIELD_COLLIDE; } -RID ParticlesStorage::particles_collision_get_heightfield_framebuffer(RID p_particles_collision) const { - return RID(); +Dependency *ParticlesStorage::particles_collision_get_dependency(RID p_particles_collision) const { + ParticlesCollision *pc = particles_collision_owner.get_or_null(p_particles_collision); + ERR_FAIL_NULL_V(pc, nullptr); + + return &pc->dependency; } +/* Particles collision instance */ + RID ParticlesStorage::particles_collision_instance_create(RID p_collision) { - return RID(); + ParticlesCollisionInstance pci; + pci.collision = p_collision; + return particles_collision_instance_owner.make_rid(pci); } void ParticlesStorage::particles_collision_instance_free(RID p_rid) { + particles_collision_instance_owner.free(p_rid); } void ParticlesStorage::particles_collision_instance_set_transform(RID p_collision_instance, const Transform3D &p_transform) { + ParticlesCollisionInstance *pci = particles_collision_instance_owner.get_or_null(p_collision_instance); + ERR_FAIL_COND(!pci); + pci->transform = p_transform; } void ParticlesStorage::particles_collision_instance_set_active(RID p_collision_instance, bool p_active) { + ParticlesCollisionInstance *pci = particles_collision_instance_owner.get_or_null(p_collision_instance); + ERR_FAIL_COND(!pci); + pci->active = p_active; } #endif // GLES3_ENABLED diff --git a/drivers/gles3/storage/particles_storage.h b/drivers/gles3/storage/particles_storage.h index 84d1f94d8c..b220c48de9 100644 --- a/drivers/gles3/storage/particles_storage.h +++ b/drivers/gles3/storage/particles_storage.h @@ -1,57 +1,315 @@ -/*************************************************************************/ -/* particles_storage.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ +/**************************************************************************/ +/* particles_storage.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ #ifndef PARTICLES_STORAGE_GLES3_H #define PARTICLES_STORAGE_GLES3_H #ifdef GLES3_ENABLED +#include "../shaders/particles_copy.glsl.gen.h" #include "core/templates/local_vector.h" #include "core/templates/rid_owner.h" #include "core/templates/self_list.h" #include "servers/rendering/storage/particles_storage.h" +#include "servers/rendering/storage/utilities.h" + +#include "platform_config.h" +#ifndef OPENGL_INCLUDE_H +#include <GLES3/gl3.h> +#else +#include OPENGL_INCLUDE_H +#endif namespace GLES3 { +enum ParticlesUniformLocation { + PARTICLES_FRAME_UNIFORM_LOCATION, + PARTICLES_GLOBALS_UNIFORM_LOCATION, + PARTICLES_MATERIAL_UNIFORM_LOCATION, +}; + class ParticlesStorage : public RendererParticlesStorage { private: static ParticlesStorage *singleton; + /* PARTICLES */ + + struct ParticleInstanceData3D { + float xform[12]; + float color[2]; // Color and custom are packed together into one vec4; + float custom[2]; + }; + + struct ParticleInstanceData2D { + float xform[8]; + float color[2]; // Color and custom are packed together into one vec4; + float custom[2]; + }; + + struct ParticlesViewSort { + Vector3 z_dir; + bool operator()(const ParticleInstanceData3D &p_a, const ParticleInstanceData3D &p_b) const { + return z_dir.dot(Vector3(p_a.xform[3], p_a.xform[7], p_a.xform[11])) < z_dir.dot(Vector3(p_b.xform[3], p_b.xform[7], p_b.xform[11])); + } + }; + + struct ParticlesFrameParams { + enum { + MAX_ATTRACTORS = 32, + MAX_COLLIDERS = 32, + MAX_3D_TEXTURES = 0 // GLES3 renderer doesn't support using 3D textures for flow field or collisions. + }; + + enum AttractorType { + ATTRACTOR_TYPE_SPHERE, + ATTRACTOR_TYPE_BOX, + ATTRACTOR_TYPE_VECTOR_FIELD, + }; + + struct Attractor { + float transform[16]; + float extents[4]; // Extents or radius. w-channel is padding. + + uint32_t type; + float strength; + float attenuation; + float directionality; + }; + + enum CollisionType { + COLLISION_TYPE_SPHERE, + COLLISION_TYPE_BOX, + COLLISION_TYPE_SDF, + COLLISION_TYPE_HEIGHT_FIELD, + COLLISION_TYPE_2D_SDF, + + }; + + struct Collider { + float transform[16]; + float extents[4]; // Extents or radius. w-channel is padding. + + uint32_t type; + float scale; + float pad0; + float pad1; + }; + + uint32_t emitting; + uint32_t cycle; + float system_phase; + float prev_system_phase; + + float explosiveness; + float randomness; + float time; + float delta; + + float particle_size; + float pad0; + float pad1; + float pad2; + + uint32_t random_seed; + uint32_t attractor_count; + uint32_t collider_count; + uint32_t frame; + + float emission_transform[16]; + + Attractor attractors[MAX_ATTRACTORS]; + Collider colliders[MAX_COLLIDERS]; + }; + + struct Particles { + RS::ParticlesMode mode = RS::PARTICLES_MODE_3D; + bool inactive = true; + double inactive_time = 0.0; + bool emitting = false; + bool one_shot = false; + int amount = 0; + double lifetime = 1.0; + double pre_process_time = 0.0; + real_t explosiveness = 0.0; + real_t randomness = 0.0; + bool restart_request = false; + AABB custom_aabb = AABB(Vector3(-4, -4, -4), Vector3(8, 8, 8)); + bool use_local_coords = false; + bool has_collision_cache = false; + + bool has_sdf_collision = false; + Transform2D sdf_collision_transform; + Rect2 sdf_collision_to_screen; + GLuint sdf_collision_texture = 0; + + RID process_material; + uint32_t frame_counter = 0; + RS::ParticlesTransformAlign transform_align = RS::PARTICLES_TRANSFORM_ALIGN_DISABLED; + + RS::ParticlesDrawOrder draw_order = RS::PARTICLES_DRAW_ORDER_INDEX; + + Vector<RID> draw_passes; + + GLuint frame_params_ubo = 0; + + // We may process particles multiple times each frame (if they have a fixed FPS higher than the game FPS). + // Unfortunately, this means we can't just use a round-robin system of 3 buffers. + // To ensure the sort buffer is accurate, we copy the last frame instance buffer just before processing. + + // Transform Feedback buffer and VAO for rendering. + // Each frame we render to this one. + GLuint front_vertex_array = 0; // Binds process buffer. Used for processing. + GLuint front_process_buffer = 0; // Transform + color + custom data + userdata + velocity + flags. Only needed for processing. + GLuint front_instance_buffer = 0; // Transform + color + custom data. In packed format needed for rendering. + + // VAO for transform feedback, contains last frame's data. + // Read from this one for particles process and then copy to last frame buffer. + GLuint back_vertex_array = 0; // Binds process buffer. Used for processing. + GLuint back_process_buffer = 0; // Transform + color + custom data + userdata + velocity + flags. Only needed for processing. + GLuint back_instance_buffer = 0; // Transform + color + custom data. In packed format needed for rendering. + + uint32_t instance_buffer_size_cache = 0; + uint32_t instance_buffer_stride_cache = 0; + uint32_t num_attrib_arrays_cache = 0; + uint32_t process_buffer_stride_cache = 0; + + // Only ever copied to, holds last frame's instance data, then swaps with sort_buffer. + GLuint last_frame_buffer = 0; + bool last_frame_buffer_filled = false; + float last_frame_phase = 0.0; + + // The frame-before-last's instance buffer. + // Use this to copy data back for sorting or computing AABB. + GLuint sort_buffer = 0; + bool sort_buffer_filled = false; + float sort_buffer_phase = 0.0; + + uint32_t userdata_count = 0; + + bool dirty = false; + Particles *update_list = nullptr; + + double phase = 0.0; + double prev_phase = 0.0; + uint64_t prev_ticks = 0; + uint32_t random_seed = 0; + + uint32_t cycle_number = 0; + + double speed_scale = 1.0; + + int fixed_fps = 30; + bool interpolate = true; + bool fractional_delta = false; + double frame_remainder = 0; + real_t collision_base_size = 0.01; + + bool clear = true; + + Transform3D emission_transform; + + HashSet<RID> collisions; + + Dependency dependency; + + double trail_length = 1.0; + bool trails_enabled = false; + + Particles() { + } + }; + + void _particles_process(Particles *p_particles, double p_delta); + void _particles_free_data(Particles *particles); + void _particles_update_buffers(Particles *particles); + void _particles_allocate_history_buffers(Particles *particles); + void _particles_update_instance_buffer(Particles *particles, const Vector3 &p_axis, const Vector3 &p_up_axis); + + template <typename T> + void _particles_reverse_lifetime_sort(Particles *particles); + + struct ParticlesShader { + RID default_shader; + RID default_material; + RID default_shader_version; + + ParticlesCopyShaderGLES3 copy_shader; + RID copy_shader_version; + } particles_shader; + + Particles *particle_update_list = nullptr; + + mutable RID_Owner<Particles, true> particles_owner; + + /* Particles Collision */ + + struct ParticlesCollision { + RS::ParticlesCollisionType type = RS::PARTICLES_COLLISION_TYPE_SPHERE_ATTRACT; + uint32_t cull_mask = 0xFFFFFFFF; + float radius = 1.0; + Vector3 extents = Vector3(1, 1, 1); + float attractor_strength = 1.0; + float attractor_attenuation = 1.0; + float attractor_directionality = 0.0; + GLuint field_texture = 0; + GLuint heightfield_texture = 0; + GLuint heightfield_fb = 0; + Size2i heightfield_fb_size; + + RS::ParticlesCollisionHeightfieldResolution heightfield_resolution = RS::PARTICLES_COLLISION_HEIGHTFIELD_RESOLUTION_1024; + + Dependency dependency; + }; + + struct ParticlesCollisionInstance { + RID collision; + Transform3D transform; + bool active = false; + }; + + mutable RID_Owner<ParticlesCollision, true> particles_collision_owner; + + mutable RID_Owner<ParticlesCollisionInstance> particles_collision_instance_owner; + public: static ParticlesStorage *get_singleton(); ParticlesStorage(); virtual ~ParticlesStorage(); + bool free(RID p_rid); + /* PARTICLES */ + bool owns_particles(RID p_rid) { return particles_owner.owns(p_rid); } + virtual RID particles_allocate() override; virtual void particles_initialize(RID p_rid) override; virtual void particles_free(RID p_rid) override; @@ -102,12 +360,51 @@ public: virtual void particles_add_collision(RID p_particles, RID p_instance) override; virtual void particles_remove_collision(RID p_particles, RID p_instance) override; - virtual void particles_set_canvas_sdf_collision(RID p_particles, bool p_enable, const Transform2D &p_xform, const Rect2 &p_to_screen, RID p_texture) override; + void particles_set_canvas_sdf_collision(RID p_particles, bool p_enable, const Transform2D &p_xform, const Rect2 &p_to_screen, GLuint p_texture); virtual void update_particles() override; virtual bool particles_is_inactive(RID p_particles) const override; + _FORCE_INLINE_ RS::ParticlesMode particles_get_mode(RID p_particles) { + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND_V(!particles, RS::PARTICLES_MODE_2D); + return particles->mode; + } + + _FORCE_INLINE_ uint32_t particles_get_amount(RID p_particles) { + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND_V(!particles, 0); + + return particles->amount; + } + + _FORCE_INLINE_ GLuint particles_get_gl_buffer(RID p_particles) { + Particles *particles = particles_owner.get_or_null(p_particles); + + if ((particles->draw_order == RS::PARTICLES_DRAW_ORDER_VIEW_DEPTH || particles->draw_order == RS::PARTICLES_DRAW_ORDER_REVERSE_LIFETIME) && particles->sort_buffer_filled) { + return particles->sort_buffer; + } + return particles->back_instance_buffer; + } + + _FORCE_INLINE_ bool particles_has_collision(RID p_particles) { + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND_V(!particles, 0); + + return particles->has_collision_cache; + } + + _FORCE_INLINE_ uint32_t particles_is_using_local_coords(RID p_particles) { + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND_V(!particles, false); + + return particles->use_local_coords; + } + + Dependency *particles_get_dependency(RID p_particles) const; + /* PARTICLES COLLISION */ + bool owns_particles_collision(RID p_rid) { return particles_collision_owner.owns(p_rid); } virtual RID particles_collision_allocate() override; virtual void particles_collision_initialize(RID p_rid) override; @@ -124,8 +421,22 @@ public: virtual void particles_collision_height_field_update(RID p_particles_collision) override; virtual void particles_collision_set_height_field_resolution(RID p_particles_collision, RS::ParticlesCollisionHeightfieldResolution p_resolution) override; virtual AABB particles_collision_get_aabb(RID p_particles_collision) const override; + Vector3 particles_collision_get_extents(RID p_particles_collision) const; virtual bool particles_collision_is_heightfield(RID p_particles_collision) const override; - virtual RID particles_collision_get_heightfield_framebuffer(RID p_particles_collision) const override; + GLuint particles_collision_get_heightfield_framebuffer(RID p_particles_collision) const; + + _FORCE_INLINE_ Size2i particles_collision_get_heightfield_size(RID p_particles_collision) const { + ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_particles_collision); + ERR_FAIL_COND_V(!particles_collision, Size2i()); + ERR_FAIL_COND_V(particles_collision->type != RS::PARTICLES_COLLISION_TYPE_HEIGHTFIELD_COLLIDE, Size2i()); + + return particles_collision->heightfield_fb_size; + } + + Dependency *particles_collision_get_dependency(RID p_particles) const; + + /* PARTICLES COLLISION INSTANCE*/ + bool owns_particles_collision_instance(RID p_rid) { return particles_collision_instance_owner.owns(p_rid); } virtual RID particles_collision_instance_create(RID p_collision) override; virtual void particles_collision_instance_free(RID p_rid) override; diff --git a/drivers/gles3/storage/render_scene_buffers_gles3.cpp b/drivers/gles3/storage/render_scene_buffers_gles3.cpp index b8e4530f56..19bf57df94 100644 --- a/drivers/gles3/storage/render_scene_buffers_gles3.cpp +++ b/drivers/gles3/storage/render_scene_buffers_gles3.cpp @@ -1,54 +1,50 @@ -/*************************************************************************/ -/* render_scene_buffers_gles3.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ +/**************************************************************************/ +/* render_scene_buffers_gles3.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ #ifdef GLES3_ENABLED #include "render_scene_buffers_gles3.h" #include "texture_storage.h" -#ifdef ANDROID_ENABLED -#define glFramebufferTextureMultiviewOVR GLES3::Config::get_singleton()->eglFramebufferTextureMultiviewOVR -#endif - RenderSceneBuffersGLES3::~RenderSceneBuffersGLES3() { free_render_buffer_data(); } -void RenderSceneBuffersGLES3::configure(RID p_render_target, const Size2i p_internal_size, const Size2i p_target_size, float p_fsr_sharpness, float p_texture_mipmap_bias, RS::ViewportMSAA p_msaa, RenderingServer::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_taa, bool p_use_debanding, uint32_t p_view_count) { +void RenderSceneBuffersGLES3::configure(RID p_render_target, const Size2i p_internal_size, const Size2i p_target_size, RS::ViewportScaling3DMode p_scaling_3d_mode, float p_fsr_sharpness, float p_texture_mipmap_bias, RS::ViewportMSAA p_msaa, RenderingServer::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_taa, bool p_use_debanding, uint32_t p_view_count) { GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton(); - GLES3::Config *config = GLES3::Config::get_singleton(); //internal_size.x = p_internal_size.x; // ignore for now //internal_size.y = p_internal_size.y; width = p_target_size.x; height = p_target_size.y; + //scaling_3d_mode = p_scaling_3d_mode //fsr_sharpness = p_fsr_sharpness; //texture_mipmap_bias = p_texture_mipmap_bias; render_target = p_render_target; @@ -62,66 +58,9 @@ void RenderSceneBuffersGLES3::configure(RID p_render_target, const Size2i p_inte GLES3::RenderTarget *rt = texture_storage->get_render_target(p_render_target); is_transparent = rt->is_transparent; - - // framebuffer - glGenFramebuffers(1, &framebuffer); - glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); - - if (view_count > 1 && config->multiview_supported) { - glBindTexture(GL_TEXTURE_2D_ARRAY, rt->color); - glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, rt->color, 0, 0, view_count); - } else { - glBindTexture(GL_TEXTURE_2D, rt->color); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rt->color, 0); - } - - glGenTextures(1, &depth_texture); - if (view_count > 1 && config->multiview_supported) { - glBindTexture(GL_TEXTURE_2D_ARRAY, depth_texture); - glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_DEPTH_COMPONENT24, rt->size.x, rt->size.y, view_count, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr); - - glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - } else { - glBindTexture(GL_TEXTURE_2D, depth_texture); - glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, rt->size.x, rt->size.y, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr); - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - } - - if (view_count > 1 && config->multiview_supported) { - glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depth_texture, 0, 0, view_count); - } else { - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depth_texture, 0); - } - - GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); - - glBindTexture(GL_TEXTURE_2D, 0); - glBindTexture(GL_TEXTURE_2D_ARRAY, 0); - glBindFramebuffer(GL_FRAMEBUFFER, texture_storage->system_fbo); - - if (status != GL_FRAMEBUFFER_COMPLETE) { - free_render_buffer_data(); - WARN_PRINT("Could not create 3D renderbuffer, status: " + texture_storage->get_framebuffer_error(status)); - return; - } } void RenderSceneBuffersGLES3::free_render_buffer_data() { - if (depth_texture) { - glDeleteTextures(1, &depth_texture); - depth_texture = 0; - } - if (framebuffer) { - glDeleteFramebuffers(1, &framebuffer); - framebuffer = 0; - } } #endif // GLES3_ENABLED diff --git a/drivers/gles3/storage/render_scene_buffers_gles3.h b/drivers/gles3/storage/render_scene_buffers_gles3.h index dbedbd22c3..d07a0812f6 100644 --- a/drivers/gles3/storage/render_scene_buffers_gles3.h +++ b/drivers/gles3/storage/render_scene_buffers_gles3.h @@ -1,32 +1,32 @@ -/*************************************************************************/ -/* render_scene_buffers_gles3.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ +/**************************************************************************/ +/* render_scene_buffers_gles3.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ #ifndef RENDER_SCENE_BUFFERS_GLES3_H #define RENDER_SCENE_BUFFERS_GLES3_H @@ -61,9 +61,6 @@ public: bool is_transparent = false; RID render_target; - GLuint internal_texture = 0; // Used for rendering when post effects are enabled - GLuint depth_texture = 0; // Main depth texture - GLuint framebuffer = 0; // Main framebuffer, contains internal_texture and depth_texture or render_target->color and depth_texture //built-in textures used for ping pong image processing and blurring struct Blur { @@ -84,7 +81,7 @@ public: private: public: virtual ~RenderSceneBuffersGLES3(); - virtual void configure(RID p_render_target, const Size2i p_internal_size, const Size2i p_target_size, float p_fsr_sharpness, float p_texture_mipmap_bias, RS::ViewportMSAA p_msaa, RenderingServer::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_taa, bool p_use_debanding, uint32_t p_view_count) override; + virtual void configure(RID p_render_target, const Size2i p_internal_size, const Size2i p_target_size, RS::ViewportScaling3DMode p_scaling_3d_mode, float p_fsr_sharpness, float p_texture_mipmap_bias, RS::ViewportMSAA p_msaa, RenderingServer::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_taa, bool p_use_debanding, uint32_t p_view_count) override; virtual void set_fsr_sharpness(float p_fsr_sharpness) override{}; virtual void set_texture_mipmap_bias(float p_texture_mipmap_bias) override{}; diff --git a/drivers/gles3/storage/texture_storage.cpp b/drivers/gles3/storage/texture_storage.cpp index 11151c4100..67526bc003 100644 --- a/drivers/gles3/storage/texture_storage.cpp +++ b/drivers/gles3/storage/texture_storage.cpp @@ -1,32 +1,32 @@ -/*************************************************************************/ -/* texture_storage.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ +/**************************************************************************/ +/* texture_storage.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ #ifdef GLES3_ENABLED @@ -251,6 +251,8 @@ void TextureStorage::canvas_texture_free(RID p_rid) { void TextureStorage::canvas_texture_set_channel(RID p_canvas_texture, RS::CanvasTextureChannel p_channel, RID p_texture) { CanvasTexture *ct = canvas_texture_owner.get_or_null(p_canvas_texture); + ERR_FAIL_NULL(ct); + switch (p_channel) { case RS::CANVAS_TEXTURE_CHANNEL_DIFFUSE: { ct->diffuse = p_texture; @@ -266,6 +268,8 @@ void TextureStorage::canvas_texture_set_channel(RID p_canvas_texture, RS::Canvas void TextureStorage::canvas_texture_set_shading_parameters(RID p_canvas_texture, const Color &p_specular_color, float p_shininess) { CanvasTexture *ct = canvas_texture_owner.get_or_null(p_canvas_texture); + ERR_FAIL_NULL(ct); + ct->specular_color.r = p_specular_color.r; ct->specular_color.g = p_specular_color.g; ct->specular_color.b = p_specular_color.b; @@ -274,11 +278,15 @@ void TextureStorage::canvas_texture_set_shading_parameters(RID p_canvas_texture, void TextureStorage::canvas_texture_set_texture_filter(RID p_canvas_texture, RS::CanvasItemTextureFilter p_filter) { CanvasTexture *ct = canvas_texture_owner.get_or_null(p_canvas_texture); + ERR_FAIL_NULL(ct); + ct->texture_filter = p_filter; } void TextureStorage::canvas_texture_set_texture_repeat(RID p_canvas_texture, RS::CanvasItemTextureRepeat p_repeat) { CanvasTexture *ct = canvas_texture_owner.get_or_null(p_canvas_texture); + ERR_FAIL_NULL(ct); + ct->texture_repeat = p_repeat; } @@ -292,6 +300,7 @@ Ref<Image> TextureStorage::_get_gl_image_and_format(const Ref<Image> &p_image, I r_real_format = p_format; bool need_decompress = false; + bool decompress_ra_to_rg = false; switch (p_format) { case Image::FORMAT_L8: { @@ -557,6 +566,34 @@ Ref<Image> TextureStorage::_get_gl_image_and_format(const Ref<Image> &p_image, I need_decompress = true; } } break; + case Image::FORMAT_ETC2_RA_AS_RG: { +#ifndef WEB_ENABLED + if (config->etc2_supported) { + r_gl_internal_format = _EXT_COMPRESSED_RGBA8_ETC2_EAC; + r_gl_format = GL_RGBA; + r_gl_type = GL_UNSIGNED_BYTE; + r_compressed = true; + } else +#endif + { + need_decompress = true; + } + decompress_ra_to_rg = true; + } break; + case Image::FORMAT_DXT5_RA_AS_RG: { +#ifndef WEB_ENABLED + if (config->s3tc_supported) { + r_gl_internal_format = _EXT_COMPRESSED_RGBA_S3TC_DXT5_EXT; + r_gl_format = GL_RGBA; + r_gl_type = GL_UNSIGNED_BYTE; + r_compressed = true; + } else +#endif + { + need_decompress = true; + } + decompress_ra_to_rg = true; + } break; default: { ERR_FAIL_V_MSG(Ref<Image>(), "Image Format: " + itos(p_format) + " is not supported by the OpenGL3 Renderer"); } @@ -567,7 +604,18 @@ Ref<Image> TextureStorage::_get_gl_image_and_format(const Ref<Image> &p_image, I image = image->duplicate(); image->decompress(); ERR_FAIL_COND_V(image->is_compressed(), image); + if (decompress_ra_to_rg) { + image->convert_ra_rgba8_to_rg(); + image->convert(Image::FORMAT_RG8); + } switch (image->get_format()) { + case Image::FORMAT_RG8: { + r_gl_format = GL_RG; + r_gl_internal_format = GL_RG8; + r_gl_type = GL_UNSIGNED_BYTE; + r_real_format = Image::FORMAT_RG8; + r_compressed = false; + } break; case Image::FORMAT_RGB8: { r_gl_format = GL_RGB; r_gl_internal_format = GL_RGB; @@ -589,7 +637,6 @@ Ref<Image> TextureStorage::_get_gl_image_and_format(const Ref<Image> &p_image, I r_gl_type = GL_UNSIGNED_BYTE; r_real_format = Image::FORMAT_RGBA8; r_compressed = false; - } break; } } @@ -614,7 +661,9 @@ void TextureStorage::texture_free(RID p_texture) { } if (t->tex_id != 0) { - glDeleteTextures(1, &t->tex_id); + if (!t->is_external) { + glDeleteTextures(1, &t->tex_id); + } t->tex_id = 0; } @@ -645,7 +694,7 @@ void TextureStorage::texture_2d_initialize(RID p_texture, const Ref<Image> &p_im texture.height = p_image->get_height(); texture.alloc_width = texture.width; texture.alloc_height = texture.height; - texture.mipmaps = p_image->get_mipmap_count(); + texture.mipmaps = p_image->get_mipmap_count() + 1; texture.format = p_image->get_format(); texture.type = Texture::TYPE_2D; texture.target = GL_TEXTURE_2D; @@ -680,6 +729,35 @@ void TextureStorage::texture_proxy_initialize(RID p_texture, RID p_base) { texture_owner.initialize_rid(p_texture, proxy_tex); } +RID TextureStorage::texture_create_external(Texture::Type p_type, Image::Format p_format, unsigned int p_image, int p_width, int p_height, int p_depth, int p_layers, RS::TextureLayeredType p_layered_type) { + Texture texture; + texture.active = true; + texture.is_external = true; + texture.type = p_type; + + switch (p_type) { + case Texture::TYPE_2D: { + texture.target = GL_TEXTURE_2D; + } break; + case Texture::TYPE_3D: { + texture.target = GL_TEXTURE_3D; + } break; + case Texture::TYPE_LAYERED: { + texture.target = GL_TEXTURE_2D_ARRAY; + } break; + } + + texture.real_format = texture.format = p_format; + texture.tex_id = p_image; + texture.alloc_width = texture.width = p_width; + texture.alloc_height = texture.height = p_height; + texture.depth = p_depth; + texture.layers = p_layers; + texture.layered_type = p_layered_type; + + return texture_owner.make_rid(texture); +} + void TextureStorage::texture_2d_update(RID p_texture, const Ref<Image> &p_image, int p_layer) { texture_set_data(p_texture, p_image, p_layer); #ifdef TOOLS_ENABLED @@ -690,6 +768,26 @@ void TextureStorage::texture_2d_update(RID p_texture, const Ref<Image> &p_image, } void TextureStorage::texture_proxy_update(RID p_texture, RID p_proxy_to) { + Texture *tex = texture_owner.get_or_null(p_texture); + ERR_FAIL_COND(!tex); + ERR_FAIL_COND(!tex->is_proxy); + Texture *proxy_to = texture_owner.get_or_null(p_proxy_to); + ERR_FAIL_COND(!proxy_to); + ERR_FAIL_COND(proxy_to->is_proxy); + + if (tex->proxy_to.is_valid()) { + Texture *prev_tex = texture_owner.get_or_null(tex->proxy_to); + ERR_FAIL_COND(!prev_tex); + prev_tex->proxies.erase(p_texture); + } + + *tex = *proxy_to; + + tex->proxy_to = p_proxy_to; + tex->is_render_target = false; + tex->is_proxy = true; + tex->proxies.clear(); + proxy_to->proxies.push_back(p_texture); } void TextureStorage::texture_2d_placeholder_initialize(RID p_texture) { @@ -991,6 +1089,10 @@ Size2 TextureStorage::texture_size_with_proxy(RID p_texture) { } } +RID TextureStorage::texture_get_rd_texture_rid(RID p_texture, bool p_srgb) const { + return RID(); +} + void TextureStorage::texture_set_data(RID p_texture, const Ref<Image> &p_image, int p_layer) { Texture *texture = texture_owner.get_or_null(p_texture); @@ -1041,15 +1143,14 @@ void TextureStorage::texture_set_data(RID p_texture, const Ref<Image> &p_image, texture->gl_set_filter(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST); texture->gl_set_repeat(RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED); - //set swizle for older format compatibility -#ifdef GLES_OVER_GL +#ifndef WEB_ENABLED switch (texture->format) { +#ifdef GLES_OVER_GL case Image::FORMAT_L8: { glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_R, GL_RED); glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_G, GL_RED); glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_B, GL_RED); glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_A, GL_ONE); - } break; case Image::FORMAT_LA8: { glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_R, GL_RED); @@ -1057,15 +1158,29 @@ void TextureStorage::texture_set_data(RID p_texture, const Ref<Image> &p_image, glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_B, GL_RED); glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_A, GL_GREEN); } break; +#endif // GLES3_OVER_GL + + case Image::FORMAT_ETC2_RA_AS_RG: + case Image::FORMAT_DXT5_RA_AS_RG: { + glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_R, GL_RED); + if (texture->format == real_format) { + // Swizzle RA from compressed texture into RG + glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_G, GL_ALPHA); + } else { + // Converted textures are already in RG, leave as-is + glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_G, GL_GREEN); + } + glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_B, GL_ZERO); + glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_A, GL_ONE); + } break; default: { glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_R, GL_RED); glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_G, GL_GREEN); glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_B, GL_BLUE); glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_A, GL_ALPHA); - } break; } -#endif +#endif // WEB_ENABLED int mipmaps = img->has_mipmaps() ? img->get_mipmap_count() + 1 : 1; @@ -1419,18 +1534,6 @@ AABB TextureStorage::decal_get_aabb(RID p_decal) const { return AABB(); } -/* DECAL INSTANCE API */ - -RID TextureStorage::decal_instance_create(RID p_decal) { - return RID(); -} - -void TextureStorage::decal_instance_free(RID p_decal_instance) { -} - -void TextureStorage::decal_instance_set_transform(RID p_decal, const Transform3D &p_transform) { -} - /* RENDER TARGET API */ GLuint TextureStorage::system_fbo = 0; @@ -1459,43 +1562,82 @@ void TextureStorage::_update_render_target(RenderTarget *rt) { glDepthMask(GL_FALSE); { - /* Front FBO */ + Texture *texture; + bool use_multiview = rt->view_count > 1 && config->multiview_supported; + GLenum texture_target = use_multiview ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D; - Texture *texture = get_texture(rt->texture); - ERR_FAIL_COND(!texture); + /* Front FBO */ - // framebuffer glGenFramebuffers(1, &rt->fbo); glBindFramebuffer(GL_FRAMEBUFFER, rt->fbo); // color - glGenTextures(1, &rt->color); - if (rt->view_count > 1 && config->multiview_supported) { - glBindTexture(GL_TEXTURE_2D_ARRAY, rt->color); - glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, rt->color_internal_format, rt->size.x, rt->size.y, rt->view_count, 0, rt->color_format, rt->color_type, nullptr); - - glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + if (rt->overridden.color.is_valid()) { + texture = get_texture(rt->overridden.color); + ERR_FAIL_COND(!texture); + + rt->color = texture->tex_id; + rt->size = Size2i(texture->width, texture->height); } else { - glBindTexture(GL_TEXTURE_2D, rt->color); - glTexImage2D(GL_TEXTURE_2D, 0, rt->color_internal_format, rt->size.x, rt->size.y, 0, rt->color_format, rt->color_type, nullptr); + texture = get_texture(rt->texture); + ERR_FAIL_COND(!texture); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - } + glGenTextures(1, &rt->color); + glBindTexture(texture_target, rt->color); + + if (use_multiview) { + glTexImage3D(texture_target, 0, rt->color_internal_format, rt->size.x, rt->size.y, rt->view_count, 0, rt->color_format, rt->color_type, nullptr); + } else { + glTexImage2D(texture_target, 0, rt->color_internal_format, rt->size.x, rt->size.y, 0, rt->color_format, rt->color_type, nullptr); + } - if (rt->view_count > 1 && config->multiview_supported) { + glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } +#ifndef IOS_ENABLED + if (use_multiview) { glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, rt->color, 0, 0, rt->view_count); } else { +#else + { +#endif glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rt->color, 0); } - GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + // depth + if (rt->overridden.depth.is_valid()) { + texture = get_texture(rt->overridden.depth); + ERR_FAIL_COND(!texture); + + rt->depth = texture->tex_id; + } else { + glGenTextures(1, &rt->depth); + glBindTexture(texture_target, rt->depth); + + if (use_multiview) { + glTexImage3D(texture_target, 0, GL_DEPTH_COMPONENT24, rt->size.x, rt->size.y, rt->view_count, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr); + } else { + glTexImage2D(texture_target, 0, GL_DEPTH_COMPONENT24, rt->size.x, rt->size.y, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr); + } + glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } +#ifndef IOS_ENABLED + if (use_multiview) { + glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, rt->depth, 0, 0, rt->view_count); + } else { +#else + { +#endif + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, rt->depth, 0); + } + + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); if (status != GL_FRAMEBUFFER_COMPLETE) { glDeleteFramebuffers(1, &rt->fbo); glDeleteTextures(1, &rt->color); @@ -1503,32 +1645,38 @@ void TextureStorage::_update_render_target(RenderTarget *rt) { rt->size.x = 0; rt->size.y = 0; rt->color = 0; - texture->tex_id = 0; - texture->active = false; + rt->depth = 0; + if (rt->overridden.color.is_null()) { + texture->tex_id = 0; + texture->active = false; + } WARN_PRINT("Could not create render target, status: " + get_framebuffer_error(status)); return; } - texture->format = rt->image_format; - texture->real_format = rt->image_format; - if (rt->view_count > 1 && config->multiview_supported) { - texture->type = Texture::TYPE_LAYERED; - texture->target = GL_TEXTURE_2D_ARRAY; - texture->layers = rt->view_count; - } else { - texture->type = Texture::TYPE_2D; - texture->target = GL_TEXTURE_2D; - texture->layers = 1; + texture->is_render_target = true; + texture->render_target = rt; + if (rt->overridden.color.is_null()) { + texture->format = rt->image_format; + texture->real_format = rt->image_format; + texture->target = texture_target; + if (rt->view_count > 1 && config->multiview_supported) { + texture->type = Texture::TYPE_LAYERED; + texture->layers = rt->view_count; + } else { + texture->type = Texture::TYPE_2D; + texture->layers = 1; + } + texture->gl_format_cache = rt->color_format; + texture->gl_type_cache = GL_UNSIGNED_BYTE; + texture->gl_internal_format_cache = rt->color_internal_format; + texture->tex_id = rt->color; + texture->width = rt->size.x; + texture->alloc_width = rt->size.x; + texture->height = rt->size.y; + texture->alloc_height = rt->size.y; + texture->active = true; } - texture->gl_format_cache = rt->color_format; - texture->gl_type_cache = GL_UNSIGNED_BYTE; - texture->gl_internal_format_cache = rt->color_internal_format; - texture->tex_id = rt->color; - texture->width = rt->size.x; - texture->alloc_width = rt->size.x; - texture->height = rt->size.y; - texture->alloc_height = rt->size.y; - texture->active = true; } glClearColor(0, 0, 0, 0); @@ -1594,19 +1742,54 @@ void TextureStorage::_clear_render_target(RenderTarget *rt) { return; } + // Dispose of the cached fbo's and the allocated textures + for (KeyValue<uint32_t, RenderTarget::RTOverridden::FBOCacheEntry> &E : rt->overridden.fbo_cache) { + glDeleteTextures(E.value.allocated_textures.size(), E.value.allocated_textures.ptr()); + // Don't delete the current FBO, we'll do that a couple lines down. + if (E.value.fbo != rt->fbo) { + glDeleteFramebuffers(1, &E.value.fbo); + } + } + rt->overridden.fbo_cache.clear(); + if (rt->fbo) { glDeleteFramebuffers(1, &rt->fbo); - glDeleteTextures(1, &rt->color); rt->fbo = 0; - rt->color = 0; } - Texture *tex = get_texture(rt->texture); - tex->alloc_height = 0; - tex->alloc_width = 0; - tex->width = 0; - tex->height = 0; - tex->active = false; + if (rt->overridden.color.is_null()) { + if (rt->texture.is_valid()) { + Texture *tex = get_texture(rt->texture); + tex->alloc_height = 0; + tex->alloc_width = 0; + tex->width = 0; + tex->height = 0; + tex->active = false; + tex->render_target = nullptr; + tex->is_render_target = false; + } + } else { + Texture *tex = get_texture(rt->overridden.color); + tex->render_target = nullptr; + tex->is_render_target = false; + } + + if (rt->overridden.color.is_valid()) { + rt->overridden.color = RID(); + } else if (rt->color) { + glDeleteTextures(1, &rt->color); + } + rt->color = 0; + + if (rt->overridden.depth.is_valid()) { + rt->overridden.depth = RID(); + } else if (rt->depth) { + glDeleteTextures(1, &rt->depth); + } + rt->depth = 0; + + rt->overridden.velocity = RID(); + rt->overridden.is_overridden = false; if (rt->backbuffer_fbo != 0) { glDeleteFramebuffers(1, &rt->backbuffer_fbo); @@ -1619,7 +1802,7 @@ void TextureStorage::_clear_render_target(RenderTarget *rt) { RID TextureStorage::render_target_create() { RenderTarget render_target; - //render_target.was_used = false; + render_target.used_in_frame = false; render_target.clear_requested = false; Texture t; @@ -1639,7 +1822,9 @@ void TextureStorage::render_target_free(RID p_rid) { Texture *t = get_texture(rt->texture); if (t) { t->is_render_target = false; - texture_free(rt->texture); + if (rt->overridden.color.is_null()) { + texture_free(rt->texture); + } //memdelete(t); } render_target_owner.free(p_rid); @@ -1666,6 +1851,9 @@ void TextureStorage::render_target_set_size(RID p_render_target, int p_width, in if (p_width == rt->size.x && p_height == rt->size.y && p_view_count == rt->view_count) { return; } + if (rt->overridden.color.is_valid()) { + return; + } _clear_render_target(rt); @@ -1683,10 +1871,91 @@ Size2i TextureStorage::render_target_get_size(RID p_render_target) const { return rt->size; } +void TextureStorage::render_target_set_override(RID p_render_target, RID p_color_texture, RID p_depth_texture, RID p_velocity_texture) { + RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_COND(!rt); + ERR_FAIL_COND(rt->direct_to_screen); + + rt->overridden.velocity = p_velocity_texture; + + if (rt->overridden.color == p_color_texture && rt->overridden.depth == p_depth_texture) { + return; + } + + if (p_color_texture.is_null() && p_depth_texture.is_null()) { + _clear_render_target(rt); + _update_render_target(rt); + return; + } + + if (!rt->overridden.is_overridden) { + _clear_render_target(rt); + } + + rt->overridden.color = p_color_texture; + rt->overridden.depth = p_depth_texture; + rt->overridden.is_overridden = true; + + uint32_t hash_key = hash_murmur3_one_64(p_color_texture.get_id()); + hash_key = hash_murmur3_one_64(p_depth_texture.get_id(), hash_key); + hash_key = hash_fmix32(hash_key); + + RBMap<uint32_t, RenderTarget::RTOverridden::FBOCacheEntry>::Element *cache; + if ((cache = rt->overridden.fbo_cache.find(hash_key)) != nullptr) { + rt->fbo = cache->get().fbo; + rt->color = cache->get().color; + rt->depth = cache->get().depth; + rt->size = cache->get().size; + rt->texture = p_color_texture; + return; + } + + _update_render_target(rt); + + RenderTarget::RTOverridden::FBOCacheEntry new_entry; + new_entry.fbo = rt->fbo; + new_entry.color = rt->color; + new_entry.depth = rt->depth; + new_entry.size = rt->size; + // Keep track of any textures we had to allocate because they weren't overridden. + if (p_color_texture.is_null()) { + new_entry.allocated_textures.push_back(rt->color); + } + if (p_depth_texture.is_null()) { + new_entry.allocated_textures.push_back(rt->depth); + } + rt->overridden.fbo_cache.insert(hash_key, new_entry); +} + +RID TextureStorage::render_target_get_override_color(RID p_render_target) const { + RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_COND_V(!rt, RID()); + + return rt->overridden.color; +} + +RID TextureStorage::render_target_get_override_depth(RID p_render_target) const { + RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_COND_V(!rt, RID()); + + return rt->overridden.depth; +} + +RID TextureStorage::render_target_get_override_velocity(RID p_render_target) const { + RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_COND_V(!rt, RID()); + + return rt->overridden.velocity; +} + RID TextureStorage::render_target_get_texture(RID p_render_target) { RenderTarget *rt = render_target_owner.get_or_null(p_render_target); ERR_FAIL_COND_V(!rt, RID()); + if (rt->overridden.color.is_valid()) { + return rt->overridden.color; + } + return rt->texture; } @@ -1696,8 +1965,10 @@ void TextureStorage::render_target_set_transparent(RID p_render_target, bool p_t rt->is_transparent = p_transparent; - _clear_render_target(rt); - _update_render_target(rt); + if (rt->overridden.color.is_null()) { + _clear_render_target(rt); + _update_render_target(rt); + } } bool TextureStorage::render_target_get_transparent(RID p_render_target) const { @@ -1718,6 +1989,11 @@ void TextureStorage::render_target_set_direct_to_screen(RID p_render_target, boo // those functions change how they operate depending on the value of DIRECT_TO_SCREEN _clear_render_target(rt); rt->direct_to_screen = p_direct_to_screen; + if (rt->direct_to_screen) { + rt->overridden.color = RID(); + rt->overridden.depth = RID(); + rt->overridden.velocity = RID(); + } _update_render_target(rt); } @@ -1750,6 +2026,7 @@ void TextureStorage::render_target_set_msaa(RID p_render_target, RS::ViewportMSA } WARN_PRINT("2D MSAA is not yet supported for GLES3."); + _clear_render_target(rt); rt->msaa = p_msaa; _update_render_target(rt); @@ -2004,7 +2281,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); @@ -2025,7 +2306,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); @@ -2049,7 +2334,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/texture_storage.h b/drivers/gles3/storage/texture_storage.h index 0b025c4370..9d3f407226 100644 --- a/drivers/gles3/storage/texture_storage.h +++ b/drivers/gles3/storage/texture_storage.h @@ -1,32 +1,32 @@ -/*************************************************************************/ -/* texture_storage.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ +/**************************************************************************/ +/* texture_storage.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ #ifndef TEXTURE_STORAGE_GLES3_H #define TEXTURE_STORAGE_GLES3_H @@ -126,9 +126,10 @@ struct Texture { RID self; bool is_proxy = false; + bool is_external = false; bool is_render_target = false; - RID proxy_to = RID(); + RID proxy_to; Vector<RID> proxies; String path; @@ -187,6 +188,7 @@ struct Texture { void copy_from(const Texture &o) { proxy_to = o.proxy_to; is_proxy = o.is_proxy; + is_external = o.is_external; width = o.width; height = o.height; alloc_width = o.alloc_width; @@ -310,6 +312,7 @@ struct RenderTarget { RID self; GLuint fbo = 0; GLuint color = 0; + GLuint depth = 0; GLuint backbuffer_fbo = 0; GLuint backbuffer = 0; @@ -333,6 +336,22 @@ struct RenderTarget { bool used_in_frame = false; RS::ViewportMSAA msaa = RS::VIEWPORT_MSAA_DISABLED; + struct RTOverridden { + bool is_overridden = false; + RID color; + RID depth; + RID velocity; + + struct FBOCacheEntry { + GLuint fbo; + GLuint color; + GLuint depth; + Size2i size; + Vector<GLuint> allocated_textures; + }; + RBMap<uint32_t, FBOCacheEntry> fbo_cache; + } overridden; + RID texture; Color clear_color = Color(1, 1, 1, 1); @@ -454,6 +473,8 @@ public: virtual void texture_3d_initialize(RID p_texture, Image::Format, int p_width, int p_height, int p_depth, bool p_mipmaps, const Vector<Ref<Image>> &p_data) override; virtual void texture_proxy_initialize(RID p_texture, RID p_base) override; //all slices, then all the mipmaps, must be coherent + RID texture_create_external(Texture::Type p_type, Image::Format p_format, unsigned int p_image, int p_width, int p_height, int p_depth, int p_layers, RS::TextureLayeredType p_layered_type = RS::TEXTURE_LAYERED_2D_ARRAY); + virtual void texture_2d_update(RID p_texture, const Ref<Image> &p_image, int p_layer = 0) override; virtual void texture_3d_update(RID p_texture, const Vector<Ref<Image>> &p_data) override{}; virtual void texture_proxy_update(RID p_proxy, RID p_base) override; @@ -484,6 +505,8 @@ public: virtual Size2 texture_size_with_proxy(RID p_proxy) override; + virtual RID texture_get_rd_texture_rid(RID p_texture, bool p_srgb = false) const override; + void texture_set_data(RID p_texture, const Ref<Image> &p_image, int p_layer = 0); void texture_set_data_partial(RID p_texture, const Ref<Image> &p_image, int src_x, int src_y, int src_w, int src_h, int dst_x, int dst_y, int p_dst_mip, int p_layer = 0); //Ref<Image> texture_get_data(RID p_texture, int p_layer = 0) const; @@ -538,9 +561,10 @@ public: /* DECAL INSTANCE */ - virtual RID decal_instance_create(RID p_decal) override; - virtual void decal_instance_free(RID p_decal_instance) override; - virtual void decal_instance_set_transform(RID p_decal, const Transform3D &p_transform) override; + virtual RID decal_instance_create(RID p_decal) override { return RID(); } + virtual void decal_instance_free(RID p_decal_instance) override {} + virtual void decal_instance_set_transform(RID p_decal, const Transform3D &p_transform) override {} + virtual void decal_instance_set_sorting_offset(RID p_decal_instance, float p_sorting_offset) override {} /* RENDER TARGET API */ @@ -593,12 +617,10 @@ public: virtual void render_target_set_vrs_texture(RID p_render_target, RID p_texture) override {} virtual RID render_target_get_vrs_texture(RID p_render_target) const override { return RID(); } - virtual void render_target_set_override_color(RID p_render_target, RID p_texture) override {} - virtual RID render_target_get_override_color(RID p_render_target) const override { return RID(); } - virtual void render_target_set_override_depth(RID p_render_target, RID p_texture) override {} - virtual RID render_target_get_override_depth(RID p_render_target) const override { return RID(); } - virtual void render_target_set_override_velocity(RID p_render_target, RID p_texture) override {} - virtual RID render_target_get_override_velocity(RID p_render_target) const override { return RID(); } + virtual void render_target_set_override(RID p_render_target, RID p_color_texture, RID p_depth_texture, RID p_velocity_texture) override; + virtual RID render_target_get_override_color(RID p_render_target) const override; + virtual RID render_target_get_override_depth(RID p_render_target) const override; + virtual RID render_target_get_override_velocity(RID p_render_target) const override; virtual RID render_target_get_texture(RID p_render_target) override; diff --git a/drivers/gles3/storage/utilities.cpp b/drivers/gles3/storage/utilities.cpp index 8e7e218bb9..e72240c69b 100644 --- a/drivers/gles3/storage/utilities.cpp +++ b/drivers/gles3/storage/utilities.cpp @@ -1,32 +1,32 @@ -/*************************************************************************/ -/* utilities.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ +/**************************************************************************/ +/* utilities.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ #ifdef GLES3_ENABLED @@ -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,21 @@ 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); - + } else if (GLES3::ParticlesStorage::get_singleton()->owns_particles(p_rid)) { + GLES3::ParticlesStorage::get_singleton()->particles_free(p_rid); 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_collision(p_rid)) { + GLES3::ParticlesStorage::get_singleton()->particles_collision_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_instance(p_rid)) { + GLES3::ParticlesStorage::get_singleton()->particles_collision_instance_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::MeshStorage::get_singleton()->owns_skeleton(p_rid)) { + GLES3::MeshStorage::get_singleton()->skeleton_free(p_rid); return true; + } else { + return false; } - */ } /* DEPENDENCIES */ @@ -207,6 +179,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); } } @@ -306,7 +284,7 @@ String Utilities::get_captured_timestamp_name(uint32_t p_index) const { void Utilities::update_dirty_resources() { MaterialStorage::get_singleton()->_update_global_shader_uniforms(); MaterialStorage::get_singleton()->_update_queued_materials(); - //MeshStorage::get_singleton()->_update_dirty_skeletons(); + MeshStorage::get_singleton()->_update_dirty_skeletons(); MeshStorage::get_singleton()->_update_dirty_multimeshes(); TextureStorage::get_singleton()->update_texture_atlas(); } diff --git a/drivers/gles3/storage/utilities.h b/drivers/gles3/storage/utilities.h index 55a875958e..92131aff8c 100644 --- a/drivers/gles3/storage/utilities.h +++ b/drivers/gles3/storage/utilities.h @@ -1,32 +1,32 @@ -/*************************************************************************/ -/* utilities.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ +/**************************************************************************/ +/* utilities.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ #ifndef UTILITIES_GLES3_H #define UTILITIES_GLES3_H |