summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/javascript_builds.yml2
-rw-r--r--core/config/project_settings.cpp26
-rw-r--r--core/config/project_settings.h6
-rw-r--r--core/core_constants.cpp1
-rw-r--r--core/extension/gdnative_interface.h1
-rw-r--r--core/extension/native_extension.cpp2
-rw-r--r--core/input/input.cpp16
-rw-r--r--core/input/input_map.cpp40
-rw-r--r--core/input/input_map.h14
-rw-r--r--core/io/config_file.cpp27
-rw-r--r--core/io/config_file.h4
-rw-r--r--core/io/image.cpp4
-rw-r--r--core/io/missing_resource.cpp4
-rw-r--r--core/io/missing_resource.h2
-rw-r--r--core/io/resource.cpp17
-rw-r--r--core/io/resource_format_binary.cpp6
-rw-r--r--core/io/resource_uid.cpp22
-rw-r--r--core/io/resource_uid.h4
-rw-r--r--core/io/zip_io.cpp58
-rw-r--r--core/io/zip_io.h6
-rw-r--r--core/math/delaunay_3d.h4
-rw-r--r--core/math/expression.cpp40
-rw-r--r--core/math/expression.h1
-rw-r--r--core/math/quick_hull.cpp2
-rw-r--r--core/object/class_db.cpp117
-rw-r--r--core/object/object.cpp62
-rw-r--r--core/object/object.h5
-rw-r--r--core/object/script_language.cpp5
-rw-r--r--core/object/script_language.h1
-rw-r--r--core/object/script_language_extension.cpp1
-rw-r--r--core/os/memory.h8
-rw-r--r--core/os/os.h8
-rw-r--r--core/string/translation_po.cpp40
-rw-r--r--core/string/ustring.cpp63
-rw-r--r--core/templates/cowdata.h32
-rw-r--r--core/templates/hash_map.h775
-rw-r--r--core/templates/hashfuncs.h85
-rw-r--r--core/templates/ordered_hash_map.h301
-rw-r--r--core/templates/paged_allocator.h8
-rw-r--r--core/templates/vector.h2
-rw-r--r--core/variant/dictionary.cpp85
-rw-r--r--core/variant/variant.h1
-rw-r--r--core/variant/variant_call.cpp27
-rw-r--r--core/variant/variant_construct.h4
-rw-r--r--core/variant/variant_op.cpp6
-rw-r--r--core/variant/variant_op.h18
-rw-r--r--doc/classes/@GlobalScope.xml27
-rw-r--r--doc/classes/AnimatedSprite2D.xml8
-rw-r--r--doc/classes/CollisionPolygon2D.xml1
-rw-r--r--doc/classes/CollisionShape2D.xml1
-rw-r--r--doc/classes/DirectionalLight3D.xml1
-rw-r--r--doc/classes/GPUParticlesCollisionHeightField3D.xml4
-rw-r--r--doc/classes/Light3D.xml2
-rw-r--r--doc/classes/OmniLight3D.xml1
-rw-r--r--doc/classes/PackedByteArray.xml23
-rw-r--r--doc/classes/PackedColorArray.xml23
-rw-r--r--doc/classes/PackedFloat32Array.xml23
-rw-r--r--doc/classes/PackedFloat64Array.xml23
-rw-r--r--doc/classes/PackedInt32Array.xml23
-rw-r--r--doc/classes/PackedInt64Array.xml23
-rw-r--r--doc/classes/PackedStringArray.xml29
-rw-r--r--doc/classes/PackedVector2Array.xml23
-rw-r--r--doc/classes/PackedVector3Array.xml23
-rw-r--r--doc/classes/ProgressBar.xml17
-rw-r--r--doc/classes/ProjectSettings.xml58
-rw-r--r--doc/classes/SceneTree.xml19
-rw-r--r--doc/classes/ScriptLanguageExtension.xml8
-rw-r--r--doc/classes/VisualShaderNodeColorFunc.xml10
-rw-r--r--doc/classes/VisualShaderNodeVectorFunc.xml66
-rw-r--r--doc/classes/float.xml12
-rw-r--r--doc/classes/int.xml12
-rwxr-xr-xdoc/tools/make_rst.py26
-rw-r--r--drivers/coreaudio/audio_driver_coreaudio.h4
-rw-r--r--drivers/coremidi/midi_driver_coremidi.cpp4
-rw-r--r--drivers/coremidi/midi_driver_coremidi.h2
-rw-r--r--drivers/gles3/rasterizer_canvas_gles3.cpp8
-rw-r--r--drivers/gles3/rasterizer_canvas_gles3.h11
-rw-r--r--drivers/gles3/rasterizer_gles3.cpp4
-rw-r--r--drivers/gles3/rasterizer_gles3.h2
-rw-r--r--drivers/gles3/rasterizer_scene_gles3.cpp1194
-rw-r--r--drivers/gles3/rasterizer_scene_gles3.h373
-rw-r--r--drivers/gles3/rasterizer_storage_gles3.cpp113
-rw-r--r--drivers/gles3/rasterizer_storage_gles3.h45
-rw-r--r--drivers/gles3/shader_gles3.cpp9
-rw-r--r--drivers/gles3/shader_gles3.h8
-rw-r--r--drivers/gles3/shaders/SCsub1
-rw-r--r--drivers/gles3/shaders/canvas.glsl28
-rw-r--r--drivers/gles3/shaders/scene.glsl2332
-rw-r--r--drivers/gles3/shaders/sky.glsl88
-rw-r--r--drivers/gles3/shaders/stdlib_inc.glsl4
-rw-r--r--drivers/gles3/shaders/tonemap_inc.glsl119
-rw-r--r--drivers/gles3/storage/config.cpp31
-rw-r--r--drivers/gles3/storage/config.h6
-rw-r--r--drivers/gles3/storage/light_storage.cpp241
-rw-r--r--drivers/gles3/storage/light_storage.h192
-rw-r--r--drivers/gles3/storage/material_storage.cpp689
-rw-r--r--drivers/gles3/storage/material_storage.h179
-rw-r--r--drivers/gles3/storage/mesh_storage.cpp104
-rw-r--r--drivers/gles3/storage/mesh_storage.h19
-rw-r--r--drivers/gles3/storage/texture_storage.cpp109
-rw-r--r--drivers/gles3/storage/texture_storage.h8
-rw-r--r--editor/animation_track_editor.cpp2
-rw-r--r--editor/animation_track_editor.h2
-rw-r--r--editor/create_dialog.cpp2
-rw-r--r--editor/debugger/editor_performance_profiler.cpp82
-rw-r--r--editor/debugger/editor_performance_profiler.h4
-rw-r--r--editor/editor_asset_installer.cpp6
-rw-r--r--editor/editor_command_palette.cpp36
-rw-r--r--editor/editor_data.cpp9
-rw-r--r--editor/editor_export.cpp3
-rw-r--r--editor/editor_file_dialog.cpp2
-rw-r--r--editor/editor_file_dialog.h2
-rw-r--r--editor/editor_help.cpp95
-rw-r--r--editor/editor_help_search.h2
-rw-r--r--editor/editor_node.cpp2
-rw-r--r--editor/editor_plugin_settings.cpp5
-rw-r--r--editor/editor_plugin_settings.h2
-rw-r--r--editor/editor_resource_preview.cpp23
-rw-r--r--editor/editor_resource_preview.h8
-rw-r--r--editor/editor_settings.cpp23
-rw-r--r--editor/editor_settings_dialog.cpp15
-rw-r--r--editor/editor_settings_dialog.h2
-rw-r--r--editor/export_template_manager.cpp6
-rw-r--r--editor/filesystem_dock.cpp12
-rw-r--r--editor/filesystem_dock.h4
-rw-r--r--editor/import/resource_importer_scene.cpp10
-rw-r--r--editor/plugin_config_dialog.cpp56
-rw-r--r--editor/plugin_config_dialog.h1
-rw-r--r--editor/plugins/mesh_instance_3d_editor_plugin.cpp69
-rw-r--r--editor/plugins/node_3d_editor_gizmos.h2
-rw-r--r--editor/plugins/script_editor_plugin.cpp6
-rw-r--r--editor/plugins/script_text_editor.cpp15
-rw-r--r--editor/plugins/skeleton_3d_editor_plugin.cpp63
-rw-r--r--editor/plugins/texture_region_editor_plugin.cpp6
-rw-r--r--editor/plugins/theme_editor_plugin.cpp110
-rw-r--r--editor/plugins/theme_editor_plugin.h2
-rw-r--r--editor/plugins/visual_shader_editor_plugin.cpp30
-rw-r--r--editor/pot_generator.cpp8
-rw-r--r--editor/pot_generator.h4
-rw-r--r--editor/project_manager.cpp10
-rw-r--r--editor/project_settings_editor.cpp8
-rw-r--r--editor/scene_tree_editor.cpp21
-rw-r--r--main/main.cpp39
-rw-r--r--main/performance.cpp4
-rw-r--r--main/performance.h4
-rw-r--r--modules/csg/csg.cpp7
-rw-r--r--modules/gdscript/doc_classes/@GDScript.xml29
-rw-r--r--modules/gdscript/editor/gdscript_highlighter.cpp5
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp8
-rw-r--r--modules/gdscript/gdscript_byte_codegen.cpp6
-rw-r--r--modules/gdscript/gdscript_compiler.cpp2
-rw-r--r--modules/gdscript/gdscript_editor.cpp43
-rw-r--r--modules/gdscript/gdscript_parser.cpp46
-rw-r--r--modules/gdscript/gdscript_parser.h5
-rw-r--r--modules/gdscript/gdscript_tokenizer.cpp10
-rw-r--r--modules/gdscript/gdscript_tokenizer.h2
-rw-r--r--modules/gdscript/language_server/gdscript_extend_parser.cpp32
-rw-r--r--modules/gdscript/language_server/gdscript_language_protocol.cpp32
-rw-r--r--modules/gdscript/language_server/gdscript_text_document.cpp18
-rw-r--r--modules/gdscript/language_server/gdscript_workspace.cpp18
-rw-r--r--modules/gdscript/tests/gdscript_test_runner.cpp10
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/function_match_parent_signature_with_default_dict_void.gd14
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/function_match_parent_signature_with_default_dict_void.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/match_multiple_variable_binds_in_branch.gd4
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/match_multiple_variable_binds_in_branch.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/features/match_multiple_variable_binds_in_pattern.gd6
-rw-r--r--modules/gdscript/tests/scripts/parser/features/match_multiple_variable_binds_in_pattern.out4
-rw-r--r--modules/mono/class_db_api_json.cpp41
-rw-r--r--modules/mono/csharp_script.cpp8
-rw-r--r--modules/mono/csharp_script.h2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs1
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs3
-rw-r--r--modules/mono/editor/bindings_generator.cpp42
-rw-r--r--modules/mono/editor/bindings_generator.h2
-rw-r--r--modules/mono/editor/code_completion.cpp6
-rw-r--r--modules/mono/mono_gd/gd_mono.cpp25
-rw-r--r--modules/mono/mono_gd/gd_mono_class.cpp19
-rw-r--r--modules/mono/utils/osx_utils.cpp4
-rw-r--r--modules/navigation/navigation_mesh_generator.cpp8
-rw-r--r--modules/openxr/extensions/openxr_vulkan_extension.cpp9
-rw-r--r--modules/openxr/openxr_api.cpp2
-rw-r--r--modules/raycast/raycast_occlusion_cull.cpp17
-rw-r--r--modules/svg/image_loader_svg.cpp2
-rw-r--r--modules/text_server_adv/text_server_adv.cpp23
-rw-r--r--modules/text_server_adv/text_server_adv.h16
-rw-r--r--modules/text_server_fb/text_server_fb.cpp21
-rw-r--r--modules/text_server_fb/text_server_fb.h16
-rw-r--r--modules/visual_script/editor/visual_script_editor.cpp12
-rw-r--r--modules/visual_script/visual_script.cpp159
-rw-r--r--modules/vorbis/audio_stream_ogg_vorbis.cpp3
-rw-r--r--platform/android/detect.py7
-rw-r--r--platform/android/export/export_plugin.cpp8
-rw-r--r--platform/iphone/detect.py7
-rw-r--r--platform/iphone/export/export_plugin.cpp28
-rw-r--r--platform/iphone/tts_ios.h6
-rw-r--r--platform/javascript/api/javascript_tools_editor_plugin.cpp5
-rw-r--r--platform/javascript/export/export_plugin.cpp3
-rw-r--r--platform/javascript/godot_webgl2.h37
-rw-r--r--platform/javascript/js/libs/library_godot_os.js4
-rw-r--r--platform/javascript/platform_config.h2
-rw-r--r--platform/linuxbsd/display_server_x11.cpp4
-rw-r--r--platform/osx/dir_access_osx.mm4
-rw-r--r--platform/osx/display_server_osx.h9
-rw-r--r--platform/osx/display_server_osx.mm38
-rw-r--r--platform/osx/export/export_plugin.cpp6
-rw-r--r--platform/osx/gl_manager_osx_legacy.h6
-rw-r--r--platform/osx/joypad_osx.h10
-rw-r--r--platform/osx/key_mapping_osx.mm4
-rw-r--r--platform/osx/tts_osx.h9
-rw-r--r--platform/osx/vulkan_context_osx.h2
-rw-r--r--platform/uwp/export/export_plugin.cpp3
-rw-r--r--platform/windows/display_server_windows.cpp12
-rw-r--r--platform/windows/display_server_windows.h1
-rw-r--r--platform/windows/os_windows.cpp62
-rw-r--r--scene/2d/camera_2d.cpp6
-rw-r--r--scene/2d/collision_polygon_2d.cpp5
-rw-r--r--scene/2d/collision_shape_2d.cpp5
-rw-r--r--scene/3d/camera_3d.cpp2
-rw-r--r--scene/3d/gpu_particles_collision_3d.cpp27
-rw-r--r--scene/3d/gpu_particles_collision_3d.h8
-rw-r--r--scene/3d/lightmap_gi.cpp5
-rw-r--r--scene/3d/voxelizer.cpp3
-rw-r--r--scene/3d/world_environment.cpp4
-rw-r--r--scene/animation/animation_tree.cpp34
-rw-r--r--scene/gui/button.cpp3
-rw-r--r--scene/gui/code_edit.cpp9
-rw-r--r--scene/gui/control.cpp10
-rw-r--r--scene/gui/graph_edit.cpp22
-rw-r--r--scene/gui/line_edit.cpp36
-rw-r--r--scene/gui/line_edit.h3
-rw-r--r--scene/gui/progress_bar.cpp61
-rw-r--r--scene/gui/progress_bar.h16
-rw-r--r--scene/gui/text_edit.cpp47
-rw-r--r--scene/gui/text_edit.h4
-rw-r--r--scene/gui/tree.cpp7
-rw-r--r--scene/main/missing_node.cpp4
-rw-r--r--scene/main/missing_node.h2
-rw-r--r--scene/main/scene_tree.cpp36
-rw-r--r--scene/main/scene_tree.h8
-rw-r--r--scene/main/shader_globals_override.cpp22
-rw-r--r--scene/main/viewport.cpp2
-rw-r--r--scene/multiplayer/multiplayer_spawner.cpp6
-rw-r--r--scene/multiplayer/scene_cache_interface.cpp6
-rw-r--r--scene/multiplayer/scene_replication_interface.cpp5
-rw-r--r--scene/multiplayer/scene_replication_state.cpp20
-rw-r--r--scene/multiplayer/scene_replication_state.h4
-rw-r--r--scene/resources/curve.cpp153
-rw-r--r--scene/resources/environment.cpp1
-rw-r--r--scene/resources/font.cpp3
-rw-r--r--scene/resources/material.cpp2
-rw-r--r--scene/resources/packed_scene.cpp15
-rw-r--r--scene/resources/polygon_path_finder.cpp8
-rw-r--r--scene/resources/skeleton_modification_stack_2d.cpp2
-rw-r--r--scene/resources/skeleton_modification_stack_3d.cpp2
-rw-r--r--scene/resources/surface_tool.cpp2
-rw-r--r--scene/resources/theme.cpp282
-rw-r--r--scene/resources/tile_set.cpp8
-rw-r--r--scene/resources/visual_shader.cpp116
-rw-r--r--scene/resources/visual_shader_nodes.cpp109
-rw-r--r--scene/resources/visual_shader_nodes.h7
-rw-r--r--servers/rendering/dummy/rasterizer_dummy.h2
-rw-r--r--servers/rendering/renderer_compositor.cpp1
-rw-r--r--servers/rendering/renderer_compositor.h3
-rw-r--r--servers/rendering/renderer_rd/effects/bokeh_dof.cpp475
-rw-r--r--servers/rendering/renderer_rd/effects/bokeh_dof.h120
-rw-r--r--servers/rendering/renderer_rd/effects/copy_effects.cpp683
-rw-r--r--servers/rendering/renderer_rd/effects/copy_effects.h220
-rw-r--r--servers/rendering/renderer_rd/effects/tone_mapper.cpp29
-rw-r--r--servers/rendering/renderer_rd/effects/tone_mapper.h3
-rw-r--r--servers/rendering/renderer_rd/effects_rd.cpp783
-rw-r--r--servers/rendering/renderer_rd/effects_rd.h217
-rw-r--r--servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp2
-rw-r--r--servers/rendering/renderer_rd/renderer_compositor_rd.h1
-rw-r--r--servers/rendering/renderer_rd/renderer_scene_gi_rd.cpp3
-rw-r--r--servers/rendering/renderer_rd/renderer_scene_render_rd.cpp327
-rw-r--r--servers/rendering/renderer_rd/renderer_scene_render_rd.h21
-rw-r--r--servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp9
-rw-r--r--servers/rendering/renderer_rd/renderer_scene_sky_rd.h2
-rw-r--r--servers/rendering/renderer_rd/shaders/blur_raster_inc.glsl21
-rw-r--r--servers/rendering/renderer_rd/shaders/effects/blur_raster.glsl (renamed from servers/rendering/renderer_rd/shaders/blur_raster.glsl)18
-rw-r--r--servers/rendering/renderer_rd/shaders/effects/blur_raster_inc.glsl26
-rw-r--r--servers/rendering/renderer_rd/shaders/effects/bokeh_dof.glsl (renamed from servers/rendering/renderer_rd/shaders/bokeh_dof.glsl)0
-rw-r--r--servers/rendering/renderer_rd/shaders/effects/bokeh_dof_inc.glsl (renamed from servers/rendering/renderer_rd/shaders/bokeh_dof_inc.glsl)0
-rw-r--r--servers/rendering/renderer_rd/shaders/effects/bokeh_dof_raster.glsl (renamed from servers/rendering/renderer_rd/shaders/bokeh_dof_raster.glsl)0
-rw-r--r--servers/rendering/renderer_rd/shaders/effects/copy.glsl (renamed from servers/rendering/renderer_rd/shaders/copy.glsl)2
-rw-r--r--servers/rendering/renderer_rd/shaders/effects/copy_to_fb.glsl (renamed from servers/rendering/renderer_rd/shaders/copy_to_fb.glsl)0
-rw-r--r--servers/rendering/renderer_rd/shaders/effects/tonemap.glsl47
-rw-r--r--servers/rendering/renderer_rd/storage_rd/material_storage.cpp28
-rw-r--r--servers/rendering/renderer_rd/storage_rd/material_storage.h9
-rw-r--r--servers/rendering/renderer_rd/storage_rd/texture_storage.cpp49
-rw-r--r--servers/rendering/renderer_viewport.cpp22
-rw-r--r--servers/rendering/rendering_server_default.cpp12
-rw-r--r--servers/rendering/rendering_server_default.h8
-rw-r--r--servers/rendering/shader_compiler.cpp24
-rw-r--r--servers/rendering/shader_types.h2
-rw-r--r--servers/rendering_server.cpp48
-rw-r--r--tests/core/math/test_geometry_2d.h301
-rw-r--r--tests/core/object/test_class_db.h29
-rw-r--r--tests/core/templates/test_hash_map.h (renamed from tests/core/templates/test_ordered_hash_map.h)66
-rw-r--r--tests/scene/test_text_edit.h361
-rw-r--r--tests/test_main.cpp2
-rw-r--r--thirdparty/README.md2
-rw-r--r--thirdparty/etcpak/ProcessRGB.cpp1084
303 files changed, 10098 insertions, 6322 deletions
diff --git a/.github/workflows/javascript_builds.yml b/.github/workflows/javascript_builds.yml
index 395dfdd7f5..00c79e8ba0 100644
--- a/.github/workflows/javascript_builds.yml
+++ b/.github/workflows/javascript_builds.yml
@@ -6,7 +6,7 @@ env:
# Only used for the cache key. Increment version to force clean build.
GODOT_BASE_BRANCH: master
SCONSFLAGS: verbose=yes warnings=extra werror=yes debug_symbols=no
- EM_VERSION: 2.0.27
+ EM_VERSION: 3.1.10
EM_CACHE_FOLDER: "emsdk-cache"
concurrency:
diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp
index 79fa6a0895..ba108d75da 100644
--- a/core/config/project_settings.cpp
+++ b/core/config/project_settings.cpp
@@ -1091,7 +1091,7 @@ bool ProjectSettings::has_custom_feature(const String &p_feature) const {
return custom_features.has(p_feature);
}
-OrderedHashMap<StringName, ProjectSettings::AutoloadInfo> ProjectSettings::get_autoload_list() const {
+const HashMap<StringName, ProjectSettings::AutoloadInfo> &ProjectSettings::get_autoload_list() const {
return autoloads;
}
@@ -1135,13 +1135,13 @@ void ProjectSettings::_bind_methods() {
void ProjectSettings::_add_builtin_input_map() {
if (InputMap::get_singleton()) {
- OrderedHashMap<String, List<Ref<InputEvent>>> builtins = InputMap::get_singleton()->get_builtins();
+ HashMap<String, List<Ref<InputEvent>>> builtins = InputMap::get_singleton()->get_builtins();
- for (OrderedHashMap<String, List<Ref<InputEvent>>>::Element E = builtins.front(); E; E = E.next()) {
+ for (KeyValue<String, List<Ref<InputEvent>>> &E : builtins) {
Array events;
// Convert list of input events into array
- for (List<Ref<InputEvent>>::Element *I = E.get().front(); I; I = I->next()) {
+ for (List<Ref<InputEvent>>::Element *I = E.value.front(); I; I = I->next()) {
events.push_back(I->get());
}
@@ -1149,7 +1149,7 @@ void ProjectSettings::_add_builtin_input_map() {
action["deadzone"] = Variant(0.5f);
action["events"] = events;
- String action_name = "input/" + E.key();
+ String action_name = "input/" + E.key;
GLOBAL_DEF(action_name, action);
input_presets.push_back(action_name);
}
@@ -1175,6 +1175,22 @@ ProjectSettings::ProjectSettings() {
GLOBAL_DEF("application/config/use_custom_user_dir", false);
GLOBAL_DEF("application/config/custom_user_dir_name", "");
GLOBAL_DEF("application/config/project_settings_override", "");
+
+ GLOBAL_DEF_BASIC("display/window/size/viewport_width", 1024);
+ custom_prop_info["display/window/size/viewport_width"] = PropertyInfo(Variant::INT, "display/window/size/viewport_width", PROPERTY_HINT_RANGE, "0,7680,1,or_greater"); // 8K resolution
+
+ GLOBAL_DEF_BASIC("display/window/size/viewport_height", 600);
+ custom_prop_info["display/window/size/viewport_height"] = PropertyInfo(Variant::INT, "display/window/size/viewport_height", PROPERTY_HINT_RANGE, "0,4320,1,or_greater"); // 8K resolution
+
+ GLOBAL_DEF_BASIC("display/window/size/resizable", true);
+ GLOBAL_DEF_BASIC("display/window/size/borderless", false);
+ GLOBAL_DEF_BASIC("display/window/size/fullscreen", false);
+ GLOBAL_DEF("display/window/size/always_on_top", false);
+ GLOBAL_DEF("display/window/size/window_width_override", 0);
+ custom_prop_info["display/window/size/window_width_override"] = PropertyInfo(Variant::INT, "display/window/size/window_width_override", PROPERTY_HINT_RANGE, "0,7680,1,or_greater"); // 8K resolution
+ GLOBAL_DEF("display/window/size/window_height_override", 0);
+ custom_prop_info["display/window/size/window_height_override"] = PropertyInfo(Variant::INT, "display/window/size/window_height_override", PROPERTY_HINT_RANGE, "0,4320,1,or_greater"); // 8K resolution
+
GLOBAL_DEF_BASIC("audio/buses/default_bus_layout", "res://default_bus_layout.tres");
custom_prop_info["audio/buses/default_bus_layout"] = PropertyInfo(Variant::STRING, "audio/buses/default_bus_layout", PROPERTY_HINT_FILE, "*.tres");
diff --git a/core/config/project_settings.h b/core/config/project_settings.h
index 614a11f726..8655526edd 100644
--- a/core/config/project_settings.h
+++ b/core/config/project_settings.h
@@ -33,7 +33,7 @@
#include "core/object/class_db.h"
#include "core/os/thread_safe.h"
-#include "core/templates/ordered_hash_map.h"
+#include "core/templates/hash_map.h"
#include "core/templates/set.h"
class ProjectSettings : public Object {
@@ -94,7 +94,7 @@ protected:
Set<String> custom_features;
Map<StringName, StringName> feature_overrides;
- OrderedHashMap<StringName, AutoloadInfo> autoloads;
+ HashMap<StringName, AutoloadInfo> autoloads;
String project_data_dir_name;
@@ -181,7 +181,7 @@ public:
bool has_custom_feature(const String &p_feature) const;
- OrderedHashMap<StringName, AutoloadInfo> get_autoload_list() const;
+ const HashMap<StringName, AutoloadInfo> &get_autoload_list() const;
void add_autoload(const AutoloadInfo &p_autoload);
void remove_autoload(const StringName &p_autoload);
bool has_autoload(const StringName &p_autoload) const;
diff --git a/core/core_constants.cpp b/core/core_constants.cpp
index 2a514b68d8..a53929a3af 100644
--- a/core/core_constants.cpp
+++ b/core/core_constants.cpp
@@ -703,6 +703,7 @@ void register_global_constants() {
BIND_CORE_ENUM_CONSTANT_CUSTOM("OP_NEGATE", Variant::OP_NEGATE);
BIND_CORE_ENUM_CONSTANT_CUSTOM("OP_POSITIVE", Variant::OP_POSITIVE);
BIND_CORE_ENUM_CONSTANT_CUSTOM("OP_MODULE", Variant::OP_MODULE);
+ BIND_CORE_ENUM_CONSTANT_CUSTOM("OP_POWER", Variant::OP_POWER);
//bitwise
BIND_CORE_ENUM_CONSTANT_CUSTOM("OP_SHIFT_LEFT", Variant::OP_SHIFT_LEFT);
BIND_CORE_ENUM_CONSTANT_CUSTOM("OP_SHIFT_RIGHT", Variant::OP_SHIFT_RIGHT);
diff --git a/core/extension/gdnative_interface.h b/core/extension/gdnative_interface.h
index 36d51ff2b9..98976b29f6 100644
--- a/core/extension/gdnative_interface.h
+++ b/core/extension/gdnative_interface.h
@@ -114,6 +114,7 @@ typedef enum {
GDNATIVE_VARIANT_OP_NEGATE,
GDNATIVE_VARIANT_OP_POSITIVE,
GDNATIVE_VARIANT_OP_MODULE,
+ GDNATIVE_VARIANT_OP_POWER,
/* bitwise */
GDNATIVE_VARIANT_OP_SHIFT_LEFT,
GDNATIVE_VARIANT_OP_SHIFT_RIGHT,
diff --git a/core/extension/native_extension.cpp b/core/extension/native_extension.cpp
index db1cbd53b4..5738b42049 100644
--- a/core/extension/native_extension.cpp
+++ b/core/extension/native_extension.cpp
@@ -426,7 +426,7 @@ Ref<Resource> NativeExtensionResourceLoader::load(const String &p_path, const St
return Ref<Resource>();
}
- if (!library_path.is_resource_file()) {
+ if (!library_path.is_resource_file() && !library_path.is_absolute_path()) {
library_path = p_path.get_base_dir().plus_file(library_path);
}
diff --git a/core/input/input.cpp b/core/input/input.cpp
index 40cea2cd80..3a2e50a674 100644
--- a/core/input/input.cpp
+++ b/core/input/input.cpp
@@ -636,21 +636,21 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em
}
}
- for (OrderedHashMap<StringName, InputMap::Action>::ConstElement E = InputMap::get_singleton()->get_action_map().front(); E; E = E.next()) {
- if (InputMap::get_singleton()->event_is_action(p_event, E.key())) {
+ for (const KeyValue<StringName, InputMap::Action> &E : InputMap::get_singleton()->get_action_map()) {
+ if (InputMap::get_singleton()->event_is_action(p_event, E.key)) {
// If not echo and action pressed state has changed
- if (!p_event->is_echo() && is_action_pressed(E.key(), false) != p_event->is_action_pressed(E.key())) {
+ if (!p_event->is_echo() && is_action_pressed(E.key, false) != p_event->is_action_pressed(E.key)) {
Action action;
action.physics_frame = Engine::get_singleton()->get_physics_frames();
action.process_frame = Engine::get_singleton()->get_process_frames();
- action.pressed = p_event->is_action_pressed(E.key());
+ action.pressed = p_event->is_action_pressed(E.key);
action.strength = 0.0f;
action.raw_strength = 0.0f;
- action.exact = InputMap::get_singleton()->event_is_action(p_event, E.key(), true);
- action_state[E.key()] = action;
+ action.exact = InputMap::get_singleton()->event_is_action(p_event, E.key, true);
+ action_state[E.key] = action;
}
- action_state[E.key()].strength = p_event->get_action_strength(E.key());
- action_state[E.key()].raw_strength = p_event->get_action_raw_strength(E.key());
+ action_state[E.key].strength = p_event->get_action_strength(E.key);
+ action_state[E.key].raw_strength = p_event->get_action_raw_strength(E.key);
}
}
diff --git a/core/input/input_map.cpp b/core/input/input_map.cpp
index ab94c00999..51c1cf9608 100644
--- a/core/input/input_map.cpp
+++ b/core/input/input_map.cpp
@@ -119,8 +119,8 @@ List<StringName> InputMap::get_actions() const {
return actions;
}
- for (OrderedHashMap<StringName, Action>::Element E = input_map.front(); E; E = E.next()) {
- actions.push_back(E.key());
+ for (const KeyValue<StringName, Action> &E : input_map) {
+ actions.push_back(E.key);
}
return actions;
@@ -203,12 +203,12 @@ Array InputMap::_action_get_events(const StringName &p_action) {
}
const List<Ref<InputEvent>> *InputMap::action_get_events(const StringName &p_action) {
- const OrderedHashMap<StringName, Action>::Element E = input_map.find(p_action);
+ HashMap<StringName, Action>::Iterator E = input_map.find(p_action);
if (!E) {
return nullptr;
}
- return &E.get().inputs;
+ return &E->value.inputs;
}
bool InputMap::event_is_action(const Ref<InputEvent> &p_event, const StringName &p_action, bool p_exact_match) const {
@@ -216,7 +216,7 @@ bool InputMap::event_is_action(const Ref<InputEvent> &p_event, const StringName
}
bool InputMap::event_get_action_status(const Ref<InputEvent> &p_event, const StringName &p_action, bool p_exact_match, bool *r_pressed, float *r_strength, float *r_raw_strength) const {
- OrderedHashMap<StringName, Action>::Element E = input_map.find(p_action);
+ HashMap<StringName, Action>::Iterator E = input_map.find(p_action);
ERR_FAIL_COND_V_MSG(!E, false, suggest_actions(p_action));
Ref<InputEventAction> input_event_action = p_event;
@@ -235,11 +235,11 @@ bool InputMap::event_get_action_status(const Ref<InputEvent> &p_event, const Str
return input_event_action->get_action() == p_action;
}
- List<Ref<InputEvent>>::Element *event = _find_event(E.get(), p_event, p_exact_match, r_pressed, r_strength, r_raw_strength);
+ List<Ref<InputEvent>>::Element *event = _find_event(E->value, p_event, p_exact_match, r_pressed, r_strength, r_raw_strength);
return event != nullptr;
}
-const OrderedHashMap<StringName, InputMap::Action> &InputMap::get_action_map() const {
+const HashMap<StringName, InputMap::Action> &InputMap::get_action_map() const {
return input_map;
}
@@ -360,7 +360,7 @@ String InputMap::get_builtin_display_name(const String &p_name) const {
return p_name;
}
-const OrderedHashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins() {
+const HashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins() {
// Return cache if it has already been built.
if (default_builtin_cache.size()) {
return default_builtin_cache;
@@ -686,19 +686,19 @@ const OrderedHashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins() {
return default_builtin_cache;
}
-const OrderedHashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins_with_feature_overrides_applied() {
+const HashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins_with_feature_overrides_applied() {
if (default_builtin_with_overrides_cache.size() > 0) {
return default_builtin_with_overrides_cache;
}
- OrderedHashMap<String, List<Ref<InputEvent>>> builtins = get_builtins();
+ HashMap<String, List<Ref<InputEvent>>> builtins = get_builtins();
// Get a list of all built in inputs which are valid overrides for the OS
// Key = builtin name (e.g. ui_accept)
// Value = override/feature names (e.g. macos, if it was defined as "ui_accept.macos" and the platform supports that feature)
Map<String, Vector<String>> builtins_with_overrides;
- for (OrderedHashMap<String, List<Ref<InputEvent>>>::Element E = builtins.front(); E; E = E.next()) {
- String fullname = E.key();
+ for (const KeyValue<String, List<Ref<InputEvent>>> &E : builtins) {
+ String fullname = E.key;
Vector<String> split = fullname.split(".");
String name = split[0];
@@ -709,8 +709,8 @@ const OrderedHashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins_with
}
}
- for (OrderedHashMap<String, List<Ref<InputEvent>>>::Element E = builtins.front(); E; E = E.next()) {
- String fullname = E.key();
+ for (const KeyValue<String, List<Ref<InputEvent>>> &E : builtins) {
+ String fullname = E.key;
Vector<String> split = fullname.split(".");
String name = split[0];
@@ -726,22 +726,22 @@ const OrderedHashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins_with
continue;
}
- default_builtin_with_overrides_cache.insert(name, E.value());
+ default_builtin_with_overrides_cache.insert(name, E.value);
}
return default_builtin_with_overrides_cache;
}
void InputMap::load_default() {
- OrderedHashMap<String, List<Ref<InputEvent>>> builtins = get_builtins_with_feature_overrides_applied();
+ HashMap<String, List<Ref<InputEvent>>> builtins = get_builtins_with_feature_overrides_applied();
- for (OrderedHashMap<String, List<Ref<InputEvent>>>::Element E = builtins.front(); E; E = E.next()) {
- String name = E.key();
+ for (const KeyValue<String, List<Ref<InputEvent>>> &E : builtins) {
+ String name = E.key;
add_action(name);
- List<Ref<InputEvent>> inputs = E.get();
- for (List<Ref<InputEvent>>::Element *I = inputs.front(); I; I = I->next()) {
+ const List<Ref<InputEvent>> &inputs = E.value;
+ for (const List<Ref<InputEvent>>::Element *I = inputs.front(); I; I = I->next()) {
Ref<InputEventKey> iek = I->get();
// For the editor, only add keyboard actions.
diff --git a/core/input/input_map.h b/core/input/input_map.h
index 79b4d1038f..2400a4a3f7 100644
--- a/core/input/input_map.h
+++ b/core/input/input_map.h
@@ -34,7 +34,7 @@
#include "core/input/input_event.h"
#include "core/object/class_db.h"
#include "core/object/object.h"
-#include "core/templates/ordered_hash_map.h"
+#include "core/templates/hash_map.h"
class InputMap : public Object {
GDCLASS(InputMap, Object);
@@ -54,9 +54,9 @@ public:
private:
static InputMap *singleton;
- mutable OrderedHashMap<StringName, Action> input_map;
- OrderedHashMap<String, List<Ref<InputEvent>>> default_builtin_cache;
- OrderedHashMap<String, List<Ref<InputEvent>>> default_builtin_with_overrides_cache;
+ mutable HashMap<StringName, Action> input_map;
+ HashMap<String, List<Ref<InputEvent>>> default_builtin_cache;
+ HashMap<String, List<Ref<InputEvent>>> default_builtin_with_overrides_cache;
List<Ref<InputEvent>>::Element *_find_event(Action &p_action, const Ref<InputEvent> &p_event, bool p_exact_match = false, bool *r_pressed = nullptr, float *r_strength = nullptr, float *r_raw_strength = nullptr) const;
@@ -85,7 +85,7 @@ public:
bool event_is_action(const Ref<InputEvent> &p_event, const StringName &p_action, bool p_exact_match = false) const;
bool event_get_action_status(const Ref<InputEvent> &p_event, const StringName &p_action, bool p_exact_match = false, bool *r_pressed = nullptr, float *r_strength = nullptr, float *r_raw_strength = nullptr) const;
- const OrderedHashMap<StringName, Action> &get_action_map() const;
+ const HashMap<StringName, Action> &get_action_map() const;
void load_from_project_settings();
void load_default();
@@ -93,8 +93,8 @@ public:
String get_builtin_display_name(const String &p_name) const;
// Use an Ordered Map so insertion order is preserved. We want the elements to be 'grouped' somewhat.
- const OrderedHashMap<String, List<Ref<InputEvent>>> &get_builtins();
- const OrderedHashMap<String, List<Ref<InputEvent>>> &get_builtins_with_feature_overrides_applied();
+ const HashMap<String, List<Ref<InputEvent>>> &get_builtins();
+ const HashMap<String, List<Ref<InputEvent>>> &get_builtins_with_feature_overrides_applied();
InputMap();
~InputMap();
diff --git a/core/io/config_file.cpp b/core/io/config_file.cpp
index bc24cac955..dd0191f43f 100644
--- a/core/io/config_file.cpp
+++ b/core/io/config_file.cpp
@@ -73,7 +73,7 @@ void ConfigFile::set_value(const String &p_section, const String &p_key, const V
} else {
if (!values.has(p_section)) {
- values[p_section] = OrderedHashMap<String, Variant>();
+ values[p_section] = HashMap<String, Variant>();
}
values[p_section][p_key] = p_value;
@@ -102,16 +102,16 @@ bool ConfigFile::has_section_key(const String &p_section, const String &p_key) c
}
void ConfigFile::get_sections(List<String> *r_sections) const {
- for (OrderedHashMap<String, OrderedHashMap<String, Variant>>::ConstElement E = values.front(); E; E = E.next()) {
- r_sections->push_back(E.key());
+ for (const KeyValue<String, HashMap<String, Variant>> &E : values) {
+ r_sections->push_back(E.key);
}
}
void ConfigFile::get_section_keys(const String &p_section, List<String> *r_keys) const {
ERR_FAIL_COND_MSG(!values.has(p_section), vformat("Cannot get keys from nonexistent section \"%s\".", p_section));
- for (OrderedHashMap<String, Variant>::ConstElement E = values[p_section].front(); E; E = E.next()) {
- r_keys->push_back(E.key());
+ for (const KeyValue<String, Variant> &E : values[p_section]) {
+ r_keys->push_back(E.key);
}
}
@@ -174,18 +174,21 @@ Error ConfigFile::save_encrypted_pass(const String &p_path, const String &p_pass
}
Error ConfigFile::_internal_save(Ref<FileAccess> file) {
- for (OrderedHashMap<String, OrderedHashMap<String, Variant>>::Element E = values.front(); E; E = E.next()) {
- if (E != values.front()) {
+ bool first = true;
+ for (const KeyValue<String, HashMap<String, Variant>> &E : values) {
+ if (first) {
+ first = false;
+ } else {
file->store_string("\n");
}
- if (!E.key().is_empty()) {
- file->store_string("[" + E.key() + "]\n\n");
+ if (!E.key.is_empty()) {
+ file->store_string("[" + E.key + "]\n\n");
}
- for (OrderedHashMap<String, Variant>::Element F = E.get().front(); F; F = F.next()) {
+ for (const KeyValue<String, Variant> &F : E.value) {
String vstr;
- VariantWriter::write_to_string(F.get(), vstr);
- file->store_string(F.key().property_name_encode() + "=" + vstr + "\n");
+ VariantWriter::write_to_string(F.value, vstr);
+ file->store_string(F.key.property_name_encode() + "=" + vstr + "\n");
}
}
diff --git a/core/io/config_file.h b/core/io/config_file.h
index 7a52b0e16a..3b07ec52f5 100644
--- a/core/io/config_file.h
+++ b/core/io/config_file.h
@@ -33,13 +33,13 @@
#include "core/io/file_access.h"
#include "core/object/ref_counted.h"
-#include "core/templates/ordered_hash_map.h"
+#include "core/templates/hash_map.h"
#include "core/variant/variant_parser.h"
class ConfigFile : public RefCounted {
GDCLASS(ConfigFile, RefCounted);
- OrderedHashMap<String, OrderedHashMap<String, Variant>> values;
+ HashMap<String, HashMap<String, Variant>> values;
PackedStringArray _get_sections() const;
PackedStringArray _get_section_keys(const String &p_section) const;
diff --git a/core/io/image.cpp b/core/io/image.cpp
index 661a9f7177..671a000e2c 100644
--- a/core/io/image.cpp
+++ b/core/io/image.cpp
@@ -436,7 +436,7 @@ static void _convert(int p_width, int p_height, const uint8_t *p_src, uint8_t *p
const uint8_t *rofs = &p_src[((y * p_width) + x) * (read_bytes + (read_alpha ? 1 : 0))];
uint8_t *wofs = &p_dst[((y * p_width) + x) * (write_bytes + (write_alpha ? 1 : 0))];
- uint8_t rgba[4];
+ uint8_t rgba[4] = { 0, 0, 0, 255 };
if (read_gray) {
rgba[0] = rofs[0];
@@ -454,7 +454,7 @@ static void _convert(int p_width, int p_height, const uint8_t *p_src, uint8_t *p
if (write_gray) {
//TODO: not correct grayscale, should use fixed point version of actual weights
- wofs[0] = uint8_t((uint16_t(rofs[0]) + uint16_t(rofs[1]) + uint16_t(rofs[2])) / 3);
+ wofs[0] = uint8_t((uint16_t(rgba[0]) + uint16_t(rgba[1]) + uint16_t(rgba[2])) / 3);
} else {
for (uint32_t i = 0; i < write_bytes; i++) {
wofs[i] = rgba[i];
diff --git a/core/io/missing_resource.cpp b/core/io/missing_resource.cpp
index 7aae6c6f0a..29814cdeb3 100644
--- a/core/io/missing_resource.cpp
+++ b/core/io/missing_resource.cpp
@@ -53,8 +53,8 @@ bool MissingResource::_get(const StringName &p_name, Variant &r_ret) const {
}
void MissingResource::_get_property_list(List<PropertyInfo> *p_list) const {
- for (OrderedHashMap<StringName, Variant>::ConstElement E = properties.front(); E; E = E.next()) {
- p_list->push_back(PropertyInfo(E.value().get_type(), E.key()));
+ for (const KeyValue<StringName, Variant> &E : properties) {
+ p_list->push_back(PropertyInfo(E.value.get_type(), E.key));
}
}
diff --git a/core/io/missing_resource.h b/core/io/missing_resource.h
index e87efe1a98..6536a4119b 100644
--- a/core/io/missing_resource.h
+++ b/core/io/missing_resource.h
@@ -38,7 +38,7 @@
class MissingResource : public Resource {
GDCLASS(MissingResource, Resource)
- OrderedHashMap<StringName, Variant> properties;
+ HashMap<StringName, Variant> properties;
String original_class;
bool recording_properties = false;
diff --git a/core/io/resource.cpp b/core/io/resource.cpp
index e6535c67a4..e81382b72e 100644
--- a/core/io/resource.cpp
+++ b/core/io/resource.cpp
@@ -478,10 +478,8 @@ void ResourceCache::clear() {
if (resources.size()) {
ERR_PRINT("Resources still in use at exit (run with --verbose for details).");
if (OS::get_singleton()->is_stdout_verbose()) {
- const String *K = nullptr;
- while ((K = resources.next(K))) {
- Resource *r = resources[*K];
- print_line(vformat("Resource still in use: %s (%s)", *K, r->get_class()));
+ for (const KeyValue<String, Resource *> &E : resources) {
+ print_line(vformat("Resource still in use: %s (%s)", E.key, E.value->get_class()));
}
}
}
@@ -516,10 +514,8 @@ Resource *ResourceCache::get(const String &p_path) {
void ResourceCache::get_cached_resources(List<Ref<Resource>> *p_resources) {
lock.read_lock();
- const String *K = nullptr;
- while ((K = resources.next(K))) {
- Resource *r = resources[*K];
- p_resources->push_back(Ref<Resource>(r));
+ for (KeyValue<String, Resource *> &E : resources) {
+ p_resources->push_back(Ref<Resource>(E.value));
}
lock.read_unlock();
}
@@ -544,9 +540,8 @@ void ResourceCache::dump(const char *p_file, bool p_short) {
ERR_FAIL_COND_MSG(f.is_null(), "Cannot create file at path '" + String::utf8(p_file) + "'.");
}
- const String *K = nullptr;
- while ((K = resources.next(K))) {
- Resource *r = resources[*K];
+ for (KeyValue<String, Resource *> &E : resources) {
+ Resource *r = E.value;
if (!type_count.has(r->get_class())) {
type_count[r->get_class()] = 0;
diff --git a/core/io/resource_format_binary.cpp b/core/io/resource_format_binary.cpp
index fbb4293961..3c854bbbe5 100644
--- a/core/io/resource_format_binary.cpp
+++ b/core/io/resource_format_binary.cpp
@@ -635,8 +635,6 @@ Error ResourceLoaderBinary::load() {
return error;
}
- int stage = 0;
-
for (int i = 0; i < external_resources.size(); i++) {
String path = external_resources[i].path;
@@ -674,8 +672,6 @@ Error ResourceLoaderBinary::load() {
}
}
}
-
- stage++;
}
for (int i = 0; i < internal_resources.size(); i++) {
@@ -700,7 +696,6 @@ Error ResourceLoaderBinary::load() {
Ref<Resource> cached = ResourceCache::get(path);
if (cached.is_valid()) {
//already loaded, don't do anything
- stage++;
error = OK;
internal_index_cache[path] = cached;
continue;
@@ -817,7 +812,6 @@ Error ResourceLoaderBinary::load() {
#ifdef TOOLS_ENABLED
res->set_edited(false);
#endif
- stage++;
if (progress) {
*progress = (i + 1) / float(internal_resources.size());
diff --git a/core/io/resource_uid.cpp b/core/io/resource_uid.cpp
index 515b7c710e..fc324a26da 100644
--- a/core/io/resource_uid.cpp
+++ b/core/io/resource_uid.cpp
@@ -149,12 +149,12 @@ Error ResourceUID::save_to_cache() {
cache_entries = 0;
- for (OrderedHashMap<ID, Cache>::Element E = unique_ids.front(); E; E = E.next()) {
- f->store_64(E.key());
- uint32_t s = E.get().cs.length();
+ for (KeyValue<ID, Cache> &E : unique_ids) {
+ f->store_64(E.key);
+ uint32_t s = E.value.cs.length();
f->store_32(s);
- f->store_buffer((const uint8_t *)E.get().cs.ptr(), s);
- E.get().saved_to_cache = true;
+ f->store_buffer((const uint8_t *)E.value.cs.ptr(), s);
+ E.value.saved_to_cache = true;
cache_entries++;
}
@@ -202,8 +202,8 @@ Error ResourceUID::update_cache() {
MutexLock l(mutex);
Ref<FileAccess> f;
- for (OrderedHashMap<ID, Cache>::Element E = unique_ids.front(); E; E = E.next()) {
- if (!E.get().saved_to_cache) {
+ for (KeyValue<ID, Cache> &E : unique_ids) {
+ if (!E.value.saved_to_cache) {
if (f.is_null()) {
f = FileAccess::open(get_cache_file(), FileAccess::READ_WRITE); //append
if (f.is_null()) {
@@ -211,11 +211,11 @@ Error ResourceUID::update_cache() {
}
f->seek_end();
}
- f->store_64(E.key());
- uint32_t s = E.get().cs.length();
+ f->store_64(E.key);
+ uint32_t s = E.value.cs.length();
f->store_32(s);
- f->store_buffer((const uint8_t *)E.get().cs.ptr(), s);
- E.get().saved_to_cache = true;
+ f->store_buffer((const uint8_t *)E.value.cs.ptr(), s);
+ E.value.saved_to_cache = true;
cache_entries++;
}
}
diff --git a/core/io/resource_uid.h b/core/io/resource_uid.h
index 0b7ffdf6d0..da42553cf5 100644
--- a/core/io/resource_uid.h
+++ b/core/io/resource_uid.h
@@ -33,7 +33,7 @@
#include "core/object/ref_counted.h"
#include "core/string/string_name.h"
-#include "core/templates/ordered_hash_map.h"
+#include "core/templates/hash_map.h"
class ResourceUID : public Object {
GDCLASS(ResourceUID, Object)
@@ -53,7 +53,7 @@ private:
bool saved_to_cache = false;
};
- OrderedHashMap<ID, Cache> unique_ids; //unique IDs and utf8 paths (less memory used)
+ HashMap<ID, Cache> unique_ids; //unique IDs and utf8 paths (less memory used)
static ResourceUID *singleton;
uint32_t cache_entries = 0;
diff --git a/core/io/zip_io.cpp b/core/io/zip_io.cpp
index 2cc844b628..e573e8de19 100644
--- a/core/io/zip_io.cpp
+++ b/core/io/zip_io.cpp
@@ -31,18 +31,19 @@
#include "zip_io.h"
void *zipio_open(voidpf opaque, const char *p_fname, int mode) {
- ZipIOData *zd = (ZipIOData *)opaque;
+ Ref<FileAccess> *fa = reinterpret_cast<Ref<FileAccess> *>(opaque);
+ ERR_FAIL_COND_V(fa == nullptr, nullptr);
String fname;
fname.parse_utf8(p_fname);
if (mode & ZLIB_FILEFUNC_MODE_WRITE) {
- zd->f = FileAccess::open(fname, FileAccess::WRITE);
+ (*fa) = FileAccess::open(fname, FileAccess::WRITE);
} else {
- zd->f = FileAccess::open(fname, FileAccess::READ);
+ (*fa) = FileAccess::open(fname, FileAccess::READ);
}
- if (zd->f.is_null()) {
+ if (fa->is_null()) {
return nullptr;
}
@@ -50,49 +51,66 @@ void *zipio_open(voidpf opaque, const char *p_fname, int mode) {
}
uLong zipio_read(voidpf opaque, voidpf stream, void *buf, uLong size) {
- ZipIOData *zd = (ZipIOData *)opaque;
- return zd->f->get_buffer((uint8_t *)buf, size);
+ Ref<FileAccess> *fa = reinterpret_cast<Ref<FileAccess> *>(opaque);
+ ERR_FAIL_COND_V(fa == nullptr, 0);
+ ERR_FAIL_COND_V(fa->is_null(), 0);
+
+ return (*fa)->get_buffer((uint8_t *)buf, size);
}
uLong zipio_write(voidpf opaque, voidpf stream, const void *buf, uLong size) {
- ZipIOData *zd = (ZipIOData *)opaque;
- zd->f->store_buffer((uint8_t *)buf, size);
+ Ref<FileAccess> *fa = reinterpret_cast<Ref<FileAccess> *>(opaque);
+ ERR_FAIL_COND_V(fa == nullptr, 0);
+ ERR_FAIL_COND_V(fa->is_null(), 0);
+
+ (*fa)->store_buffer((uint8_t *)buf, size);
return size;
}
long zipio_tell(voidpf opaque, voidpf stream) {
- ZipIOData *zd = (ZipIOData *)opaque;
- return zd->f->get_position();
+ Ref<FileAccess> *fa = reinterpret_cast<Ref<FileAccess> *>(opaque);
+ ERR_FAIL_COND_V(fa == nullptr, 0);
+ ERR_FAIL_COND_V(fa->is_null(), 0);
+
+ return (*fa)->get_position();
}
long zipio_seek(voidpf opaque, voidpf stream, uLong offset, int origin) {
- ZipIOData *zd = (ZipIOData *)opaque;
+ Ref<FileAccess> *fa = reinterpret_cast<Ref<FileAccess> *>(opaque);
+ ERR_FAIL_COND_V(fa == nullptr, 0);
+ ERR_FAIL_COND_V(fa->is_null(), 0);
uint64_t pos = offset;
switch (origin) {
case ZLIB_FILEFUNC_SEEK_CUR:
- pos = zd->f->get_position() + offset;
+ pos = (*fa)->get_position() + offset;
break;
case ZLIB_FILEFUNC_SEEK_END:
- pos = zd->f->get_length() + offset;
+ pos = (*fa)->get_length() + offset;
break;
default:
break;
}
- zd->f->seek(pos);
+ (*fa)->seek(pos);
return 0;
}
int zipio_close(voidpf opaque, voidpf stream) {
- ZipIOData *zd = (ZipIOData *)opaque;
- memdelete(zd);
+ Ref<FileAccess> *fa = reinterpret_cast<Ref<FileAccess> *>(opaque);
+ ERR_FAIL_COND_V(fa == nullptr, 0);
+ ERR_FAIL_COND_V(fa->is_null(), 0);
+
+ fa->unref();
return 0;
}
int zipio_testerror(voidpf opaque, voidpf stream) {
- ZipIOData *zd = (ZipIOData *)opaque;
- return (zd->f.is_valid() && zd->f->get_error() != OK) ? 1 : 0;
+ Ref<FileAccess> *fa = reinterpret_cast<Ref<FileAccess> *>(opaque);
+ ERR_FAIL_COND_V(fa == nullptr, 1);
+ ERR_FAIL_COND_V(fa->is_null(), 0);
+
+ return (fa->is_valid() && (*fa)->get_error() != OK) ? 1 : 0;
}
voidpf zipio_alloc(voidpf opaque, uInt items, uInt size) {
@@ -105,9 +123,9 @@ void zipio_free(voidpf opaque, voidpf address) {
memfree(address);
}
-zlib_filefunc_def zipio_create_io() {
+zlib_filefunc_def zipio_create_io(Ref<FileAccess> *p_data) {
zlib_filefunc_def io;
- io.opaque = (void *)memnew(ZipIOData);
+ io.opaque = (void *)p_data;
io.zopen_file = zipio_open;
io.zread_file = zipio_read;
io.zwrite_file = zipio_write;
diff --git a/core/io/zip_io.h b/core/io/zip_io.h
index 3bcd1f830d..f137bd2bbf 100644
--- a/core/io/zip_io.h
+++ b/core/io/zip_io.h
@@ -39,10 +39,6 @@
#include "thirdparty/minizip/unzip.h"
#include "thirdparty/minizip/zip.h"
-struct ZipIOData {
- Ref<FileAccess> f;
-};
-
void *zipio_open(voidpf opaque, const char *p_fname, int mode);
uLong zipio_read(voidpf opaque, voidpf stream, void *buf, uLong size);
uLong zipio_write(voidpf opaque, voidpf stream, const void *buf, uLong size);
@@ -57,6 +53,6 @@ int zipio_testerror(voidpf opaque, voidpf stream);
voidpf zipio_alloc(voidpf opaque, uInt items, uInt size);
void zipio_free(voidpf opaque, voidpf address);
-zlib_filefunc_def zipio_create_io();
+zlib_filefunc_def zipio_create_io(Ref<FileAccess> *p_data);
#endif // ZIP_IO_H
diff --git a/core/math/delaunay_3d.h b/core/math/delaunay_3d.h
index 7ad5f76645..f8a10ec87e 100644
--- a/core/math/delaunay_3d.h
+++ b/core/math/delaunay_3d.h
@@ -323,7 +323,6 @@ public:
E = N;
}
- uint32_t good_triangles = 0;
for (uint32_t j = 0; j < triangles.size(); j++) {
if (triangles[j].bad) {
continue;
@@ -360,11 +359,8 @@ public:
}
}
}
-
- good_triangles++;
}
- //print_line("at point " + itos(i) + "/" + itos(point_count) + " simplices added " + itos(good_triangles) + "/" + itos(simplex_list.size()) + " - triangles: " + itos(triangles.size()));
triangles.clear();
triangles_inserted.clear();
}
diff --git a/core/math/expression.cpp b/core/math/expression.cpp
index 9dd1257474..97dc175d94 100644
--- a/core/math/expression.cpp
+++ b/core/math/expression.cpp
@@ -155,7 +155,12 @@ Error Expression::_get_token(Token &r_token) {
return OK;
}
case '*': {
- r_token.type = TK_OP_MUL;
+ if (expression[str_ofs] == '*') {
+ r_token.type = TK_OP_POW;
+ str_ofs++;
+ } else {
+ r_token.type = TK_OP_MUL;
+ }
return OK;
}
case '%': {
@@ -542,6 +547,7 @@ const char *Expression::token_name[TK_MAX] = {
"OP MUL",
"OP DIV",
"OP MOD",
+ "OP POW",
"OP SHIFT LEFT",
"OP SHIFT RIGHT",
"OP BIT AND",
@@ -1013,6 +1019,9 @@ Expression::ENode *Expression::_parse_expression() {
case TK_OP_MOD:
op = Variant::OP_MODULE;
break;
+ case TK_OP_POW:
+ op = Variant::OP_POWER;
+ break;
case TK_OP_SHIFT_LEFT:
op = Variant::OP_SHIFT_LEFT;
break;
@@ -1066,35 +1075,38 @@ Expression::ENode *Expression::_parse_expression() {
bool unary = false;
switch (expression[i].op) {
- case Variant::OP_BIT_NEGATE:
+ case Variant::OP_POWER:
priority = 0;
+ break;
+ case Variant::OP_BIT_NEGATE:
+ priority = 1;
unary = true;
break;
case Variant::OP_NEGATE:
- priority = 1;
+ priority = 2;
unary = true;
break;
case Variant::OP_MULTIPLY:
case Variant::OP_DIVIDE:
case Variant::OP_MODULE:
- priority = 2;
+ priority = 3;
break;
case Variant::OP_ADD:
case Variant::OP_SUBTRACT:
- priority = 3;
+ priority = 4;
break;
case Variant::OP_SHIFT_LEFT:
case Variant::OP_SHIFT_RIGHT:
- priority = 4;
+ priority = 5;
break;
case Variant::OP_BIT_AND:
- priority = 5;
+ priority = 6;
break;
case Variant::OP_BIT_XOR:
- priority = 6;
+ priority = 7;
break;
case Variant::OP_BIT_OR:
- priority = 7;
+ priority = 8;
break;
case Variant::OP_LESS:
case Variant::OP_LESS_EQUAL:
@@ -1102,20 +1114,20 @@ Expression::ENode *Expression::_parse_expression() {
case Variant::OP_GREATER_EQUAL:
case Variant::OP_EQUAL:
case Variant::OP_NOT_EQUAL:
- priority = 8;
+ priority = 9;
break;
case Variant::OP_IN:
- priority = 10;
+ priority = 11;
break;
case Variant::OP_NOT:
- priority = 11;
+ priority = 12;
unary = true;
break;
case Variant::OP_AND:
- priority = 12;
+ priority = 13;
break;
case Variant::OP_OR:
- priority = 13;
+ priority = 14;
break;
default: {
_set_error("Parser bug, invalid operator in expression: " + itos(expression[i].op));
diff --git a/core/math/expression.h b/core/math/expression.h
index d43cc4091a..6ea3c1611f 100644
--- a/core/math/expression.h
+++ b/core/math/expression.h
@@ -85,6 +85,7 @@ private:
TK_OP_MUL,
TK_OP_DIV,
TK_OP_MOD,
+ TK_OP_POW,
TK_OP_SHIFT_LEFT,
TK_OP_SHIFT_RIGHT,
TK_OP_BIT_AND,
diff --git a/core/math/quick_hull.cpp b/core/math/quick_hull.cpp
index 8e87d44b7f..3614bfadf8 100644
--- a/core/math/quick_hull.cpp
+++ b/core/math/quick_hull.cpp
@@ -384,7 +384,6 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry3D::MeshData &r_
if (O->get().plane.is_equal_approx(f.plane)) {
//merge and delete edge and contiguous face, while repointing edges (uuugh!)
int ois = O->get().indices.size();
- int merged = 0;
for (int j = 0; j < ois; j++) {
//search a
@@ -399,7 +398,6 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry3D::MeshData &r_
if (idx != a) {
f.indices.insert(i + 1, idx);
i++;
- merged++;
}
Edge e2(idx, idxn);
diff --git a/core/object/class_db.cpp b/core/object/class_db.cpp
index 593f27b7cf..d0fcde832b 100644
--- a/core/object/class_db.cpp
+++ b/core/object/class_db.cpp
@@ -90,10 +90,8 @@ bool ClassDB::is_parent_class(const StringName &p_class, const StringName &p_inh
void ClassDB::get_class_list(List<StringName> *p_classes) {
OBJTYPE_RLOCK;
- const StringName *k = nullptr;
-
- while ((k = classes.next(k))) {
- p_classes->push_back(*k);
+ for (const KeyValue<StringName, ClassInfo> &E : classes) {
+ p_classes->push_back(E.key);
}
p_classes->sort();
@@ -102,11 +100,9 @@ void ClassDB::get_class_list(List<StringName> *p_classes) {
void ClassDB::get_inheriters_from_class(const StringName &p_class, List<StringName> *p_classes) {
OBJTYPE_RLOCK;
- const StringName *k = nullptr;
-
- while ((k = classes.next(k))) {
- if (*k != p_class && _is_parent_class(*k, p_class)) {
- p_classes->push_back(*k);
+ for (const KeyValue<StringName, ClassInfo> &E : classes) {
+ if (E.key != p_class && _is_parent_class(E.key, p_class)) {
+ p_classes->push_back(E.key);
}
}
}
@@ -114,11 +110,9 @@ void ClassDB::get_inheriters_from_class(const StringName &p_class, List<StringNa
void ClassDB::get_direct_inheriters_from_class(const StringName &p_class, List<StringName> *p_classes) {
OBJTYPE_RLOCK;
- const StringName *k = nullptr;
-
- while ((k = classes.next(k))) {
- if (*k != p_class && _get_parent_class(*k) == p_class) {
- p_classes->push_back(*k);
+ for (const KeyValue<StringName, ClassInfo> &E : classes) {
+ if (E.key != p_class && _get_parent_class(E.key) == p_class) {
+ p_classes->push_back(E.key);
}
}
}
@@ -172,17 +166,12 @@ uint64_t ClassDB::get_api_hash(APIType p_api) {
uint64_t hash = hash_djb2_one_64(HashMapHasherDefault::hash(VERSION_FULL_CONFIG));
- List<StringName> names;
-
- const StringName *k = nullptr;
+ List<StringName> class_list;
+ ClassDB::get_class_list(&class_list);
+ // Must be alphabetically sorted for hash to compute.
+ class_list.sort_custom<StringName::AlphCompare>();
- while ((k = classes.next(k))) {
- names.push_back(*k);
- }
- //must be alphabetically sorted for hash to compute
- names.sort_custom<StringName::AlphCompare>();
-
- for (const StringName &E : names) {
+ for (const StringName &E : class_list) {
ClassInfo *t = classes.getptr(E);
ERR_FAIL_COND_V_MSG(!t, 0, "Cannot get class '" + String(E) + "'.");
if (t->api != p_api || !t->exposed) {
@@ -195,10 +184,8 @@ uint64_t ClassDB::get_api_hash(APIType p_api) {
List<StringName> snames;
- k = nullptr;
-
- while ((k = t->method_map.next(k))) {
- String name = k->operator String();
+ for (const KeyValue<StringName, MethodBind *> &F : t->method_map) {
+ String name = F.key.operator String();
ERR_CONTINUE(name.is_empty());
@@ -206,7 +193,7 @@ uint64_t ClassDB::get_api_hash(APIType p_api) {
continue; // Ignore non-virtual methods that start with an underscore
}
- snames.push_back(*k);
+ snames.push_back(F.key);
}
snames.sort_custom<StringName::AlphCompare>();
@@ -241,10 +228,8 @@ uint64_t ClassDB::get_api_hash(APIType p_api) {
List<StringName> snames;
- k = nullptr;
-
- while ((k = t->constant_map.next(k))) {
- snames.push_back(*k);
+ for (const KeyValue<StringName, int> &F : t->constant_map) {
+ snames.push_back(F.key);
}
snames.sort_custom<StringName::AlphCompare>();
@@ -259,10 +244,8 @@ uint64_t ClassDB::get_api_hash(APIType p_api) {
List<StringName> snames;
- k = nullptr;
-
- while ((k = t->signal_map.next(k))) {
- snames.push_back(*k);
+ for (const KeyValue<StringName, MethodInfo> &F : t->signal_map) {
+ snames.push_back(F.key);
}
snames.sort_custom<StringName::AlphCompare>();
@@ -280,10 +263,8 @@ uint64_t ClassDB::get_api_hash(APIType p_api) {
List<StringName> snames;
- k = nullptr;
-
- while ((k = t->property_setget.next(k))) {
- snames.push_back(*k);
+ for (const KeyValue<StringName, PropertySetGet> &F : t->property_setget) {
+ snames.push_back(F.key);
}
snames.sort_custom<StringName::AlphCompare>();
@@ -474,10 +455,8 @@ void ClassDB::get_method_list(const StringName &p_class, List<MethodInfo> *p_met
#else
- const StringName *K = nullptr;
-
- while ((K = type->method_map.next(K))) {
- MethodBind *m = type->method_map[*K];
+ for (KeyValue<StringName, MethodBind *> &E : type->method_map) {
+ MethodBind *m = E.value;
MethodInfo minfo = info_from_bind(m);
p_methods->push_back(minfo);
}
@@ -603,10 +582,9 @@ void ClassDB::get_integer_constant_list(const StringName &p_class, List<String>
p_constants->push_back(E);
}
#else
- const StringName *K = nullptr;
- while ((K = type->constant_map.next(K))) {
- p_constants->push_back(*K);
+ for (const KeyValue<StringName, int> &E : type->constant_map) {
+ p_constants->push_back(E.key);
}
#endif
@@ -667,12 +645,11 @@ StringName ClassDB::get_integer_constant_enum(const StringName &p_class, const S
ClassInfo *type = classes.getptr(p_class);
while (type) {
- const StringName *k = nullptr;
- while ((k = type->enum_map.next(k))) {
- List<StringName> &constants_list = type->enum_map.get(*k);
+ for (KeyValue<StringName, List<StringName>> &E : type->enum_map) {
+ List<StringName> &constants_list = E.value;
const List<StringName>::Element *found = constants_list.find(p_name);
if (found) {
- return *k;
+ return E.key;
}
}
@@ -692,9 +669,8 @@ void ClassDB::get_enum_list(const StringName &p_class, List<StringName> *p_enums
ClassInfo *type = classes.getptr(p_class);
while (type) {
- const StringName *k = nullptr;
- while ((k = type->enum_map.next(k))) {
- p_enums->push_back(*k);
+ for (KeyValue<StringName, List<StringName>> &E : type->enum_map) {
+ p_enums->push_back(E.key);
}
if (p_no_inheritance) {
@@ -800,9 +776,8 @@ void ClassDB::get_signal_list(const StringName &p_class, List<MethodInfo> *p_sig
ClassInfo *check = type;
while (check) {
- const StringName *S = nullptr;
- while ((S = check->signal_map.next(S))) {
- p_signals->push_back(check->signal_map[*S]);
+ for (KeyValue<StringName, MethodInfo> &E : check->signal_map) {
+ p_signals->push_back(E.value);
}
if (p_no_inheritance) {
@@ -1397,10 +1372,8 @@ void ClassDB::add_resource_base_extension(const StringName &p_extension, const S
}
void ClassDB::get_resource_base_extensions(List<String> *p_extensions) {
- const StringName *K = nullptr;
-
- while ((K = resource_base_extensions.next(K))) {
- p_extensions->push_back(*K);
+ for (const KeyValue<StringName, StringName> &E : resource_base_extensions) {
+ p_extensions->push_back(E.key);
}
}
@@ -1409,12 +1382,9 @@ bool ClassDB::is_resource_extension(const StringName &p_extension) {
}
void ClassDB::get_extensions_for_type(const StringName &p_class, List<String> *p_extensions) {
- const StringName *K = nullptr;
-
- while ((K = resource_base_extensions.next(K))) {
- StringName cmp = resource_base_extensions[*K];
- if (is_parent_class(p_class, cmp) || is_parent_class(cmp, p_class)) {
- p_extensions->push_back(*K);
+ for (const KeyValue<StringName, StringName> &E : resource_base_extensions) {
+ if (is_parent_class(p_class, E.value) || is_parent_class(E.value, p_class)) {
+ p_extensions->push_back(E.key);
}
}
}
@@ -1556,14 +1526,11 @@ void ClassDB::cleanup_defaults() {
void ClassDB::cleanup() {
//OBJTYPE_LOCK; hah not here
- const StringName *k = nullptr;
-
- while ((k = classes.next(k))) {
- ClassInfo &ti = classes[*k];
+ for (KeyValue<StringName, ClassInfo> &E : classes) {
+ ClassInfo &ti = E.value;
- const StringName *m = nullptr;
- while ((m = ti.method_map.next(m))) {
- memdelete(ti.method_map[*m]);
+ for (KeyValue<StringName, MethodBind *> &F : ti.method_map) {
+ memdelete(F.value);
}
}
classes.clear();
diff --git a/core/object/object.cpp b/core/object/object.cpp
index 2b62041533..797eecd312 100644
--- a/core/object/object.cpp
+++ b/core/object/object.cpp
@@ -417,9 +417,9 @@ void Object::set(const StringName &p_name, const Variant &p_value, bool *r_valid
return;
} else {
- OrderedHashMap<StringName, Variant>::Element *E = metadata_properties.getptr(p_name);
- if (E) {
- E->get() = p_value;
+ Variant **V = metadata_properties.getptr(p_name);
+ if (V) {
+ **V = p_value;
if (r_valid) {
*r_valid = true;
}
@@ -508,10 +508,10 @@ Variant Object::get(const StringName &p_name, bool *r_valid) const {
return ret;
}
- const OrderedHashMap<StringName, Variant>::Element *E = metadata_properties.getptr(p_name);
+ const Variant *const *V = metadata_properties.getptr(p_name);
- if (E) {
- ret = E->get();
+ if (V) {
+ ret = **V;
if (r_valid) {
*r_valid = true;
}
@@ -666,9 +666,9 @@ void Object::get_property_list(List<PropertyInfo> *p_list, bool p_reversed) cons
script_instance->get_property_list(p_list);
}
- for (OrderedHashMap<StringName, Variant>::ConstElement K = metadata.front(); K; K = K.next()) {
- PropertyInfo pi = PropertyInfo(K.value().get_type(), "metadata/" + K.key().operator String());
- if (K.value().get_type() == Variant::OBJECT) {
+ for (const KeyValue<StringName, Variant> &K : metadata) {
+ PropertyInfo pi = PropertyInfo(K.value.get_type(), "metadata/" + K.key.operator String());
+ if (K.value.get_type() == Variant::OBJECT) {
pi.hint = PROPERTY_HINT_RESOURCE_TYPE;
pi.hint_string = "Resource";
}
@@ -944,13 +944,13 @@ void Object::set_meta(const StringName &p_name, const Variant &p_value) {
return;
}
- OrderedHashMap<StringName, Variant>::Element E = metadata.find(p_name);
+ HashMap<StringName, Variant>::Iterator E = metadata.find(p_name);
if (E) {
- E.value() = p_value;
+ E->value = p_value;
} else {
ERR_FAIL_COND(!p_name.operator String().is_valid_identifier());
- E = metadata.insert(p_name, p_value);
- metadata_properties["metadata/" + p_name.operator String()] = E;
+ Variant *V = &metadata.insert(p_name, p_value)->value;
+ metadata_properties["metadata/" + p_name.operator String()] = V;
notify_property_list_changed();
}
}
@@ -993,16 +993,16 @@ Array Object::_get_method_list_bind() const {
Vector<StringName> Object::_get_meta_list_bind() const {
Vector<StringName> _metaret;
- for (OrderedHashMap<StringName, Variant>::ConstElement K = metadata.front(); K; K = K.next()) {
- _metaret.push_back(K.key());
+ for (const KeyValue<StringName, Variant> &K : metadata) {
+ _metaret.push_back(K.key);
}
return _metaret;
}
void Object::get_meta_list(List<StringName> *p_list) const {
- for (OrderedHashMap<StringName, Variant>::ConstElement K = metadata.front(); K; K = K.next()) {
- p_list->push_back(K.key());
+ for (const KeyValue<StringName, Variant> &K : metadata) {
+ p_list->push_back(K.key);
}
}
@@ -1250,21 +1250,18 @@ void Object::get_signal_list(List<MethodInfo> *p_signals) const {
ClassDB::get_signal_list(get_class_name(), p_signals);
//find maybe usersignals?
- const StringName *S = nullptr;
- while ((S = signal_map.next(S))) {
- if (!signal_map[*S].user.name.is_empty()) {
+ for (const KeyValue<StringName, SignalData> &E : signal_map) {
+ if (!E.value.user.name.is_empty()) {
//user signal
- p_signals->push_back(signal_map[*S].user);
+ p_signals->push_back(E.value.user);
}
}
}
void Object::get_all_signal_connections(List<Connection> *p_connections) const {
- const StringName *S = nullptr;
-
- while ((S = signal_map.next(S))) {
- const SignalData *s = &signal_map[*S];
+ for (const KeyValue<StringName, SignalData> &E : signal_map) {
+ const SignalData *s = &E.value;
for (int i = 0; i < s->slot_map.size(); i++) {
p_connections->push_back(s->slot_map.getv(i).conn);
@@ -1285,10 +1282,9 @@ void Object::get_signal_connection_list(const StringName &p_signal, List<Connect
int Object::get_persistent_signal_connection_count() const {
int count = 0;
- const StringName *S = nullptr;
- while ((S = signal_map.next(S))) {
- const SignalData *s = &signal_map[*S];
+ for (const KeyValue<StringName, SignalData> &E : signal_map) {
+ const SignalData *s = &E.value;
for (int i = 0; i < s->slot_map.size(); i++) {
if (s->slot_map.getv(i).conn.flags & CONNECT_PERSIST) {
@@ -1866,15 +1862,15 @@ Object::~Object() {
_extension_instance = nullptr;
}
- const StringName *S = nullptr;
-
if (_emitting) {
//@todo this may need to actually reach the debugger prioritarily somehow because it may crash before
ERR_PRINT("Object " + to_string() + " was freed or unreferenced while a signal is being emitted from it. Try connecting to the signal using 'CONNECT_DEFERRED' flag, or use queue_free() to free the object (if this object is a Node) to avoid this error and potential crashes.");
}
- while ((S = signal_map.next(nullptr))) {
- SignalData *s = &signal_map[*S];
+ while (signal_map.size()) {
+ // Avoid regular iteration so erasing is safe.
+ KeyValue<StringName, SignalData> &E = *signal_map.begin();
+ SignalData *s = &E.value;
//brute force disconnect for performance
int slot_count = s->slot_map.size();
@@ -1884,7 +1880,7 @@ Object::~Object() {
slot_list[i].value.conn.callable.get_object()->connections.erase(slot_list[i].value.cE);
}
- signal_map.erase(*S);
+ signal_map.erase(E.key);
}
//signals from nodes that connect to this node
diff --git a/core/object/object.h b/core/object/object.h
index c3e3c68b59..00cb73593b 100644
--- a/core/object/object.h
+++ b/core/object/object.h
@@ -39,7 +39,6 @@
#include "core/templates/hash_map.h"
#include "core/templates/list.h"
#include "core/templates/map.h"
-#include "core/templates/ordered_hash_map.h"
#include "core/templates/safe_refcount.h"
#include "core/templates/set.h"
#include "core/templates/vmap.h"
@@ -515,8 +514,8 @@ private:
#endif
ScriptInstance *script_instance = nullptr;
Variant script; // Reference does not exist yet, store it in a Variant.
- OrderedHashMap<StringName, Variant> metadata;
- HashMap<StringName, OrderedHashMap<StringName, Variant>::Element> metadata_properties;
+ HashMap<StringName, Variant> metadata;
+ HashMap<StringName, Variant *> metadata_properties;
mutable StringName _class_name;
mutable const StringName *_class_ptr = nullptr;
diff --git a/core/object/script_language.cpp b/core/object/script_language.cpp
index a5d25bf533..c1036e3413 100644
--- a/core/object/script_language.cpp
+++ b/core/object/script_language.cpp
@@ -253,10 +253,9 @@ StringName ScriptServer::get_global_class_native_base(const String &p_class) {
}
void ScriptServer::get_global_class_list(List<StringName> *r_global_classes) {
- const StringName *K = nullptr;
List<StringName> classes;
- while ((K = global_classes.next(K))) {
- classes.push_back(*K);
+ for (const KeyValue<StringName, GlobalScriptClass> &E : global_classes) {
+ classes.push_back(E.key);
}
classes.sort_custom<StringName::AlphCompare>();
for (const StringName &E : classes) {
diff --git a/core/object/script_language.h b/core/object/script_language.h
index f58ef45743..bd87427eaf 100644
--- a/core/object/script_language.h
+++ b/core/object/script_language.h
@@ -346,6 +346,7 @@ public:
LOOKUP_RESULT_CLASS_CONSTANT,
LOOKUP_RESULT_CLASS_PROPERTY,
LOOKUP_RESULT_CLASS_METHOD,
+ LOOKUP_RESULT_CLASS_SIGNAL,
LOOKUP_RESULT_CLASS_ENUM,
LOOKUP_RESULT_CLASS_TBD_GLOBALSCOPE,
LOOKUP_RESULT_MAX
diff --git a/core/object/script_language_extension.cpp b/core/object/script_language_extension.cpp
index 21d7685674..5af79bbea3 100644
--- a/core/object/script_language_extension.cpp
+++ b/core/object/script_language_extension.cpp
@@ -157,6 +157,7 @@ void ScriptLanguageExtension::_bind_methods() {
BIND_ENUM_CONSTANT(LOOKUP_RESULT_CLASS_CONSTANT);
BIND_ENUM_CONSTANT(LOOKUP_RESULT_CLASS_PROPERTY);
BIND_ENUM_CONSTANT(LOOKUP_RESULT_CLASS_METHOD);
+ BIND_ENUM_CONSTANT(LOOKUP_RESULT_CLASS_SIGNAL);
BIND_ENUM_CONSTANT(LOOKUP_RESULT_CLASS_ENUM);
BIND_ENUM_CONSTANT(LOOKUP_RESULT_CLASS_TBD_GLOBALSCOPE);
BIND_ENUM_CONSTANT(LOOKUP_RESULT_MAX);
diff --git a/core/os/memory.h b/core/os/memory.h
index baa96ef3e9..42ba9634e2 100644
--- a/core/os/memory.h
+++ b/core/os/memory.h
@@ -197,4 +197,12 @@ struct _GlobalNilClass {
static _GlobalNil _nil;
};
+template <class T>
+class DefaultTypedAllocator {
+public:
+ template <class... Args>
+ _FORCE_INLINE_ T *new_allocation(const Args &&...p_args) { return memnew(T(p_args...)); }
+ _FORCE_INLINE_ void delete_allocation(T *p_allocation) { memdelete(p_allocation); }
+};
+
#endif // MEMORY_H
diff --git a/core/os/os.h b/core/os/os.h
index 5eac77d634..157b8ab992 100644
--- a/core/os/os.h
+++ b/core/os/os.h
@@ -82,11 +82,6 @@ public:
RENDER_SEPARATE_THREAD
};
- enum RenderMainThreadMode {
- RENDER_MAIN_THREAD_ONLY,
- RENDER_ANY_THREAD,
- };
-
protected:
friend class Main;
// Needed by tests to setup command-line args.
@@ -94,7 +89,6 @@ protected:
HasServerFeatureCallback has_server_feature_callback = nullptr;
RenderThreadMode _render_thread_mode = RENDER_THREAD_SAFE;
- RenderMainThreadMode _render_main_thread_mode = RENDER_ANY_THREAD;
// Functions used by Main to initialize/deinitialize the OS.
void add_logger(Logger *p_logger);
@@ -258,8 +252,6 @@ public:
virtual uint64_t get_free_static_memory() const;
RenderThreadMode get_render_thread_mode() const { return _render_thread_mode; }
- RenderMainThreadMode get_render_main_thread_mode() const { return _render_main_thread_mode; }
- void set_render_main_thread_mode(RenderMainThreadMode p_thread_mode) { _render_main_thread_mode = p_thread_mode; }
virtual String get_locale() const;
String get_locale_language() const;
diff --git a/core/string/translation_po.cpp b/core/string/translation_po.cpp
index 3f94e064ec..fa656b634d 100644
--- a/core/string/translation_po.cpp
+++ b/core/string/translation_po.cpp
@@ -70,21 +70,14 @@ Dictionary TranslationPO::_get_messages() const {
Dictionary d;
- List<StringName> context_l;
- translation_map.get_key_list(&context_l);
- for (const StringName &ctx : context_l) {
- const HashMap<StringName, Vector<StringName>> &id_str_map = translation_map[ctx];
-
+ for (const KeyValue<StringName, HashMap<StringName, Vector<StringName>>> &E : translation_map) {
Dictionary d2;
- List<StringName> id_l;
- id_str_map.get_key_list(&id_l);
- // Save list of id and strs associated with a context in a temporary dictionary.
- for (List<StringName>::Element *E2 = id_l.front(); E2; E2 = E2->next()) {
- StringName id = E2->get();
- d2[id] = id_str_map[id];
+
+ for (const KeyValue<StringName, Vector<StringName>> &E2 : E.value) {
+ d2[E2.key] = E2.value;
}
- d[ctx] = d2;
+ d[E.key] = d2;
}
return d;
@@ -274,31 +267,24 @@ void TranslationPO::get_message_list(List<StringName> *r_messages) const {
// OptimizedTranslation uses this function to get the list of msgid.
// Return all the keys of translation_map under "" context.
- List<StringName> context_l;
- translation_map.get_key_list(&context_l);
-
- for (const StringName &E : context_l) {
- if (String(E) != "") {
+ for (const KeyValue<StringName, HashMap<StringName, Vector<StringName>>> &E : translation_map) {
+ if (E.key != StringName()) {
continue;
}
- List<StringName> msgid_l;
- translation_map[E].get_key_list(&msgid_l);
-
- for (List<StringName>::Element *E2 = msgid_l.front(); E2; E2 = E2->next()) {
- r_messages->push_back(E2->get());
+ for (const KeyValue<StringName, Vector<StringName>> &E2 : E.value) {
+ r_messages->push_back(E2.key);
}
}
}
int TranslationPO::get_message_count() const {
- List<StringName> context_l;
- translation_map.get_key_list(&context_l);
-
int count = 0;
- for (const StringName &E : context_l) {
- count += translation_map[E].size();
+
+ for (const KeyValue<StringName, HashMap<StringName, Vector<StringName>>> &E : translation_map) {
+ count += E.value.size();
}
+
return count;
}
diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp
index 5d998d22d4..015dfbc651 100644
--- a/core/string/ustring.cpp
+++ b/core/string/ustring.cpp
@@ -4779,6 +4779,17 @@ Vector<uint8_t> String::to_utf32_buffer() const {
}
#ifdef TOOLS_ENABLED
+/**
+ * "Tools TRanslate". Performs string replacement for internationalization
+ * within the editor. A translation context can optionally be specified to
+ * disambiguate between identical source strings in translations. When
+ * placeholders are desired, use `vformat(TTR("Example: %s"), some_string)`.
+ * If a string mentions a quantity (and may therefore need a dynamic plural form),
+ * use `TTRN()` instead of `TTR()`.
+ *
+ * NOTE: Only use `TTR()` in editor-only code (typically within the `editor/` folder).
+ * For translations that can be supplied by exported projects, use `RTR()` instead.
+ */
String TTR(const String &p_text, const String &p_context) {
if (TranslationServer::get_singleton()) {
return TranslationServer::get_singleton()->tool_translate(p_text, p_context);
@@ -4787,6 +4798,18 @@ String TTR(const String &p_text, const String &p_context) {
return p_text;
}
+/**
+ * "Tools TRanslate for N items". Performs string replacement for
+ * internationalization within the editor. A translation context can optionally
+ * be specified to disambiguate between identical source strings in
+ * translations. Use `TTR()` if the string doesn't need dynamic plural form.
+ * When placeholders are desired, use
+ * `vformat(TTRN("%d item", "%d items", some_integer), some_integer)`.
+ * The placeholder must be present in both strings to avoid run-time warnings in `vformat()`.
+ *
+ * NOTE: Only use `TTRN()` in editor-only code (typically within the `editor/` folder).
+ * For translations that can be supplied by exported projects, use `RTRN()` instead.
+ */
String TTRN(const String &p_text, const String &p_text_plural, int p_n, const String &p_context) {
if (TranslationServer::get_singleton()) {
return TranslationServer::get_singleton()->tool_translate_plural(p_text, p_text_plural, p_n, p_context);
@@ -4799,9 +4822,10 @@ String TTRN(const String &p_text, const String &p_text_plural, int p_n, const St
return p_text_plural;
}
-/* DTR and DTRN are used for the documentation, handling descriptions extracted
- * from the XML.
- * They also replace `$DOCS_URL` with the actual URL to the documentation's branch,
+/**
+ * "Docs TRanslate". Used for the editor class reference documentation,
+ * handling descriptions extracted from the XML.
+ * It also replaces `$DOCS_URL` with the actual URL to the documentation's branch,
* to allow dehardcoding it in the XML and doing proper substitutions everywhere.
*/
String DTR(const String &p_text, const String &p_context) {
@@ -4815,6 +4839,12 @@ String DTR(const String &p_text, const String &p_context) {
return text.replace("$DOCS_URL", VERSION_DOCS_URL);
}
+/**
+ * "Docs TRanslate for N items". Used for the editor class reference documentation
+ * (with support for plurals), handling descriptions extracted from the XML.
+ * It also replaces `$DOCS_URL` with the actual URL to the documentation's branch,
+ * to allow dehardcoding it in the XML and doing proper substitutions everywhere.
+ */
String DTRN(const String &p_text, const String &p_text_plural, int p_n, const String &p_context) {
const String text = p_text.dedent().strip_edges();
const String text_plural = p_text_plural.dedent().strip_edges();
@@ -4831,6 +4861,19 @@ String DTRN(const String &p_text, const String &p_text_plural, int p_n, const St
}
#endif
+/**
+ * "Run-time TRanslate". Performs string replacement for internationalization
+ * within a running project. The translation string must be supplied by the
+ * project, as Godot does not provide built-in translations for `RTR()` strings
+ * to keep binary size low. A translation context can optionally be specified to
+ * disambiguate between identical source strings in translations. When
+ * placeholders are desired, use `vformat(RTR("Example: %s"), some_string)`.
+ * If a string mentions a quantity (and may therefore need a dynamic plural form),
+ * use `RTRN()` instead of `RTR()`.
+ *
+ * NOTE: Do not use `RTR()` in editor-only code (typically within the `editor/`
+ * folder). For editor translations, use `TTR()` instead.
+ */
String RTR(const String &p_text, const String &p_context) {
if (TranslationServer::get_singleton()) {
String rtr = TranslationServer::get_singleton()->tool_translate(p_text, p_context);
@@ -4844,6 +4887,20 @@ String RTR(const String &p_text, const String &p_context) {
return p_text;
}
+/**
+ * "Run-time TRanslate for N items". Performs string replacement for
+ * internationalization within a running project. The translation string must be
+ * supplied by the project, as Godot does not provide built-in translations for
+ * `RTRN()` strings to keep binary size low. A translation context can
+ * optionally be specified to disambiguate between identical source strings in
+ * translations. Use `RTR()` if the string doesn't need dynamic plural form.
+ * When placeholders are desired, use
+ * `vformat(RTRN("%d item", "%d items", some_integer), some_integer)`.
+ * The placeholder must be present in both strings to avoid run-time warnings in `vformat()`.
+ *
+ * NOTE: Do not use `RTRN()` in editor-only code (typically within the `editor/`
+ * folder). For editor translations, use `TTRN()` instead.
+ */
String RTRN(const String &p_text, const String &p_text_plural, int p_n, const String &p_context) {
if (TranslationServer::get_singleton()) {
String rtr = TranslationServer::get_singleton()->tool_translate_plural(p_text, p_text_plural, p_n, p_context);
diff --git a/core/templates/cowdata.h b/core/templates/cowdata.h
index f1ac32928f..e760fc2176 100644
--- a/core/templates/cowdata.h
+++ b/core/templates/cowdata.h
@@ -183,6 +183,8 @@ public:
}
int find(const T &p_val, int p_from = 0) const;
+ int rfind(const T &p_val, int p_from = -1) const;
+ int count(const T &p_val) const;
_FORCE_INLINE_ CowData() {}
_FORCE_INLINE_ ~CowData();
@@ -350,6 +352,36 @@ int CowData<T>::find(const T &p_val, int p_from) const {
}
template <class T>
+int CowData<T>::rfind(const T &p_val, int p_from) const {
+ const int s = size();
+
+ if (p_from < 0) {
+ p_from = s + p_from;
+ }
+ if (p_from < 0 || p_from >= s) {
+ p_from = s - 1;
+ }
+
+ for (int i = p_from; i >= 0; i--) {
+ if (get(i) == p_val) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+template <class T>
+int CowData<T>::count(const T &p_val) const {
+ int amount = 0;
+ for (int i = 0; i < size(); i++) {
+ if (get(i) == p_val) {
+ amount++;
+ }
+ }
+ return amount;
+}
+
+template <class T>
void CowData<T>::_ref(const CowData *p_from) {
_ref(*p_from);
}
diff --git a/core/templates/hash_map.h b/core/templates/hash_map.h
index fa5677cc70..55292d3eb5 100644
--- a/core/templates/hash_map.h
+++ b/core/templates/hash_map.h
@@ -31,524 +31,553 @@
#ifndef HASH_MAP_H
#define HASH_MAP_H
-#include "core/error/error_macros.h"
#include "core/math/math_funcs.h"
#include "core/os/memory.h"
-#include "core/string/ustring.h"
#include "core/templates/hashfuncs.h"
-#include "core/templates/list.h"
+#include "core/templates/paged_allocator.h"
+#include "core/templates/pair.h"
/**
- * @class HashMap
+ * A HashMap implementation that uses open addressing with Robin Hood hashing.
+ * Robin Hood hashing swaps out entries that have a smaller probing distance
+ * than the to-be-inserted entry, that evens out the average probing distance
+ * and enables faster lookups. Backward shift deletion is employed to further
+ * improve the performance and to avoid infinite loops in rare cases.
*
- * Implementation of a standard Hashing HashMap, for quick lookups of Data associated with a Key.
- * The implementation provides hashers for the default types, if you need a special kind of hasher, provide
- * your own.
- * @param TKey Key, search is based on it, needs to be hasheable. It is unique in this container.
- * @param TData Data, data associated with the key
- * @param Hasher Hasher object, needs to provide a valid static hash function for TKey
- * @param Comparator comparator object, needs to be able to safely compare two TKey values.
- * It needs to ensure that x == x for any items inserted in the map. Bear in mind that nan != nan when implementing an equality check.
- * @param MIN_HASH_TABLE_POWER Miminum size of the hash table, as a power of two. You rarely need to change this parameter.
- * @param RELATIONSHIP Relationship at which the hash table is resized. if amount of elements is RELATIONSHIP
- * times bigger than the hash table, table is resized to solve this condition. if RELATIONSHIP is zero, table is always MIN_HASH_TABLE_POWER.
+ * Keys and values are stored in a double linked list by insertion order. This
+ * has a slight performance overhead on lookup, which can be mostly compensated
+ * using a paged allocator if required.
*
+ * The assignment operator copy the pairs from one map to the other.
*/
-template <class TKey, class TData, class Hasher = HashMapHasherDefault, class Comparator = HashMapComparatorDefault<TKey>, uint8_t MIN_HASH_TABLE_POWER = 3, uint8_t RELATIONSHIP = 8>
+template <class TKey, class TValue>
+struct HashMapElement {
+ HashMapElement *next = nullptr;
+ HashMapElement *prev = nullptr;
+ KeyValue<TKey, TValue> data;
+ HashMapElement() {}
+ HashMapElement(const TKey &p_key, const TValue &p_value) :
+ data(p_key, p_value) {}
+};
+
+template <class TKey, class TValue,
+ class Hasher = HashMapHasherDefault,
+ class Comparator = HashMapComparatorDefault<TKey>,
+ class Allocator = DefaultTypedAllocator<HashMapElement<TKey, TValue>>>
class HashMap {
public:
- struct Pair {
- TKey key;
- TData data;
+ const uint32_t MIN_CAPACITY_INDEX = 2; // Use a prime.
+ const float MAX_OCCUPANCY = 0.75;
+ const uint32_t EMPTY_HASH = 0;
- Pair(const TKey &p_key) :
- key(p_key),
- data() {}
- Pair(const TKey &p_key, const TData &p_data) :
- key(p_key),
- data(p_data) {
- }
- };
+private:
+ Allocator element_alloc;
+ HashMapElement<TKey, TValue> **elements = nullptr;
+ uint32_t *hashes = nullptr;
+ HashMapElement<TKey, TValue> *head_element = nullptr;
+ HashMapElement<TKey, TValue> *tail_element = nullptr;
- struct Element {
- private:
- friend class HashMap;
+ uint32_t capacity_index = 0;
+ uint32_t num_elements = 0;
- uint32_t hash = 0;
- Element *next = nullptr;
- Element() {}
- Pair pair;
+ _FORCE_INLINE_ uint32_t _hash(const TKey &p_key) const {
+ uint32_t hash = Hasher::hash(p_key);
- public:
- const TKey &key() const {
- return pair.key;
+ if (unlikely(hash == EMPTY_HASH)) {
+ hash = EMPTY_HASH + 1;
}
- TData &value() {
- return pair.data;
- }
+ return hash;
+ }
- const TData &value() const {
- return pair.value();
+ _FORCE_INLINE_ uint32_t _get_probe_length(uint32_t p_pos, uint32_t p_hash, uint32_t p_capacity) const {
+ uint32_t original_pos = p_hash % p_capacity;
+ return (p_pos - original_pos + p_capacity) % p_capacity;
+ }
+
+ bool _lookup_pos(const TKey &p_key, uint32_t &r_pos) const {
+ if (elements == nullptr) {
+ return false; // Failed lookups, no elements
}
- Element(const TKey &p_key) :
- pair(p_key) {}
- Element(const Element &p_other) :
- hash(p_other.hash),
- pair(p_other.pair.key, p_other.pair.data) {}
- };
+ uint32_t capacity = hash_table_size_primes[capacity_index];
+ uint32_t hash = _hash(p_key);
+ uint32_t pos = hash % capacity;
+ uint32_t distance = 0;
-private:
- Element **hash_table = nullptr;
- uint8_t hash_table_power = 0;
- uint32_t elements = 0;
+ while (true) {
+ if (hashes[pos] == EMPTY_HASH) {
+ return false;
+ }
- void make_hash_table() {
- ERR_FAIL_COND(hash_table);
+ if (distance > _get_probe_length(pos, hashes[pos], capacity)) {
+ return false;
+ }
- hash_table = memnew_arr(Element *, (1 << MIN_HASH_TABLE_POWER));
+ if (hashes[pos] == hash && Comparator::compare(elements[pos]->data.key, p_key)) {
+ r_pos = pos;
+ return true;
+ }
- hash_table_power = MIN_HASH_TABLE_POWER;
- elements = 0;
- for (int i = 0; i < (1 << MIN_HASH_TABLE_POWER); i++) {
- hash_table[i] = nullptr;
+ pos = (pos + 1) % capacity;
+ distance++;
}
}
- void erase_hash_table() {
- ERR_FAIL_COND_MSG(elements, "Cannot erase hash table if there are still elements inside.");
+ void _insert_with_hash(uint32_t p_hash, HashMapElement<TKey, TValue> *p_value) {
+ uint32_t capacity = hash_table_size_primes[capacity_index];
+ uint32_t hash = p_hash;
+ HashMapElement<TKey, TValue> *value = p_value;
+ uint32_t distance = 0;
+ uint32_t pos = hash % capacity;
- memdelete_arr(hash_table);
- hash_table = nullptr;
- hash_table_power = 0;
- elements = 0;
- }
+ while (true) {
+ if (hashes[pos] == EMPTY_HASH) {
+ elements[pos] = value;
+ hashes[pos] = hash;
- void check_hash_table() {
- int new_hash_table_power = -1;
+ num_elements++;
- if ((int)elements > ((1 << hash_table_power) * RELATIONSHIP)) {
- /* rehash up */
- new_hash_table_power = hash_table_power + 1;
-
- while ((int)elements > ((1 << new_hash_table_power) * RELATIONSHIP)) {
- new_hash_table_power++;
+ return;
}
- } else if ((hash_table_power > (int)MIN_HASH_TABLE_POWER) && ((int)elements < ((1 << (hash_table_power - 1)) * RELATIONSHIP))) {
- /* rehash down */
- new_hash_table_power = hash_table_power - 1;
-
- while ((int)elements < ((1 << (new_hash_table_power - 1)) * RELATIONSHIP)) {
- new_hash_table_power--;
+ // Not an empty slot, let's check the probing length of the existing one.
+ uint32_t existing_probe_len = _get_probe_length(pos, hashes[pos], capacity);
+ if (existing_probe_len < distance) {
+ SWAP(hash, hashes[pos]);
+ SWAP(value, elements[pos]);
+ distance = existing_probe_len;
}
- if (new_hash_table_power < (int)MIN_HASH_TABLE_POWER) {
- new_hash_table_power = MIN_HASH_TABLE_POWER;
- }
+ pos = (pos + 1) % capacity;
+ distance++;
}
+ }
- if (new_hash_table_power == -1) {
- return;
- }
+ void _resize_and_rehash(uint32_t p_new_capacity_index) {
+ uint32_t old_capacity = hash_table_size_primes[capacity_index];
- Element **new_hash_table = memnew_arr(Element *, ((uint64_t)1 << new_hash_table_power));
- ERR_FAIL_COND_MSG(!new_hash_table, "Out of memory.");
+ // Capacity can't be 0.
+ capacity_index = MAX((uint32_t)MIN_CAPACITY_INDEX, p_new_capacity_index);
- for (int i = 0; i < (1 << new_hash_table_power); i++) {
- new_hash_table[i] = nullptr;
- }
+ uint32_t capacity = hash_table_size_primes[capacity_index];
- if (hash_table) {
- for (int i = 0; i < (1 << hash_table_power); i++) {
- while (hash_table[i]) {
- Element *se = hash_table[i];
- hash_table[i] = se->next;
- int new_pos = se->hash & ((1 << new_hash_table_power) - 1);
- se->next = new_hash_table[new_pos];
- new_hash_table[new_pos] = se;
- }
- }
+ HashMapElement<TKey, TValue> **old_elements = elements;
+ uint32_t *old_hashes = hashes;
- memdelete_arr(hash_table);
- }
- hash_table = new_hash_table;
- hash_table_power = new_hash_table_power;
- }
+ num_elements = 0;
+ hashes = reinterpret_cast<uint32_t *>(Memory::alloc_static(sizeof(uint32_t) * capacity));
+ elements = reinterpret_cast<HashMapElement<TKey, TValue> **>(Memory::alloc_static(sizeof(HashMapElement<TKey, TValue> *) * capacity));
- /* I want to have only one function.. */
- _FORCE_INLINE_ const Element *get_element(const TKey &p_key) const {
- uint32_t hash = Hasher::hash(p_key);
- uint32_t index = hash & ((1 << hash_table_power) - 1);
+ for (uint32_t i = 0; i < capacity; i++) {
+ hashes[i] = 0;
+ elements[i] = nullptr;
+ }
- Element *e = hash_table[index];
+ if (old_capacity == 0) {
+ // Nothing to do.
+ return;
+ }
- while (e) {
- /* checking hash first avoids comparing key, which may take longer */
- if (e->hash == hash && Comparator::compare(e->pair.key, p_key)) {
- /* the pair exists in this hashtable, so just update data */
- return e;
+ for (uint32_t i = 0; i < old_capacity; i++) {
+ if (old_hashes[i] == EMPTY_HASH) {
+ continue;
}
- e = e->next;
+ _insert_with_hash(old_hashes[i], old_elements[i]);
}
- return nullptr;
- }
-
- Element *create_element(const TKey &p_key) {
- /* if element doesn't exist, create it */
- Element *e = memnew(Element(p_key));
- ERR_FAIL_COND_V_MSG(!e, nullptr, "Out of memory.");
- uint32_t hash = Hasher::hash(p_key);
- uint32_t index = hash & ((1 << hash_table_power) - 1);
- e->next = hash_table[index];
- e->hash = hash;
-
- hash_table[index] = e;
- elements++;
-
- return e;
+ Memory::free_static(old_elements);
+ Memory::free_static(old_hashes);
}
- void copy_from(const HashMap &p_t) {
- if (&p_t == this) {
- return; /* much less bother with that */
- }
+ _FORCE_INLINE_ HashMapElement<TKey, TValue> *_insert(const TKey &p_key, const TValue &p_value, bool p_front_insert = false) {
+ uint32_t capacity = hash_table_size_primes[capacity_index];
+ if (unlikely(elements == nullptr)) {
+ // Allocate on demand to save memory.
- clear();
+ hashes = reinterpret_cast<uint32_t *>(Memory::alloc_static(sizeof(uint32_t) * capacity));
+ elements = reinterpret_cast<HashMapElement<TKey, TValue> **>(Memory::alloc_static(sizeof(HashMapElement<TKey, TValue> *) * capacity));
- if (!p_t.hash_table || p_t.hash_table_power == 0) {
- return; /* not copying from empty table */
+ for (uint32_t i = 0; i < capacity; i++) {
+ hashes[i] = EMPTY_HASH;
+ elements[i] = nullptr;
+ }
}
- hash_table = memnew_arr(Element *, (uint64_t)1 << p_t.hash_table_power);
- hash_table_power = p_t.hash_table_power;
- elements = p_t.elements;
-
- for (int i = 0; i < (1 << p_t.hash_table_power); i++) {
- hash_table[i] = nullptr;
+ uint32_t pos = 0;
+ bool exists = _lookup_pos(p_key, pos);
- const Element *e = p_t.hash_table[i];
-
- while (e) {
- Element *le = memnew(Element(*e)); /* local element */
+ if (exists) {
+ elements[pos]->data.value = p_value;
+ return elements[pos];
+ } else {
+ if (num_elements + 1 > MAX_OCCUPANCY * capacity) {
+ ERR_FAIL_COND_V_MSG(capacity_index + 1 == HASH_TABLE_SIZE_MAX, nullptr, "Hash table maximum capacity reached, aborting insertion.");
+ _resize_and_rehash(capacity_index + 1);
+ }
- /* add to list and reassign pointers */
- le->next = hash_table[i];
- hash_table[i] = le;
+ HashMapElement<TKey, TValue> *elem = element_alloc.new_allocation(HashMapElement<TKey, TValue>(p_key, p_value));
- e = e->next;
+ if (tail_element == nullptr) {
+ head_element = elem;
+ tail_element = elem;
+ } else if (p_front_insert) {
+ head_element->prev = elem;
+ elem->next = head_element;
+ head_element = elem;
+ } else {
+ tail_element->next = elem;
+ elem->prev = tail_element;
+ tail_element = elem;
}
+
+ uint32_t hash = _hash(p_key);
+ _insert_with_hash(hash, elem);
+ return elem;
}
}
public:
- Element *set(const TKey &p_key, const TData &p_data) {
- return set(Pair(p_key, p_data));
- }
+ _FORCE_INLINE_ uint32_t get_capacity() const { return hash_table_size_primes[capacity_index]; }
+ _FORCE_INLINE_ uint32_t size() const { return num_elements; }
- Element *set(const Pair &p_pair) {
- Element *e = nullptr;
- if (!hash_table) {
- make_hash_table(); // if no table, make one
- } else {
- e = const_cast<Element *>(get_element(p_pair.key));
- }
+ /* Standard Godot Container API */
- /* if we made it up to here, the pair doesn't exist, create and assign */
+ bool is_empty() const {
+ return num_elements == 0;
+ }
- if (!e) {
- e = create_element(p_pair.key);
- if (!e) {
- return nullptr;
- }
- check_hash_table(); // perform mantenience routine
+ void clear() {
+ if (elements == nullptr) {
+ return;
}
+ uint32_t capacity = hash_table_size_primes[capacity_index];
+ for (uint32_t i = 0; i < capacity; i++) {
+ if (hashes[i] == EMPTY_HASH) {
+ continue;
+ }
- e->pair.data = p_pair.data;
- return e;
- }
+ hashes[i] = EMPTY_HASH;
+ element_alloc.delete_allocation(elements[i]);
+ elements[i] = nullptr;
+ }
- bool has(const TKey &p_key) const {
- return getptr(p_key) != nullptr;
+ tail_element = nullptr;
+ head_element = nullptr;
+ num_elements = 0;
}
- /**
- * Get a key from data, return a const reference.
- * WARNING: this doesn't check errors, use either getptr and check nullptr, or check
- * first with has(key)
- */
-
- const TData &get(const TKey &p_key) const {
- const TData *res = getptr(p_key);
- CRASH_COND_MSG(!res, "Map key not found.");
- return *res;
+ TValue &get(const TKey &p_key) {
+ uint32_t pos = 0;
+ bool exists = _lookup_pos(p_key, pos);
+ CRASH_COND_MSG(!exists, "HashMap key not found.");
+ return elements[pos]->data.value;
}
- TData &get(const TKey &p_key) {
- TData *res = getptr(p_key);
- CRASH_COND_MSG(!res, "Map key not found.");
- return *res;
+ const TValue &get(const TKey &p_key) const {
+ uint32_t pos = 0;
+ bool exists = _lookup_pos(p_key, pos);
+ CRASH_COND_MSG(!exists, "HashMap key not found.");
+ return elements[pos]->data.value;
}
- /**
- * Same as get, except it can return nullptr when item was not found.
- * This is mainly used for speed purposes.
- */
+ const TValue *getptr(const TKey &p_key) const {
+ uint32_t pos = 0;
+ bool exists = _lookup_pos(p_key, pos);
- _FORCE_INLINE_ TData *getptr(const TKey &p_key) {
- if (unlikely(!hash_table)) {
- return nullptr;
+ if (exists) {
+ return &elements[pos]->data.value;
}
+ return nullptr;
+ }
- Element *e = const_cast<Element *>(get_element(p_key));
+ TValue *getptr(const TKey &p_key) {
+ uint32_t pos = 0;
+ bool exists = _lookup_pos(p_key, pos);
- if (e) {
- return &e->pair.data;
+ if (exists) {
+ return &elements[pos]->data.value;
}
-
return nullptr;
}
- _FORCE_INLINE_ const TData *getptr(const TKey &p_key) const {
- if (unlikely(!hash_table)) {
- return nullptr;
- }
+ _FORCE_INLINE_ bool has(const TKey &p_key) const {
+ uint32_t _pos = 0;
+ return _lookup_pos(p_key, _pos);
+ }
- const Element *e = const_cast<Element *>(get_element(p_key));
+ bool erase(const TKey &p_key) {
+ uint32_t pos = 0;
+ bool exists = _lookup_pos(p_key, pos);
- if (e) {
- return &e->pair.data;
+ if (!exists) {
+ return false;
}
- return nullptr;
- }
+ uint32_t capacity = hash_table_size_primes[capacity_index];
+ uint32_t next_pos = (pos + 1) % capacity;
+ while (hashes[next_pos] != EMPTY_HASH && _get_probe_length(next_pos, hashes[next_pos], capacity) != 0) {
+ SWAP(hashes[next_pos], hashes[pos]);
+ SWAP(elements[next_pos], elements[pos]);
+ pos = next_pos;
+ next_pos = (pos + 1) % capacity;
+ }
- /**
- * Same as get, except it can return nullptr when item was not found.
- * This version is custom, will take a hash and a custom key (that should support operator==()
- */
+ hashes[pos] = EMPTY_HASH;
- template <class C>
- _FORCE_INLINE_ TData *custom_getptr(C p_custom_key, uint32_t p_custom_hash) {
- if (unlikely(!hash_table)) {
- return nullptr;
+ if (head_element == elements[pos]) {
+ head_element = elements[pos]->next;
}
- uint32_t hash = p_custom_hash;
- uint32_t index = hash & ((1 << hash_table_power) - 1);
-
- Element *e = hash_table[index];
+ if (tail_element == elements[pos]) {
+ tail_element = elements[pos]->prev;
+ }
- while (e) {
- /* checking hash first avoids comparing key, which may take longer */
- if (e->hash == hash && Comparator::compare(e->pair.key, p_custom_key)) {
- /* the pair exists in this hashtable, so just update data */
- return &e->pair.data;
- }
+ if (elements[pos]->prev) {
+ elements[pos]->prev->next = elements[pos]->next;
+ }
- e = e->next;
+ if (elements[pos]->next) {
+ elements[pos]->next->prev = elements[pos]->prev;
}
- return nullptr;
+ element_alloc.delete_allocation(elements[pos]);
+ elements[pos] = nullptr;
+
+ num_elements--;
+ return true;
}
- template <class C>
- _FORCE_INLINE_ const TData *custom_getptr(C p_custom_key, uint32_t p_custom_hash) const {
- if (unlikely(!hash_table)) {
- return nullptr;
+ // Reserves space for a number of elements, useful to avoid many resizes and rehashes.
+ // If adding a known (possibly large) number of elements at once, must be larger than old capacity.
+ void reserve(uint32_t p_new_capacity) {
+ uint32_t new_index = capacity_index;
+
+ while (hash_table_size_primes[new_index] < p_new_capacity) {
+ ERR_FAIL_COND_MSG(new_index + 1 == (uint32_t)HASH_TABLE_SIZE_MAX, nullptr);
+ new_index++;
}
- uint32_t hash = p_custom_hash;
- uint32_t index = hash & ((1 << hash_table_power) - 1);
+ if (new_index == capacity_index) {
+ return;
+ }
+
+ if (elements == nullptr) {
+ capacity_index = new_index;
+ return; // Unallocated yet.
+ }
+ _resize_and_rehash(new_index);
+ }
- const Element *e = hash_table[index];
+ /** Iterator API **/
- while (e) {
- /* checking hash first avoids comparing key, which may take longer */
- if (e->hash == hash && Comparator::compare(e->pair.key, p_custom_key)) {
- /* the pair exists in this hashtable, so just update data */
- return &e->pair.data;
+ struct Iterator {
+ _FORCE_INLINE_ KeyValue<TKey, TValue> &operator*() const {
+ return E->data;
+ }
+ _FORCE_INLINE_ KeyValue<TKey, TValue> *operator->() const { return &E->data; }
+ _FORCE_INLINE_ Iterator &operator++() {
+ if (E) {
+ E = E->next;
}
-
- e = e->next;
+ return *this;
+ }
+ _FORCE_INLINE_ Iterator &operator--() {
+ if (E) {
+ E = E->prev;
+ }
+ return *this;
}
- return nullptr;
- }
+ _FORCE_INLINE_ bool operator==(const Iterator &b) const { return E == b.E; }
+ _FORCE_INLINE_ bool operator!=(const Iterator &b) const { return E != b.E; }
- /**
- * Erase an item, return true if erasing was successful
- */
+ _FORCE_INLINE_ operator bool() const {
+ return E != nullptr;
+ }
- bool erase(const TKey &p_key) {
- if (unlikely(!hash_table)) {
- return false;
+ _FORCE_INLINE_ Iterator(HashMapElement<TKey, TValue> *p_E) { E = p_E; }
+ _FORCE_INLINE_ Iterator() {}
+ _FORCE_INLINE_ Iterator(const Iterator &p_it) { E = p_it.E; }
+ _FORCE_INLINE_ void operator=(const Iterator &p_it) {
+ E = p_it.E;
}
- uint32_t hash = Hasher::hash(p_key);
- uint32_t index = hash & ((1 << hash_table_power) - 1);
-
- Element *e = hash_table[index];
- Element *p = nullptr;
- while (e) {
- /* checking hash first avoids comparing key, which may take longer */
- if (e->hash == hash && Comparator::compare(e->pair.key, p_key)) {
- if (p) {
- p->next = e->next;
- } else {
- //begin of list
- hash_table[index] = e->next;
- }
-
- memdelete(e);
- elements--;
-
- if (elements == 0) {
- erase_hash_table();
- } else {
- check_hash_table();
- }
- return true;
+ private:
+ HashMapElement<TKey, TValue> *E = nullptr;
+ };
+
+ struct ConstIterator {
+ _FORCE_INLINE_ const KeyValue<TKey, TValue> &operator*() const {
+ return E->data;
+ }
+ _FORCE_INLINE_ const KeyValue<TKey, TValue> *operator->() const { return &E->data; }
+ _FORCE_INLINE_ ConstIterator &operator++() {
+ if (E) {
+ E = E->next;
+ }
+ return *this;
+ }
+ _FORCE_INLINE_ ConstIterator &operator--() {
+ if (E) {
+ E = E->prev;
}
+ return *this;
+ }
+
+ _FORCE_INLINE_ bool operator==(const ConstIterator &b) const { return E == b.E; }
+ _FORCE_INLINE_ bool operator!=(const ConstIterator &b) const { return E != b.E; }
- p = e;
- e = e->next;
+ _FORCE_INLINE_ operator bool() const {
+ return E != nullptr;
}
- return false;
- }
+ _FORCE_INLINE_ ConstIterator(const HashMapElement<TKey, TValue> *p_E) { E = p_E; }
+ _FORCE_INLINE_ ConstIterator() {}
+ _FORCE_INLINE_ ConstIterator(const ConstIterator &p_it) { E = p_it.E; }
+ _FORCE_INLINE_ void operator=(const ConstIterator &p_it) {
+ E = p_it.E;
+ }
- inline const TData &operator[](const TKey &p_key) const { //constref
+ private:
+ const HashMapElement<TKey, TValue> *E = nullptr;
+ };
- return get(p_key);
+ _FORCE_INLINE_ Iterator begin() {
+ return Iterator(head_element);
+ }
+ _FORCE_INLINE_ Iterator end() {
+ return Iterator(nullptr);
+ }
+ _FORCE_INLINE_ Iterator last() {
+ return Iterator(tail_element);
}
- inline TData &operator[](const TKey &p_key) { //assignment
- Element *e = nullptr;
- if (!hash_table) {
- make_hash_table(); // if no table, make one
- } else {
- e = const_cast<Element *>(get_element(p_key));
+ _FORCE_INLINE_ Iterator find(const TKey &p_key) {
+ uint32_t pos = 0;
+ bool exists = _lookup_pos(p_key, pos);
+ if (!exists) {
+ return end();
}
+ return Iterator(elements[pos]);
+ }
- /* if we made it up to here, the pair doesn't exist, create */
- if (!e) {
- e = create_element(p_key);
- CRASH_COND(!e);
- check_hash_table(); // perform mantenience routine
+ _FORCE_INLINE_ void remove(const Iterator &p_iter) {
+ if (p_iter) {
+ erase(p_iter->key);
}
+ }
- return e->pair.data;
+ _FORCE_INLINE_ ConstIterator begin() const {
+ return ConstIterator(head_element);
+ }
+ _FORCE_INLINE_ ConstIterator end() const {
+ return ConstIterator(nullptr);
+ }
+ _FORCE_INLINE_ ConstIterator last() const {
+ return ConstIterator(tail_element);
}
- /**
- * Get the next key to p_key, and the first key if p_key is null.
- * Returns a pointer to the next key if found, nullptr otherwise.
- * Adding/Removing elements while iterating will, of course, have unexpected results, don't do it.
- *
- * Example:
- *
- * const TKey *k=nullptr;
- *
- * while( (k=table.next(k)) ) {
- *
- * print( *k );
- * }
- *
- */
- const TKey *next(const TKey *p_key) const {
- if (unlikely(!hash_table)) {
- return nullptr;
+ _FORCE_INLINE_ ConstIterator find(const TKey &p_key) const {
+ uint32_t pos = 0;
+ bool exists = _lookup_pos(p_key, pos);
+ if (!exists) {
+ return end();
}
+ return ConstIterator(elements[pos]);
+ }
- if (!p_key) { /* get the first key */
-
- for (int i = 0; i < (1 << hash_table_power); i++) {
- if (hash_table[i]) {
- return &hash_table[i]->pair.key;
- }
- }
-
- } else { /* get the next key */
+ /* Indexing */
- const Element *e = get_element(*p_key);
- ERR_FAIL_COND_V_MSG(!e, nullptr, "Invalid key supplied.");
- if (e->next) {
- /* if there is a "next" in the list, return that */
- return &e->next->pair.key;
- } else {
- /* go to next elements */
- uint32_t index = e->hash & ((1 << hash_table_power) - 1);
- index++;
- for (int i = index; i < (1 << hash_table_power); i++) {
- if (hash_table[i]) {
- return &hash_table[i]->pair.key;
- }
- }
- }
+ const TValue &operator[](const TKey &p_key) const {
+ uint32_t pos = 0;
+ bool exists = _lookup_pos(p_key, pos);
+ CRASH_COND(!exists);
+ return elements[pos]->data.value;
+ }
- /* nothing found, was at end */
+ TValue &operator[](const TKey &p_key) {
+ uint32_t pos = 0;
+ bool exists = _lookup_pos(p_key, pos);
+ if (!exists) {
+ return _insert(p_key, TValue())->data.value;
+ } else {
+ return elements[pos]->data.value;
}
-
- return nullptr; /* nothing found */
}
- inline unsigned int size() const {
- return elements;
- }
+ /* Insert */
- inline bool is_empty() const {
- return elements == 0;
+ Iterator insert(const TKey &p_key, const TValue &p_value, bool p_front_insert = false) {
+ return Iterator(_insert(p_key, p_value, p_front_insert));
}
- void clear() {
- /* clean up */
- if (hash_table) {
- for (int i = 0; i < (1 << hash_table_power); i++) {
- while (hash_table[i]) {
- Element *e = hash_table[i];
- hash_table[i] = e->next;
- memdelete(e);
- }
- }
+ /* Constructors */
- memdelete_arr(hash_table);
+ HashMap(const HashMap &p_other) {
+ reserve(hash_table_size_primes[p_other.capacity_index]);
+
+ if (p_other.num_elements == 0) {
+ return;
}
- hash_table = nullptr;
- hash_table_power = 0;
- elements = 0;
+ for (const KeyValue<TKey, TValue> &E : p_other) {
+ insert(E.key, E.value);
+ }
}
- void operator=(const HashMap &p_table) {
- copy_from(p_table);
- }
+ void operator=(const HashMap &p_other) {
+ if (this == &p_other) {
+ return; // Ignore self assignment.
+ }
+ if (num_elements != 0) {
+ clear();
+ }
- void get_key_list(List<TKey> *r_keys) const {
- if (unlikely(!hash_table)) {
- return;
+ reserve(hash_table_size_primes[p_other.capacity_index]);
+
+ if (p_other.elements == nullptr) {
+ return; // Nothing to copy.
}
- for (int i = 0; i < (1 << hash_table_power); i++) {
- Element *e = hash_table[i];
- while (e) {
- r_keys->push_back(e->pair.key);
- e = e->next;
- }
+
+ for (const KeyValue<TKey, TValue> &E : p_other) {
+ insert(E.key, E.value);
}
}
- HashMap() {}
+ HashMap(uint32_t p_initial_capacity) {
+ // Capacity can't be 0.
+ capacity_index = 0;
+ reserve(p_initial_capacity);
+ }
+ HashMap() {
+ capacity_index = MIN_CAPACITY_INDEX;
+ }
- HashMap(const HashMap &p_table) {
- copy_from(p_table);
+ uint32_t debug_get_hash(uint32_t p_index) {
+ if (num_elements == 0) {
+ return 0;
+ }
+ ERR_FAIL_INDEX_V(p_index, get_capacity(), 0);
+ return hashes[p_index];
+ }
+ Iterator debug_get_element(uint32_t p_index) {
+ if (num_elements == 0) {
+ return Iterator();
+ }
+ ERR_FAIL_INDEX_V(p_index, get_capacity(), Iterator());
+ return Iterator(elements[p_index]);
}
~HashMap() {
clear();
+
+ if (elements != nullptr) {
+ Memory::free_static(elements);
+ Memory::free_static(hashes);
+ }
}
};
diff --git a/core/templates/hashfuncs.h b/core/templates/hashfuncs.h
index 2a129f97d5..eb73ff4ede 100644
--- a/core/templates/hashfuncs.h
+++ b/core/templates/hashfuncs.h
@@ -31,14 +31,22 @@
#ifndef HASHFUNCS_H
#define HASHFUNCS_H
+#include "core/math/aabb.h"
#include "core/math/math_defs.h"
#include "core/math/math_funcs.h"
+#include "core/math/rect2.h"
+#include "core/math/rect2i.h"
+#include "core/math/vector2.h"
+#include "core/math/vector2i.h"
+#include "core/math/vector3.h"
+#include "core/math/vector3i.h"
#include "core/object/object_id.h"
#include "core/string/node_path.h"
#include "core/string/string_name.h"
#include "core/string/ustring.h"
#include "core/templates/rid.h"
#include "core/typedefs.h"
+
/**
* Hashing functions
*/
@@ -178,6 +186,49 @@ struct HashMapHasherDefault {
static _FORCE_INLINE_ uint32_t hash(const StringName &p_string_name) { return p_string_name.hash(); }
static _FORCE_INLINE_ uint32_t hash(const NodePath &p_path) { return p_path.hash(); }
+ static _FORCE_INLINE_ uint32_t hash(const Vector2i &p_vec) {
+ uint32_t h = hash_djb2_one_32(p_vec.x);
+ return hash_djb2_one_32(p_vec.y, h);
+ }
+ static _FORCE_INLINE_ uint32_t hash(const Vector3i &p_vec) {
+ uint32_t h = hash_djb2_one_32(p_vec.x);
+ h = hash_djb2_one_32(p_vec.y, h);
+ return hash_djb2_one_32(p_vec.z, h);
+ }
+
+ static _FORCE_INLINE_ uint32_t hash(const Vector2 &p_vec) {
+ uint32_t h = hash_djb2_one_float(p_vec.x);
+ return hash_djb2_one_float(p_vec.y, h);
+ }
+ static _FORCE_INLINE_ uint32_t hash(const Vector3 &p_vec) {
+ uint32_t h = hash_djb2_one_float(p_vec.x);
+ h = hash_djb2_one_float(p_vec.y, h);
+ return hash_djb2_one_float(p_vec.z, h);
+ }
+
+ static _FORCE_INLINE_ uint32_t hash(const Rect2i &p_rect) {
+ uint32_t h = hash_djb2_one_32(p_rect.position.x);
+ h = hash_djb2_one_32(p_rect.position.y, h);
+ h = hash_djb2_one_32(p_rect.size.x, h);
+ return hash_djb2_one_32(p_rect.size.y, h);
+ }
+
+ static _FORCE_INLINE_ uint32_t hash(const Rect2 &p_rect) {
+ uint32_t h = hash_djb2_one_float(p_rect.position.x);
+ h = hash_djb2_one_float(p_rect.position.y, h);
+ h = hash_djb2_one_float(p_rect.size.x, h);
+ return hash_djb2_one_float(p_rect.size.y, h);
+ }
+
+ static _FORCE_INLINE_ uint32_t hash(const AABB &p_aabb) {
+ uint32_t h = hash_djb2_one_float(p_aabb.position.x);
+ h = hash_djb2_one_float(p_aabb.position.y, h);
+ h = hash_djb2_one_float(p_aabb.position.z, h);
+ h = hash_djb2_one_float(p_aabb.size.x, h);
+ h = hash_djb2_one_float(p_aabb.size.y, h);
+ return hash_djb2_one_float(p_aabb.size.z, h);
+ }
+
//static _FORCE_INLINE_ uint32_t hash(const void* p_ptr) { return uint32_t(uint64_t(p_ptr))*(0x9e3779b1L); }
};
@@ -196,4 +247,38 @@ struct HashMapComparatorDefault {
}
};
+constexpr uint32_t HASH_TABLE_SIZE_MAX = 29;
+
+const uint32_t hash_table_size_primes[HASH_TABLE_SIZE_MAX] = {
+ 5,
+ 13,
+ 23,
+ 47,
+ 97,
+ 193,
+ 389,
+ 769,
+ 1543,
+ 3079,
+ 6151,
+ 12289,
+ 24593,
+ 49157,
+ 98317,
+ 196613,
+ 393241,
+ 786433,
+ 1572869,
+ 3145739,
+ 6291469,
+ 12582917,
+ 25165843,
+ 50331653,
+ 100663319,
+ 201326611,
+ 402653189,
+ 805306457,
+ 1610612741,
+};
+
#endif // HASHFUNCS_H
diff --git a/core/templates/ordered_hash_map.h b/core/templates/ordered_hash_map.h
deleted file mode 100644
index 3d1f3a08ec..0000000000
--- a/core/templates/ordered_hash_map.h
+++ /dev/null
@@ -1,301 +0,0 @@
-/*************************************************************************/
-/* ordered_hash_map.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. */
-/*************************************************************************/
-
-#ifndef ORDERED_HASH_MAP_H
-#define ORDERED_HASH_MAP_H
-
-#include "core/templates/hash_map.h"
-#include "core/templates/list.h"
-#include "core/templates/pair.h"
-
-/**
- * A hash map which allows to iterate elements in insertion order.
- * Insertion, lookup, deletion have O(1) complexity.
- * The API aims to be consistent with Map rather than HashMap, because the
- * former is more frequently used and is more coherent with the rest of the
- * codebase.
- * Deletion during iteration is safe and will preserve the order.
- */
-template <class K, class V, class Hasher = HashMapHasherDefault, class Comparator = HashMapComparatorDefault<K>, uint8_t MIN_HASH_TABLE_POWER = 3, uint8_t RELATIONSHIP = 8>
-class OrderedHashMap {
- typedef List<Pair<const K *, V>> InternalList;
- typedef HashMap<K, typename InternalList::Element *, Hasher, Comparator, MIN_HASH_TABLE_POWER, RELATIONSHIP> InternalMap;
-
- InternalList list;
- InternalMap map;
-
-public:
- class Element {
- friend class OrderedHashMap<K, V, Hasher, Comparator, MIN_HASH_TABLE_POWER, RELATIONSHIP>;
-
- typename InternalList::Element *list_element = nullptr;
- typename InternalList::Element *prev_element = nullptr;
- typename InternalList::Element *next_element = nullptr;
-
- Element(typename InternalList::Element *p_element) {
- list_element = p_element;
-
- if (list_element) {
- next_element = list_element->next();
- prev_element = list_element->prev();
- }
- }
-
- public:
- _FORCE_INLINE_ Element() {}
-
- Element next() const {
- return Element(next_element);
- }
-
- Element prev() const {
- return Element(prev_element);
- }
-
- Element(const Element &other) :
- list_element(other.list_element),
- prev_element(other.prev_element),
- next_element(other.next_element) {
- }
-
- void operator=(const Element &other) {
- list_element = other.list_element;
- next_element = other.next_element;
- prev_element = other.prev_element;
- }
-
- _FORCE_INLINE_ bool operator==(const Element &p_other) const {
- return this->list_element == p_other.list_element;
- }
- _FORCE_INLINE_ bool operator!=(const Element &p_other) const {
- return this->list_element != p_other.list_element;
- }
-
- operator bool() const {
- return (list_element != nullptr);
- }
-
- const K &key() const {
- CRASH_COND(!list_element);
- return *(list_element->get().first);
- }
-
- V &value() {
- CRASH_COND(!list_element);
- return list_element->get().second;
- }
-
- const V &value() const {
- CRASH_COND(!list_element);
- return list_element->get().second;
- }
-
- V &get() {
- CRASH_COND(!list_element);
- return list_element->get().second;
- }
-
- const V &get() const {
- CRASH_COND(!list_element);
- return list_element->get().second;
- }
- };
-
- class ConstElement {
- friend class OrderedHashMap<K, V, Hasher, Comparator, MIN_HASH_TABLE_POWER, RELATIONSHIP>;
-
- const typename InternalList::Element *list_element = nullptr;
-
- ConstElement(const typename InternalList::Element *p_element) :
- list_element(p_element) {
- }
-
- public:
- _FORCE_INLINE_ ConstElement() {}
-
- ConstElement(const ConstElement &other) :
- list_element(other.list_element) {
- }
-
- void operator=(const ConstElement &other) {
- list_element = other.list_element;
- }
-
- ConstElement next() const {
- return ConstElement(list_element ? list_element->next() : nullptr);
- }
-
- ConstElement prev() const {
- return ConstElement(list_element ? list_element->prev() : nullptr);
- }
-
- _FORCE_INLINE_ bool operator==(const ConstElement &p_other) const {
- return this->list_element == p_other.list_element;
- }
- _FORCE_INLINE_ bool operator!=(const ConstElement &p_other) const {
- return this->list_element != p_other.list_element;
- }
-
- operator bool() const {
- return (list_element != nullptr);
- }
-
- const K &key() const {
- CRASH_COND(!list_element);
- return *(list_element->get().first);
- }
-
- const V &value() const {
- CRASH_COND(!list_element);
- return list_element->get().second;
- }
-
- const V &get() const {
- CRASH_COND(!list_element);
- return list_element->get().second;
- }
- };
-
- ConstElement find(const K &p_key) const {
- typename InternalList::Element *const *list_element = map.getptr(p_key);
- if (list_element) {
- return ConstElement(*list_element);
- }
- return ConstElement(nullptr);
- }
-
- Element find(const K &p_key) {
- typename InternalList::Element **list_element = map.getptr(p_key);
- if (list_element) {
- return Element(*list_element);
- }
- return Element(nullptr);
- }
-
- Element insert(const K &p_key, const V &p_value) {
- typename InternalList::Element **list_element = map.getptr(p_key);
- if (list_element) {
- (*list_element)->get().second = p_value;
- return Element(*list_element);
- }
- // Incorrectly set the first value of the pair with a value that will
- // be invalid as soon as we leave this function...
- typename InternalList::Element *new_element = list.push_back(Pair<const K *, V>(&p_key, p_value));
- // ...this is needed here in case the hashmap recursively reference itself...
- typename InternalMap::Element *e = map.set(p_key, new_element);
- // ...now we can set the right value !
- new_element->get().first = &e->key();
-
- return Element(new_element);
- }
-
- void erase(Element &p_element) {
- map.erase(p_element.key());
- list.erase(p_element.list_element);
- p_element.list_element = nullptr;
- }
-
- bool erase(const K &p_key) {
- typename InternalList::Element **list_element = map.getptr(p_key);
- if (list_element) {
- list.erase(*list_element);
- map.erase(p_key);
- return true;
- }
- return false;
- }
-
- inline bool has(const K &p_key) const {
- return map.has(p_key);
- }
-
- const V &operator[](const K &p_key) const {
- ConstElement e = find(p_key);
- CRASH_COND(!e);
- return e.value();
- }
-
- V &operator[](const K &p_key) {
- Element e = find(p_key);
- if (!e) {
- // consistent with Map behaviour
- e = insert(p_key, V());
- }
- return e.value();
- }
-
- inline Element front() {
- return Element(list.front());
- }
-
- inline Element back() {
- return Element(list.back());
- }
-
- inline ConstElement front() const {
- return ConstElement(list.front());
- }
-
- inline ConstElement back() const {
- return ConstElement(list.back());
- }
-
- inline bool is_empty() const { return list.is_empty(); }
- inline int size() const { return list.size(); }
-
- const void *id() const {
- return list.id();
- }
-
- void clear() {
- map.clear();
- list.clear();
- }
-
-private:
- void _copy_from(const OrderedHashMap &p_map) {
- for (ConstElement E = p_map.front(); E; E = E.next()) {
- insert(E.key(), E.value());
- }
- }
-
-public:
- void operator=(const OrderedHashMap &p_map) {
- _copy_from(p_map);
- }
-
- OrderedHashMap(const OrderedHashMap &p_map) {
- _copy_from(p_map);
- }
-
- _FORCE_INLINE_ OrderedHashMap() {}
-};
-
-#endif // ORDERED_HASH_MAP_H
diff --git a/core/templates/paged_allocator.h b/core/templates/paged_allocator.h
index b9067e2edd..cf5911a847 100644
--- a/core/templates/paged_allocator.h
+++ b/core/templates/paged_allocator.h
@@ -50,6 +50,10 @@ class PagedAllocator {
SpinLock spin_lock;
public:
+ enum {
+ DEFAULT_PAGE_SIZE = 4096
+ };
+
template <class... Args>
T *alloc(const Args &&...p_args) {
if (thread_safe) {
@@ -121,7 +125,9 @@ public:
page_shift = get_shift_from_power_of_2(page_size);
}
- PagedAllocator(uint32_t p_page_size = 4096) { // power of 2 recommended because of alignment with OS page sizes. Even if element is bigger, its still a multiple and get rounded amount of pages
+ // Power of 2 recommended because of alignment with OS page sizes.
+ // Even if element is bigger, it's still a multiple and gets rounded to amount of pages.
+ PagedAllocator(uint32_t p_page_size = DEFAULT_PAGE_SIZE) {
configure(p_page_size);
}
diff --git a/core/templates/vector.h b/core/templates/vector.h
index d87e76139b..2ac7c7630a 100644
--- a/core/templates/vector.h
+++ b/core/templates/vector.h
@@ -92,6 +92,8 @@ public:
_FORCE_INLINE_ const T &operator[](int p_index) const { return _cowdata.get(p_index); }
Error insert(int p_pos, T p_val) { return _cowdata.insert(p_pos, p_val); }
int find(const T &p_val, int p_from = 0) const { return _cowdata.find(p_val, p_from); }
+ int rfind(const T &p_val, int p_from = -1) const { return _cowdata.rfind(p_val, p_from); }
+ int count(const T &p_val) const { return _cowdata.count(p_val); }
void append_array(Vector<T> p_other);
diff --git a/core/variant/dictionary.cpp b/core/variant/dictionary.cpp
index 0f2f8fc8ed..46543da2c2 100644
--- a/core/variant/dictionary.cpp
+++ b/core/variant/dictionary.cpp
@@ -30,7 +30,7 @@
#include "dictionary.h"
-#include "core/templates/ordered_hash_map.h"
+#include "core/templates/hash_map.h"
#include "core/templates/safe_refcount.h"
#include "core/variant/variant.h"
// required in this order by VariantInternal, do not remove this comment.
@@ -41,7 +41,7 @@
struct DictionaryPrivate {
SafeRefCount refcount;
- OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator> variant_map;
+ HashMap<Variant, Variant, VariantHasher, VariantComparator> variant_map;
};
void Dictionary::get_key_list(List<Variant> *p_keys) const {
@@ -49,16 +49,16 @@ void Dictionary::get_key_list(List<Variant> *p_keys) const {
return;
}
- for (OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator>::Element E = _p->variant_map.front(); E; E = E.next()) {
- p_keys->push_back(E.key());
+ for (const KeyValue<Variant, Variant> &E : _p->variant_map) {
+ p_keys->push_back(E.key);
}
}
Variant Dictionary::get_key_at_index(int p_index) const {
int index = 0;
- for (OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator>::Element E = _p->variant_map.front(); E; E = E.next()) {
+ for (const KeyValue<Variant, Variant> &E : _p->variant_map) {
if (index == p_index) {
- return E.key();
+ return E.key;
}
index++;
}
@@ -68,9 +68,9 @@ Variant Dictionary::get_key_at_index(int p_index) const {
Variant Dictionary::get_value_at_index(int p_index) const {
int index = 0;
- for (OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator>::Element E = _p->variant_map.front(); E; E = E.next()) {
+ for (const KeyValue<Variant, Variant> &E : _p->variant_map) {
if (index == p_index) {
- return E.value();
+ return E.value;
}
index++;
}
@@ -97,50 +97,50 @@ const Variant &Dictionary::operator[](const Variant &p_key) const {
}
const Variant *Dictionary::getptr(const Variant &p_key) const {
- OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator>::ConstElement E;
+ HashMap<Variant, Variant, VariantHasher, VariantComparator>::ConstIterator E;
if (p_key.get_type() == Variant::STRING_NAME) {
const StringName *sn = VariantInternal::get_string_name(&p_key);
- E = ((const OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator> *)&_p->variant_map)->find(sn->operator String());
+ E = ((const HashMap<Variant, Variant, VariantHasher, VariantComparator> *)&_p->variant_map)->find(sn->operator String());
} else {
- E = ((const OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator> *)&_p->variant_map)->find(p_key);
+ E = ((const HashMap<Variant, Variant, VariantHasher, VariantComparator> *)&_p->variant_map)->find(p_key);
}
if (!E) {
return nullptr;
}
- return &E.get();
+ return &E->value;
}
Variant *Dictionary::getptr(const Variant &p_key) {
- OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator>::Element E;
+ HashMap<Variant, Variant, VariantHasher, VariantComparator>::Iterator E;
if (p_key.get_type() == Variant::STRING_NAME) {
const StringName *sn = VariantInternal::get_string_name(&p_key);
- E = ((OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator> *)&_p->variant_map)->find(sn->operator String());
+ E = ((HashMap<Variant, Variant, VariantHasher, VariantComparator> *)&_p->variant_map)->find(sn->operator String());
} else {
- E = ((OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator> *)&_p->variant_map)->find(p_key);
+ E = ((HashMap<Variant, Variant, VariantHasher, VariantComparator> *)&_p->variant_map)->find(p_key);
}
if (!E) {
return nullptr;
}
- return &E.get();
+ return &E->value;
}
Variant Dictionary::get_valid(const Variant &p_key) const {
- OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator>::ConstElement E;
+ HashMap<Variant, Variant, VariantHasher, VariantComparator>::ConstIterator E;
if (p_key.get_type() == Variant::STRING_NAME) {
const StringName *sn = VariantInternal::get_string_name(&p_key);
- E = ((const OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator> *)&_p->variant_map)->find(sn->operator String());
+ E = ((const HashMap<Variant, Variant, VariantHasher, VariantComparator> *)&_p->variant_map)->find(sn->operator String());
} else {
- E = ((const OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator> *)&_p->variant_map)->find(p_key);
+ E = ((const HashMap<Variant, Variant, VariantHasher, VariantComparator> *)&_p->variant_map)->find(p_key);
}
if (!E) {
return Variant();
}
- return E.get();
+ return E->value;
}
Variant Dictionary::get(const Variant &p_key, const Variant &p_default) const {
@@ -210,9 +210,9 @@ bool Dictionary::recursive_equal(const Dictionary &p_dictionary, int recursion_c
return true;
}
recursion_count++;
- for (OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator>::ConstElement this_E = ((const OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator> *)&_p->variant_map)->front(); this_E; this_E = this_E.next()) {
- OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator>::ConstElement other_E = ((const OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator> *)&p_dictionary._p->variant_map)->find(this_E.key());
- if (!other_E || !this_E.value().hash_compare(other_E.value(), recursion_count)) {
+ for (const KeyValue<Variant, Variant> &this_E : _p->variant_map) {
+ HashMap<Variant, Variant, VariantHasher, VariantComparator>::ConstIterator other_E = ((const HashMap<Variant, Variant, VariantHasher, VariantComparator> *)&p_dictionary._p->variant_map)->find(this_E.key);
+ if (!other_E || !this_E.value.hash_compare(other_E->value, recursion_count)) {
return false;
}
}
@@ -261,9 +261,9 @@ uint32_t Dictionary::recursive_hash(int recursion_count) const {
uint32_t h = hash_djb2_one_32(Variant::DICTIONARY);
recursion_count++;
- for (OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator>::Element E = _p->variant_map.front(); E; E = E.next()) {
- h = hash_djb2_one_32(E.key().recursive_hash(recursion_count), h);
- h = hash_djb2_one_32(E.value().recursive_hash(recursion_count), h);
+ for (const KeyValue<Variant, Variant> &E : _p->variant_map) {
+ h = hash_djb2_one_32(E.key.recursive_hash(recursion_count), h);
+ h = hash_djb2_one_32(E.value.recursive_hash(recursion_count), h);
}
return h;
@@ -278,8 +278,8 @@ Array Dictionary::keys() const {
varr.resize(size());
int i = 0;
- for (OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator>::Element E = _p->variant_map.front(); E; E = E.next()) {
- varr[i] = E.key();
+ for (const KeyValue<Variant, Variant> &E : _p->variant_map) {
+ varr[i] = E.key;
i++;
}
@@ -295,8 +295,8 @@ Array Dictionary::values() const {
varr.resize(size());
int i = 0;
- for (OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator>::Element E = _p->variant_map.front(); E; E = E.next()) {
- varr[i] = E.get();
+ for (const KeyValue<Variant, Variant> &E : _p->variant_map) {
+ varr[i] = E.value;
i++;
}
@@ -306,16 +306,23 @@ Array Dictionary::values() const {
const Variant *Dictionary::next(const Variant *p_key) const {
if (p_key == nullptr) {
// caller wants to get the first element
- if (_p->variant_map.front()) {
- return &_p->variant_map.front().key();
+ if (_p->variant_map.begin()) {
+ return &_p->variant_map.begin()->key;
}
return nullptr;
}
- OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator>::Element E = _p->variant_map.find(*p_key);
+ HashMap<Variant, Variant, VariantHasher, VariantComparator>::Iterator E = _p->variant_map.find(*p_key);
- if (E && E.next()) {
- return &E.next().key();
+ if (!E) {
+ return nullptr;
+ }
+
+ ++E;
+
+ if (E) {
+ return &E->key;
}
+
return nullptr;
}
@@ -333,12 +340,12 @@ Dictionary Dictionary::recursive_duplicate(bool p_deep, int recursion_count) con
if (p_deep) {
recursion_count++;
- for (OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator>::Element E = _p->variant_map.front(); E; E = E.next()) {
- n[E.key().recursive_duplicate(true, recursion_count)] = E.value().recursive_duplicate(true, recursion_count);
+ for (const KeyValue<Variant, Variant> &E : _p->variant_map) {
+ n[E.key.recursive_duplicate(true, recursion_count)] = E.value.recursive_duplicate(true, recursion_count);
}
} else {
- for (OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator>::Element E = _p->variant_map.front(); E; E = E.next()) {
- n[E.key()] = E.value();
+ for (const KeyValue<Variant, Variant> &E : _p->variant_map) {
+ n[E.key] = E.value;
}
}
diff --git a/core/variant/variant.h b/core/variant/variant.h
index 475bf7158d..726ba120b5 100644
--- a/core/variant/variant.h
+++ b/core/variant/variant.h
@@ -473,6 +473,7 @@ public:
OP_NEGATE,
OP_POSITIVE,
OP_MODULE,
+ OP_POWER,
//bitwise
OP_SHIFT_LEFT,
OP_SHIFT_RIGHT,
diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp
index f4b2af5a94..882a89b8ba 100644
--- a/core/variant/variant_call.cpp
+++ b/core/variant/variant_call.cpp
@@ -1873,6 +1873,9 @@ static void _register_variant_builtin_methods() {
bind_method(PackedByteArray, sort, sarray(), varray());
bind_method(PackedByteArray, bsearch, sarray("value", "before"), varray(true));
bind_method(PackedByteArray, duplicate, sarray(), varray());
+ bind_method(PackedByteArray, find, sarray("value", "from"), varray(0));
+ bind_method(PackedByteArray, rfind, sarray("value", "from"), varray(-1));
+ bind_method(PackedByteArray, count, sarray("value"), varray());
bind_function(PackedByteArray, get_string_from_ascii, _VariantCall::func_PackedByteArray_get_string_from_ascii, sarray(), varray());
bind_function(PackedByteArray, get_string_from_utf8, _VariantCall::func_PackedByteArray_get_string_from_utf8, sarray(), varray());
@@ -1935,6 +1938,9 @@ static void _register_variant_builtin_methods() {
bind_method(PackedInt32Array, sort, sarray(), varray());
bind_method(PackedInt32Array, bsearch, sarray("value", "before"), varray(true));
bind_method(PackedInt32Array, duplicate, sarray(), varray());
+ bind_method(PackedInt32Array, find, sarray("value", "from"), varray(0));
+ bind_method(PackedInt32Array, rfind, sarray("value", "from"), varray(-1));
+ bind_method(PackedInt32Array, count, sarray("value"), varray());
/* Int64 Array */
@@ -1955,6 +1961,9 @@ static void _register_variant_builtin_methods() {
bind_method(PackedInt64Array, sort, sarray(), varray());
bind_method(PackedInt64Array, bsearch, sarray("value", "before"), varray(true));
bind_method(PackedInt64Array, duplicate, sarray(), varray());
+ bind_method(PackedInt64Array, find, sarray("value", "from"), varray(0));
+ bind_method(PackedInt64Array, rfind, sarray("value", "from"), varray(-1));
+ bind_method(PackedInt64Array, count, sarray("value"), varray());
/* Float32 Array */
@@ -1975,6 +1984,9 @@ static void _register_variant_builtin_methods() {
bind_method(PackedFloat32Array, sort, sarray(), varray());
bind_method(PackedFloat32Array, bsearch, sarray("value", "before"), varray(true));
bind_method(PackedFloat32Array, duplicate, sarray(), varray());
+ bind_method(PackedFloat32Array, find, sarray("value", "from"), varray(0));
+ bind_method(PackedFloat32Array, rfind, sarray("value", "from"), varray(-1));
+ bind_method(PackedFloat32Array, count, sarray("value"), varray());
/* Float64 Array */
@@ -1995,6 +2007,9 @@ static void _register_variant_builtin_methods() {
bind_method(PackedFloat64Array, sort, sarray(), varray());
bind_method(PackedFloat64Array, bsearch, sarray("value", "before"), varray(true));
bind_method(PackedFloat64Array, duplicate, sarray(), varray());
+ bind_method(PackedFloat64Array, find, sarray("value", "from"), varray(0));
+ bind_method(PackedFloat64Array, rfind, sarray("value", "from"), varray(-1));
+ bind_method(PackedFloat64Array, count, sarray("value"), varray());
/* String Array */
@@ -2015,6 +2030,9 @@ static void _register_variant_builtin_methods() {
bind_method(PackedStringArray, sort, sarray(), varray());
bind_method(PackedStringArray, bsearch, sarray("value", "before"), varray(true));
bind_method(PackedStringArray, duplicate, sarray(), varray());
+ bind_method(PackedStringArray, find, sarray("value", "from"), varray(0));
+ bind_method(PackedStringArray, rfind, sarray("value", "from"), varray(-1));
+ bind_method(PackedStringArray, count, sarray("value"), varray());
/* Vector2 Array */
@@ -2035,6 +2053,9 @@ static void _register_variant_builtin_methods() {
bind_method(PackedVector2Array, sort, sarray(), varray());
bind_method(PackedVector2Array, bsearch, sarray("value", "before"), varray(true));
bind_method(PackedVector2Array, duplicate, sarray(), varray());
+ bind_method(PackedVector2Array, find, sarray("value", "from"), varray(0));
+ bind_method(PackedVector2Array, rfind, sarray("value", "from"), varray(-1));
+ bind_method(PackedVector2Array, count, sarray("value"), varray());
/* Vector3 Array */
@@ -2055,6 +2076,9 @@ static void _register_variant_builtin_methods() {
bind_method(PackedVector3Array, sort, sarray(), varray());
bind_method(PackedVector3Array, bsearch, sarray("value", "before"), varray(true));
bind_method(PackedVector3Array, duplicate, sarray(), varray());
+ bind_method(PackedVector3Array, find, sarray("value", "from"), varray(0));
+ bind_method(PackedVector3Array, rfind, sarray("value", "from"), varray(-1));
+ bind_method(PackedVector3Array, count, sarray("value"), varray());
/* Color Array */
@@ -2075,6 +2099,9 @@ static void _register_variant_builtin_methods() {
bind_method(PackedColorArray, sort, sarray(), varray());
bind_method(PackedColorArray, bsearch, sarray("value", "before"), varray(true));
bind_method(PackedColorArray, duplicate, sarray(), varray());
+ bind_method(PackedColorArray, find, sarray("value", "from"), varray(0));
+ bind_method(PackedColorArray, rfind, sarray("value", "from"), varray(-1));
+ bind_method(PackedColorArray, count, sarray("value"), varray());
/* Register constants */
diff --git a/core/variant/variant_construct.h b/core/variant/variant_construct.h
index ce2e9af04f..638c0136f3 100644
--- a/core/variant/variant_construct.h
+++ b/core/variant/variant_construct.h
@@ -344,7 +344,7 @@ public:
return;
}
- VariantTypeChanger<Array>::change(&r_ret);
+ r_ret = Array();
Array &dst_arr = *VariantGetInternalPtr<Array>::get_ptr(&r_ret);
const T &src_arr = *VariantGetInternalPtr<T>::get_ptr(p_args[0]);
@@ -356,7 +356,7 @@ public:
}
static inline void validated_construct(Variant *r_ret, const Variant **p_args) {
- VariantTypeChanger<Array>::change(r_ret);
+ *r_ret = Array();
Array &dst_arr = *VariantGetInternalPtr<Array>::get_ptr(r_ret);
const T &src_arr = *VariantGetInternalPtr<T>::get_ptr(p_args[0]);
diff --git a/core/variant/variant_op.cpp b/core/variant/variant_op.cpp
index 35e0319aa3..adace2b534 100644
--- a/core/variant/variant_op.cpp
+++ b/core/variant/variant_op.cpp
@@ -361,6 +361,11 @@ void Variant::_register_variant_operators() {
register_op<OperatorEvaluatorStringModT<PackedVector3Array>>(Variant::OP_MODULE, Variant::STRING, Variant::PACKED_VECTOR3_ARRAY);
register_op<OperatorEvaluatorStringModT<PackedColorArray>>(Variant::OP_MODULE, Variant::STRING, Variant::PACKED_COLOR_ARRAY);
+ register_op<OperatorEvaluatorPow<int64_t, int64_t, int64_t>>(Variant::OP_POWER, Variant::INT, Variant::INT);
+ register_op<OperatorEvaluatorPow<double, int64_t, double>>(Variant::OP_POWER, Variant::INT, Variant::FLOAT);
+ register_op<OperatorEvaluatorPow<double, double, double>>(Variant::OP_POWER, Variant::FLOAT, Variant::FLOAT);
+ register_op<OperatorEvaluatorPow<double, double, int64_t>>(Variant::OP_POWER, Variant::FLOAT, Variant::INT);
+
register_op<OperatorEvaluatorNeg<int64_t, int64_t>>(Variant::OP_NEGATE, Variant::INT, Variant::NIL);
register_op<OperatorEvaluatorNeg<double, double>>(Variant::OP_NEGATE, Variant::FLOAT, Variant::NIL);
register_op<OperatorEvaluatorNeg<Vector2, Vector2>>(Variant::OP_NEGATE, Variant::VECTOR2, Variant::NIL);
@@ -929,6 +934,7 @@ static const char *_op_names[Variant::OP_MAX] = {
"unary-",
"unary+",
"%",
+ "**",
"<<",
">>",
"&",
diff --git a/core/variant/variant_op.h b/core/variant/variant_op.h
index f72a92d31a..3e9bae1078 100644
--- a/core/variant/variant_op.h
+++ b/core/variant/variant_op.h
@@ -92,6 +92,24 @@ public:
};
template <class R, class A, class B>
+class OperatorEvaluatorPow {
+public:
+ static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
+ const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left);
+ const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right);
+ *r_ret = R(Math::pow((double)a, (double)b));
+ r_valid = true;
+ }
+ static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
+ *VariantGetInternalPtr<R>::get_ptr(r_ret) = R(Math::pow((double)*VariantGetInternalPtr<A>::get_ptr(left), (double)*VariantGetInternalPtr<B>::get_ptr(right)));
+ }
+ static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
+ PtrToArg<R>::encode(R(Math::pow((double)PtrToArg<A>::convert(left), (double)PtrToArg<B>::convert(right))), r_ret);
+ }
+ static Variant::Type get_return_type() { return GetTypeInfo<R>::VARIANT_TYPE; }
+};
+
+template <class R, class A, class B>
class OperatorEvaluatorXForm {
public:
static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml
index a40b851efc..5f81c80887 100644
--- a/doc/classes/@GlobalScope.xml
+++ b/doc/classes/@GlobalScope.xml
@@ -2799,40 +2799,43 @@
<constant name="OP_MODULE" value="12" enum="Variant.Operator">
Remainder/modulo operator ([code]%[/code]).
</constant>
- <constant name="OP_SHIFT_LEFT" value="13" enum="Variant.Operator">
+ <constant name="OP_POWER" value="13" enum="Variant.Operator">
+ Power operator ([code]**[/code]).
+ </constant>
+ <constant name="OP_SHIFT_LEFT" value="14" enum="Variant.Operator">
Left shift operator ([code]&lt;&lt;[/code]).
</constant>
- <constant name="OP_SHIFT_RIGHT" value="14" enum="Variant.Operator">
+ <constant name="OP_SHIFT_RIGHT" value="15" enum="Variant.Operator">
Right shift operator ([code]&gt;&gt;[/code]).
</constant>
- <constant name="OP_BIT_AND" value="15" enum="Variant.Operator">
+ <constant name="OP_BIT_AND" value="16" enum="Variant.Operator">
Bitwise AND operator ([code]&amp;[/code]).
</constant>
- <constant name="OP_BIT_OR" value="16" enum="Variant.Operator">
+ <constant name="OP_BIT_OR" value="17" enum="Variant.Operator">
Bitwise OR operator ([code]|[/code]).
</constant>
- <constant name="OP_BIT_XOR" value="17" enum="Variant.Operator">
+ <constant name="OP_BIT_XOR" value="18" enum="Variant.Operator">
Bitwise XOR operator ([code]^[/code]).
</constant>
- <constant name="OP_BIT_NEGATE" value="18" enum="Variant.Operator">
+ <constant name="OP_BIT_NEGATE" value="19" enum="Variant.Operator">
Bitwise NOT operator ([code]~[/code]).
</constant>
- <constant name="OP_AND" value="19" enum="Variant.Operator">
+ <constant name="OP_AND" value="20" enum="Variant.Operator">
Logical AND operator ([code]and[/code] or [code]&amp;&amp;[/code]).
</constant>
- <constant name="OP_OR" value="20" enum="Variant.Operator">
+ <constant name="OP_OR" value="21" enum="Variant.Operator">
Logical OR operator ([code]or[/code] or [code]||[/code]).
</constant>
- <constant name="OP_XOR" value="21" enum="Variant.Operator">
+ <constant name="OP_XOR" value="22" enum="Variant.Operator">
Logical XOR operator (not implemented in GDScript).
</constant>
- <constant name="OP_NOT" value="22" enum="Variant.Operator">
+ <constant name="OP_NOT" value="23" enum="Variant.Operator">
Logical NOT operator ([code]not[/code] or [code]![/code]).
</constant>
- <constant name="OP_IN" value="23" enum="Variant.Operator">
+ <constant name="OP_IN" value="24" enum="Variant.Operator">
Logical IN operator ([code]in[/code]).
</constant>
- <constant name="OP_MAX" value="24" enum="Variant.Operator">
+ <constant name="OP_MAX" value="25" enum="Variant.Operator">
Represents the size of the [enum Variant.Operator] enum.
</constant>
</constants>
diff --git a/doc/classes/AnimatedSprite2D.xml b/doc/classes/AnimatedSprite2D.xml
index e89134d1ac..638d142791 100644
--- a/doc/classes/AnimatedSprite2D.xml
+++ b/doc/classes/AnimatedSprite2D.xml
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="AnimatedSprite2D" inherits="Node2D" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Sprite node that can use multiple textures for animation.
+ Sprite node that contains multiple textures as frames to play for animation.
</brief_description>
<description>
- Animations are created using a [SpriteFrames] resource, which can be configured in the editor via the SpriteFrames panel.
+ [AnimatedSprite2D] is similar to the [Sprite2D] node, except it carries multiple textures as animation frames. Animations are created using a [SpriteFrames] resource, which allows you to import image files (or a folder containing said files) to provide the animation frames for the sprite. The [SpriteFrames] resource can be configured in the editor via the SpriteFrames bottom panel.
[b]Note:[/b] You can associate a set of normal or specular maps by creating additional [SpriteFrames] resources with a [code]_normal[/code] or [code]_specular[/code] suffix. For example, having 3 [SpriteFrames] resources [code]run[/code], [code]run_normal[/code], and [code]run_specular[/code] will make it so the [code]run[/code] animation uses normal and specular maps.
</description>
<tutorials>
@@ -29,7 +29,7 @@
</methods>
<members>
<member name="animation" type="StringName" setter="set_animation" getter="get_animation" default="&amp;&quot;default&quot;">
- The current animation from the [code]frames[/code] resource. If this value changes, the [code]frame[/code] counter is reset.
+ The current animation from the [member frames] resource. If this value changes, the [code]frame[/code] counter is reset.
</member>
<member name="centered" type="bool" setter="set_centered" getter="is_centered" default="true">
If [code]true[/code], texture will be centered.
@@ -44,7 +44,7 @@
The displayed animation frame's index.
</member>
<member name="frames" type="SpriteFrames" setter="set_sprite_frames" getter="get_sprite_frames">
- The [SpriteFrames] resource containing the animation(s).
+ The [SpriteFrames] resource containing the animation(s). Allows you the option to load, edit, clear, make unique and save the states of the [SpriteFrames] resource.
</member>
<member name="offset" type="Vector2" setter="set_offset" getter="get_offset" default="Vector2(0, 0)">
The texture's drawing offset.
diff --git a/doc/classes/CollisionPolygon2D.xml b/doc/classes/CollisionPolygon2D.xml
index a9d8a85226..f290fc9801 100644
--- a/doc/classes/CollisionPolygon2D.xml
+++ b/doc/classes/CollisionPolygon2D.xml
@@ -18,6 +18,7 @@
</member>
<member name="one_way_collision" type="bool" setter="set_one_way_collision" getter="is_one_way_collision_enabled" default="false">
If [code]true[/code], only edges that face up, relative to [CollisionPolygon2D]'s rotation, will collide with other objects.
+ [b]Note:[/b] This property has no effect if this [CollisionPolygon2D] is a child of an [Area2D] node.
</member>
<member name="one_way_collision_margin" type="float" setter="set_one_way_collision_margin" getter="get_one_way_collision_margin" default="1.0">
The margin used for one-way collision (in pixels). Higher values will make the shape thicker, and work better for colliders that enter the polygon at a high velocity.
diff --git a/doc/classes/CollisionShape2D.xml b/doc/classes/CollisionShape2D.xml
index f3a4cbc2d6..246e0e8663 100644
--- a/doc/classes/CollisionShape2D.xml
+++ b/doc/classes/CollisionShape2D.xml
@@ -18,6 +18,7 @@
</member>
<member name="one_way_collision" type="bool" setter="set_one_way_collision" getter="is_one_way_collision_enabled" default="false">
Sets whether this collision shape should only detect collision on one side (top or bottom).
+ [b]Note:[/b] This property has no effect if this [CollisionShape2D] is a child of an [Area2D] node.
</member>
<member name="one_way_collision_margin" type="float" setter="set_one_way_collision_margin" getter="get_one_way_collision_margin" default="1.0">
The margin used for one-way collision (in pixels). Higher values will make the shape thicker, and work better for colliders that enter the shape at a high velocity.
diff --git a/doc/classes/DirectionalLight3D.xml b/doc/classes/DirectionalLight3D.xml
index 754518239c..9c943b8c8b 100644
--- a/doc/classes/DirectionalLight3D.xml
+++ b/doc/classes/DirectionalLight3D.xml
@@ -34,7 +34,6 @@
<member name="directional_shadow_split_3" type="float" setter="set_param" getter="get_param" default="0.5">
The distance from shadow split 2 to split 3. Relative to [member directional_shadow_max_distance]. Only used when [member directional_shadow_mode] is [constant SHADOW_PARALLEL_4_SPLITS].
</member>
- <member name="shadow_bias" type="float" setter="set_param" getter="get_param" overrides="Light3D" default="0.1" />
<member name="sky_mode" type="int" setter="set_sky_mode" getter="get_sky_mode" enum="DirectionalLight3D.SkyMode" default="0">
Set whether this [DirectionalLight3D] is visible in the sky, in the scene, or both in the sky and in the scene. See [enum SkyMode] for options.
</member>
diff --git a/doc/classes/GPUParticlesCollisionHeightField3D.xml b/doc/classes/GPUParticlesCollisionHeightField3D.xml
index e7e14375d9..a7b59ea684 100644
--- a/doc/classes/GPUParticlesCollisionHeightField3D.xml
+++ b/doc/classes/GPUParticlesCollisionHeightField3D.xml
@@ -9,9 +9,7 @@
<members>
<member name="extents" type="Vector3" setter="set_extents" getter="get_extents" default="Vector3(1, 1, 1)">
</member>
- <member name="follow_camera_enabled" type="bool" setter="set_follow_camera_mode" getter="is_follow_camera_mode_enabled" default="false">
- </member>
- <member name="follow_camera_push_ratio" type="float" setter="set_follow_camera_push_ratio" getter="get_follow_camera_push_ratio" default="0.1">
+ <member name="follow_camera_enabled" type="bool" setter="set_follow_camera_enabled" getter="is_follow_camera_enabled" default="false">
</member>
<member name="resolution" type="int" setter="set_resolution" getter="get_resolution" enum="GPUParticlesCollisionHeightField3D.Resolution" default="2">
</member>
diff --git a/doc/classes/Light3D.xml b/doc/classes/Light3D.xml
index b7822f1bb0..4d8fd63257 100644
--- a/doc/classes/Light3D.xml
+++ b/doc/classes/Light3D.xml
@@ -80,7 +80,7 @@
<member name="light_specular" type="float" setter="set_param" getter="get_param" default="0.5">
The intensity of the specular blob in objects affected by the light. At [code]0[/code], the light becomes a pure diffuse light. When not baking emission, this can be used to avoid unrealistic reflections when placing lights above an emissive surface.
</member>
- <member name="shadow_bias" type="float" setter="set_param" getter="get_param" default="0.2">
+ <member name="shadow_bias" type="float" setter="set_param" getter="get_param" default="0.1">
Used to adjust shadow appearance. Too small a value results in self-shadowing ("shadow acne"), while too large a value causes shadows to separate from casters ("peter-panning"). Adjust as needed.
</member>
<member name="shadow_blur" type="float" setter="set_param" getter="get_param" default="1.0">
diff --git a/doc/classes/OmniLight3D.xml b/doc/classes/OmniLight3D.xml
index f83d31a9b5..ce63dbdbc1 100644
--- a/doc/classes/OmniLight3D.xml
+++ b/doc/classes/OmniLight3D.xml
@@ -19,6 +19,7 @@
<member name="omni_shadow_mode" type="int" setter="set_shadow_mode" getter="get_shadow_mode" enum="OmniLight3D.ShadowMode" default="1">
See [enum ShadowMode].
</member>
+ <member name="shadow_bias" type="float" setter="set_param" getter="get_param" overrides="Light3D" default="0.2" />
</members>
<constants>
<constant name="SHADOW_DUAL_PARABOLOID" value="0" enum="ShadowMode">
diff --git a/doc/classes/PackedByteArray.xml b/doc/classes/PackedByteArray.xml
index 61dc1eef00..5d0861bcf3 100644
--- a/doc/classes/PackedByteArray.xml
+++ b/doc/classes/PackedByteArray.xml
@@ -61,6 +61,13 @@
Returns a new [PackedByteArray] with the data compressed. Set the compression mode using one of [enum File.CompressionMode]'s constants.
</description>
</method>
+ <method name="count" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="int" />
+ <description>
+ Returns the number of times an element is in the array.
+ </description>
+ </method>
<method name="decode_double" qualifiers="const">
<return type="float" />
<argument index="0" name="byte_offset" type="int" />
@@ -257,6 +264,14 @@
Assigns the given value to all elements in the array. This can typically be used together with [method resize] to create an array with a given size and initialized elements.
</description>
</method>
+ <method name="find" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="int" />
+ <argument index="1" name="from" type="int" default="0" />
+ <description>
+ Searches the array for a value and returns its index or [code]-1[/code] if not found. Optionally, the initial search index can be passed.
+ </description>
+ </method>
<method name="get_string_from_ascii" qualifiers="const">
<return type="String" />
<description>
@@ -352,6 +367,14 @@
Reverses the order of the elements in the array.
</description>
</method>
+ <method name="rfind" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="int" />
+ <argument index="1" name="from" type="int" default="-1" />
+ <description>
+ Searches the array in reverse order. Optionally, a start search index can be passed. If negative, the start index is considered relative to the end of the array.
+ </description>
+ </method>
<method name="set">
<return type="void" />
<argument index="0" name="index" type="int" />
diff --git a/doc/classes/PackedColorArray.xml b/doc/classes/PackedColorArray.xml
index f880771c77..12a553af49 100644
--- a/doc/classes/PackedColorArray.xml
+++ b/doc/classes/PackedColorArray.xml
@@ -54,6 +54,13 @@
[b]Note:[/b] Calling [method bsearch] on an unsorted array results in unexpected behavior.
</description>
</method>
+ <method name="count" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="Color" />
+ <description>
+ Returns the number of times an element is in the array.
+ </description>
+ </method>
<method name="duplicate">
<return type="PackedColorArray" />
<description>
@@ -67,6 +74,14 @@
Assigns the given value to all elements in the array. This can typically be used together with [method resize] to create an array with a given size and initialized elements.
</description>
</method>
+ <method name="find" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="Color" />
+ <argument index="1" name="from" type="int" default="0" />
+ <description>
+ Searches the array for a value and returns its index or [code]-1[/code] if not found. Optionally, the initial search index can be passed.
+ </description>
+ </method>
<method name="has" qualifiers="const">
<return type="bool" />
<argument index="0" name="value" type="Color" />
@@ -115,6 +130,14 @@
Reverses the order of the elements in the array.
</description>
</method>
+ <method name="rfind" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="Color" />
+ <argument index="1" name="from" type="int" default="-1" />
+ <description>
+ Searches the array in reverse order. Optionally, a start search index can be passed. If negative, the start index is considered relative to the end of the array.
+ </description>
+ </method>
<method name="set">
<return type="void" />
<argument index="0" name="index" type="int" />
diff --git a/doc/classes/PackedFloat32Array.xml b/doc/classes/PackedFloat32Array.xml
index e2b877ad5f..0a114e6c06 100644
--- a/doc/classes/PackedFloat32Array.xml
+++ b/doc/classes/PackedFloat32Array.xml
@@ -55,6 +55,13 @@
[b]Note:[/b] Calling [method bsearch] on an unsorted array results in unexpected behavior.
</description>
</method>
+ <method name="count" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="float" />
+ <description>
+ Returns the number of times an element is in the array.
+ </description>
+ </method>
<method name="duplicate">
<return type="PackedFloat32Array" />
<description>
@@ -68,6 +75,14 @@
Assigns the given value to all elements in the array. This can typically be used together with [method resize] to create an array with a given size and initialized elements.
</description>
</method>
+ <method name="find" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="float" />
+ <argument index="1" name="from" type="int" default="0" />
+ <description>
+ Searches the array for a value and returns its index or [code]-1[/code] if not found. Optionally, the initial search index can be passed.
+ </description>
+ </method>
<method name="has" qualifiers="const">
<return type="bool" />
<argument index="0" name="value" type="float" />
@@ -116,6 +131,14 @@
Reverses the order of the elements in the array.
</description>
</method>
+ <method name="rfind" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="float" />
+ <argument index="1" name="from" type="int" default="-1" />
+ <description>
+ Searches the array in reverse order. Optionally, a start search index can be passed. If negative, the start index is considered relative to the end of the array.
+ </description>
+ </method>
<method name="set">
<return type="void" />
<argument index="0" name="index" type="int" />
diff --git a/doc/classes/PackedFloat64Array.xml b/doc/classes/PackedFloat64Array.xml
index be7a52b7f4..0327559f5b 100644
--- a/doc/classes/PackedFloat64Array.xml
+++ b/doc/classes/PackedFloat64Array.xml
@@ -55,6 +55,13 @@
[b]Note:[/b] Calling [method bsearch] on an unsorted array results in unexpected behavior.
</description>
</method>
+ <method name="count" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="float" />
+ <description>
+ Returns the number of times an element is in the array.
+ </description>
+ </method>
<method name="duplicate">
<return type="PackedFloat64Array" />
<description>
@@ -68,6 +75,14 @@
Assigns the given value to all elements in the array. This can typically be used together with [method resize] to create an array with a given size and initialized elements.
</description>
</method>
+ <method name="find" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="float" />
+ <argument index="1" name="from" type="int" default="0" />
+ <description>
+ Searches the array for a value and returns its index or [code]-1[/code] if not found. Optionally, the initial search index can be passed.
+ </description>
+ </method>
<method name="has" qualifiers="const">
<return type="bool" />
<argument index="0" name="value" type="float" />
@@ -116,6 +131,14 @@
Reverses the order of the elements in the array.
</description>
</method>
+ <method name="rfind" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="float" />
+ <argument index="1" name="from" type="int" default="-1" />
+ <description>
+ Searches the array in reverse order. Optionally, a start search index can be passed. If negative, the start index is considered relative to the end of the array.
+ </description>
+ </method>
<method name="set">
<return type="void" />
<argument index="0" name="index" type="int" />
diff --git a/doc/classes/PackedInt32Array.xml b/doc/classes/PackedInt32Array.xml
index 108273d859..f8b606d266 100644
--- a/doc/classes/PackedInt32Array.xml
+++ b/doc/classes/PackedInt32Array.xml
@@ -55,6 +55,13 @@
[b]Note:[/b] Calling [method bsearch] on an unsorted array results in unexpected behavior.
</description>
</method>
+ <method name="count" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="int" />
+ <description>
+ Returns the number of times an element is in the array.
+ </description>
+ </method>
<method name="duplicate">
<return type="PackedInt32Array" />
<description>
@@ -68,6 +75,14 @@
Assigns the given value to all elements in the array. This can typically be used together with [method resize] to create an array with a given size and initialized elements.
</description>
</method>
+ <method name="find" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="int" />
+ <argument index="1" name="from" type="int" default="0" />
+ <description>
+ Searches the array for a value and returns its index or [code]-1[/code] if not found. Optionally, the initial search index can be passed.
+ </description>
+ </method>
<method name="has" qualifiers="const">
<return type="bool" />
<argument index="0" name="value" type="int" />
@@ -116,6 +131,14 @@
Reverses the order of the elements in the array.
</description>
</method>
+ <method name="rfind" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="int" />
+ <argument index="1" name="from" type="int" default="-1" />
+ <description>
+ Searches the array in reverse order. Optionally, a start search index can be passed. If negative, the start index is considered relative to the end of the array.
+ </description>
+ </method>
<method name="set">
<return type="void" />
<argument index="0" name="index" type="int" />
diff --git a/doc/classes/PackedInt64Array.xml b/doc/classes/PackedInt64Array.xml
index c34f2fc75e..ea3e304d35 100644
--- a/doc/classes/PackedInt64Array.xml
+++ b/doc/classes/PackedInt64Array.xml
@@ -55,6 +55,13 @@
[b]Note:[/b] Calling [method bsearch] on an unsorted array results in unexpected behavior.
</description>
</method>
+ <method name="count" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="int" />
+ <description>
+ Returns the number of times an element is in the array.
+ </description>
+ </method>
<method name="duplicate">
<return type="PackedInt64Array" />
<description>
@@ -68,6 +75,14 @@
Assigns the given value to all elements in the array. This can typically be used together with [method resize] to create an array with a given size and initialized elements.
</description>
</method>
+ <method name="find" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="int" />
+ <argument index="1" name="from" type="int" default="0" />
+ <description>
+ Searches the array for a value and returns its index or [code]-1[/code] if not found. Optionally, the initial search index can be passed.
+ </description>
+ </method>
<method name="has" qualifiers="const">
<return type="bool" />
<argument index="0" name="value" type="int" />
@@ -116,6 +131,14 @@
Reverses the order of the elements in the array.
</description>
</method>
+ <method name="rfind" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="int" />
+ <argument index="1" name="from" type="int" default="-1" />
+ <description>
+ Searches the array in reverse order. Optionally, a start search index can be passed. If negative, the start index is considered relative to the end of the array.
+ </description>
+ </method>
<method name="set">
<return type="void" />
<argument index="0" name="index" type="int" />
diff --git a/doc/classes/PackedStringArray.xml b/doc/classes/PackedStringArray.xml
index 536f5f02eb..a4653344f0 100644
--- a/doc/classes/PackedStringArray.xml
+++ b/doc/classes/PackedStringArray.xml
@@ -5,6 +5,12 @@
</brief_description>
<description>
An array specifically designed to hold [String]s. Packs data tightly, so it saves memory for large array sizes.
+ If you want to join the strings in the array, use [method String.join].
+ [codeblock]
+ var string_array = PackedStringArray(["hello", "world"])
+ var string = " ".join(string_array)
+ print(string) # "hello world"
+ [/codeblock]
</description>
<tutorials>
<link title="OS Test Demo">https://godotengine.org/asset-library/asset/677</link>
@@ -55,6 +61,13 @@
[b]Note:[/b] Calling [method bsearch] on an unsorted array results in unexpected behavior.
</description>
</method>
+ <method name="count" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="String" />
+ <description>
+ Returns the number of times an element is in the array.
+ </description>
+ </method>
<method name="duplicate">
<return type="PackedStringArray" />
<description>
@@ -68,6 +81,14 @@
Assigns the given value to all elements in the array. This can typically be used together with [method resize] to create an array with a given size and initialized elements.
</description>
</method>
+ <method name="find" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="String" />
+ <argument index="1" name="from" type="int" default="0" />
+ <description>
+ Searches the array for a value and returns its index or [code]-1[/code] if not found. Optionally, the initial search index can be passed.
+ </description>
+ </method>
<method name="has" qualifiers="const">
<return type="bool" />
<argument index="0" name="value" type="String" />
@@ -116,6 +137,14 @@
Reverses the order of the elements in the array.
</description>
</method>
+ <method name="rfind" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="String" />
+ <argument index="1" name="from" type="int" default="-1" />
+ <description>
+ Searches the array in reverse order. Optionally, a start search index can be passed. If negative, the start index is considered relative to the end of the array.
+ </description>
+ </method>
<method name="set">
<return type="void" />
<argument index="0" name="index" type="int" />
diff --git a/doc/classes/PackedVector2Array.xml b/doc/classes/PackedVector2Array.xml
index 29423d1cde..8f3e5d173d 100644
--- a/doc/classes/PackedVector2Array.xml
+++ b/doc/classes/PackedVector2Array.xml
@@ -55,6 +55,13 @@
[b]Note:[/b] Calling [method bsearch] on an unsorted array results in unexpected behavior.
</description>
</method>
+ <method name="count" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="Vector2" />
+ <description>
+ Returns the number of times an element is in the array.
+ </description>
+ </method>
<method name="duplicate">
<return type="PackedVector2Array" />
<description>
@@ -68,6 +75,14 @@
Assigns the given value to all elements in the array. This can typically be used together with [method resize] to create an array with a given size and initialized elements.
</description>
</method>
+ <method name="find" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="Vector2" />
+ <argument index="1" name="from" type="int" default="0" />
+ <description>
+ Searches the array for a value and returns its index or [code]-1[/code] if not found. Optionally, the initial search index can be passed.
+ </description>
+ </method>
<method name="has" qualifiers="const">
<return type="bool" />
<argument index="0" name="value" type="Vector2" />
@@ -116,6 +131,14 @@
Reverses the order of the elements in the array.
</description>
</method>
+ <method name="rfind" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="Vector2" />
+ <argument index="1" name="from" type="int" default="-1" />
+ <description>
+ Searches the array in reverse order. Optionally, a start search index can be passed. If negative, the start index is considered relative to the end of the array.
+ </description>
+ </method>
<method name="set">
<return type="void" />
<argument index="0" name="index" type="int" />
diff --git a/doc/classes/PackedVector3Array.xml b/doc/classes/PackedVector3Array.xml
index 6d460cecab..1207293c32 100644
--- a/doc/classes/PackedVector3Array.xml
+++ b/doc/classes/PackedVector3Array.xml
@@ -54,6 +54,13 @@
[b]Note:[/b] Calling [method bsearch] on an unsorted array results in unexpected behavior.
</description>
</method>
+ <method name="count" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="Vector3" />
+ <description>
+ Returns the number of times an element is in the array.
+ </description>
+ </method>
<method name="duplicate">
<return type="PackedVector3Array" />
<description>
@@ -67,6 +74,14 @@
Assigns the given value to all elements in the array. This can typically be used together with [method resize] to create an array with a given size and initialized elements.
</description>
</method>
+ <method name="find" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="Vector3" />
+ <argument index="1" name="from" type="int" default="0" />
+ <description>
+ Searches the array for a value and returns its index or [code]-1[/code] if not found. Optionally, the initial search index can be passed.
+ </description>
+ </method>
<method name="has" qualifiers="const">
<return type="bool" />
<argument index="0" name="value" type="Vector3" />
@@ -115,6 +130,14 @@
Reverses the order of the elements in the array.
</description>
</method>
+ <method name="rfind" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="Vector3" />
+ <argument index="1" name="from" type="int" default="-1" />
+ <description>
+ Searches the array in reverse order. Optionally, a start search index can be passed. If negative, the start index is considered relative to the end of the array.
+ </description>
+ </method>
<method name="set">
<return type="void" />
<argument index="0" name="index" type="int" />
diff --git a/doc/classes/ProgressBar.xml b/doc/classes/ProgressBar.xml
index 1e9ab7c375..8a781c51fb 100644
--- a/doc/classes/ProgressBar.xml
+++ b/doc/classes/ProgressBar.xml
@@ -9,10 +9,27 @@
<tutorials>
</tutorials>
<members>
+ <member name="fill_mode" type="int" setter="set_fill_mode" getter="get_fill_mode" default="0">
+ The fill direction. See [enum FillMode] for possible values.
+ </member>
<member name="percent_visible" type="bool" setter="set_percent_visible" getter="is_percent_visible" default="true">
If [code]true[/code], the fill percentage is displayed on the bar.
</member>
</members>
+ <constants>
+ <constant name="FILL_BEGIN_TO_END" value="0" enum="FillMode">
+ The progress bar fills from begin to end horizontally, according to the language direction. If [method Control.is_layout_rtl] returns [code]false[/code], it fills from left to right, and if it returns [code]true[/code], it fills from right to left.
+ </constant>
+ <constant name="FILL_END_TO_BEGIN" value="1" enum="FillMode">
+ The progress bar fills from end to begin horizontally, according to the language direction. If [method Control.is_layout_rtl] returns [code]false[/code], it fills from right to left, and if it returns [code]true[/code], it fills from left to right.
+ </constant>
+ <constant name="FILL_TOP_TO_BOTTOM" value="2" enum="FillMode">
+ The progress fills from top to bottom.
+ </constant>
+ <constant name="FILL_BOTTOM_TO_TOP" value="3" enum="FillMode">
+ The progress fills from bottom to top.
+ </constant>
+ </constants>
<theme_items>
<theme_item name="font_color" data_type="color" type="Color" default="Color(0.95, 0.95, 0.95, 1)">
The color of the text.
diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml
index 7c6d6d1c10..b54c129369 100644
--- a/doc/classes/ProjectSettings.xml
+++ b/doc/classes/ProjectSettings.xml
@@ -1557,18 +1557,6 @@
[b]Note:[/b] This property is only read when the project starts. To change the physics FPS at runtime, set [member Engine.physics_ticks_per_second] instead.
[b]Note:[/b] Only 8 physics ticks may be simulated per rendered frame at most. If more than 8 physics ticks have to be simulated per rendered frame to keep up with rendering, the game will appear to slow down (even if [code]delta[/code] is used consistently in physics calculations). Therefore, it is recommended not to increase [member physics/common/physics_ticks_per_second] above 240. Otherwise, the game will slow down when the rendering framerate goes below 30 FPS.
</member>
- <member name="rendering/2d/opengl/batching_send_null" type="int" setter="" getter="" default="0">
- </member>
- <member name="rendering/2d/opengl/batching_stream" type="int" setter="" getter="" default="0">
- </member>
- <member name="rendering/2d/opengl/legacy_orphan_buffers" type="int" setter="" getter="" default="0">
- </member>
- <member name="rendering/2d/opengl/legacy_stream" type="int" setter="" getter="" default="0">
- </member>
- <member name="rendering/2d/options/ninepatch_mode" type="int" setter="" getter="" default="1">
- </member>
- <member name="rendering/2d/options/use_software_skinning" type="bool" setter="" getter="" default="true">
- </member>
<member name="rendering/2d/sdf/oversize" type="int" setter="" getter="" default="1">
</member>
<member name="rendering/2d/sdf/scale" type="int" setter="" getter="" default="1">
@@ -1594,32 +1582,6 @@
</member>
<member name="rendering/anti_aliasing/screen_space_roughness_limiter/limit" type="float" setter="" getter="" default="0.18">
</member>
- <member name="rendering/batching/debug/diagnose_frame" type="bool" setter="" getter="" default="false">
- </member>
- <member name="rendering/batching/debug/flash_batching" type="bool" setter="" getter="" default="false">
- </member>
- <member name="rendering/batching/lights/max_join_items" type="int" setter="" getter="" default="32">
- </member>
- <member name="rendering/batching/lights/scissor_area_threshold" type="float" setter="" getter="" default="1.0">
- </member>
- <member name="rendering/batching/options/single_rect_fallback" type="bool" setter="" getter="" default="false">
- </member>
- <member name="rendering/batching/options/use_batching" type="bool" setter="" getter="" default="false">
- </member>
- <member name="rendering/batching/options/use_batching_in_editor" type="bool" setter="" getter="" default="false">
- </member>
- <member name="rendering/batching/parameters/batch_buffer_size" type="int" setter="" getter="" default="16384">
- </member>
- <member name="rendering/batching/parameters/colored_vertex_format_threshold" type="float" setter="" getter="" default="0.25">
- </member>
- <member name="rendering/batching/parameters/item_reordering_lookahead" type="int" setter="" getter="" default="4">
- </member>
- <member name="rendering/batching/parameters/max_join_item_commands" type="int" setter="" getter="" default="16">
- </member>
- <member name="rendering/batching/precision/uv_contract" type="bool" setter="" getter="" default="false">
- </member>
- <member name="rendering/batching/precision/uv_contract_amount" type="int" setter="" getter="" default="100">
- </member>
<member name="rendering/camera/depth_of_field/depth_of_field_bokeh_quality" type="int" setter="" getter="" default="1">
Sets the quality of the depth of field effect. Higher quality takes more samples, which is slower but looks smoother.
</member>
@@ -1629,13 +1591,16 @@
<member name="rendering/camera/depth_of_field/depth_of_field_use_jitter" type="bool" setter="" getter="" default="false">
If [code]true[/code], jitters DOF samples to make effect slightly blurrier and hide lines created from low sample rates. This can result in a slightly grainy appearance when used with a low number of samples.
</member>
+ <member name="rendering/driver/depth_prepass/disable_for_vendors" type="String" setter="" getter="" default="&quot;PowerVR,Mali,Adreno,Apple&quot;">
+ Disables [member rendering/driver/depth_prepass/enable] conditionally for certain venders. By default, disables the depth prepass for mobile devices as mobile devices do not benefit from the depth prepass due to their unique architecture.
+ </member>
<member name="rendering/driver/depth_prepass/enable" type="bool" setter="" getter="" default="true">
If [code]true[/code], performs a previous depth pass before rendering 3D materials. This increases performance significantly in scenes with high overdraw, when complex materials and lighting are used. However, in scenes with few occluded surfaces, the depth prepass may reduce performance. If your game is viewed from a fixed angle that makes it easy to avoid overdraw (such as top-down or side-scrolling perspective), consider disabling the depth prepass to improve performance. This setting can be changed at run-time to optimize performance depending on the scene currently being viewed.
- [b]Note:[/b] Only supported when using the Vulkan Clustered backend (not Vulkan Mobile or OpenGL). When using Vulkan Mobile or OpenGL, there is no depth prepass performed.
+ [b]Note:[/b] Only supported when using the Vulkan Clustered backend or the OpenGL backend. When using Vulkan Mobile there is no depth prepass performed.
</member>
<member name="rendering/driver/driver_name" type="String" setter="" getter="" default="&quot;vulkan&quot;">
The video driver to use.
- [b]Note:[/b] OpenGL support is currently incomplete. Only basic 2D rendering is supported, and single-window mode is required for correct operation.
+ [b]Note:[/b] OpenGL support is currently incomplete. Only basic rendering is supported.
[b]Note:[/b] The backend in use can be overridden at runtime via the [code]--rendering-driver[/code] command line argument.
[b]FIXME:[/b] No longer valid after DisplayServer split:
In such cases, this property is not updated, so use [code]OS.get_current_video_driver[/code] to query it at run-time.
@@ -1715,10 +1680,6 @@
<member name="rendering/environment/volumetric_fog/volume_size" type="int" setter="" getter="" default="64">
Base size used to determine size of froxel buffer in the camera X-axis and Y-axis. The final size is scaled by the aspect ratio of the screen, so actual values may differ from what is set. Set a larger size for more detailed fog, set a smaller size for better performance.
</member>
- <member name="rendering/gles2/compatibility/disable_half_float" type="bool" setter="" getter="" default="false">
- </member>
- <member name="rendering/gles2/compatibility/enable_high_float.Android" type="bool" setter="" getter="" default="false">
- </member>
<member name="rendering/global_illumination/gi/use_half_resolution" type="bool" setter="" getter="" default="false">
If [code]true[/code], renders [VoxelGI] and SDFGI ([member Environment.sdfgi_enabled]) buffers at halved resolution (e.g. 960×540 when the viewport size is 1920×1080). This improves performance significantly when VoxelGI or SDFGI is enabled, at the cost of artifacts that may be visible on polygon edges. The loss in quality becomes less noticeable as the viewport resolution increases. [LightmapGI] rendering is not affected by this setting.
[b]Note:[/b] This property is only read when the project starts. To set half-resolution GI at run-time, call [method RenderingServer.gi_set_use_half_resolution] instead.
@@ -1761,6 +1722,15 @@
</member>
<member name="rendering/limits/global_shader_variables/buffer_size" type="int" setter="" getter="" default="65536">
</member>
+ <member name="rendering/limits/opengl/max_lights_per_object" type="int" setter="" getter="" default="8">
+ Max number of lights renderable per object. This is further limited by hardware support. Setting this low will slightly reduce memory usage, may decrease shader compile times, and may result in faster rendering on low-end, mobile, or web devices.
+ </member>
+ <member name="rendering/limits/opengl/max_renderable_elements" type="int" setter="" getter="" default="65536">
+ Max amount of elements renderable in a frame. If more elements than this are visible per frame, they will not be drawn. Keep in mind elements refer to mesh surfaces and not meshes themselves. Setting this low will slightly reduce memory usage and may decrease shader compile times, particularly on web. For most uses, the default value is suitable, but consider lowering as much as possible on web export.
+ </member>
+ <member name="rendering/limits/opengl/max_renderable_lights" type="int" setter="" getter="" default="256">
+ Max number of lights renderable in a frame. If more lights than this number are used, they will be ignored. Setting this low will slightly reduce memory usage and may decrease shader compile times, particularly on web. For most uses, the default value is suitable, but consider lowering as much as possible on web export.
+ </member>
<member name="rendering/limits/spatial_indexer/threaded_cull_minimum_instances" type="int" setter="" getter="" default="1000">
</member>
<member name="rendering/limits/spatial_indexer/update_iterations_per_frame" type="int" setter="" getter="" default="10">
diff --git a/doc/classes/SceneTree.xml b/doc/classes/SceneTree.xml
index 970f5125cd..3cd2ab0903 100644
--- a/doc/classes/SceneTree.xml
+++ b/doc/classes/SceneTree.xml
@@ -18,9 +18,9 @@
<argument index="0" name="group" type="StringName" />
<argument index="1" name="method" type="StringName" />
<description>
- Calls [code]method[/code] on each member of the given group. You can pass arguments to [code]method[/code] by specifying them at the end of the method call. This method is equivalent of calling [method call_group_flags] with [constant GROUP_CALL_DEFAULT] flag.
+ Calls [code]method[/code] on each member of the given group. You can pass arguments to [code]method[/code] by specifying them at the end of the method call.
[b]Note:[/b] Due to design limitations, [method call_group] will fail silently if one of the arguments is [code]null[/code].
- [b]Note:[/b] [method call_group] will always call methods with an one-frame delay, in a way similar to [method Object.call_deferred]. To call methods immediately, use [method call_group_flags] with the [constant GROUP_CALL_REALTIME] flag.
+ [b]Note:[/b] [method call_group] will call methods immediately on all members at once, which can cause stuttering if an expensive method is called on lots of members. To wait for one frame after [method call_group] was called, use [method call_group_flags] with the [constant GROUP_CALL_DEFERRED] flag.
</description>
</method>
<method name="call_group_flags" qualifiers="vararg">
@@ -30,11 +30,12 @@
<argument index="2" name="method" type="StringName" />
<description>
Calls [code]method[/code] on each member of the given group, respecting the given [enum GroupCallFlags]. You can pass arguments to [code]method[/code] by specifying them at the end of the method call.
- [b]Note:[/b] Due to design limitations, [method call_group_flags] will fail silently if one of the arguments is [code]null[/code].
[codeblock]
- # Call the method immediately and in reverse order.
- get_tree().call_group_flags(SceneTree.GROUP_CALL_REALTIME | SceneTree.GROUP_CALL_REVERSE, "bases", "destroy")
+ # Call the method in a deferred manner and in reverse order.
+ get_tree().call_group_flags(SceneTree.GROUP_CALL_DEFERRED | SceneTree.GROUP_CALL_REVERSE)
[/codeblock]
+ [b]Note:[/b] Due to design limitations, [method call_group_flags] will fail silently if one of the arguments is [code]null[/code].
+ [b]Note:[/b] Group call flags are used to control the method calling behavior. By default, methods will be called immediately in a way similar to [method call_group]. However, if the [constant GROUP_CALL_DEFERRED] flag is present in the [code]flags[/code] argument, methods will be called with a one-frame delay in a way similar to [method Object.set_deferred].
</description>
</method>
<method name="change_scene">
@@ -139,6 +140,7 @@
<argument index="1" name="notification" type="int" />
<description>
Sends the given notification to all members of the [code]group[/code].
+ [b]Note:[/b] [method notify_group] will immediately notify all members at once, which can cause stuttering if an expensive method is called as a result of sending the notification lots of members. To wait for one frame, use [method notify_group_flags] with the [constant GROUP_CALL_DEFERRED] flag.
</description>
</method>
<method name="notify_group_flags">
@@ -148,6 +150,7 @@
<argument index="2" name="notification" type="int" />
<description>
Sends the given notification to all members of the [code]group[/code], respecting the given [enum GroupCallFlags].
+ [b]Note:[/b] Group call flags are used to control the notification sending behavior. By default, notifications will be sent immediately in a way similar to [method notify_group]. However, if the [constant GROUP_CALL_DEFERRED] flag is present in the [code]flags[/code] argument, notifications will be sent with a one-frame delay in a way similar to using [code]Object.call_deferred("notification", ...)[/code].
</description>
</method>
<method name="queue_delete">
@@ -189,6 +192,7 @@
<argument index="2" name="value" type="Variant" />
<description>
Sets the given [code]property[/code] to [code]value[/code] on all members of the given group.
+ [b]Note:[/b] [method set_group] will set the property immediately on all members at once, which can cause stuttering if a property with an expensive setter is set on lots of members. To wait for one frame, use [method set_group_flags] with the [constant GROUP_CALL_DEFERRED] flag.
</description>
</method>
<method name="set_group_flags">
@@ -199,6 +203,7 @@
<argument index="3" name="value" type="Variant" />
<description>
Sets the given [code]property[/code] to [code]value[/code] on all members of the given group, respecting the given [enum GroupCallFlags].
+ [b]Note:[/b] Group call flags are used to control the property setting behavior. By default, properties will be set immediately in a way similar to [method set_group]. However, if the [constant GROUP_CALL_DEFERRED] flag is present in the [code]flags[/code] argument, properties will be set with a one-frame delay in a way similar to [method Object.call_deferred].
</description>
</method>
<method name="set_multiplayer">
@@ -297,8 +302,8 @@
<constant name="GROUP_CALL_REVERSE" value="1" enum="GroupCallFlags">
Call a group in reverse scene order.
</constant>
- <constant name="GROUP_CALL_REALTIME" value="2" enum="GroupCallFlags">
- Call a group immediately (calls are normally made on idle).
+ <constant name="GROUP_CALL_DEFERRED" value="2" enum="GroupCallFlags">
+ Call a group with a one-frame delay (idle frame, not physics).
</constant>
<constant name="GROUP_CALL_UNIQUE" value="4" enum="GroupCallFlags">
Call a group only once even if the call is executed many times.
diff --git a/doc/classes/ScriptLanguageExtension.xml b/doc/classes/ScriptLanguageExtension.xml
index d66bb6a7c7..0f757cf806 100644
--- a/doc/classes/ScriptLanguageExtension.xml
+++ b/doc/classes/ScriptLanguageExtension.xml
@@ -372,11 +372,13 @@
</constant>
<constant name="LOOKUP_RESULT_CLASS_METHOD" value="4" enum="LookupResultType">
</constant>
- <constant name="LOOKUP_RESULT_CLASS_ENUM" value="5" enum="LookupResultType">
+ <constant name="LOOKUP_RESULT_CLASS_SIGNAL" value="5" enum="LookupResultType">
</constant>
- <constant name="LOOKUP_RESULT_CLASS_TBD_GLOBALSCOPE" value="6" enum="LookupResultType">
+ <constant name="LOOKUP_RESULT_CLASS_ENUM" value="6" enum="LookupResultType">
</constant>
- <constant name="LOOKUP_RESULT_MAX" value="7" enum="LookupResultType">
+ <constant name="LOOKUP_RESULT_CLASS_TBD_GLOBALSCOPE" value="7" enum="LookupResultType">
+ </constant>
+ <constant name="LOOKUP_RESULT_MAX" value="8" enum="LookupResultType">
</constant>
<constant name="LOCATION_LOCAL" value="0" enum="CodeCompletionLocation">
The option is local to the location of the code completion query - e.g. a local variable.
diff --git a/doc/classes/VisualShaderNodeColorFunc.xml b/doc/classes/VisualShaderNodeColorFunc.xml
index 4a73b1662e..6116e0df68 100644
--- a/doc/classes/VisualShaderNodeColorFunc.xml
+++ b/doc/classes/VisualShaderNodeColorFunc.xml
@@ -24,7 +24,13 @@
return vec3(max3, max3, max3);
[/codeblock]
</constant>
- <constant name="FUNC_SEPIA" value="1" enum="Function">
+ <constant name="FUNC_HSV2RGB" value="1" enum="Function">
+ Converts HSV vector to RGB equivalent.
+ </constant>
+ <constant name="FUNC_RGB2HSV" value="2" enum="Function">
+ Converts RGB vector to HSV equivalent.
+ </constant>
+ <constant name="FUNC_SEPIA" value="3" enum="Function">
Applies sepia tone effect using the following formula:
[codeblock]
vec3 c = input;
@@ -34,7 +40,7 @@
return vec3(r, g, b);
[/codeblock]
</constant>
- <constant name="FUNC_MAX" value="2" enum="Function">
+ <constant name="FUNC_MAX" value="4" enum="Function">
Represents the size of the [enum Function] enum.
</constant>
</constants>
diff --git a/doc/classes/VisualShaderNodeVectorFunc.xml b/doc/classes/VisualShaderNodeVectorFunc.xml
index dc6628a9af..bc4e12c0b3 100644
--- a/doc/classes/VisualShaderNodeVectorFunc.xml
+++ b/doc/classes/VisualShaderNodeVectorFunc.xml
@@ -26,100 +26,94 @@
<constant name="FUNC_RECIPROCAL" value="3" enum="Function">
Returns [code]1/vector[/code].
</constant>
- <constant name="FUNC_RGB2HSV" value="4" enum="Function">
- Converts RGB vector to HSV equivalent.
- </constant>
- <constant name="FUNC_HSV2RGB" value="5" enum="Function">
- Converts HSV vector to RGB equivalent.
- </constant>
- <constant name="FUNC_ABS" value="6" enum="Function">
+ <constant name="FUNC_ABS" value="4" enum="Function">
Returns the absolute value of the parameter.
</constant>
- <constant name="FUNC_ACOS" value="7" enum="Function">
+ <constant name="FUNC_ACOS" value="5" enum="Function">
Returns the arc-cosine of the parameter.
</constant>
- <constant name="FUNC_ACOSH" value="8" enum="Function">
+ <constant name="FUNC_ACOSH" value="6" enum="Function">
Returns the inverse hyperbolic cosine of the parameter.
</constant>
- <constant name="FUNC_ASIN" value="9" enum="Function">
+ <constant name="FUNC_ASIN" value="7" enum="Function">
Returns the arc-sine of the parameter.
</constant>
- <constant name="FUNC_ASINH" value="10" enum="Function">
+ <constant name="FUNC_ASINH" value="8" enum="Function">
Returns the inverse hyperbolic sine of the parameter.
</constant>
- <constant name="FUNC_ATAN" value="11" enum="Function">
+ <constant name="FUNC_ATAN" value="9" enum="Function">
Returns the arc-tangent of the parameter.
</constant>
- <constant name="FUNC_ATANH" value="12" enum="Function">
+ <constant name="FUNC_ATANH" value="10" enum="Function">
Returns the inverse hyperbolic tangent of the parameter.
</constant>
- <constant name="FUNC_CEIL" value="13" enum="Function">
+ <constant name="FUNC_CEIL" value="11" enum="Function">
Finds the nearest integer that is greater than or equal to the parameter.
</constant>
- <constant name="FUNC_COS" value="14" enum="Function">
+ <constant name="FUNC_COS" value="12" enum="Function">
Returns the cosine of the parameter.
</constant>
- <constant name="FUNC_COSH" value="15" enum="Function">
+ <constant name="FUNC_COSH" value="13" enum="Function">
Returns the hyperbolic cosine of the parameter.
</constant>
- <constant name="FUNC_DEGREES" value="16" enum="Function">
+ <constant name="FUNC_DEGREES" value="14" enum="Function">
Converts a quantity in radians to degrees.
</constant>
- <constant name="FUNC_EXP" value="17" enum="Function">
+ <constant name="FUNC_EXP" value="15" enum="Function">
Base-e Exponential.
</constant>
- <constant name="FUNC_EXP2" value="18" enum="Function">
+ <constant name="FUNC_EXP2" value="16" enum="Function">
Base-2 Exponential.
</constant>
- <constant name="FUNC_FLOOR" value="19" enum="Function">
+ <constant name="FUNC_FLOOR" value="17" enum="Function">
Finds the nearest integer less than or equal to the parameter.
</constant>
- <constant name="FUNC_FRAC" value="20" enum="Function">
+ <constant name="FUNC_FRAC" value="18" enum="Function">
Computes the fractional part of the argument.
</constant>
- <constant name="FUNC_INVERSE_SQRT" value="21" enum="Function">
+ <constant name="FUNC_INVERSE_SQRT" value="19" enum="Function">
Returns the inverse of the square root of the parameter.
</constant>
- <constant name="FUNC_LOG" value="22" enum="Function">
+ <constant name="FUNC_LOG" value="20" enum="Function">
Natural logarithm.
</constant>
- <constant name="FUNC_LOG2" value="23" enum="Function">
+ <constant name="FUNC_LOG2" value="21" enum="Function">
Base-2 logarithm.
</constant>
- <constant name="FUNC_RADIANS" value="24" enum="Function">
+ <constant name="FUNC_RADIANS" value="22" enum="Function">
Converts a quantity in degrees to radians.
</constant>
- <constant name="FUNC_ROUND" value="25" enum="Function">
+ <constant name="FUNC_ROUND" value="23" enum="Function">
Finds the nearest integer to the parameter.
</constant>
- <constant name="FUNC_ROUNDEVEN" value="26" enum="Function">
+ <constant name="FUNC_ROUNDEVEN" value="24" enum="Function">
Finds the nearest even integer to the parameter.
</constant>
- <constant name="FUNC_SIGN" value="27" enum="Function">
+ <constant name="FUNC_SIGN" value="25" enum="Function">
Extracts the sign of the parameter, i.e. returns [code]-1[/code] if the parameter is negative, [code]1[/code] if it's positive and [code]0[/code] otherwise.
</constant>
- <constant name="FUNC_SIN" value="28" enum="Function">
+ <constant name="FUNC_SIN" value="26" enum="Function">
Returns the sine of the parameter.
</constant>
- <constant name="FUNC_SINH" value="29" enum="Function">
+ <constant name="FUNC_SINH" value="27" enum="Function">
Returns the hyperbolic sine of the parameter.
</constant>
- <constant name="FUNC_SQRT" value="30" enum="Function">
+ <constant name="FUNC_SQRT" value="28" enum="Function">
Returns the square root of the parameter.
</constant>
- <constant name="FUNC_TAN" value="31" enum="Function">
+ <constant name="FUNC_TAN" value="29" enum="Function">
Returns the tangent of the parameter.
</constant>
- <constant name="FUNC_TANH" value="32" enum="Function">
+ <constant name="FUNC_TANH" value="30" enum="Function">
Returns the hyperbolic tangent of the parameter.
</constant>
- <constant name="FUNC_TRUNC" value="33" enum="Function">
+ <constant name="FUNC_TRUNC" value="31" enum="Function">
Returns a value equal to the nearest integer to the parameter whose absolute value is not larger than the absolute value of the parameter.
</constant>
- <constant name="FUNC_ONEMINUS" value="34" enum="Function">
+ <constant name="FUNC_ONEMINUS" value="32" enum="Function">
Returns [code]1.0 - vector[/code].
</constant>
- <constant name="FUNC_MAX" value="35" enum="Function">
+ <constant name="FUNC_MAX" value="33" enum="Function">
Represents the size of the [enum Function] enum.
</constant>
</constants>
diff --git a/doc/classes/float.xml b/doc/classes/float.xml
index 50961f9c7f..f36b1fddeb 100644
--- a/doc/classes/float.xml
+++ b/doc/classes/float.xml
@@ -124,6 +124,18 @@
Multiplies a [float] and an [int]. The result is a [float].
</description>
</operator>
+ <operator name="operator **">
+ <return type="float" />
+ <argument index="0" name="right" type="float" />
+ <description>
+ </description>
+ </operator>
+ <operator name="operator **">
+ <return type="float" />
+ <argument index="0" name="right" type="int" />
+ <description>
+ </description>
+ </operator>
<operator name="operator +">
<return type="float" />
<argument index="0" name="right" type="float" />
diff --git a/doc/classes/int.xml b/doc/classes/int.xml
index 609291b69c..2eceba40fa 100644
--- a/doc/classes/int.xml
+++ b/doc/classes/int.xml
@@ -171,6 +171,18 @@
Multiplies two [int]s.
</description>
</operator>
+ <operator name="operator **">
+ <return type="float" />
+ <argument index="0" name="right" type="float" />
+ <description>
+ </description>
+ </operator>
+ <operator name="operator **">
+ <return type="int" />
+ <argument index="0" name="right" type="int" />
+ <description>
+ </description>
+ </operator>
<operator name="operator +">
<return type="String" />
<argument index="0" name="right" type="String" />
diff --git a/doc/tools/make_rst.py b/doc/tools/make_rst.py
index eba4cee33a..2e227ce578 100755
--- a/doc/tools/make_rst.py
+++ b/doc/tools/make_rst.py
@@ -677,7 +677,8 @@ def make_rst_class(class_def, state, dry_run, output_dir): # type: (ClassDef, S
for value in e.values.values():
f.write("- **{}** = **{}**".format(value.name, value.value))
if value.text is not None and value.text.strip() != "":
- f.write(" --- " + rstize_text(value.text.strip(), state))
+ # If value.text contains a bullet point list, each entry needs additional indentation
+ f.write(" --- " + indent_bullets(rstize_text(value.text.strip(), state)))
f.write("\n\n")
@@ -904,7 +905,7 @@ def rstize_text(text, state): # type: (str, State) -> str
pre_text = text[:pos]
indent_level = 0
- while text[pos + 1] == "\t":
+ while pos + 1 < len(text) and text[pos + 1] == "\t":
pos += 1
indent_level += 1
post_text = text[pos + 1 :]
@@ -1409,6 +1410,8 @@ def sanitize_operator_name(dirty_name, state): # type: (str, State) -> str
clear_name = "div"
elif clear_name == "%":
clear_name = "mod"
+ elif clear_name == "**":
+ clear_name = "pow"
elif clear_name == "unary+":
clear_name = "unplus"
@@ -1438,5 +1441,24 @@ def sanitize_operator_name(dirty_name, state): # type: (str, State) -> str
return clear_name
+def indent_bullets(text): # type: (str) -> str
+ # Take the text and check each line for a bullet point represented by "-".
+ # Where found, indent the given line by a further "\t".
+ # Used to properly indent bullet points contained in the description for enum values.
+ # Ignore the first line - text will be prepended to it so bullet points wouldn't work anyway.
+ bullet_points = "-"
+
+ lines = text.splitlines(keepends=True)
+ for line_index, line in enumerate(lines[1:], start=1):
+ pos = 0
+ while pos < len(line) and line[pos] == "\t":
+ pos += 1
+
+ if pos < len(line) and line[pos] in bullet_points:
+ lines[line_index] = line[:pos] + "\t" + line[pos:]
+
+ return "".join(lines)
+
+
if __name__ == "__main__":
main()
diff --git a/drivers/coreaudio/audio_driver_coreaudio.h b/drivers/coreaudio/audio_driver_coreaudio.h
index b19f133d89..f86037f092 100644
--- a/drivers/coreaudio/audio_driver_coreaudio.h
+++ b/drivers/coreaudio/audio_driver_coreaudio.h
@@ -35,9 +35,9 @@
#include "servers/audio_server.h"
-#include <AudioUnit/AudioUnit.h>
+#import <AudioUnit/AudioUnit.h>
#ifdef OSX_ENABLED
-#include <CoreAudio/AudioHardware.h>
+#import <CoreAudio/AudioHardware.h>
#endif
class AudioDriverCoreAudio : public AudioDriver {
diff --git a/drivers/coremidi/midi_driver_coremidi.cpp b/drivers/coremidi/midi_driver_coremidi.cpp
index ecd10f900b..dc69ab9472 100644
--- a/drivers/coremidi/midi_driver_coremidi.cpp
+++ b/drivers/coremidi/midi_driver_coremidi.cpp
@@ -34,8 +34,8 @@
#include "core/string/print_string.h"
-#include <CoreAudio/HostTime.h>
-#include <CoreServices/CoreServices.h>
+#import <CoreAudio/HostTime.h>
+#import <CoreServices/CoreServices.h>
void MIDIDriverCoreMidi::read(const MIDIPacketList *packet_list, void *read_proc_ref_con, void *src_conn_ref_con) {
MIDIPacket *packet = const_cast<MIDIPacket *>(packet_list->packet);
diff --git a/drivers/coremidi/midi_driver_coremidi.h b/drivers/coremidi/midi_driver_coremidi.h
index be0a9f610e..0085141c6d 100644
--- a/drivers/coremidi/midi_driver_coremidi.h
+++ b/drivers/coremidi/midi_driver_coremidi.h
@@ -36,7 +36,7 @@
#include "core/os/midi_driver.h"
#include "core/templates/vector.h"
-#include <CoreMIDI/CoreMIDI.h>
+#import <CoreMIDI/CoreMIDI.h>
#include <stdio.h>
class MIDIDriverCoreMidi : public MIDIDriver {
diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp
index 1197f4aac1..df54686574 100644
--- a/drivers/gles3/rasterizer_canvas_gles3.cpp
+++ b/drivers/gles3/rasterizer_canvas_gles3.cpp
@@ -179,12 +179,12 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_
//print_line("w: " + itos(ssize.width) + " s: " + rtos(canvas_scale));
state_buffer.tex_to_sdf = 1.0 / ((canvas_scale.x + canvas_scale.y) * 0.5);
- glBindBufferBase(GL_UNIFORM_BUFFER, BASE_UNIFORM_BUFFER_OBJECT, state.canvas_state_buffer);
+ glBindBufferBase(GL_UNIFORM_BUFFER, BASE_UNIFORM_LOCATION, state.canvas_state_buffer);
glBufferData(GL_UNIFORM_BUFFER, sizeof(StateBuffer), &state_buffer, GL_STREAM_DRAW);
GLuint global_buffer = material_storage->global_variables_get_uniform_buffer();
- glBindBufferBase(GL_UNIFORM_BUFFER, GLOBAL_UNIFORM_BUFFER_OBJECT, global_buffer);
+ glBindBufferBase(GL_UNIFORM_BUFFER, GLOBAL_UNIFORM_LOCATION, global_buffer);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
}
@@ -522,7 +522,7 @@ void RasterizerCanvasGLES3::_render_item(RID p_render_target, const Item *p_item
}
}
- glBindBufferBase(GL_UNIFORM_BUFFER, INSTANCE_UNIFORM_BUFFER_OBJECT, state.canvas_instance_data_buffers[state.current_buffer]);
+ glBindBufferBase(GL_UNIFORM_BUFFER, INSTANCE_UNIFORM_LOCATION, state.canvas_instance_data_buffers[state.current_buffer]);
#ifdef JAVASCRIPT_ENABLED
//WebGL 2.0 does not support mapping buffers, so use slow glBufferData instead
glBufferData(GL_UNIFORM_BUFFER, sizeof(InstanceData), &state.instance_data_array[0], GL_DYNAMIC_DRAW);
@@ -728,7 +728,7 @@ void RasterizerCanvasGLES3::_render_batch(uint32_t &r_index) {
}
}
- glBindBufferBase(GL_UNIFORM_BUFFER, INSTANCE_UNIFORM_BUFFER_OBJECT, state.canvas_instance_data_buffers[state.current_buffer]);
+ glBindBufferBase(GL_UNIFORM_BUFFER, INSTANCE_UNIFORM_LOCATION, state.canvas_instance_data_buffers[state.current_buffer]);
#ifdef JAVASCRIPT_ENABLED
//WebGL 2.0 does not support mapping buffers, so use slow glBufferData instead
glBufferData(GL_UNIFORM_BUFFER, sizeof(InstanceData) * r_index, state.instance_data_array, GL_DYNAMIC_DRAW);
diff --git a/drivers/gles3/rasterizer_canvas_gles3.h b/drivers/gles3/rasterizer_canvas_gles3.h
index b77b295de9..aedde7c265 100644
--- a/drivers/gles3/rasterizer_canvas_gles3.h
+++ b/drivers/gles3/rasterizer_canvas_gles3.h
@@ -97,13 +97,12 @@ class RasterizerCanvasGLES3 : public RendererCanvasRender {
};
public:
- //TODO move to Material storage
enum {
- BASE_UNIFORM_BUFFER_OBJECT = 0,
- GLOBAL_UNIFORM_BUFFER_OBJECT = 1,
- LIGHT_UNIFORM_BUFFER_OBJECT = 2,
- INSTANCE_UNIFORM_BUFFER_OBJECT = 3,
- MATERIAL_UNIFORM_BUFFER_OBJECT = 4,
+ BASE_UNIFORM_LOCATION = 0,
+ GLOBAL_UNIFORM_LOCATION = 1,
+ LIGHT_UNIFORM_LOCATION = 2,
+ INSTANCE_UNIFORM_LOCATION = 3,
+ MATERIAL_UNIFORM_LOCATION = 4,
};
struct StateBuffer {
diff --git a/drivers/gles3/rasterizer_gles3.cpp b/drivers/gles3/rasterizer_gles3.cpp
index b35134dcfb..69f69099c7 100644
--- a/drivers/gles3/rasterizer_gles3.cpp
+++ b/drivers/gles3/rasterizer_gles3.cpp
@@ -271,10 +271,6 @@ RasterizerGLES3::RasterizerGLES3() {
storage = memnew(RasterizerStorageGLES3);
canvas = memnew(RasterizerCanvasGLES3(storage));
scene = memnew(RasterizerSceneGLES3(storage));
-
- texture_storage->set_main_thread_id(Thread::get_caller_id());
- // make sure the OS knows to only access the renderer from the main thread
- OS::get_singleton()->set_render_main_thread_mode(OS::RENDER_MAIN_THREAD_ONLY);
}
RasterizerGLES3::~RasterizerGLES3() {
diff --git a/drivers/gles3/rasterizer_gles3.h b/drivers/gles3/rasterizer_gles3.h
index 33bb97d105..ad3d3d7325 100644
--- a/drivers/gles3/rasterizer_gles3.h
+++ b/drivers/gles3/rasterizer_gles3.h
@@ -92,9 +92,9 @@ public:
static void make_current() {
_create_func = _create_current;
+ low_end = true;
}
- virtual bool is_low_end() const { return true; }
uint64_t get_frame_number() const { return frame; }
double get_frame_delta_time() const { return delta; }
diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp
index e0373ab8e8..68657b9152 100644
--- a/drivers/gles3/rasterizer_scene_gles3.cpp
+++ b/drivers/gles3/rasterizer_scene_gles3.cpp
@@ -31,6 +31,7 @@
#include "rasterizer_scene_gles3.h"
#include "core/config/project_settings.h"
#include "servers/rendering/rendering_server_default.h"
+#include "storage/config.h"
#ifdef GLES3_ENABLED
@@ -42,66 +43,169 @@ RasterizerSceneGLES3 *RasterizerSceneGLES3::get_singleton() {
return singleton;
}
-RasterizerSceneGLES3::GeometryInstance *RasterizerSceneGLES3::geometry_instance_create(RID p_base) {
- return nullptr;
+RendererSceneRender::GeometryInstance *RasterizerSceneGLES3::geometry_instance_create(RID p_base) {
+ RS::InstanceType type = storage->get_base_type(p_base);
+ ERR_FAIL_COND_V(!((1 << type) & RS::INSTANCE_GEOMETRY_MASK), nullptr);
+
+ GeometryInstanceGLES3 *ginstance = geometry_instance_alloc.alloc();
+ ginstance->data = memnew(GeometryInstanceGLES3::Data);
+
+ ginstance->data->base = p_base;
+ ginstance->data->base_type = type;
+
+ _geometry_instance_mark_dirty(ginstance);
+
+ return ginstance;
}
void RasterizerSceneGLES3::geometry_instance_set_skeleton(GeometryInstance *p_geometry_instance, RID p_skeleton) {
+ GeometryInstanceGLES3 *ginstance = static_cast<GeometryInstanceGLES3 *>(p_geometry_instance);
+ ERR_FAIL_COND(!ginstance);
+ ginstance->data->skeleton = p_skeleton;
+
+ _geometry_instance_mark_dirty(ginstance);
+ ginstance->data->dirty_dependencies = true;
}
void RasterizerSceneGLES3::geometry_instance_set_material_override(GeometryInstance *p_geometry_instance, RID p_override) {
+ GeometryInstanceGLES3 *ginstance = static_cast<GeometryInstanceGLES3 *>(p_geometry_instance);
+ ERR_FAIL_COND(!ginstance);
+ ginstance->data->material_override = p_override;
+
+ _geometry_instance_mark_dirty(ginstance);
+ ginstance->data->dirty_dependencies = true;
}
void RasterizerSceneGLES3::geometry_instance_set_material_overlay(GeometryInstance *p_geometry_instance, RID p_overlay) {
+ GeometryInstanceGLES3 *ginstance = static_cast<GeometryInstanceGLES3 *>(p_geometry_instance);
+ ERR_FAIL_COND(!ginstance);
+ ginstance->data->material_overlay = p_overlay;
+
+ _geometry_instance_mark_dirty(ginstance);
+ ginstance->data->dirty_dependencies = true;
}
-void RasterizerSceneGLES3::geometry_instance_set_surface_materials(GeometryInstance *p_geometry_instance, const Vector<RID> &p_material) {
+void RasterizerSceneGLES3::geometry_instance_set_surface_materials(GeometryInstance *p_geometry_instance, const Vector<RID> &p_materials) {
+ GeometryInstanceGLES3 *ginstance = static_cast<GeometryInstanceGLES3 *>(p_geometry_instance);
+ ERR_FAIL_COND(!ginstance);
+ ginstance->data->surface_materials = p_materials;
+
+ _geometry_instance_mark_dirty(ginstance);
+ ginstance->data->dirty_dependencies = true;
}
void RasterizerSceneGLES3::geometry_instance_set_mesh_instance(GeometryInstance *p_geometry_instance, RID p_mesh_instance) {
+ GeometryInstanceGLES3 *ginstance = static_cast<GeometryInstanceGLES3 *>(p_geometry_instance);
+ ERR_FAIL_COND(!ginstance);
+ ERR_FAIL_COND(!ginstance);
+ ginstance->mesh_instance = p_mesh_instance;
+
+ _geometry_instance_mark_dirty(ginstance);
}
-void RasterizerSceneGLES3::geometry_instance_set_transform(GeometryInstance *p_geometry_instance, const Transform3D &p_transform, const AABB &p_aabb, const AABB &p_transformed_aabbb) {
+void RasterizerSceneGLES3::geometry_instance_set_transform(GeometryInstance *p_geometry_instance, const Transform3D &p_transform, const AABB &p_aabb, const AABB &p_transformed_aabb) {
+ GeometryInstanceGLES3 *ginstance = static_cast<GeometryInstanceGLES3 *>(p_geometry_instance);
+ ERR_FAIL_COND(!ginstance);
+ ginstance->transform = p_transform;
+ ginstance->mirror = p_transform.basis.determinant() < 0;
+ ginstance->data->aabb = p_aabb;
+ ginstance->transformed_aabb = p_transformed_aabb;
+
+ Vector3 model_scale_vec = p_transform.basis.get_scale_abs();
+ // handle non uniform scale here
+
+ float max_scale = MAX(model_scale_vec.x, MAX(model_scale_vec.y, model_scale_vec.z));
+ float min_scale = MIN(model_scale_vec.x, MIN(model_scale_vec.y, model_scale_vec.z));
+ ginstance->non_uniform_scale = max_scale >= 0.0 && (min_scale / max_scale) < 0.9;
+
+ ginstance->lod_model_scale = max_scale;
}
void RasterizerSceneGLES3::geometry_instance_set_layer_mask(GeometryInstance *p_geometry_instance, uint32_t p_layer_mask) {
+ GeometryInstanceGLES3 *ginstance = static_cast<GeometryInstanceGLES3 *>(p_geometry_instance);
+ ERR_FAIL_COND(!ginstance);
+ ginstance->layer_mask = p_layer_mask;
}
void RasterizerSceneGLES3::geometry_instance_set_lod_bias(GeometryInstance *p_geometry_instance, float p_lod_bias) {
+ GeometryInstanceGLES3 *ginstance = static_cast<GeometryInstanceGLES3 *>(p_geometry_instance);
+ ERR_FAIL_COND(!ginstance);
+ ginstance->lod_bias = p_lod_bias;
}
void RasterizerSceneGLES3::geometry_instance_set_transparency(GeometryInstance *p_geometry_instance, float p_transparency) {
+ GeometryInstanceGLES3 *ginstance = static_cast<GeometryInstanceGLES3 *>(p_geometry_instance);
+ ERR_FAIL_COND(!ginstance);
+ ginstance->force_alpha = CLAMP(1.0 - p_transparency, 0, 1);
}
void RasterizerSceneGLES3::geometry_instance_set_fade_range(GeometryInstance *p_geometry_instance, bool p_enable_near, float p_near_begin, float p_near_end, bool p_enable_far, float p_far_begin, float p_far_end) {
+ GeometryInstanceGLES3 *ginstance = static_cast<GeometryInstanceGLES3 *>(p_geometry_instance);
+ ERR_FAIL_COND(!ginstance);
+ ginstance->fade_near = p_enable_near;
+ ginstance->fade_near_begin = p_near_begin;
+ ginstance->fade_near_end = p_near_end;
+ ginstance->fade_far = p_enable_far;
+ ginstance->fade_far_begin = p_far_begin;
+ ginstance->fade_far_end = p_far_end;
}
void RasterizerSceneGLES3::geometry_instance_set_parent_fade_alpha(GeometryInstance *p_geometry_instance, float p_alpha) {
+ GeometryInstanceGLES3 *ginstance = static_cast<GeometryInstanceGLES3 *>(p_geometry_instance);
+ ERR_FAIL_COND(!ginstance);
+ ginstance->parent_fade_alpha = p_alpha;
}
void RasterizerSceneGLES3::geometry_instance_set_use_baked_light(GeometryInstance *p_geometry_instance, bool p_enable) {
+ GeometryInstanceGLES3 *ginstance = static_cast<GeometryInstanceGLES3 *>(p_geometry_instance);
+ ERR_FAIL_COND(!ginstance);
+ ginstance->data->use_baked_light = p_enable;
+
+ _geometry_instance_mark_dirty(ginstance);
}
void RasterizerSceneGLES3::geometry_instance_set_use_dynamic_gi(GeometryInstance *p_geometry_instance, bool p_enable) {
+ GeometryInstanceGLES3 *ginstance = static_cast<GeometryInstanceGLES3 *>(p_geometry_instance);
+ ERR_FAIL_COND(!ginstance);
+ ginstance->data->use_dynamic_gi = p_enable;
+ _geometry_instance_mark_dirty(ginstance);
}
void RasterizerSceneGLES3::geometry_instance_set_use_lightmap(GeometryInstance *p_geometry_instance, RID p_lightmap_instance, const Rect2 &p_lightmap_uv_scale, int p_lightmap_slice_index) {
+ GeometryInstanceGLES3 *ginstance = static_cast<GeometryInstanceGLES3 *>(p_geometry_instance);
+ ERR_FAIL_COND(!ginstance);
}
void RasterizerSceneGLES3::geometry_instance_set_lightmap_capture(GeometryInstance *p_geometry_instance, const Color *p_sh9) {
+ GeometryInstanceGLES3 *ginstance = static_cast<GeometryInstanceGLES3 *>(p_geometry_instance);
+ ERR_FAIL_COND(!ginstance);
}
void RasterizerSceneGLES3::geometry_instance_set_instance_shader_parameters_offset(GeometryInstance *p_geometry_instance, int32_t p_offset) {
+ GeometryInstanceGLES3 *ginstance = static_cast<GeometryInstanceGLES3 *>(p_geometry_instance);
+ ERR_FAIL_COND(!ginstance);
+ ginstance->shader_parameters_offset = p_offset;
+ _geometry_instance_mark_dirty(ginstance);
}
void RasterizerSceneGLES3::geometry_instance_set_cast_double_sided_shadows(GeometryInstance *p_geometry_instance, bool p_enable) {
+ GeometryInstanceGLES3 *ginstance = static_cast<GeometryInstanceGLES3 *>(p_geometry_instance);
+ ERR_FAIL_COND(!ginstance);
+ ginstance->data->cast_double_sided_shadows = p_enable;
+ _geometry_instance_mark_dirty(ginstance);
}
uint32_t RasterizerSceneGLES3::geometry_instance_get_pair_mask() {
- return 0;
+ return 0; //(1 << RS::INSTANCE_LIGHT);
+ // For now, nothing is paired
}
void RasterizerSceneGLES3::geometry_instance_pair_light_instances(GeometryInstance *p_geometry_instance, const RID *p_light_instances, uint32_t p_light_instance_count) {
+ GeometryInstanceGLES3 *ginstance = static_cast<GeometryInstanceGLES3 *>(p_geometry_instance);
+ ERR_FAIL_COND(!ginstance);
+
+ ginstance->omni_light_count = 0;
+ ginstance->spot_light_count = 0;
}
void RasterizerSceneGLES3::geometry_instance_pair_reflection_probe_instances(GeometryInstance *p_geometry_instance, const RID *p_reflection_probe_instances, uint32_t p_reflection_probe_instance_count) {
@@ -114,9 +218,314 @@ void RasterizerSceneGLES3::geometry_instance_pair_voxel_gi_instances(GeometryIns
}
void RasterizerSceneGLES3::geometry_instance_set_softshadow_projector_pairing(GeometryInstance *p_geometry_instance, bool p_softshadow, bool p_projector) {
+ GeometryInstanceGLES3 *ginstance = static_cast<GeometryInstanceGLES3 *>(p_geometry_instance);
+ ERR_FAIL_COND(!ginstance);
}
void RasterizerSceneGLES3::geometry_instance_free(GeometryInstance *p_geometry_instance) {
+ GeometryInstanceGLES3 *ginstance = static_cast<GeometryInstanceGLES3 *>(p_geometry_instance);
+ ERR_FAIL_COND(!ginstance);
+ GeometryInstanceSurface *surf = ginstance->surface_caches;
+ while (surf) {
+ GeometryInstanceSurface *next = surf->next;
+ geometry_instance_surface_alloc.free(surf);
+ surf = next;
+ }
+ memdelete(ginstance->data);
+ geometry_instance_alloc.free(ginstance);
+}
+
+void RasterizerSceneGLES3::_geometry_instance_mark_dirty(GeometryInstance *p_geometry_instance) {
+ GeometryInstanceGLES3 *ginstance = static_cast<GeometryInstanceGLES3 *>(p_geometry_instance);
+ if (ginstance->dirty_list_element.in_list()) {
+ return;
+ }
+
+ //clear surface caches
+ GeometryInstanceSurface *surf = ginstance->surface_caches;
+
+ while (surf) {
+ GeometryInstanceSurface *next = surf->next;
+ geometry_instance_surface_alloc.free(surf);
+ surf = next;
+ }
+
+ ginstance->surface_caches = nullptr;
+
+ geometry_instance_dirty_list.add(&ginstance->dirty_list_element);
+}
+
+void RasterizerSceneGLES3::_update_dirty_geometry_instances() {
+ while (geometry_instance_dirty_list.first()) {
+ _geometry_instance_update(geometry_instance_dirty_list.first()->self());
+ }
+}
+
+void RasterizerSceneGLES3::_geometry_instance_dependency_changed(RendererStorage::DependencyChangedNotification p_notification, RendererStorage::DependencyTracker *p_tracker) {
+ switch (p_notification) {
+ case RendererStorage::DEPENDENCY_CHANGED_MATERIAL:
+ case RendererStorage::DEPENDENCY_CHANGED_MESH:
+ case RendererStorage::DEPENDENCY_CHANGED_PARTICLES:
+ case RendererStorage::DEPENDENCY_CHANGED_MULTIMESH:
+ case RendererStorage::DEPENDENCY_CHANGED_SKELETON_DATA: {
+ static_cast<RasterizerSceneGLES3 *>(singleton)->_geometry_instance_mark_dirty(static_cast<GeometryInstance *>(p_tracker->userdata));
+ } break;
+ case RendererStorage::DEPENDENCY_CHANGED_MULTIMESH_VISIBLE_INSTANCES: {
+ GeometryInstanceGLES3 *ginstance = static_cast<GeometryInstanceGLES3 *>(p_tracker->userdata);
+ if (ginstance->data->base_type == RS::INSTANCE_MULTIMESH) {
+ ginstance->instance_count = GLES3::MeshStorage::get_singleton()->multimesh_get_instances_to_draw(ginstance->data->base);
+ }
+ } break;
+ default: {
+ //rest of notifications of no interest
+ } break;
+ }
+}
+
+void RasterizerSceneGLES3::_geometry_instance_dependency_deleted(const RID &p_dependency, RendererStorage::DependencyTracker *p_tracker) {
+ static_cast<RasterizerSceneGLES3 *>(singleton)->_geometry_instance_mark_dirty(static_cast<GeometryInstance *>(p_tracker->userdata));
+}
+
+void RasterizerSceneGLES3::_geometry_instance_add_surface_with_material(GeometryInstanceGLES3 *ginstance, uint32_t p_surface, GLES3::SceneMaterialData *p_material, uint32_t p_material_id, uint32_t p_shader_id, RID p_mesh) {
+ GLES3::MeshStorage *mesh_storage = GLES3::MeshStorage::get_singleton();
+
+ bool has_read_screen_alpha = p_material->shader_data->uses_screen_texture || p_material->shader_data->uses_depth_texture || p_material->shader_data->uses_normal_texture;
+ bool has_base_alpha = ((p_material->shader_data->uses_alpha && !p_material->shader_data->uses_alpha_clip) || has_read_screen_alpha);
+ bool has_blend_alpha = p_material->shader_data->uses_blend_alpha;
+ bool has_alpha = has_base_alpha || has_blend_alpha;
+
+ uint32_t flags = 0;
+
+ if (p_material->shader_data->uses_screen_texture) {
+ flags |= GeometryInstanceSurface::FLAG_USES_SCREEN_TEXTURE;
+ }
+
+ if (p_material->shader_data->uses_depth_texture) {
+ flags |= GeometryInstanceSurface::FLAG_USES_DEPTH_TEXTURE;
+ }
+
+ if (p_material->shader_data->uses_normal_texture) {
+ flags |= GeometryInstanceSurface::FLAG_USES_NORMAL_TEXTURE;
+ }
+
+ if (ginstance->data->cast_double_sided_shadows) {
+ flags |= GeometryInstanceSurface::FLAG_USES_DOUBLE_SIDED_SHADOWS;
+ }
+
+ if (has_alpha || has_read_screen_alpha || p_material->shader_data->depth_draw == GLES3::SceneShaderData::DEPTH_DRAW_DISABLED || p_material->shader_data->depth_test == GLES3::SceneShaderData::DEPTH_TEST_DISABLED) {
+ //material is only meant for alpha pass
+ flags |= GeometryInstanceSurface::FLAG_PASS_ALPHA;
+ if (p_material->shader_data->uses_depth_pre_pass && !(p_material->shader_data->depth_draw == GLES3::SceneShaderData::DEPTH_DRAW_DISABLED || p_material->shader_data->depth_test == GLES3::SceneShaderData::DEPTH_TEST_DISABLED)) {
+ flags |= GeometryInstanceSurface::FLAG_PASS_DEPTH;
+ flags |= GeometryInstanceSurface::FLAG_PASS_SHADOW;
+ }
+ } else {
+ flags |= GeometryInstanceSurface::FLAG_PASS_OPAQUE;
+ flags |= GeometryInstanceSurface::FLAG_PASS_DEPTH;
+ flags |= GeometryInstanceSurface::FLAG_PASS_SHADOW;
+ }
+
+ GLES3::SceneMaterialData *material_shadow = nullptr;
+ void *surface_shadow = nullptr;
+ if (!p_material->shader_data->uses_particle_trails && !p_material->shader_data->writes_modelview_or_projection && !p_material->shader_data->uses_vertex && !p_material->shader_data->uses_discard && !p_material->shader_data->uses_depth_pre_pass && !p_material->shader_data->uses_alpha_clip) {
+ flags |= GeometryInstanceSurface::FLAG_USES_SHARED_SHADOW_MATERIAL;
+ material_shadow = static_cast<GLES3::SceneMaterialData *>(GLES3::MaterialStorage::get_singleton()->material_get_data(scene_globals.default_material, RS::SHADER_SPATIAL));
+
+ RID shadow_mesh = mesh_storage->mesh_get_shadow_mesh(p_mesh);
+
+ if (shadow_mesh.is_valid()) {
+ surface_shadow = mesh_storage->mesh_get_surface(shadow_mesh, p_surface);
+ }
+
+ } else {
+ material_shadow = p_material;
+ }
+
+ GeometryInstanceSurface *sdcache = geometry_instance_surface_alloc.alloc();
+
+ sdcache->flags = flags;
+
+ sdcache->shader = p_material->shader_data;
+ sdcache->material = p_material;
+ sdcache->surface = mesh_storage->mesh_get_surface(p_mesh, p_surface);
+ sdcache->primitive = mesh_storage->mesh_surface_get_primitive(sdcache->surface);
+ sdcache->surface_index = p_surface;
+
+ if (ginstance->data->dirty_dependencies) {
+ storage->base_update_dependency(p_mesh, &ginstance->data->dependency_tracker);
+ }
+
+ //shadow
+ sdcache->shader_shadow = material_shadow->shader_data;
+ sdcache->material_shadow = material_shadow;
+
+ sdcache->surface_shadow = surface_shadow ? surface_shadow : sdcache->surface;
+
+ sdcache->owner = ginstance;
+
+ sdcache->next = ginstance->surface_caches;
+ ginstance->surface_caches = sdcache;
+
+ //sortkey
+
+ sdcache->sort.sort_key1 = 0;
+ sdcache->sort.sort_key2 = 0;
+
+ sdcache->sort.surface_index = p_surface;
+ sdcache->sort.material_id_low = p_material_id & 0x0000FFFF;
+ sdcache->sort.material_id_hi = p_material_id >> 16;
+ sdcache->sort.shader_id = p_shader_id;
+ sdcache->sort.geometry_id = p_mesh.get_local_index();
+ sdcache->sort.priority = p_material->priority;
+}
+
+void RasterizerSceneGLES3::_geometry_instance_add_surface_with_material_chain(GeometryInstanceGLES3 *ginstance, uint32_t p_surface, GLES3::SceneMaterialData *p_material_data, RID p_mat_src, RID p_mesh) {
+ GLES3::SceneMaterialData *material_data = p_material_data;
+ GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton();
+
+ _geometry_instance_add_surface_with_material(ginstance, p_surface, material_data, p_mat_src.get_local_index(), material_storage->material_get_shader_id(p_mat_src), p_mesh);
+
+ while (material_data->next_pass.is_valid()) {
+ RID next_pass = material_data->next_pass;
+ material_data = static_cast<GLES3::SceneMaterialData *>(material_storage->material_get_data(next_pass, RS::SHADER_SPATIAL));
+ if (!material_data || !material_data->shader_data->valid) {
+ break;
+ }
+ if (ginstance->data->dirty_dependencies) {
+ material_storage->material_update_dependency(next_pass, &ginstance->data->dependency_tracker);
+ }
+ _geometry_instance_add_surface_with_material(ginstance, p_surface, material_data, next_pass.get_local_index(), material_storage->material_get_shader_id(next_pass), p_mesh);
+ }
+}
+
+void RasterizerSceneGLES3::_geometry_instance_add_surface(GeometryInstanceGLES3 *ginstance, uint32_t p_surface, RID p_material, RID p_mesh) {
+ GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton();
+ RID m_src;
+
+ m_src = ginstance->data->material_override.is_valid() ? ginstance->data->material_override : p_material;
+
+ GLES3::SceneMaterialData *material_data = nullptr;
+
+ if (m_src.is_valid()) {
+ material_data = static_cast<GLES3::SceneMaterialData *>(material_storage->material_get_data(m_src, RS::SHADER_SPATIAL));
+ if (!material_data || !material_data->shader_data->valid) {
+ material_data = nullptr;
+ }
+ }
+
+ if (material_data) {
+ if (ginstance->data->dirty_dependencies) {
+ material_storage->material_update_dependency(m_src, &ginstance->data->dependency_tracker);
+ }
+ } else {
+ material_data = static_cast<GLES3::SceneMaterialData *>(material_storage->material_get_data(scene_globals.default_material, RS::SHADER_SPATIAL));
+ m_src = scene_globals.default_material;
+ }
+
+ ERR_FAIL_COND(!material_data);
+
+ _geometry_instance_add_surface_with_material_chain(ginstance, p_surface, material_data, m_src, p_mesh);
+
+ if (ginstance->data->material_overlay.is_valid()) {
+ m_src = ginstance->data->material_overlay;
+
+ material_data = static_cast<GLES3::SceneMaterialData *>(material_storage->material_get_data(m_src, RS::SHADER_SPATIAL));
+ if (material_data && material_data->shader_data->valid) {
+ if (ginstance->data->dirty_dependencies) {
+ material_storage->material_update_dependency(m_src, &ginstance->data->dependency_tracker);
+ }
+
+ _geometry_instance_add_surface_with_material_chain(ginstance, p_surface, material_data, m_src, p_mesh);
+ }
+ }
+}
+
+void RasterizerSceneGLES3::_geometry_instance_update(GeometryInstance *p_geometry_instance) {
+ GLES3::MeshStorage *mesh_storage = GLES3::MeshStorage::get_singleton();
+ GeometryInstanceGLES3 *ginstance = static_cast<GeometryInstanceGLES3 *>(p_geometry_instance);
+
+ if (ginstance->data->dirty_dependencies) {
+ ginstance->data->dependency_tracker.update_begin();
+ }
+
+ //add geometry for drawing
+ switch (ginstance->data->base_type) {
+ case RS::INSTANCE_MESH: {
+ const RID *materials = nullptr;
+ uint32_t surface_count;
+ RID mesh = ginstance->data->base;
+
+ materials = mesh_storage->mesh_get_surface_count_and_materials(mesh, surface_count);
+ if (materials) {
+ //if no materials, no surfaces.
+ const RID *inst_materials = ginstance->data->surface_materials.ptr();
+ uint32_t surf_mat_count = ginstance->data->surface_materials.size();
+
+ for (uint32_t j = 0; j < surface_count; j++) {
+ RID material = (j < surf_mat_count && inst_materials[j].is_valid()) ? inst_materials[j] : materials[j];
+ _geometry_instance_add_surface(ginstance, j, material, mesh);
+ }
+ }
+
+ ginstance->instance_count = 1;
+
+ } break;
+
+ case RS::INSTANCE_MULTIMESH: {
+ RID mesh = mesh_storage->multimesh_get_mesh(ginstance->data->base);
+ if (mesh.is_valid()) {
+ const RID *materials = nullptr;
+ uint32_t surface_count;
+
+ materials = mesh_storage->mesh_get_surface_count_and_materials(mesh, surface_count);
+ if (materials) {
+ for (uint32_t j = 0; j < surface_count; j++) {
+ _geometry_instance_add_surface(ginstance, j, materials[j], mesh);
+ }
+ }
+
+ ginstance->instance_count = mesh_storage->multimesh_get_instances_to_draw(ginstance->data->base);
+ }
+
+ } break;
+ case RS::INSTANCE_PARTICLES: {
+ } break;
+
+ default: {
+ }
+ }
+
+ //Fill push constant
+
+ bool store_transform = true;
+ ginstance->base_flags = 0;
+
+ if (ginstance->data->base_type == RS::INSTANCE_MULTIMESH) {
+ ginstance->base_flags |= INSTANCE_DATA_FLAG_MULTIMESH;
+ if (mesh_storage->multimesh_get_transform_format(ginstance->data->base) == RS::MULTIMESH_TRANSFORM_2D) {
+ ginstance->base_flags |= INSTANCE_DATA_FLAG_MULTIMESH_FORMAT_2D;
+ }
+ if (mesh_storage->multimesh_uses_colors(ginstance->data->base)) {
+ ginstance->base_flags |= INSTANCE_DATA_FLAG_MULTIMESH_HAS_COLOR;
+ }
+ if (mesh_storage->multimesh_uses_custom_data(ginstance->data->base)) {
+ ginstance->base_flags |= INSTANCE_DATA_FLAG_MULTIMESH_HAS_CUSTOM_DATA;
+ }
+
+ //ginstance->transforms_uniform_set = mesh_storage->multimesh_get_3d_uniform_set(ginstance->data->base, scene_globals.default_shader_rd, TRANSFORMS_UNIFORM_SET);
+
+ } else if (ginstance->data->base_type == RS::INSTANCE_PARTICLES) {
+ } else if (ginstance->data->base_type == RS::INSTANCE_MESH) {
+ }
+
+ ginstance->store_transform_cache = store_transform;
+
+ if (ginstance->data->dirty_dependencies) {
+ ginstance->data->dependency_tracker.update_end();
+ ginstance->data->dirty_dependencies = false;
+ }
+
+ ginstance->dirty_list_element.remove_from_list();
}
/* SHADOW ATLAS API */
@@ -238,36 +647,70 @@ void RasterizerSceneGLES3::_update_dirty_skys() {
dirty_sky_list = nullptr;
}
-void RasterizerSceneGLES3::_draw_sky(Sky *p_sky, const CameraMatrix &p_projection, const Transform3D &p_transform, float p_custom_fov, float p_energy, const Basis &p_sky_orientation) {
- ERR_FAIL_COND(!p_sky);
+void RasterizerSceneGLES3::_draw_sky(Environment *p_env, const CameraMatrix &p_projection, const Transform3D &p_transform) {
+ GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton();
+ ERR_FAIL_COND(!p_env);
- glDepthMask(GL_TRUE);
- glEnable(GL_DEPTH_TEST);
- glDisable(GL_CULL_FACE);
- glDisable(GL_BLEND);
- glDepthFunc(GL_LEQUAL);
- glColorMask(1, 1, 1, 1);
+ Sky *sky = sky_owner.get_or_null(p_env->sky);
+ ERR_FAIL_COND(!sky);
+
+ GLES3::SkyMaterialData *material_data = nullptr;
+ RID sky_material;
+
+ RS::EnvironmentBG background = p_env->background;
+
+ if (sky) {
+ ERR_FAIL_COND(!sky);
+ sky_material = sky->material;
+
+ if (sky_material.is_valid()) {
+ material_data = static_cast<GLES3::SkyMaterialData *>(material_storage->material_get_data(sky_material, RS::SHADER_SKY));
+ if (!material_data || !material_data->shader_data->valid) {
+ material_data = nullptr;
+ }
+ }
+
+ if (!material_data) {
+ sky_material = sky_globals.default_material;
+ material_data = static_cast<GLES3::SkyMaterialData *>(material_storage->material_get_data(sky_material, RS::SHADER_SKY));
+ }
+ } else if (background == RS::ENV_BG_CLEAR_COLOR || background == RS::ENV_BG_COLOR) {
+ sky_material = sky_globals.fog_material;
+ material_data = static_cast<GLES3::SkyMaterialData *>(material_storage->material_get_data(sky_material, RS::SHADER_SKY));
+ }
+
+ ERR_FAIL_COND(!material_data);
+ material_data->bind_uniforms();
+
+ GLES3::SkyShaderData *shader_data = material_data->shader_data;
- //state.sky_shader.version_bind_shader(sky_globals.default_shader, SkyShaderGLES3::MODE_BACKGROUND);
- //glBindBufferBase(GL_UNIFORM_BUFFER, 0, state.canvas_instance_data_buffers[state.current_buffer]); // Canvas data updated here
- //glBindBufferBase(GL_UNIFORM_BUFFER, 1, state.canvas_instance_data_buffers[state.current_buffer]); // Global data
- //glBindBufferBase(GL_UNIFORM_BUFFER, 2, state.canvas_instance_data_buffers[state.current_buffer]); // Directional light data
- //glBindBufferBase(GL_UNIFORM_BUFFER, 3, state.canvas_instance_data_buffers[state.current_buffer]); // Material uniforms
+ ERR_FAIL_COND(!shader_data);
+
+ //glBindBufferBase(GL_UNIFORM_BUFFER, 2, p_sky.directional light data); // Directional light data
// Camera
CameraMatrix camera;
- if (p_custom_fov) {
+ if (p_env->sky_custom_fov) {
float near_plane = p_projection.get_z_near();
float far_plane = p_projection.get_z_far();
float aspect = p_projection.get_aspect();
- camera.set_perspective(p_custom_fov, aspect, near_plane, far_plane);
-
+ camera.set_perspective(p_env->sky_custom_fov, aspect, near_plane, far_plane);
} else {
camera = p_projection;
}
-
+ Basis sky_transform = p_env->sky_orientation;
+ sky_transform.invert();
+ sky_transform = p_transform.basis * sky_transform;
+
+ GLES3::MaterialStorage::get_singleton()->shaders.sky_shader.version_bind_shader(shader_data->version, SkyShaderGLES3::MODE_BACKGROUND);
+ GLES3::MaterialStorage::get_singleton()->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::ORIENTATION, sky_transform, shader_data->version, SkyShaderGLES3::MODE_BACKGROUND);
+ GLES3::MaterialStorage::get_singleton()->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::PROJECTION, camera.matrix[2][0], camera.matrix[0][0], camera.matrix[2][1], camera.matrix[1][1], shader_data->version, SkyShaderGLES3::MODE_BACKGROUND);
+ GLES3::MaterialStorage::get_singleton()->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::POSITION, p_transform.origin, shader_data->version, SkyShaderGLES3::MODE_BACKGROUND);
+ GLES3::MaterialStorage::get_singleton()->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::TIME, time, shader_data->version, SkyShaderGLES3::MODE_BACKGROUND);
+ // Bind a vertex array or else OpenGL complains. We won't actually use it
+ glBindVertexArray(sky_globals.quad_array);
glDrawArrays(GL_TRIANGLES, 0, 3);
}
@@ -599,20 +1042,267 @@ void RasterizerSceneGLES3::voxel_gi_update(RID p_probe, bool p_update_light_inst
void RasterizerSceneGLES3::voxel_gi_set_quality(RS::VoxelGIQuality) {
}
+void RasterizerSceneGLES3::_fill_render_list(RenderListType p_render_list, const RenderDataGLES3 *p_render_data, PassMode p_pass_mode, bool p_append) {
+ GLES3::MeshStorage *mesh_storage = GLES3::MeshStorage::get_singleton();
+
+ if (p_render_list == RENDER_LIST_OPAQUE) {
+ scene_state.used_screen_texture = false;
+ scene_state.used_normal_texture = false;
+ scene_state.used_depth_texture = false;
+ }
+
+ Plane near_plane;
+ if (p_render_data->cam_orthogonal) {
+ near_plane = Plane(-p_render_data->cam_transform.basis.get_column(Vector3::AXIS_Z), p_render_data->cam_transform.origin);
+ near_plane.d += p_render_data->cam_projection.get_z_near();
+ }
+ float z_max = p_render_data->cam_projection.get_z_far() - p_render_data->cam_projection.get_z_near();
+
+ RenderList *rl = &render_list[p_render_list];
+
+ // Parse any updates on our geometry, updates surface caches and such
+ _update_dirty_geometry_instances();
+
+ if (!p_append) {
+ rl->clear();
+ if (p_render_list == RENDER_LIST_OPAQUE) {
+ render_list[RENDER_LIST_ALPHA].clear(); //opaque fills alpha too
+ }
+ }
+
+ //fill list
+
+ for (int i = 0; i < (int)p_render_data->instances->size(); i++) {
+ GeometryInstanceGLES3 *inst = static_cast<GeometryInstanceGLES3 *>((*p_render_data->instances)[i]);
+
+ if (p_render_data->cam_orthogonal) {
+ Vector3 support_min = inst->transformed_aabb.get_support(-near_plane.normal);
+ inst->depth = near_plane.distance_to(support_min);
+ } else {
+ Vector3 aabb_center = inst->transformed_aabb.position + (inst->transformed_aabb.size * 0.5);
+ inst->depth = p_render_data->cam_transform.origin.distance_to(aabb_center);
+ }
+ uint32_t depth_layer = CLAMP(int(inst->depth * 16 / z_max), 0, 15);
+
+ uint32_t flags = inst->base_flags; //fill flags if appropriate
+
+ if (inst->non_uniform_scale) {
+ flags |= INSTANCE_DATA_FLAGS_NON_UNIFORM_SCALE;
+ }
+
+ //Process lights here, determine if they need extra passes
+ if (p_pass_mode == PASS_MODE_COLOR) {
+ }
+
+ inst->flags_cache = flags;
+
+ GeometryInstanceSurface *surf = inst->surface_caches;
+
+ while (surf) {
+ // LOD
+
+ if (p_render_data->screen_mesh_lod_threshold > 0.0 && mesh_storage->mesh_surface_has_lod(surf->surface)) {
+ //lod
+ Vector3 lod_support_min = inst->transformed_aabb.get_support(-p_render_data->lod_camera_plane.normal);
+ Vector3 lod_support_max = inst->transformed_aabb.get_support(p_render_data->lod_camera_plane.normal);
+
+ float distance_min = p_render_data->lod_camera_plane.distance_to(lod_support_min);
+ float distance_max = p_render_data->lod_camera_plane.distance_to(lod_support_max);
+
+ float distance = 0.0;
+
+ if (distance_min * distance_max < 0.0) {
+ //crossing plane
+ distance = 0.0;
+ } else if (distance_min >= 0.0) {
+ distance = distance_min;
+ } else if (distance_max <= 0.0) {
+ distance = -distance_max;
+ }
+
+ if (p_render_data->cam_orthogonal) {
+ distance = 1.0;
+ }
+
+ uint32_t indices;
+ surf->lod_index = mesh_storage->mesh_surface_get_lod(surf->surface, inst->lod_model_scale * inst->lod_bias, distance * p_render_data->lod_distance_multiplier, p_render_data->screen_mesh_lod_threshold, &indices);
+ /*
+ if (p_render_data->render_info) {
+ indices = _indices_to_primitives(surf->primitive, indices);
+ if (p_render_list == RENDER_LIST_OPAQUE) { //opaque
+ p_render_data->render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_VISIBLE][RS::VIEWPORT_RENDER_INFO_PRIMITIVES_IN_FRAME] += indices;
+ } else if (p_render_list == RENDER_LIST_SECONDARY) { //shadow
+ p_render_data->render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_SHADOW][RS::VIEWPORT_RENDER_INFO_PRIMITIVES_IN_FRAME] += indices;
+ }
+ }
+ */
+ } else {
+ surf->lod_index = 0;
+ /*
+ if (p_render_data->render_info) {
+ uint32_t to_draw = mesh_storage->mesh_surface_get_vertices_drawn_count(surf->surface);
+ to_draw = _indices_to_primitives(surf->primitive, to_draw);
+ to_draw *= inst->instance_count;
+ if (p_render_list == RENDER_LIST_OPAQUE) { //opaque
+ p_render_data->render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_VISIBLE][RS::VIEWPORT_RENDER_INFO_PRIMITIVES_IN_FRAME] += mesh_storage->mesh_surface_get_vertices_drawn_count(surf->surface);
+ } else if (p_render_list == RENDER_LIST_SECONDARY) { //shadow
+ p_render_data->render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_SHADOW][RS::VIEWPORT_RENDER_INFO_PRIMITIVES_IN_FRAME] += mesh_storage->mesh_surface_get_vertices_drawn_count(surf->surface);
+ }
+ }
+ */
+ }
+
+ // ADD Element
+ if (p_pass_mode == PASS_MODE_COLOR) {
+#ifdef DEBUG_ENABLED
+ bool force_alpha = unlikely(get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_OVERDRAW);
+#else
+ bool force_alpha = false;
+#endif
+ if (!force_alpha && (surf->flags & GeometryInstanceSurface::FLAG_PASS_OPAQUE)) {
+ rl->add_element(surf);
+ }
+ if (force_alpha || (surf->flags & GeometryInstanceSurface::FLAG_PASS_ALPHA)) {
+ render_list[RENDER_LIST_ALPHA].add_element(surf);
+ }
+
+ if (surf->flags & GeometryInstanceSurface::FLAG_USES_SCREEN_TEXTURE) {
+ scene_state.used_screen_texture = true;
+ }
+ if (surf->flags & GeometryInstanceSurface::FLAG_USES_NORMAL_TEXTURE) {
+ scene_state.used_normal_texture = true;
+ }
+ if (surf->flags & GeometryInstanceSurface::FLAG_USES_DEPTH_TEXTURE) {
+ scene_state.used_depth_texture = true;
+ }
+
+ /*
+ Add elements here if there are shadows
+ */
+
+ } else if (p_pass_mode == PASS_MODE_SHADOW) {
+ if (surf->flags & GeometryInstanceSurface::FLAG_PASS_SHADOW) {
+ rl->add_element(surf);
+ }
+ } else {
+ if (surf->flags & (GeometryInstanceSurface::FLAG_PASS_DEPTH | GeometryInstanceSurface::FLAG_PASS_OPAQUE)) {
+ rl->add_element(surf);
+ }
+ }
+
+ surf->sort.depth_layer = depth_layer;
+
+ surf = surf->next;
+ }
+ }
+}
+
+void RasterizerSceneGLES3::_setup_environment(const RenderDataGLES3 *p_render_data, bool p_no_fog, const Size2i &p_screen_size, bool p_flip_y, const Color &p_default_bg_color, bool p_pancake_shadows) {
+ CameraMatrix correction;
+ correction.set_depth_correction(p_flip_y);
+ CameraMatrix projection = correction * p_render_data->cam_projection;
+ //store camera into ubo
+ RasterizerStorageGLES3::store_camera(projection, scene_state.ubo.projection_matrix);
+ RasterizerStorageGLES3::store_camera(projection.inverse(), scene_state.ubo.inv_projection_matrix);
+ RasterizerStorageGLES3::store_transform(p_render_data->cam_transform, scene_state.ubo.inv_view_matrix);
+ RasterizerStorageGLES3::store_transform(p_render_data->cam_transform.affine_inverse(), scene_state.ubo.view_matrix);
+
+ scene_state.ubo.directional_light_count = 1;
+
+ scene_state.ubo.z_far = p_render_data->z_far;
+ scene_state.ubo.z_near = p_render_data->z_near;
+
+ scene_state.ubo.pancake_shadows = p_pancake_shadows;
+
+ scene_state.ubo.viewport_size[0] = p_screen_size.x;
+ scene_state.ubo.viewport_size[1] = p_screen_size.y;
+
+ Size2 screen_pixel_size = Vector2(1.0, 1.0) / Size2(p_screen_size);
+ scene_state.ubo.screen_pixel_size[0] = screen_pixel_size.x;
+ scene_state.ubo.screen_pixel_size[1] = screen_pixel_size.y;
+
+ //time global variables
+ scene_state.ubo.time = time;
+
+ if (is_environment(p_render_data->environment)) {
+ Environment *env = environment_owner.get_or_null(p_render_data->environment);
+ RS::EnvironmentBG env_bg = env->background;
+ RS::EnvironmentAmbientSource ambient_src = env->ambient_source;
+
+ float bg_energy = env->bg_energy;
+ scene_state.ubo.ambient_light_color_energy[3] = bg_energy;
+
+ scene_state.ubo.ambient_color_sky_mix = env->ambient_sky_contribution;
+
+ //ambient
+ if (ambient_src == RS::ENV_AMBIENT_SOURCE_BG && (env_bg == RS::ENV_BG_CLEAR_COLOR || env_bg == RS::ENV_BG_COLOR)) {
+ Color color = env_bg == RS::ENV_BG_CLEAR_COLOR ? p_default_bg_color : env->bg_color;
+ color = color.srgb_to_linear();
+
+ scene_state.ubo.ambient_light_color_energy[0] = color.r * bg_energy;
+ scene_state.ubo.ambient_light_color_energy[1] = color.g * bg_energy;
+ scene_state.ubo.ambient_light_color_energy[2] = color.b * bg_energy;
+ } else {
+ float energy = env->ambient_light_energy;
+ Color color = env->ambient_light;
+ color = color.srgb_to_linear();
+ scene_state.ubo.ambient_light_color_energy[0] = color.r * energy;
+ scene_state.ubo.ambient_light_color_energy[1] = color.g * energy;
+ scene_state.ubo.ambient_light_color_energy[2] = color.b * energy;
+
+ Basis sky_transform = env->sky_orientation;
+ sky_transform = sky_transform.inverse() * p_render_data->cam_transform.basis;
+ RasterizerStorageGLES3::store_transform_3x3(sky_transform, scene_state.ubo.radiance_inverse_xform);
+ }
+
+ scene_state.ubo.fog_enabled = env->fog_enabled;
+ scene_state.ubo.fog_density = env->fog_density;
+ scene_state.ubo.fog_height = env->fog_height;
+ scene_state.ubo.fog_height_density = env->fog_height_density;
+ scene_state.ubo.fog_aerial_perspective = env->fog_aerial_perspective;
+
+ Color fog_color = env->fog_light_color.srgb_to_linear();
+ float fog_energy = env->fog_light_energy;
+
+ scene_state.ubo.fog_light_color[0] = fog_color.r * fog_energy;
+ scene_state.ubo.fog_light_color[1] = fog_color.g * fog_energy;
+ scene_state.ubo.fog_light_color[2] = fog_color.b * fog_energy;
+
+ scene_state.ubo.fog_sun_scatter = env->fog_sun_scatter;
+
+ } else {
+ }
+
+ if (scene_state.ubo_buffer == 0) {
+ glGenBuffers(1, &scene_state.ubo_buffer);
+ }
+ glBindBufferBase(GL_UNIFORM_BUFFER, SCENE_DATA_UNIFORM_LOCATION, scene_state.ubo_buffer);
+ glBufferData(GL_UNIFORM_BUFFER, sizeof(SceneState::UBO), &scene_state.ubo, GL_STREAM_DRAW);
+ glBindBuffer(GL_UNIFORM_BUFFER, 0);
+}
+
void RasterizerSceneGLES3::render_scene(RID p_render_buffers, const CameraData *p_camera_data, const PagedArray<GeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_voxel_gi_instances, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, const PagedArray<RID> &p_fog_volumes, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_mesh_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data, RendererScene::RenderInfo *r_render_info) {
GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton();
+ GLES3::Config *config = GLES3::Config::get_singleton();
RENDER_TIMESTAMP("Setup 3D Scene");
- // assign render data
+
+ RenderBuffers *rb = nullptr;
+ if (p_render_buffers.is_valid()) {
+ rb = render_buffers_owner.get_or_null(p_render_buffers);
+ ERR_FAIL_COND(!rb);
+ }
+
+ // Assign render data
// Use the format from rendererRD
RenderDataGLES3 render_data;
{
render_data.render_buffers = p_render_buffers;
-
+ render_data.transparent_bg = rb->is_transparent;
// Our first camera is used by default
render_data.cam_transform = p_camera_data->main_transform;
render_data.cam_projection = p_camera_data->main_projection;
render_data.view_projection[0] = p_camera_data->main_projection;
- render_data.cam_ortogonal = p_camera_data->is_orthogonal;
+ render_data.cam_orthogonal = p_camera_data->is_orthogonal;
render_data.view_count = p_camera_data->view_count;
for (uint32_t v = 0; v < p_camera_data->view_count; v++) {
@@ -625,10 +1315,6 @@ void RasterizerSceneGLES3::render_scene(RID p_render_buffers, const CameraData *
render_data.instances = &p_instances;
render_data.lights = &p_lights;
render_data.reflection_probes = &p_reflection_probes;
- //render_data.voxel_gi_instances = &p_voxel_gi_instances;
- //render_data.decals = &p_decals;
- //render_data.lightmaps = &p_lightmaps;
- //render_data.fog_volumes = &p_fog_volumes;
render_data.environment = p_environment;
render_data.camera_effects = p_camera_effects;
render_data.shadow_atlas = p_shadow_atlas;
@@ -655,14 +1341,14 @@ void RasterizerSceneGLES3::render_scene(RID p_render_buffers, const CameraData *
render_data.reflection_probes = &empty;
}
- RenderBuffers *rb = nullptr;
- //RasterizerStorageGLES3::RenderTarget *rt = nullptr;
- if (p_render_buffers.is_valid()) {
- rb = render_buffers_owner.get_or_null(p_render_buffers);
- ERR_FAIL_COND(!rb);
- //rt = texture_storage->render_target_owner.get_or_null(rb->render_target);
- //ERR_FAIL_COND(!rt);
- }
+ bool reverse_cull = false;
+
+ ///////////
+ // Fill Light lists here
+ //////////
+
+ GLuint global_buffer = GLES3::MaterialStorage::get_singleton()->global_variables_get_uniform_buffer();
+ glBindBufferBase(GL_UNIFORM_BUFFER, SCENE_GLOBALS_UNIFORM_LOCATION, global_buffer);
Color clear_color;
if (p_render_buffers.is_valid()) {
@@ -675,11 +1361,84 @@ void RasterizerSceneGLES3::render_scene(RID p_render_buffers, const CameraData *
bool fb_cleared = false;
- glDepthFunc(GL_LEQUAL);
+ Size2i screen_size;
+ screen_size.x = rb->width;
+ screen_size.y = rb->height;
- /* Depth Prepass */
+ SceneState::TonemapUBO tonemap_ubo;
+ if (is_environment(p_environment)) {
+ tonemap_ubo.exposure = env->exposure;
+ tonemap_ubo.white = env->white;
+ tonemap_ubo.tonemapper = int32_t(env->tone_mapper);
+ }
+
+ if (scene_state.tonemap_buffer == 0) {
+ // Only create if using 3D
+ glGenBuffers(1, &scene_state.tonemap_buffer);
+ }
+ glBindBufferBase(GL_UNIFORM_BUFFER, SCENE_TONEMAP_UNIFORM_LOCATION, scene_state.tonemap_buffer);
+ glBufferData(GL_UNIFORM_BUFFER, sizeof(SceneState::TonemapUBO), &tonemap_ubo, GL_STREAM_DRAW);
+
+ _setup_environment(&render_data, render_data.reflection_probe.is_valid(), screen_size, !render_data.reflection_probe.is_valid(), clear_color, false);
+
+ _fill_render_list(RENDER_LIST_OPAQUE, &render_data, PASS_MODE_COLOR);
+ render_list[RENDER_LIST_OPAQUE].sort_by_key();
+ render_list[RENDER_LIST_ALPHA].sort_by_reverse_depth_and_priority();
glBindFramebuffer(GL_FRAMEBUFFER, rb->framebuffer);
+ glViewport(0, 0, rb->width, rb->height);
+
+ // Do depth prepass if it's explicitly enabled
+ bool use_depth_prepass = config->use_depth_prepass;
+
+ // Don't do depth prepass we are rendering overdraw
+ use_depth_prepass = use_depth_prepass && get_debug_draw_mode() != RS::VIEWPORT_DEBUG_DRAW_OVERDRAW;
+
+ if (use_depth_prepass) {
+ //pre z pass
+
+ glDisable(GL_BLEND);
+ glDepthMask(GL_TRUE);
+ glEnable(GL_DEPTH_TEST);
+ glDepthFunc(GL_LEQUAL);
+ glDisable(GL_SCISSOR_TEST);
+ glCullFace(GL_BACK);
+ glEnable(GL_CULL_FACE);
+ scene_state.cull_mode = GLES3::SceneShaderData::CULL_BACK;
+
+ glColorMask(0, 0, 0, 0);
+ glClearDepth(1.0f);
+ glClear(GL_DEPTH_BUFFER_BIT);
+
+ uint32_t spec_constant_base_flags = 0;
+
+ RenderListParameters render_list_params(render_list[RENDER_LIST_OPAQUE].elements.ptr(), render_list[RENDER_LIST_OPAQUE].elements.size(), reverse_cull, spec_constant_base_flags, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, render_data.lod_camera_plane, render_data.lod_distance_multiplier, render_data.screen_mesh_lod_threshold);
+ _render_list_template<PASS_MODE_DEPTH>(&render_list_params, &render_data, 0, render_list[RENDER_LIST_OPAQUE].elements.size());
+
+ glColorMask(1, 1, 1, 1);
+
+ fb_cleared = true;
+ scene_state.used_depth_prepass = true;
+ } else {
+ scene_state.used_depth_prepass = false;
+ }
+
+ glBlendEquation(GL_FUNC_ADD);
+
+ if (render_data.transparent_bg) {
+ glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+ glEnable(GL_BLEND);
+ } else {
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glDisable(GL_BLEND);
+ }
+ scene_state.current_blend_mode = GLES3::SceneShaderData::BLEND_MODE_MIX;
+
+ glEnable(GL_DEPTH_TEST);
+ glDepthFunc(GL_LEQUAL);
+ glDepthMask(GL_TRUE);
+ scene_state.current_depth_test = GLES3::SceneShaderData::DEPTH_TEST_ENABLED;
+ scene_state.current_depth_draw = GLES3::SceneShaderData::DEPTH_DRAW_OPAQUE;
if (!fb_cleared) {
glClearDepth(1.0f);
@@ -690,9 +1449,6 @@ void RasterizerSceneGLES3::render_scene(RID p_render_buffers, const CameraData *
bool keep_color = false;
if (get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_OVERDRAW) {
- clear_color = Color(0, 0, 0, 1);
- }
- if (get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_OVERDRAW) {
clear_color = Color(0, 0, 0, 1); //in overdraw mode, BG should always be black
} else if (is_environment(p_environment)) {
RS::EnvironmentBG bg_mode = environment_get_background(p_environment);
@@ -723,16 +1479,43 @@ void RasterizerSceneGLES3::render_scene(RID p_render_buffers, const CameraData *
default: {
}
}
+ // Draw sky cubemap
}
if (!keep_color) {
glClearBufferfv(GL_COLOR, 0, clear_color.components);
}
+ uint32_t spec_constant_base_flags = 0;
+ //Render Opaque Objects
+ RenderListParameters render_list_params(render_list[RENDER_LIST_OPAQUE].elements.ptr(), render_list[RENDER_LIST_OPAQUE].elements.size(), reverse_cull, spec_constant_base_flags, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, render_data.lod_camera_plane, render_data.lod_distance_multiplier, render_data.screen_mesh_lod_threshold);
+
+ _render_list_template<PASS_MODE_COLOR>(&render_list_params, &render_data, 0, render_list[RENDER_LIST_OPAQUE].elements.size());
+
if (draw_sky) {
- //_draw_sky(sky, render_data.cam_projection, render_data.cam_transform, env->sky_custom_fov, env->bg_energy, env->sky_orientation);
+ if (scene_state.current_depth_test != GLES3::SceneShaderData::DEPTH_TEST_ENABLED) {
+ glEnable(GL_DEPTH_TEST);
+ scene_state.current_depth_test = GLES3::SceneShaderData::DEPTH_TEST_ENABLED;
+ }
+ glEnable(GL_DEPTH_TEST);
+ glDepthMask(GL_FALSE);
+ glDisable(GL_BLEND);
+ glEnable(GL_CULL_FACE);
+ glCullFace(GL_BACK);
+ scene_state.current_depth_test = GLES3::SceneShaderData::DEPTH_TEST_ENABLED;
+ scene_state.current_depth_draw = GLES3::SceneShaderData::DEPTH_DRAW_DISABLED;
+ scene_state.cull_mode = GLES3::SceneShaderData::CULL_BACK;
+
+ _draw_sky(env, render_data.cam_projection, render_data.cam_transform);
}
+ glEnable(GL_BLEND);
+
+ //Render transparent pass
+ RenderListParameters render_list_params_alpha(render_list[RENDER_LIST_ALPHA].elements.ptr(), render_list[RENDER_LIST_ALPHA].elements.size(), reverse_cull, spec_constant_base_flags, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, render_data.lod_camera_plane, render_data.lod_distance_multiplier, render_data.screen_mesh_lod_threshold);
+
+ _render_list_template<PASS_MODE_COLOR_TRANSPARENT>(&render_list_params_alpha, &render_data, 0, render_list[RENDER_LIST_ALPHA].elements.size(), true);
+
if (p_render_buffers.is_valid()) {
/*
RENDER_TIMESTAMP("Tonemap");
@@ -741,9 +1524,225 @@ void RasterizerSceneGLES3::render_scene(RID p_render_buffers, const CameraData *
_render_buffers_debug_draw(p_render_buffers, p_shadow_atlas, p_occluder_debug_tex);
}
+ glDisable(GL_BLEND);
texture_storage->render_target_disable_clear_request(rb->render_target);
}
+template <PassMode p_pass_mode>
+void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, const RenderDataGLES3 *p_render_data, uint32_t p_from_element, uint32_t p_to_element, bool p_alpha_pass) {
+ GLES3::MeshStorage *mesh_storage = GLES3::MeshStorage::get_singleton();
+
+ GLuint prev_vertex_array_gl = 0;
+ GLuint prev_index_array_gl = 0;
+
+ GLES3::SceneMaterialData *prev_material_data = nullptr;
+ GLES3::SceneShaderData *prev_shader = nullptr;
+
+ SceneShaderGLES3::ShaderVariant shader_variant = SceneShaderGLES3::MODE_COLOR; // Assigned to silence wrong -Wmaybe-initialized.
+
+ switch (p_pass_mode) {
+ case PASS_MODE_COLOR:
+ case PASS_MODE_COLOR_TRANSPARENT: {
+ } break;
+ case PASS_MODE_COLOR_ADDITIVE: {
+ shader_variant = SceneShaderGLES3::MODE_ADDITIVE;
+ } break;
+ case PASS_MODE_SHADOW:
+ case PASS_MODE_DEPTH: {
+ shader_variant = SceneShaderGLES3::MODE_DEPTH;
+ } break;
+ }
+
+ for (uint32_t i = p_from_element; i < p_to_element; i++) {
+ const GeometryInstanceSurface *surf = p_params->elements[i];
+ const GeometryInstanceGLES3 *inst = surf->owner;
+
+ if (p_pass_mode == PASS_MODE_COLOR && !(surf->flags & GeometryInstanceSurface::FLAG_PASS_OPAQUE)) {
+ continue; // Objects with "Depth-prepass" transparency are included in both render lists, but should only be rendered in the transparent pass
+ }
+
+ if (inst->instance_count == 0) {
+ continue;
+ }
+
+ //uint32_t base_spec_constants = p_params->spec_constant_base_flags;
+
+ GLES3::SceneShaderData *shader;
+ GLES3::SceneMaterialData *material_data;
+ void *mesh_surface;
+
+ if (p_pass_mode == PASS_MODE_SHADOW) {
+ shader = surf->shader_shadow;
+ material_data = surf->material_shadow;
+ mesh_surface = surf->surface_shadow;
+ } else {
+ shader = surf->shader;
+ material_data = surf->material;
+ mesh_surface = surf->surface;
+ }
+
+ if (!mesh_surface) {
+ continue;
+ }
+
+ if (p_pass_mode == PASS_MODE_COLOR_TRANSPARENT) {
+ if (scene_state.current_depth_test != shader->depth_test) {
+ if (shader->depth_test == GLES3::SceneShaderData::DEPTH_TEST_DISABLED) {
+ glDisable(GL_DEPTH_TEST);
+ } else {
+ glEnable(GL_DEPTH_TEST);
+ }
+ scene_state.current_depth_test = shader->depth_test;
+ }
+ }
+
+ if (scene_state.current_depth_draw != shader->depth_draw) {
+ switch (shader->depth_draw) {
+ case GLES3::SceneShaderData::DEPTH_DRAW_OPAQUE: {
+ glDepthMask(p_pass_mode == PASS_MODE_COLOR);
+ } break;
+ case GLES3::SceneShaderData::DEPTH_DRAW_ALWAYS: {
+ glDepthMask(GL_TRUE);
+ } break;
+ case GLES3::SceneShaderData::DEPTH_DRAW_DISABLED: {
+ glDepthMask(GL_FALSE);
+ } break;
+ }
+
+ scene_state.current_depth_draw = shader->depth_draw;
+ }
+
+ if (p_pass_mode == PASS_MODE_COLOR_TRANSPARENT || p_pass_mode == PASS_MODE_COLOR_ADDITIVE) {
+ GLES3::SceneShaderData::BlendMode desired_blend_mode;
+ if (p_pass_mode == PASS_MODE_COLOR_ADDITIVE) {
+ desired_blend_mode = GLES3::SceneShaderData::BLEND_MODE_ADD;
+ } else {
+ desired_blend_mode = shader->blend_mode;
+ }
+
+ if (desired_blend_mode != scene_state.current_blend_mode) {
+ switch (desired_blend_mode) {
+ case GLES3::SceneShaderData::BLEND_MODE_MIX: {
+ glBlendEquation(GL_FUNC_ADD);
+ if (p_render_data->transparent_bg) {
+ glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+ } else {
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ }
+
+ } break;
+ case GLES3::SceneShaderData::BLEND_MODE_ADD: {
+ glBlendEquation(GL_FUNC_ADD);
+ glBlendFunc(p_pass_mode == PASS_MODE_COLOR_TRANSPARENT ? GL_SRC_ALPHA : GL_ONE, GL_ONE);
+
+ } break;
+ case GLES3::SceneShaderData::BLEND_MODE_SUB: {
+ glBlendEquation(GL_FUNC_REVERSE_SUBTRACT);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE);
+ } break;
+ case GLES3::SceneShaderData::BLEND_MODE_MUL: {
+ glBlendEquation(GL_FUNC_ADD);
+ if (p_render_data->transparent_bg) {
+ glBlendFuncSeparate(GL_DST_COLOR, GL_ZERO, GL_DST_ALPHA, GL_ZERO);
+ } else {
+ glBlendFuncSeparate(GL_DST_COLOR, GL_ZERO, GL_ZERO, GL_ONE);
+ }
+
+ } break;
+ case GLES3::SceneShaderData::BLEND_MODE_ALPHA_TO_COVERAGE: {
+ // Do nothing for now.
+ } break;
+ }
+ scene_state.current_blend_mode = desired_blend_mode;
+ }
+ }
+
+ //find cull variant
+ GLES3::SceneShaderData::Cull cull_mode = shader->cull_mode;
+
+ if ((surf->flags & GeometryInstanceSurface::FLAG_USES_DOUBLE_SIDED_SHADOWS)) {
+ cull_mode = GLES3::SceneShaderData::CULL_DISABLED;
+ } else {
+ bool mirror = inst->mirror;
+ if (p_params->reverse_cull) {
+ mirror = !mirror;
+ }
+ if (cull_mode == GLES3::SceneShaderData::CULL_FRONT && mirror) {
+ cull_mode = GLES3::SceneShaderData::CULL_BACK;
+ } else if (cull_mode == GLES3::SceneShaderData::CULL_BACK && mirror) {
+ cull_mode = GLES3::SceneShaderData::CULL_FRONT;
+ }
+ }
+
+ if (scene_state.cull_mode != cull_mode) {
+ if (cull_mode == GLES3::SceneShaderData::CULL_DISABLED) {
+ glDisable(GL_CULL_FACE);
+ } else {
+ if (scene_state.cull_mode == GLES3::SceneShaderData::CULL_DISABLED) {
+ // Last time was disabled, so enable and set proper face.
+ glEnable(GL_CULL_FACE);
+ }
+ glCullFace(cull_mode == GLES3::SceneShaderData::CULL_FRONT ? GL_FRONT : GL_BACK);
+ }
+ scene_state.cull_mode = cull_mode;
+ }
+
+ RS::PrimitiveType primitive = surf->primitive;
+ static const GLenum prim[5] = { GL_POINTS, GL_LINES, GL_LINE_STRIP, GL_TRIANGLES, GL_TRIANGLE_STRIP };
+ GLenum primitive_gl = prim[int(primitive)];
+
+ GLuint vertex_array_gl = 0;
+ GLuint index_array_gl = 0;
+
+ //skeleton and blend shape
+ if (surf->owner->mesh_instance.is_valid()) {
+ mesh_storage->mesh_instance_surface_get_vertex_arrays_and_format(surf->owner->mesh_instance, surf->surface_index, shader->vertex_input_mask, vertex_array_gl);
+ } else {
+ mesh_storage->mesh_surface_get_vertex_arrays_and_format(mesh_surface, shader->vertex_input_mask, vertex_array_gl);
+ }
+
+ index_array_gl = mesh_storage->mesh_surface_get_index_buffer(mesh_surface, surf->lod_index);
+
+ if (prev_vertex_array_gl != vertex_array_gl) {
+ glBindVertexArray(vertex_array_gl);
+ prev_vertex_array_gl = vertex_array_gl;
+ }
+
+ bool use_index_buffer = false;
+ if (prev_index_array_gl != index_array_gl) {
+ if (index_array_gl != 0) {
+ // Bind index each time so we can use LODs
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_array_gl);
+ use_index_buffer = true;
+ }
+ prev_index_array_gl = index_array_gl;
+ }
+
+ // Update pipeline information here
+
+ Transform3D world_transform;
+ if (inst->store_transform_cache) {
+ world_transform = inst->transform;
+ }
+
+ if (prev_material_data != material_data) {
+ material_data->bind_uniforms();
+ }
+
+ if (prev_shader != shader) {
+ GLES3::MaterialStorage::get_singleton()->shaders.scene_shader.version_bind_shader(shader->version, shader_variant);
+ }
+
+ GLES3::MaterialStorage::get_singleton()->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::WORLD_TRANSFORM, world_transform, shader->version, shader_variant);
+
+ if (use_index_buffer) {
+ glDrawElements(primitive_gl, mesh_storage->mesh_surface_get_vertices_drawn_count(mesh_surface), mesh_storage->mesh_surface_get_index_type(mesh_surface), 0);
+ } else {
+ glDrawArrays(primitive_gl, 0, mesh_storage->mesh_surface_get_vertices_drawn_count(mesh_surface));
+ }
+ }
+}
+
void RasterizerSceneGLES3::render_material(const Transform3D &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, const PagedArray<GeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region) {
}
@@ -879,6 +1878,8 @@ void RasterizerSceneGLES3::render_buffers_configure(RID p_render_buffers, RID p_
GLES3::RenderTarget *rt = texture_storage->get_render_target(p_render_target);
+ rb->is_transparent = rt->flags[RendererTextureStorage::RENDER_TARGET_TRANSPARENT];
+
// framebuffer
glGenFramebuffers(1, &rb->framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, rb->framebuffer);
@@ -1007,6 +2008,38 @@ RasterizerSceneGLES3::RasterizerSceneGLES3(RasterizerStorageGLES3 *p_storage) {
storage = p_storage;
{
+ String global_defines;
+ global_defines += "#define MAX_GLOBAL_VARIABLES 256\n"; // TODO: this is arbitrary for now
+ material_storage->shaders.scene_shader.initialize(global_defines);
+ scene_globals.shader_default_version = material_storage->shaders.scene_shader.version_create();
+ material_storage->shaders.scene_shader.version_bind_shader(scene_globals.shader_default_version, SceneShaderGLES3::MODE_COLOR);
+ }
+
+ {
+ //default material and shader
+ scene_globals.default_shader = material_storage->shader_allocate();
+ material_storage->shader_initialize(scene_globals.default_shader);
+ material_storage->shader_set_code(scene_globals.default_shader, R"(
+// Default 3D material shader (clustered).
+
+shader_type spatial;
+
+void vertex() {
+ ROUGHNESS = 0.8;
+}
+
+void fragment() {
+ ALBEDO = vec3(0.6);
+ ROUGHNESS = 0.8;
+ METALLIC = 0.2;
+}
+)");
+ scene_globals.default_material = material_storage->material_allocate();
+ material_storage->material_initialize(scene_globals.default_material);
+ material_storage->material_set_shader(scene_globals.default_material, scene_globals.default_shader);
+ }
+
+ {
// Initialize Sky stuff
sky_globals.roughness_layers = GLOBAL_GET("rendering/reflections/sky_reflections/roughness_layers");
sky_globals.ggx_samples = GLOBAL_GET("rendering/reflections/sky_reflections/ggx_samples");
@@ -1014,9 +2047,9 @@ RasterizerSceneGLES3::RasterizerSceneGLES3(RasterizerStorageGLES3 *p_storage) {
String global_defines;
global_defines += "#define MAX_GLOBAL_VARIABLES 256\n"; // TODO: this is arbitrary for now
global_defines += "\n#define MAX_DIRECTIONAL_LIGHT_DATA_STRUCTS " + itos(sky_globals.max_directional_lights) + "\n";
- state.sky_shader.initialize(global_defines);
- sky_globals.shader_default_version = state.sky_shader.version_create();
- state.sky_shader.version_bind_shader(sky_globals.shader_default_version, SkyShaderGLES3::MODE_BACKGROUND);
+ material_storage->shaders.sky_shader.initialize(global_defines);
+ sky_globals.shader_default_version = material_storage->shaders.sky_shader.version_create();
+ material_storage->shaders.sky_shader.version_bind_shader(sky_globals.shader_default_version, SkyShaderGLES3::MODE_BACKGROUND);
}
{
@@ -1038,12 +2071,79 @@ void sky() {
material_storage->material_set_shader(sky_globals.default_material, sky_globals.default_shader);
}
+ {
+ sky_globals.fog_shader = material_storage->shader_allocate();
+ material_storage->shader_initialize(sky_globals.fog_shader);
+
+ material_storage->shader_set_code(sky_globals.fog_shader, R"(
+// Default clear color sky shader.
+
+shader_type sky;
+
+uniform vec4 clear_color;
+
+void sky() {
+ COLOR = clear_color.rgb;
+}
+)");
+ sky_globals.fog_material = material_storage->material_allocate();
+ material_storage->material_initialize(sky_globals.fog_material);
+
+ material_storage->material_set_shader(sky_globals.fog_material, sky_globals.fog_shader);
+ }
+ {
+ {
+ //quad buffers
+
+ glGenBuffers(1, &sky_globals.quad);
+ glBindBuffer(GL_ARRAY_BUFFER, sky_globals.quad);
+ {
+ const float qv[16] = {
+ -1,
+ -1,
+ 0,
+ 0,
+ -1,
+ 1,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ -1,
+ 1,
+ 0,
+ };
+
+ glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 16, qv, GL_STATIC_DRAW);
+ }
+
+ glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind
+
+ glGenVertexArrays(1, &sky_globals.quad_array);
+ glBindVertexArray(sky_globals.quad_array);
+ glBindBuffer(GL_ARRAY_BUFFER, sky_globals.quad);
+ glVertexAttribPointer(RS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, nullptr);
+ glEnableVertexAttribArray(RS::ARRAY_VERTEX);
+ glVertexAttribPointer(RS::ARRAY_TEX_UV, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, CAST_INT_TO_UCHAR_PTR(8));
+ glEnableVertexAttribArray(RS::ARRAY_TEX_UV);
+ glBindVertexArray(0);
+ glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind
+ }
+ }
}
RasterizerSceneGLES3::~RasterizerSceneGLES3() {
- state.sky_shader.version_free(sky_globals.shader_default_version);
+ GLES3::MaterialStorage::get_singleton()->shaders.scene_shader.version_free(scene_globals.shader_default_version);
+ storage->free(scene_globals.default_material);
+ storage->free(scene_globals.default_shader);
+ GLES3::MaterialStorage::get_singleton()->shaders.sky_shader.version_free(sky_globals.shader_default_version);
storage->free(sky_globals.default_material);
storage->free(sky_globals.default_shader);
+ storage->free(sky_globals.fog_material);
+ storage->free(sky_globals.fog_shader);
}
#endif // GLES3_ENABLED
diff --git a/drivers/gles3/rasterizer_scene_gles3.h b/drivers/gles3/rasterizer_scene_gles3.h
index ed529beb25..ac2f3c932a 100644
--- a/drivers/gles3/rasterizer_scene_gles3.h
+++ b/drivers/gles3/rasterizer_scene_gles3.h
@@ -34,6 +34,7 @@
#ifdef GLES3_ENABLED
#include "core/math/camera_matrix.h"
+#include "core/templates/paged_allocator.h"
#include "core/templates/rid_owner.h"
#include "core/templates/self_list.h"
#include "rasterizer_storage_gles3.h"
@@ -44,13 +45,47 @@
#include "shader_gles3.h"
#include "shaders/sky.glsl.gen.h"
-// Copied from renderer_scene_render_rd
+enum RenderListType {
+ RENDER_LIST_OPAQUE, //used for opaque objects
+ RENDER_LIST_ALPHA, //used for transparent objects
+ RENDER_LIST_SECONDARY, //used for shadows and other objects
+ RENDER_LIST_MAX
+};
+
+enum PassMode {
+ PASS_MODE_COLOR,
+ PASS_MODE_COLOR_TRANSPARENT,
+ PASS_MODE_COLOR_ADDITIVE,
+ PASS_MODE_SHADOW,
+ PASS_MODE_DEPTH,
+};
+
+// These should share as much as possible with SkyUniform Location
+enum SceneUniformLocation {
+ SCENE_TONEMAP_UNIFORM_LOCATION,
+ SCENE_GLOBALS_UNIFORM_LOCATION,
+ SCENE_DATA_UNIFORM_LOCATION,
+ SCENE_MATERIAL_UNIFORM_LOCATION,
+ SCENE_RADIANCE_UNIFORM_LOCATION,
+ SCENE_OMNILIGHT_UNIFORM_LOCATION,
+ SCENE_SPOTLIGHT_UNIFORM_LOCATION,
+};
+
+enum SkyUniformLocation {
+ SKY_TONEMAP_UNIFORM_LOCATION,
+ SKY_GLOBALS_UNIFORM_LOCATION,
+ SKY_SCENE_DATA_UNIFORM_LOCATION,
+ SKY_DIRECTIONAL_LIGHT_UNIFORM_LOCATION,
+ SKY_MATERIAL_UNIFORM_LOCATION,
+};
+
struct RenderDataGLES3 {
RID render_buffers = RID();
+ bool transparent_bg = false;
Transform3D cam_transform = Transform3D();
CameraMatrix cam_projection = CameraMatrix();
- bool cam_ortogonal = false;
+ bool cam_orthogonal = false;
// For stereo rendering
uint32_t view_count = 1;
@@ -91,17 +126,324 @@ private:
RS::ViewportDebugDraw debug_draw = RS::VIEWPORT_DEBUG_DRAW_DISABLED;
uint64_t scene_pass = 0;
- /* Sky */
struct SkyGlobals {
- RID shader_current_version;
RID shader_default_version;
RID default_material;
RID default_shader;
+ RID fog_material;
+ RID fog_shader;
+ GLuint quad = 0;
+ GLuint quad_array = 0;
uint32_t max_directional_lights = 4;
uint32_t roughness_layers = 8;
uint32_t ggx_samples = 128;
} sky_globals;
+ struct SceneGlobals {
+ RID shader_default_version;
+ RID default_material;
+ RID default_shader;
+ } scene_globals;
+
+ struct SceneState {
+ struct UBO {
+ float projection_matrix[16];
+ float inv_projection_matrix[16];
+ float inv_view_matrix[16];
+ float view_matrix[16];
+
+ float viewport_size[2];
+ float screen_pixel_size[2];
+
+ float ambient_light_color_energy[4];
+
+ float ambient_color_sky_mix;
+ uint32_t ambient_flags;
+ uint32_t material_uv2_mode;
+ float opaque_prepass_threshold;
+ //bool use_ambient_light;
+ //bool use_ambient_cubemap;
+ //bool use_reflection_cubemap;
+
+ float radiance_inverse_xform[12];
+
+ uint32_t directional_light_count;
+ float z_far;
+ float z_near;
+ uint32_t pancake_shadows;
+
+ uint32_t fog_enabled;
+ float fog_density;
+ float fog_height;
+ float fog_height_density;
+
+ float fog_light_color[3];
+ float fog_sun_scatter;
+
+ float fog_aerial_perspective;
+ float time;
+ uint32_t pad[2];
+ };
+ static_assert(sizeof(UBO) % 16 == 0, "Scene UBO size must be a multiple of 16 bytes");
+
+ struct TonemapUBO {
+ float exposure = 1.0;
+ float white = 1.0;
+ int32_t tonemapper = 0;
+ int32_t pad = 0;
+ };
+ static_assert(sizeof(TonemapUBO) % 16 == 0, "Tonemap UBO size must be a multiple of 16 bytes");
+
+ UBO ubo;
+ GLuint ubo_buffer = 0;
+ GLuint tonemap_buffer = 0;
+
+ bool used_depth_prepass = false;
+
+ GLES3::SceneShaderData::BlendMode current_blend_mode = GLES3::SceneShaderData::BLEND_MODE_MIX;
+ GLES3::SceneShaderData::DepthDraw current_depth_draw = GLES3::SceneShaderData::DEPTH_DRAW_OPAQUE;
+ GLES3::SceneShaderData::DepthTest current_depth_test = GLES3::SceneShaderData::DEPTH_TEST_DISABLED;
+ GLES3::SceneShaderData::Cull cull_mode = GLES3::SceneShaderData::CULL_BACK;
+
+ bool texscreen_copied = false;
+ bool used_screen_texture = false;
+ bool used_normal_texture = false;
+ bool used_depth_texture = false;
+ } scene_state;
+
+ struct GeometryInstanceGLES3;
+
+ // Cached data for drawing surfaces
+ struct GeometryInstanceSurface {
+ enum {
+ FLAG_PASS_DEPTH = 1,
+ FLAG_PASS_OPAQUE = 2,
+ FLAG_PASS_ALPHA = 4,
+ FLAG_PASS_SHADOW = 8,
+ FLAG_USES_SHARED_SHADOW_MATERIAL = 128,
+ FLAG_USES_SCREEN_TEXTURE = 2048,
+ FLAG_USES_DEPTH_TEXTURE = 4096,
+ FLAG_USES_NORMAL_TEXTURE = 8192,
+ FLAG_USES_DOUBLE_SIDED_SHADOWS = 16384,
+ };
+
+ union {
+ struct {
+ uint64_t lod_index : 8;
+ uint64_t surface_index : 8;
+ uint64_t geometry_id : 32;
+ uint64_t material_id_low : 16;
+
+ uint64_t material_id_hi : 16;
+ uint64_t shader_id : 32;
+ uint64_t uses_softshadow : 1;
+ uint64_t uses_projector : 1;
+ uint64_t uses_forward_gi : 1;
+ uint64_t uses_lightmap : 1;
+ uint64_t depth_layer : 4;
+ uint64_t priority : 8;
+ };
+ struct {
+ uint64_t sort_key1;
+ uint64_t sort_key2;
+ };
+ } sort;
+
+ RS::PrimitiveType primitive = RS::PRIMITIVE_MAX;
+ uint32_t flags = 0;
+ uint32_t surface_index = 0;
+ uint32_t lod_index = 0;
+
+ void *surface = nullptr;
+ GLES3::SceneShaderData *shader = nullptr;
+ GLES3::SceneMaterialData *material = nullptr;
+
+ void *surface_shadow = nullptr;
+ GLES3::SceneShaderData *shader_shadow = nullptr;
+ GLES3::SceneMaterialData *material_shadow = nullptr;
+
+ GeometryInstanceSurface *next = nullptr;
+ GeometryInstanceGLES3 *owner = nullptr;
+ };
+
+ struct GeometryInstanceGLES3 : public GeometryInstance {
+ //used during rendering
+ bool mirror = false;
+ bool non_uniform_scale = false;
+ float lod_bias = 0.0;
+ float lod_model_scale = 1.0;
+ AABB transformed_aabb; //needed for LOD
+ float depth = 0;
+ uint32_t flags_cache = 0;
+ bool store_transform_cache = true;
+ int32_t shader_parameters_offset = -1;
+
+ uint32_t layer_mask = 1;
+ uint32_t instance_count = 0;
+
+ RID mesh_instance;
+ bool can_sdfgi = false;
+ bool using_projectors = false;
+ bool using_softshadows = false;
+ bool fade_near = false;
+ float fade_near_begin = 0;
+ float fade_near_end = 0;
+ bool fade_far = false;
+ float fade_far_begin = 0;
+ float fade_far_end = 0;
+ float force_alpha = 1.0;
+ float parent_fade_alpha = 1.0;
+
+ uint32_t omni_light_count = 0;
+ uint32_t omni_lights[8];
+ uint32_t spot_light_count = 0;
+ uint32_t spot_lights[8];
+
+ //used during setup
+ uint32_t base_flags = 0;
+ Transform3D transform;
+ GeometryInstanceSurface *surface_caches = nullptr;
+ SelfList<GeometryInstanceGLES3> dirty_list_element;
+
+ struct Data {
+ //data used less often goes into regular heap
+ RID base;
+ RS::InstanceType base_type;
+
+ RID skeleton;
+ Vector<RID> surface_materials;
+ RID material_override;
+ RID material_overlay;
+ AABB aabb;
+
+ bool use_dynamic_gi = false;
+ bool use_baked_light = false;
+ bool cast_double_sided_shadows = false;
+ bool mirror = false;
+ bool dirty_dependencies = false;
+
+ RendererStorage::DependencyTracker dependency_tracker;
+ };
+
+ Data *data = nullptr;
+
+ GeometryInstanceGLES3() :
+ dirty_list_element(this) {}
+ };
+
+ enum {
+ INSTANCE_DATA_FLAGS_NON_UNIFORM_SCALE = 1 << 5,
+ INSTANCE_DATA_FLAG_USE_GI_BUFFERS = 1 << 6,
+ INSTANCE_DATA_FLAG_USE_LIGHTMAP_CAPTURE = 1 << 8,
+ INSTANCE_DATA_FLAG_USE_LIGHTMAP = 1 << 9,
+ INSTANCE_DATA_FLAG_USE_SH_LIGHTMAP = 1 << 10,
+ INSTANCE_DATA_FLAG_USE_VOXEL_GI = 1 << 11,
+ INSTANCE_DATA_FLAG_MULTIMESH = 1 << 12,
+ INSTANCE_DATA_FLAG_MULTIMESH_FORMAT_2D = 1 << 13,
+ INSTANCE_DATA_FLAG_MULTIMESH_HAS_COLOR = 1 << 14,
+ INSTANCE_DATA_FLAG_MULTIMESH_HAS_CUSTOM_DATA = 1 << 15,
+ };
+
+ static void _geometry_instance_dependency_changed(RendererStorage::DependencyChangedNotification p_notification, RendererStorage::DependencyTracker *p_tracker);
+ static void _geometry_instance_dependency_deleted(const RID &p_dependency, RendererStorage::DependencyTracker *p_tracker);
+
+ SelfList<GeometryInstanceGLES3>::List geometry_instance_dirty_list;
+
+ // Use PagedAllocator instead of RID to maximize performance
+ PagedAllocator<GeometryInstanceGLES3> geometry_instance_alloc;
+ PagedAllocator<GeometryInstanceSurface> geometry_instance_surface_alloc;
+
+ void _geometry_instance_add_surface_with_material(GeometryInstanceGLES3 *ginstance, uint32_t p_surface, GLES3::SceneMaterialData *p_material, uint32_t p_material_id, uint32_t p_shader_id, RID p_mesh);
+ void _geometry_instance_add_surface_with_material_chain(GeometryInstanceGLES3 *ginstance, uint32_t p_surface, GLES3::SceneMaterialData *p_material, RID p_mat_src, RID p_mesh);
+ void _geometry_instance_add_surface(GeometryInstanceGLES3 *ginstance, uint32_t p_surface, RID p_material, RID p_mesh);
+ void _geometry_instance_mark_dirty(GeometryInstance *p_geometry_instance);
+ void _geometry_instance_update(GeometryInstance *p_geometry_instance);
+ void _update_dirty_geometry_instances();
+
+ struct RenderListParameters {
+ GeometryInstanceSurface **elements = nullptr;
+ int element_count = 0;
+ bool reverse_cull = false;
+ uint32_t spec_constant_base_flags = 0;
+ bool force_wireframe = false;
+ Plane lod_plane;
+ float lod_distance_multiplier = 0.0;
+ float screen_mesh_lod_threshold = 0.0;
+
+ RenderListParameters(GeometryInstanceSurface **p_elements, int p_element_count, bool p_reverse_cull, uint32_t p_spec_constant_base_flags, bool p_force_wireframe = false, const Plane &p_lod_plane = Plane(), float p_lod_distance_multiplier = 0.0, float p_screen_mesh_lod_threshold = 0.0) {
+ elements = p_elements;
+ element_count = p_element_count;
+ reverse_cull = p_reverse_cull;
+ spec_constant_base_flags = p_spec_constant_base_flags;
+ force_wireframe = p_force_wireframe;
+ lod_plane = p_lod_plane;
+ lod_distance_multiplier = p_lod_distance_multiplier;
+ screen_mesh_lod_threshold = p_screen_mesh_lod_threshold;
+ }
+ };
+
+ struct RenderList {
+ LocalVector<GeometryInstanceSurface *> elements;
+
+ void clear() {
+ elements.clear();
+ }
+
+ //should eventually be replaced by radix
+
+ struct SortByKey {
+ _FORCE_INLINE_ bool operator()(const GeometryInstanceSurface *A, const GeometryInstanceSurface *B) const {
+ return (A->sort.sort_key2 == B->sort.sort_key2) ? (A->sort.sort_key1 < B->sort.sort_key1) : (A->sort.sort_key2 < B->sort.sort_key2);
+ }
+ };
+
+ void sort_by_key() {
+ SortArray<GeometryInstanceSurface *, SortByKey> sorter;
+ sorter.sort(elements.ptr(), elements.size());
+ }
+
+ void sort_by_key_range(uint32_t p_from, uint32_t p_size) {
+ SortArray<GeometryInstanceSurface *, SortByKey> sorter;
+ sorter.sort(elements.ptr() + p_from, p_size);
+ }
+
+ struct SortByDepth {
+ _FORCE_INLINE_ bool operator()(const GeometryInstanceSurface *A, const GeometryInstanceSurface *B) const {
+ return (A->owner->depth < B->owner->depth);
+ }
+ };
+
+ void sort_by_depth() { //used for shadows
+
+ SortArray<GeometryInstanceSurface *, SortByDepth> sorter;
+ sorter.sort(elements.ptr(), elements.size());
+ }
+
+ struct SortByReverseDepthAndPriority {
+ _FORCE_INLINE_ bool operator()(const GeometryInstanceSurface *A, const GeometryInstanceSurface *B) const {
+ return (A->sort.priority == B->sort.priority) ? (A->owner->depth > B->owner->depth) : (A->sort.priority < B->sort.priority);
+ }
+ };
+
+ void sort_by_reverse_depth_and_priority() { //used for alpha
+
+ SortArray<GeometryInstanceSurface *, SortByReverseDepthAndPriority> sorter;
+ sorter.sort(elements.ptr(), elements.size());
+ }
+
+ _FORCE_INLINE_ void add_element(GeometryInstanceSurface *p_element) {
+ elements.push_back(p_element);
+ }
+ };
+
+ RenderList render_list[RENDER_LIST_MAX];
+
+ void _setup_environment(const RenderDataGLES3 *p_render_data, bool p_no_fog, const Size2i &p_screen_size, bool p_flip_y, const Color &p_default_bg_color, bool p_pancake_shadows);
+ void _fill_render_list(RenderListType p_render_list, const RenderDataGLES3 *p_render_data, PassMode p_pass_mode, bool p_append = false);
+
+ template <PassMode p_pass_mode>
+ _FORCE_INLINE_ void _render_list_template(RenderListParameters *p_params, const RenderDataGLES3 *p_render_data, uint32_t p_from_element, uint32_t p_to_element, bool p_alpha_pass = false);
+
protected:
double time;
double time_step = 0;
@@ -117,6 +459,8 @@ protected:
//bool use_debanding = false;
//uint32_t view_count = 1;
+ 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
@@ -319,7 +663,7 @@ protected:
Sky *dirty_list = nullptr;
//State to track when radiance cubemap needs updating
- //SkyMaterialData *prev_material;
+ GLES3::SkyMaterialData *prev_material;
Vector3 prev_position = Vector3(0.0, 0.0, 0.0);
float prev_time = 0.0f;
@@ -335,17 +679,12 @@ protected:
void _invalidate_sky(Sky *p_sky);
void _update_dirty_skys();
- void _draw_sky(Sky *p_sky, const CameraMatrix &p_projection, const Transform3D &p_transform, float p_custom_fov, float p_energy, const Basis &p_sky_orientation);
+ void _draw_sky(Environment *p_env, const CameraMatrix &p_projection, const Transform3D &p_transform);
public:
RasterizerStorageGLES3 *storage;
RasterizerCanvasGLES3 *canvas;
- // References to shaders are needed in public space so they can be accessed in RasterizerStorageGLES3
- struct State {
- SkyShaderGLES3 sky_shader;
- } state;
-
GeometryInstance *geometry_instance_create(RID p_base) override;
void geometry_instance_set_skeleton(GeometryInstance *p_geometry_instance, RID p_skeleton) override;
void geometry_instance_set_material_override(GeometryInstance *p_geometry_instance, RID p_override) override;
@@ -388,9 +727,15 @@ public:
/* SDFGI UPDATE */
void sdfgi_update(RID p_render_buffers, RID p_environment, const Vector3 &p_world_position) override {}
- int sdfgi_get_pending_region_count(RID p_render_buffers) const override { return 0; }
- AABB sdfgi_get_pending_region_bounds(RID p_render_buffers, int p_region) const override { return AABB(); }
- uint32_t sdfgi_get_pending_region_cascade(RID p_render_buffers, int p_region) const override { return 0; }
+ int sdfgi_get_pending_region_count(RID p_render_buffers) const override {
+ return 0;
+ }
+ AABB sdfgi_get_pending_region_bounds(RID p_render_buffers, int p_region) const override {
+ return AABB();
+ }
+ uint32_t sdfgi_get_pending_region_cascade(RID p_render_buffers, int p_region) const override {
+ return 0;
+ }
/* SKY API */
diff --git a/drivers/gles3/rasterizer_storage_gles3.cpp b/drivers/gles3/rasterizer_storage_gles3.cpp
index cca445bf00..3c28289bd0 100644
--- a/drivers/gles3/rasterizer_storage_gles3.cpp
+++ b/drivers/gles3/rasterizer_storage_gles3.cpp
@@ -342,25 +342,14 @@ void RasterizerStorageGLES3::canvas_light_occluder_set_polylines(RID p_occluder,
*/
RS::InstanceType RasterizerStorageGLES3::get_base_type(RID p_rid) const {
- return RS::INSTANCE_NONE;
-
- /*
- if (mesh_owner.owns(p_rid)) {
+ if (GLES3::MeshStorage::get_singleton()->owns_mesh(p_rid)) {
return RS::INSTANCE_MESH;
- } else if (light_owner.owns(p_rid)) {
- return RS::INSTANCE_LIGHT;
- } else if (multimesh_owner.owns(p_rid)) {
+ } else if (GLES3::MeshStorage::get_singleton()->owns_multimesh(p_rid)) {
return RS::INSTANCE_MULTIMESH;
- } else if (immediate_owner.owns(p_rid)) {
- return RS::INSTANCE_IMMEDIATE;
- } else if (reflection_probe_owner.owns(p_rid)) {
- return RS::INSTANCE_REFLECTION_PROBE;
- } else if (lightmap_capture_data_owner.owns(p_rid)) {
- return RS::INSTANCE_LIGHTMAP_CAPTURE;
- } else {
- return RS::INSTANCE_NONE;
+ } else if (GLES3::LightStorage::get_singleton()->owns_light(p_rid)) {
+ return RS::INSTANCE_LIGHT;
}
-*/
+ return RS::INSTANCE_NONE;
}
bool RasterizerStorageGLES3::free(RID p_rid) {
@@ -379,89 +368,23 @@ bool RasterizerStorageGLES3::free(RID p_rid) {
} else if (GLES3::MaterialStorage::get_singleton()->owns_material(p_rid)) {
GLES3::MaterialStorage::get_singleton()->material_free(p_rid);
return true;
- } else {
- return false;
- }
- /*
- } else if (skeleton_owner.owns(p_rid)) {
- Skeleton *s = skeleton_owner.get_or_null(p_rid);
-
- if (s->update_list.in_list()) {
- skeleton_update_list.remove(&s->update_list);
- }
-
- for (Set<InstanceBaseDependency *>::Element *E = s->instances.front(); E; E = E->next()) {
- E->get()->skeleton = RID();
- }
-
- skeleton_allocate(p_rid, 0, false);
-
- if (s->tex_id) {
- glDeleteTextures(1, &s->tex_id);
- }
-
- skeleton_owner.free(p_rid);
- memdelete(s);
-
+ } else if (GLES3::MeshStorage::get_singleton()->owns_mesh(p_rid)) {
+ GLES3::MeshStorage::get_singleton()->mesh_free(p_rid);
return true;
- } else if (mesh_owner.owns(p_rid)) {
- Mesh *mesh = mesh_owner.get_or_null(p_rid);
-
- mesh->instance_remove_deps();
- mesh_clear(p_rid);
-
- while (mesh->multimeshes.first()) {
- MultiMesh *multimesh = mesh->multimeshes.first()->self();
- multimesh->mesh = RID();
- multimesh->dirty_aabb = true;
-
- mesh->multimeshes.remove(mesh->multimeshes.first());
-
- if (!multimesh->update_list.in_list()) {
- multimesh_update_list.add(&multimesh->update_list);
- }
- }
-
- mesh_owner.free(p_rid);
- memdelete(mesh);
-
+ } else if (GLES3::MeshStorage::get_singleton()->owns_multimesh(p_rid)) {
+ GLES3::MeshStorage::get_singleton()->multimesh_free(p_rid);
return true;
- } else if (multimesh_owner.owns(p_rid)) {
- MultiMesh *multimesh = multimesh_owner.get_or_null(p_rid);
- multimesh->instance_remove_deps();
-
- if (multimesh->mesh.is_valid()) {
- Mesh *mesh = mesh_owner.get_or_null(multimesh->mesh);
- if (mesh) {
- mesh->multimeshes.remove(&multimesh->mesh_list);
- }
- }
-
- multimesh_allocate(p_rid, 0, RS::MULTIMESH_TRANSFORM_3D, RS::MULTIMESH_COLOR_NONE);
-
- _update_dirty_multimeshes();
-
- multimesh_owner.free(p_rid);
- memdelete(multimesh);
-
+ } else if (GLES3::MeshStorage::get_singleton()->owns_mesh_instance(p_rid)) {
+ GLES3::MeshStorage::get_singleton()->mesh_instance_free(p_rid);
return true;
- } else if (immediate_owner.owns(p_rid)) {
- Immediate *im = immediate_owner.get_or_null(p_rid);
- im->instance_remove_deps();
-
- immediate_owner.free(p_rid);
- memdelete(im);
-
- return true;
- } else if (light_owner.owns(p_rid)) {
- Light *light = light_owner.get_or_null(p_rid);
- light->instance_remove_deps();
-
- light_owner.free(p_rid);
- memdelete(light);
-
+ } else if (GLES3::LightStorage::get_singleton()->owns_light(p_rid)) {
+ GLES3::LightStorage::get_singleton()->light_free(p_rid);
return true;
- } else if (reflection_probe_owner.owns(p_rid)) {
+ } 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();
diff --git a/drivers/gles3/rasterizer_storage_gles3.h b/drivers/gles3/rasterizer_storage_gles3.h
index 6401771abb..fa74fbd5f6 100644
--- a/drivers/gles3/rasterizer_storage_gles3.h
+++ b/drivers/gles3/rasterizer_storage_gles3.h
@@ -41,6 +41,7 @@
#include "servers/rendering/shader_compiler.h"
#include "servers/rendering/shader_language.h"
#include "storage/config.h"
+#include "storage/light_storage.h"
#include "storage/material_storage.h"
#include "storage/mesh_storage.h"
#include "storage/texture_storage.h"
@@ -55,6 +56,48 @@ public:
GLES3::Config *config = nullptr;
+ static _FORCE_INLINE_ void store_transform(const Transform3D &p_mtx, float *p_array) {
+ p_array[0] = p_mtx.basis.rows[0][0];
+ p_array[1] = p_mtx.basis.rows[1][0];
+ p_array[2] = p_mtx.basis.rows[2][0];
+ p_array[3] = 0;
+ p_array[4] = p_mtx.basis.rows[0][1];
+ p_array[5] = p_mtx.basis.rows[1][1];
+ p_array[6] = p_mtx.basis.rows[2][1];
+ p_array[7] = 0;
+ p_array[8] = p_mtx.basis.rows[0][2];
+ p_array[9] = p_mtx.basis.rows[1][2];
+ p_array[10] = p_mtx.basis.rows[2][2];
+ p_array[11] = 0;
+ p_array[12] = p_mtx.origin.x;
+ p_array[13] = p_mtx.origin.y;
+ p_array[14] = p_mtx.origin.z;
+ p_array[15] = 1;
+ }
+
+ static _FORCE_INLINE_ void store_transform_3x3(const Basis &p_mtx, float *p_array) {
+ p_array[0] = p_mtx.rows[0][0];
+ p_array[1] = p_mtx.rows[1][0];
+ p_array[2] = p_mtx.rows[2][0];
+ p_array[3] = 0;
+ p_array[4] = p_mtx.rows[0][1];
+ p_array[5] = p_mtx.rows[1][1];
+ p_array[6] = p_mtx.rows[2][1];
+ p_array[7] = 0;
+ p_array[8] = p_mtx.rows[0][2];
+ p_array[9] = p_mtx.rows[1][2];
+ p_array[10] = p_mtx.rows[2][2];
+ p_array[11] = 0;
+ }
+
+ static _FORCE_INLINE_ void store_camera(const CameraMatrix &p_mtx, float *p_array) {
+ for (int i = 0; i < 4; i++) {
+ for (int j = 0; j < 4; j++) {
+ p_array[i * 4 + j] = p_mtx.matrix[i][j];
+ }
+ }
+ }
+
struct Resources {
GLuint mipmap_blur_fbo;
GLuint mipmap_blur_color;
@@ -302,7 +345,7 @@ inline void RasterizerStorageGLES3::buffer_orphan_and_upload(unsigned int p_buff
}
inline String RasterizerStorageGLES3::get_framebuffer_error(GLenum p_status) {
-#ifdef DEBUG_ENABLED
+#if defined(DEBUG_ENABLED) && defined(GLES_OVER_GL)
if (p_status == GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT) {
return "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT";
} else if (p_status == GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT) {
diff --git a/drivers/gles3/shader_gles3.cpp b/drivers/gles3/shader_gles3.cpp
index e356fa8c1f..b3f37207da 100644
--- a/drivers/gles3/shader_gles3.cpp
+++ b/drivers/gles3/shader_gles3.cpp
@@ -171,6 +171,15 @@ void ShaderGLES3::_build_variant_code(StringBuilder &builder, uint32_t p_variant
}
builder.append("\n"); //make sure defines begin at newline
+ // Default to highp precision unless specified otherwise.
+ builder.append("precision highp float;\n");
+ builder.append("precision highp int;\n");
+#ifndef GLES_OVER_GL
+ builder.append("precision highp sampler2D;\n");
+ builder.append("precision highp samplerCube;\n");
+ builder.append("precision highp sampler2DArray;\n");
+#endif
+
for (uint32_t i = 0; i < p_template.chunks.size(); i++) {
const StageTemplate::Chunk &chunk = p_template.chunks[i];
switch (chunk.type) {
diff --git a/drivers/gles3/shader_gles3.h b/drivers/gles3/shader_gles3.h
index 15281064af..763d3bfa8b 100644
--- a/drivers/gles3/shader_gles3.h
+++ b/drivers/gles3/shader_gles3.h
@@ -73,10 +73,11 @@ private:
//versions
CharString general_defines;
- // A version is a high-level construct which is a combination of built-in and user-defined shader code
- // Variants use #idefs to toggle behaviour on and off to change behaviour of the shader
+ // A version is a high-level construct which is a combination of built-in and user-defined shader code, Each user-created Shader makes one version
+ // Variants use #ifdefs to toggle behaviour on and off to change behaviour of the shader
+ // All variants are compiled each time a new version is created
// Specializations use #ifdefs to toggle behaviour on and off for performance, on supporting hardware, they will compile a version with everything enabled, and then compile more copies to improve performance
- // Use specializations to enable and disabled advanced features, use variants to toggle behaviour when different data may be used (e.g. using a samplerArray vs a sampler)
+ // Use specializations to enable and disabled advanced features, use variants to toggle behaviour when different data may be used (e.g. using a samplerArray vs a sampler, or doing a depth prepass vs a color pass)
struct Version {
Vector<StringName> texture_uniforms;
CharString uniforms;
@@ -217,6 +218,7 @@ protected:
ERR_FAIL_INDEX_V(p_which, uniform_count, -1);
Version *version = version_owner.get_or_null(p_version);
ERR_FAIL_COND_V(!version, -1);
+ ERR_FAIL_INDEX_V(p_variant, int(version->variants.size()), -1);
return version->variants[p_variant].lookup_ptr(p_specialization)->uniform_location[p_which];
}
diff --git a/drivers/gles3/shaders/SCsub b/drivers/gles3/shaders/SCsub
index 79247f38d4..ec32badc19 100644
--- a/drivers/gles3/shaders/SCsub
+++ b/drivers/gles3/shaders/SCsub
@@ -8,4 +8,5 @@ env.Depends("#drivers/gles3/shaders/copy.glsl.gen.h", "#core/math/transform_2d.h
if "GLES3_GLSL" in env["BUILDERS"]:
env.GLES3_GLSL("canvas.glsl")
env.GLES3_GLSL("copy.glsl")
+ env.GLES3_GLSL("scene.glsl")
env.GLES3_GLSL("sky.glsl")
diff --git a/drivers/gles3/shaders/canvas.glsl b/drivers/gles3/shaders/canvas.glsl
index 41d308b776..381a0e8a73 100644
--- a/drivers/gles3/shaders/canvas.glsl
+++ b/drivers/gles3/shaders/canvas.glsl
@@ -21,6 +21,15 @@ layout(location = 10) in uvec4 bone_attrib;
layout(location = 11) in vec4 weight_attrib;
#endif
+
+// This needs to be outside clang-format so the ubo comment is in the right place
+#ifdef MATERIAL_UNIFORMS_USED
+layout(std140) uniform MaterialUniforms{ //ubo:4
+
+#MATERIAL_UNIFORMS
+
+};
+#endif
/* clang-format on */
#include "canvas_uniforms_inc.glsl"
#include "stdlib_inc.glsl"
@@ -38,15 +47,6 @@ out vec2 pixel_size_interp;
#endif
-#ifdef MATERIAL_UNIFORMS_USED
-layout(std140) uniform MaterialUniforms{
-//ubo:4
-
-#MATERIAL_UNIFORMS
-
-};
-#endif
-
#GLOBALS
void main() {
@@ -518,8 +518,8 @@ void main() {
float px_size = max(0.5 * dot((vec2(px_range) / msdf_size), dest_size), 1.0);
float d = msdf_median(msdf_sample.r, msdf_sample.g, msdf_sample.b, msdf_sample.a) - 0.5;
- if (outline_thickness > 0) {
- float cr = clamp(outline_thickness, 0.0, px_range / 2) / px_range;
+ if (outline_thickness > 0.0) {
+ float cr = clamp(outline_thickness, 0.0, px_range / 2.0) / px_range;
float a = clamp((d + cr) * px_size, 0.0, 1.0);
color.a = a * color.a;
} else {
@@ -710,8 +710,8 @@ void main() {
vec2 pos_rot = pos_norm * mat2(vec2(0.7071067811865476, -0.7071067811865476), vec2(0.7071067811865476, 0.7071067811865476)); //is there a faster way to 45 degrees rot?
float tex_ofs;
float distance;
- if (pos_rot.y > 0) {
- if (pos_rot.x > 0) {
+ if (pos_rot.y > 0.0) {
+ if (pos_rot.x > 0.0) {
tex_ofs = pos_box.y * 0.125 + 0.125;
distance = shadow_pos.x;
} else {
@@ -719,7 +719,7 @@ void main() {
distance = shadow_pos.y;
}
} else {
- if (pos_rot.x < 0) {
+ if (pos_rot.x < 0.0) {
tex_ofs = pos_box.y * -0.125 + (0.5 + 0.125);
distance = -shadow_pos.x;
} else {
diff --git a/drivers/gles3/shaders/scene.glsl b/drivers/gles3/shaders/scene.glsl
index ebb00e81d0..ea28685be7 100644
--- a/drivers/gles3/shaders/scene.glsl
+++ b/drivers/gles3/shaders/scene.glsl
@@ -1,983 +1,553 @@
/* clang-format off */
-[vertex]
+#[modes]
-#ifdef USE_GLES_OVER_GL
-#define lowp
-#define mediump
-#define highp
-#else
-precision highp float;
-precision highp int;
-#endif
-
-#define SHADER_IS_SRGB true //TODO remove
-
-#define M_PI 3.14159265359
-
-//
-// attributes
-//
-
-layout(location = 0) in highp vec4 vertex_attrib;
-/* clang-format on */
-layout(location = 1) in vec3 normal_attrib;
-
-#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP)
-layout(location = 2) in vec4 tangent_attrib;
-#endif
+mode_color = #define BASE_PASS
+mode_additive = #define USE_ADDITIVE_LIGHTING
+mode_depth = #define MODE_RENDER_DEPTH
-#if defined(ENABLE_COLOR_INTERP)
-layout(location = 3) in vec4 color_attrib;
-#endif
+#[specializations]
-#if defined(ENABLE_UV_INTERP)
-layout(location = 4) in vec2 uv_attrib;
-#endif
+USE_LIGHTMAP = false
+USE_LIGHT_DIRECTIONAL = false
+USE_LIGHT_POSITIONAL = false
-#if defined(ENABLE_UV2_INTERP) || defined(USE_LIGHTMAP)
-layout(location = 5) in vec2 uv2_attrib;
-#endif
-
-#ifdef USE_SKELETON
-
-#ifdef USE_SKELETON_SOFTWARE
-layout(location = 13) in highp vec4 bone_transform_row_0;
-layout(location = 14) in highp vec4 bone_transform_row_1;
-layout(location = 15) in highp vec4 bone_transform_row_2;
+#[vertex]
-#else
+#define M_PI 3.14159265359
-layout(location = 6) in vec4 bone_ids;
-layout(location = 7) in highp vec4 bone_weights;
+#define SHADER_IS_SRGB true
-uniform highp sampler2D bone_transforms; // texunit:-1
-uniform ivec2 skeleton_texture_size;
+#include "stdlib_inc.glsl"
+#if !defined(MODE_RENDER_DEPTH) || defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED) ||defined(LIGHT_CLEARCOAT_USED)
+#ifndef NORMAL_USED
+#define NORMAL_USED
#endif
-
#endif
-#ifdef USE_INSTANCING
+/*
+from RenderingServer:
+ARRAY_VERTEX = 0, // RG32F or RGB32F (depending on 2D bit)
+ARRAY_NORMAL = 1, // A2B10G10R10, A is ignored.
+ARRAY_TANGENT = 2, // A2B10G10R10, A flips sign of binormal.
+ARRAY_COLOR = 3, // RGBA8
+ARRAY_TEX_UV = 4, // RG32F
+ARRAY_TEX_UV2 = 5, // RG32F
+ARRAY_CUSTOM0 = 6, // Depends on ArrayCustomFormat.
+ARRAY_CUSTOM1 = 7,
+ARRAY_CUSTOM2 = 8,
+ARRAY_CUSTOM3 = 9,
+ARRAY_BONES = 10, // RGBA16UI (x2 if 8 weights)
+ARRAY_WEIGHTS = 11, // RGBA16UNORM (x2 if 8 weights)
+ARRAY_INDEX = 12, // 16 or 32 bits depending on length > 0xFFFF.
+ARRAY_MAX = 13
+*/
-layout(location = 8) in highp vec4 instance_xform_row_0;
-layout(location = 9) in highp vec4 instance_xform_row_1;
-layout(location = 10) in highp vec4 instance_xform_row_2;
+/* INPUT ATTRIBS */
-layout(location = 11) in highp vec4 instance_color;
-layout(location = 12) in highp vec4 instance_custom_data;
+layout(location = 0) in highp vec3 vertex_attrib;
+/* clang-format on */
+#ifdef NORMAL_USED
+layout(location = 1) in vec3 normal_attrib;
#endif
-//
-// uniforms
-//
-
-uniform highp mat4 inv_view_matrix;
-uniform highp mat4 view_matrix;
-uniform highp mat4 projection_matrix;
-uniform highp mat4 projection_inverse_matrix;
-
-uniform highp mat4 world_transform;
-
-uniform highp float time;
-
-uniform highp vec2 viewport_size;
-
-#ifdef RENDER_DEPTH
-uniform float light_bias;
-uniform float light_normal_bias;
+#if defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED)
+layout(location = 2) in vec4 tangent_attrib;
#endif
-//
-// varyings
-//
-
-#if defined(RENDER_DEPTH) && defined(USE_RGBA_SHADOWS)
-out highp vec4 position_interp;
+#if defined(COLOR_USED)
+layout(location = 3) in vec4 color_attrib;
#endif
-out highp vec3 vertex_interp;
-out vec3 normal_interp;
-
-#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP)
-out vec3 tangent_interp;
-out vec3 binormal_interp;
+#ifdef UV_USED
+layout(location = 4) in vec2 uv_attrib;
#endif
-#if defined(ENABLE_COLOR_INTERP)
-out vec4 color_interp;
+#if defined(UV2_USED) || defined(USE_LIGHTMAP)
+layout(location = 5) in vec2 uv2_attrib;
#endif
-#if defined(ENABLE_UV_INTERP)
-out vec2 uv_interp;
+#if defined(CUSTOM0_USED)
+layout(location = 6) in vec4 custom0_attrib;
#endif
-#if defined(ENABLE_UV2_INTERP) || defined(USE_LIGHTMAP)
-out vec2 uv2_interp;
+#if defined(CUSTOM1_USED)
+layout(location = 7) in vec4 custom1_attrib;
#endif
-/* clang-format off */
-
-VERTEX_SHADER_GLOBALS
-
-/* clang-format on */
-
-#ifdef RENDER_DEPTH_DUAL_PARABOLOID
-
-out highp float dp_clip;
-uniform highp float shadow_dual_paraboloid_render_zfar;
-uniform highp float shadow_dual_paraboloid_render_side;
-
+#if defined(CUSTOM2_USED)
+layout(location = 8) in vec4 custom2_attrib;
#endif
-#if defined(USE_SHADOW) && defined(USE_LIGHTING)
-
-uniform highp mat4 light_shadow_matrix;
-out highp vec4 shadow_coord;
-
-#if defined(LIGHT_USE_PSSM2) || defined(LIGHT_USE_PSSM4)
-uniform highp mat4 light_shadow_matrix2;
-out highp vec4 shadow_coord2;
+#if defined(CUSTOM3_USED)
+layout(location = 9) in vec4 custom3_attrib;
#endif
-#if defined(LIGHT_USE_PSSM4)
-
-uniform highp mat4 light_shadow_matrix3;
-uniform highp mat4 light_shadow_matrix4;
-out highp vec4 shadow_coord3;
-out highp vec4 shadow_coord4;
-
+#if defined(BONES_USED)
+layout(location = 10) in uvec4 bone_attrib;
#endif
+#if defined(WEIGHTS_USED)
+layout(location = 11) in vec4 weight_attrib;
#endif
-#if defined(USE_VERTEX_LIGHTING) && defined(USE_LIGHTING)
+layout(std140) uniform GlobalVariableData { //ubo:1
+ vec4 global_variables[MAX_GLOBAL_VARIABLES];
+};
-out highp vec3 diffuse_interp;
-out highp vec3 specular_interp;
+layout(std140) uniform SceneData { // ubo:2
+ highp mat4 projection_matrix;
+ highp mat4 inv_projection_matrix;
+ highp mat4 inv_view_matrix;
+ highp mat4 view_matrix;
-// general for all lights
-uniform highp vec4 light_color;
-uniform highp vec4 shadow_color;
-uniform highp float light_specular;
+ vec2 viewport_size;
+ vec2 screen_pixel_size;
-// directional
-uniform highp vec3 light_direction;
+ mediump vec4 ambient_light_color_energy;
-// omni
-uniform highp vec3 light_position;
+ mediump float ambient_color_sky_mix;
+ uint ambient_flags;
+ bool material_uv2_mode;
+ float opaque_prepass_threshold;
+ //bool use_ambient_light;
+ //bool use_ambient_cubemap;
+ //bool use_reflection_cubemap;
-uniform highp float light_range;
-uniform highp float light_attenuation;
-
-// spot
-uniform highp float light_spot_attenuation;
-uniform highp float light_spot_range;
-uniform highp float light_spot_angle;
-
-void light_compute(
- vec3 N,
- vec3 L,
- vec3 V,
- vec3 light_color,
- vec3 attenuation,
- float roughness) {
-//this makes lights behave closer to linear, but then addition of lights looks bad
-//better left disabled
-
-//#define SRGB_APPROX(m_var) m_var = pow(m_var,0.4545454545);
-/*
-#define SRGB_APPROX(m_var) {\
- float S1 = sqrt(m_var);\
- float S2 = sqrt(S1);\
- float S3 = sqrt(S2);\
- m_var = 0.662002687 * S1 + 0.684122060 * S2 - 0.323583601 * S3 - 0.0225411470 * m_var;\
- }
-*/
-#define SRGB_APPROX(m_var)
+ mat3 radiance_inverse_xform;
- float NdotL = dot(N, L);
- float cNdotL = max(NdotL, 0.0); // clamped NdotL
- float NdotV = dot(N, V);
- float cNdotV = max(NdotV, 0.0);
+ uint directional_light_count;
+ float z_far;
+ float z_near;
+ float pad;
-#if defined(DIFFUSE_OREN_NAYAR)
- vec3 diffuse_brdf_NL;
-#else
- float diffuse_brdf_NL; // BRDF times N.L for calculating diffuse radiance
-#endif
+ bool fog_enabled;
+ float fog_density;
+ float fog_height;
+ float fog_height_density;
-#if defined(DIFFUSE_LAMBERT_WRAP)
- // energy conserving lambert wrap shader
- diffuse_brdf_NL = max(0.0, (NdotL + roughness) / ((1.0 + roughness) * (1.0 + roughness)));
+ vec3 fog_light_color;
+ float fog_sun_scatter;
-#elif defined(DIFFUSE_OREN_NAYAR)
+ float fog_aerial_perspective;
- {
- // see http://mimosa-pudica.net/improved-oren-nayar.html
- float LdotV = dot(L, V);
+ float time;
+ float reflection_multiplier; // one normally, zero when rendering reflections
- float s = LdotV - NdotL * NdotV;
- float t = mix(1.0, max(NdotL, NdotV), step(0.0, s));
+ bool pancake_shadows;
+}
+scene_data;
- float sigma2 = roughness * roughness; // TODO: this needs checking
- vec3 A = 1.0 + sigma2 * (-0.5 / (sigma2 + 0.33) + 0.17 * diffuse_color / (sigma2 + 0.13));
- float B = 0.45 * sigma2 / (sigma2 + 0.09);
+uniform highp mat4 world_transform;
- diffuse_brdf_NL = cNdotL * (A + vec3(B) * s / t) * (1.0 / M_PI);
- }
-#else
- // lambert by default for everything else
- diffuse_brdf_NL = cNdotL * (1.0 / M_PI);
+#ifdef USE_LIGHTMAP
+uniform highp vec4 lightmap_uv_rect;
#endif
- SRGB_APPROX(diffuse_brdf_NL)
-
- diffuse_interp += light_color * diffuse_brdf_NL * attenuation;
-
- if (roughness > 0.0) {
- // D
- float specular_brdf_NL = 0.0;
+/* Varyings */
-#if !defined(SPECULAR_DISABLED)
- //normalized blinn always unless disabled
- vec3 H = normalize(V + L);
- float cNdotH = max(dot(N, H), 0.0);
- float shininess = exp2(15.0 * (1.0 - roughness) + 1.0) * 0.25;
- float blinn = pow(cNdotH, shininess) * cNdotL;
- blinn *= (shininess + 8.0) * (1.0 / (8.0 * M_PI));
- specular_brdf_NL = blinn;
+out highp vec3 vertex_interp;
+#ifdef NORMAL_USED
+out vec3 normal_interp;
#endif
- SRGB_APPROX(specular_brdf_NL)
- specular_interp += specular_brdf_NL * light_color * attenuation * (1.0 / M_PI);
- }
-}
-
+#if defined(COLOR_USED)
+out vec4 color_interp;
#endif
-#ifdef USE_VERTEX_LIGHTING
-
-#ifdef USE_REFLECTION_PROBE1
-
-uniform highp mat4 refprobe1_local_matrix;
-out mediump vec4 refprobe1_reflection_normal_blend;
-uniform highp vec3 refprobe1_box_extents;
-
-#ifndef USE_LIGHTMAP
-out mediump vec3 refprobe1_ambient_normal;
+#if defined(UV_USED)
+out vec2 uv_interp;
#endif
-#endif //reflection probe1
-
-#ifdef USE_REFLECTION_PROBE2
-
-uniform highp mat4 refprobe2_local_matrix;
-out mediump vec4 refprobe2_reflection_normal_blend;
-uniform highp vec3 refprobe2_box_extents;
+#if defined(UV2_USED)
+out vec2 uv2_interp;
+#else
+#ifdef USE_LIGHTMAP
+out vec2 uv2_interp;
+#endif
+#endif
-#ifndef USE_LIGHTMAP
-out mediump vec3 refprobe2_ambient_normal;
+#if defined(TANGENT_USED) || defined(ENABLE_NORMALMAP) || defined(LIGHT_USE_ANISOTROPY)
+out vec3 tangent_interp;
+out vec3 binormal_interp;
#endif
-#endif //reflection probe2
+#if defined(MATERIAL_UNIFORMS_USED)
-#endif //vertex lighting for refprobes
+/* clang-format off */
+layout(std140) uniform MaterialUniforms { // ubo:3
-#if defined(FOG_DEPTH_ENABLED) || defined(FOG_HEIGHT_ENABLED)
+#MATERIAL_UNIFORMS
-out vec4 fog_interp;
+};
+/* clang-format on */
-uniform mediump vec4 fog_color_base;
-#ifdef LIGHT_MODE_DIRECTIONAL
-uniform mediump vec4 fog_sun_color_amount;
#endif
-uniform bool fog_transmit_enabled;
-uniform mediump float fog_transmit_curve;
+/* clang-format off */
-#ifdef FOG_DEPTH_ENABLED
-uniform highp float fog_depth_begin;
-uniform mediump float fog_depth_curve;
-uniform mediump float fog_max_distance;
-#endif
+#GLOBALS
-#ifdef FOG_HEIGHT_ENABLED
-uniform highp float fog_height_min;
-uniform highp float fog_height_max;
-uniform mediump float fog_height_curve;
-#endif
+/* clang-format on */
-#endif //fog
+out highp vec4 position_interp;
-void main() {
- highp vec4 vertex = vertex_attrib;
+invariant gl_Position;
- mat4 model_matrix = world_transform;
+void main() {
+ highp vec3 vertex = vertex_attrib;
-#ifdef USE_INSTANCING
- {
- highp mat4 m = mat4(
- instance_xform_row_0,
- instance_xform_row_1,
- instance_xform_row_2,
- vec4(0.0, 0.0, 0.0, 1.0));
- model_matrix = model_matrix * transpose(m);
- }
+ highp mat4 model_matrix = world_transform;
+#ifdef NORMAL_USED
+ vec3 normal = normal_attrib * 2.0 - 1.0;
#endif
+ highp mat3 model_normal_matrix = mat3(model_matrix);
- vec3 normal = normal_attrib;
-
-#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP)
- vec3 tangent = tangent_attrib.xyz;
- float binormalf = tangent_attrib.a;
- vec3 binormal = normalize(cross(normal, tangent) * binormalf);
+#if defined(TANGENT_USED) || defined(ENABLE_NORMALMAP) || defined(LIGHT_USE_ANISOTROPY)
+ vec3 tangent;
+ float binormalf;
+ tangent = normal_tangent_attrib.xyz;
+ binormalf = normal_tangent_attrib.a;
#endif
-#if defined(ENABLE_COLOR_INTERP)
+#if defined(COLOR_USED)
color_interp = color_attrib;
-#ifdef USE_INSTANCING
- color_interp *= instance_color;
#endif
+
+#if defined(TANGENT_USED) || defined(ENABLE_NORMALMAP) || defined(LIGHT_USE_ANISOTROPY)
+ vec3 binormal = normalize(cross(normal, tangent) * binormalf);
#endif
-#if defined(ENABLE_UV_INTERP)
+#if defined(UV_USED)
uv_interp = uv_attrib;
#endif
-#if defined(ENABLE_UV2_INTERP) || defined(USE_LIGHTMAP)
+#ifdef USE_LIGHTMAP
+ uv2_interp = lightmap_uv_rect.zw * uv2_attrib + lightmap_uv_rect.xy;
+#else
+#if defined(UV2_USED)
uv2_interp = uv2_attrib;
#endif
+#endif
#if defined(OVERRIDE_POSITION)
highp vec4 position;
#endif
+ highp mat4 projection_matrix = scene_data.projection_matrix;
+ highp mat4 inv_projection_matrix = scene_data.inv_projection_matrix;
-#if !defined(SKIP_TRANSFORM_USED) && defined(VERTEX_WORLD_COORDS_USED)
- vertex = model_matrix * vertex;
- normal = normalize((model_matrix * vec4(normal, 0.0)).xyz);
-#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP)
-
- tangent = normalize((model_matrix * vec4(tangent, 0.0)).xyz);
- binormal = normalize((model_matrix * vec4(binormal, 0.0)).xyz);
-#endif
-#endif
-
-#ifdef USE_SKELETON
-
- highp mat4 bone_transform = mat4(0.0);
-
-#ifdef USE_SKELETON_SOFTWARE
- // passing the transform as attributes
-
- bone_transform[0] = vec4(bone_transform_row_0.x, bone_transform_row_1.x, bone_transform_row_2.x, 0.0);
- bone_transform[1] = vec4(bone_transform_row_0.y, bone_transform_row_1.y, bone_transform_row_2.y, 0.0);
- bone_transform[2] = vec4(bone_transform_row_0.z, bone_transform_row_1.z, bone_transform_row_2.z, 0.0);
- bone_transform[3] = vec4(bone_transform_row_0.w, bone_transform_row_1.w, bone_transform_row_2.w, 1.0);
-
-#else
- // look up transform from the "pose texture"
- {
- for (int i = 0; i < 4; i++) {
- ivec2 tex_ofs = ivec2(int(bone_ids[i]) * 3, 0);
+ vec4 instance_custom = vec4(0.0);
- highp mat4 b = mat4(
- texel2DFetch(bone_transforms, skeleton_texture_size, tex_ofs + ivec2(0, 0)),
- texel2DFetch(bone_transforms, skeleton_texture_size, tex_ofs + ivec2(1, 0)),
- texel2DFetch(bone_transforms, skeleton_texture_size, tex_ofs + ivec2(2, 0)),
- vec4(0.0, 0.0, 0.0, 1.0));
+ // Using world coordinates
+#if !defined(SKIP_TRANSFORM_USED) && defined(VERTEX_WORLD_COORDS_USED)
- bone_transform += transpose(b) * bone_weights[i];
- }
- }
+ vertex = (model_matrix * vec4(vertex, 1.0)).xyz;
+#ifdef NORMAL_USED
+ normal = model_normal_matrix * normal;
#endif
- model_matrix = model_matrix * bone_transform;
+#if defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED)
-#endif
-
-#ifdef USE_INSTANCING
- vec4 instance_custom = instance_custom_data;
-#else
- vec4 instance_custom = vec4(0.0);
+ tangent = model_normal_matrix * tangent;
+ binormal = model_normal_matrix * binormal;
#endif
+#endif
- mat4 local_projection_matrix = projection_matrix;
-
- mat4 modelview = view_matrix * model_matrix;
float roughness = 1.0;
-#define projection_matrix local_projection_matrix
-#define world_transform model_matrix
+ highp mat4 modelview = scene_data.view_matrix * model_matrix;
+ highp mat3 modelview_normal = mat3(scene_data.view_matrix) * model_normal_matrix;
float point_size = 1.0;
{
- /* clang-format off */
-
-VERTEX_SHADER_CODE
-
- /* clang-format on */
+#CODE : VERTEX
}
gl_PointSize = point_size;
- vec4 outvec = vertex;
- // use local coordinates
+ // Using local coordinates (default)
#if !defined(SKIP_TRANSFORM_USED) && !defined(VERTEX_WORLD_COORDS_USED)
- vertex = modelview * vertex;
- normal = normalize((modelview * vec4(normal, 0.0)).xyz);
-#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP)
- tangent = normalize((modelview * vec4(tangent, 0.0)).xyz);
- binormal = normalize((modelview * vec4(binormal, 0.0)).xyz);
-#endif
+ vertex = (modelview * vec4(vertex, 1.0)).xyz;
+#ifdef NORMAL_USED
+ normal = modelview_normal * normal;
#endif
-#if !defined(SKIP_TRANSFORM_USED) && defined(VERTEX_WORLD_COORDS_USED)
- vertex = view_matrix * vertex;
- normal = normalize((view_matrix * vec4(normal, 0.0)).xyz);
-#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP)
- tangent = normalize((view_matrix * vec4(tangent, 0.0)).xyz);
- binormal = normalize((view_matrix * vec4(binormal, 0.0)).xyz);
-#endif
#endif
- vertex_interp = vertex.xyz;
- normal_interp = normal;
+#if defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED)
-#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP)
- tangent_interp = tangent;
- binormal_interp = binormal;
+ binormal = modelview_normal * binormal;
+ tangent = modelview_normal * tangent;
#endif
-#ifdef RENDER_DEPTH
-
-#ifdef RENDER_DEPTH_DUAL_PARABOLOID
-
- vertex_interp.z *= shadow_dual_paraboloid_render_side;
- normal_interp.z *= shadow_dual_paraboloid_render_side;
-
- dp_clip = vertex_interp.z; //this attempts to avoid noise caused by objects sent to the other parabolloid side due to bias
-
- //for dual paraboloid shadow mapping, this is the fastest but least correct way, as it curves straight edges
-
- highp vec3 vtx = vertex_interp + normalize(vertex_interp) * light_bias;
- highp float distance = length(vtx);
- vtx = normalize(vtx);
- vtx.xy /= 1.0 - vtx.z;
- vtx.z = (distance / shadow_dual_paraboloid_render_zfar);
- vtx.z = vtx.z * 2.0 - 1.0;
-
- vertex_interp = vtx;
-
-#else
- float z_ofs = light_bias;
- z_ofs += (1.0 - abs(normal_interp.z)) * light_normal_bias;
-
- vertex_interp.z -= z_ofs;
-#endif //dual parabolloid
-
-#endif //depth
-
-//vertex lighting
-#if defined(USE_VERTEX_LIGHTING) && defined(USE_LIGHTING)
- //vertex shaded version of lighting (more limited)
- vec3 L;
- vec3 light_att;
-
-#ifdef LIGHT_MODE_OMNI
- vec3 light_vec = light_position - vertex_interp;
- float light_length = length(light_vec);
-
- float normalized_distance = light_length / light_range;
-
- if (normalized_distance < 1.0) {
- float omni_attenuation = pow(1.0 - normalized_distance, light_attenuation);
-
- vec3 attenuation = vec3(omni_attenuation);
- light_att = vec3(omni_attenuation);
- } else {
- light_att = vec3(0.0);
- }
-
- L = normalize(light_vec);
+ // Using world coordinates
+#if !defined(SKIP_TRANSFORM_USED) && defined(VERTEX_WORLD_COORDS_USED)
+ vertex = (scene_data.view_matrix * vec4(vertex, 1.0)).xyz;
+#ifdef NORMAL_USED
+ normal = (scene_data.view_matrix * vec4(normal, 0.0)).xyz;
#endif
-#ifdef LIGHT_MODE_SPOT
-
- vec3 light_rel_vec = light_position - vertex_interp;
- float light_length = length(light_rel_vec);
- float normalized_distance = light_length / light_range;
-
- if (normalized_distance < 1.0) {
- float spot_attenuation = pow(1.0 - normalized_distance, light_attenuation);
- vec3 spot_dir = light_direction;
-
- float spot_cutoff = light_spot_angle;
-
- float angle = dot(-normalize(light_rel_vec), spot_dir);
-
- if (angle > spot_cutoff) {
- float scos = max(angle, spot_cutoff);
- float spot_rim = max(0.0001, (1.0 - scos) / (1.0 - spot_cutoff));
-
- spot_attenuation *= 1.0 - pow(spot_rim, light_spot_attenuation);
-
- light_att = vec3(spot_attenuation);
- } else {
- light_att = vec3(0.0);
- }
- } else {
- light_att = vec3(0.0);
- }
-
- L = normalize(light_rel_vec);
-
+#if defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED)
+ binormal = (scene_data.view_matrix * vec4(binormal, 0.0)).xyz;
+ tangent = (scene_data.view_matrix * vec4(tangent, 0.0)).xyz;
#endif
-
-#ifdef LIGHT_MODE_DIRECTIONAL
- vec3 light_vec = -light_direction;
- light_att = vec3(1.0); //no base attenuation
- L = normalize(light_vec);
#endif
- diffuse_interp = vec3(0.0);
- specular_interp = vec3(0.0);
- light_compute(normal_interp, L, -normalize(vertex_interp), light_color.rgb, light_att, roughness);
-
+ vertex_interp = vertex;
+#ifdef NORMAL_USED
+ normal_interp = normal;
#endif
-//shadows (for both vertex and fragment)
-#if defined(USE_SHADOW) && defined(USE_LIGHTING)
-
- vec4 vi4 = vec4(vertex_interp, 1.0);
- shadow_coord = light_shadow_matrix * vi4;
-
-#if defined(LIGHT_USE_PSSM2) || defined(LIGHT_USE_PSSM4)
- shadow_coord2 = light_shadow_matrix2 * vi4;
+#if defined(TANGENT_USED) || defined(ENABLE_NORMALMAP) || defined(LIGHT_USE_ANISOTROPY)
+ tangent_interp = tangent;
+ binormal_interp = binormal;
#endif
-#if defined(LIGHT_USE_PSSM4)
- shadow_coord3 = light_shadow_matrix3 * vi4;
- shadow_coord4 = light_shadow_matrix4 * vi4;
-
+#if defined(OVERRIDE_POSITION)
+ gl_Position = position;
+#else
+ gl_Position = projection_matrix * vec4(vertex_interp, 1.0);
#endif
-#endif //use shadow and use lighting
-
-#ifdef USE_VERTEX_LIGHTING
-
-#ifdef USE_REFLECTION_PROBE1
- {
- vec3 ref_normal = normalize(reflect(vertex_interp, normal_interp));
- vec3 local_pos = (refprobe1_local_matrix * vec4(vertex_interp, 1.0)).xyz;
- vec3 inner_pos = abs(local_pos / refprobe1_box_extents);
- float blend = max(inner_pos.x, max(inner_pos.y, inner_pos.z));
-
- {
- vec3 local_ref_vec = (refprobe1_local_matrix * vec4(ref_normal, 0.0)).xyz;
- refprobe1_reflection_normal_blend.xyz = local_ref_vec;
- refprobe1_reflection_normal_blend.a = blend;
+#ifdef MODE_RENDER_DEPTH
+ if (scene_data.pancake_shadows) {
+ if (gl_Position.z <= 0.00001) {
+ gl_Position.z = 0.00001;
}
-#ifndef USE_LIGHTMAP
-
- refprobe1_ambient_normal = (refprobe1_local_matrix * vec4(normal_interp, 0.0)).xyz;
-#endif
}
-
-#endif //USE_REFLECTION_PROBE1
-
-#ifdef USE_REFLECTION_PROBE2
- {
- vec3 ref_normal = normalize(reflect(vertex_interp, normal_interp));
- vec3 local_pos = (refprobe2_local_matrix * vec4(vertex_interp, 1.0)).xyz;
- vec3 inner_pos = abs(local_pos / refprobe2_box_extents);
- float blend = max(inner_pos.x, max(inner_pos.y, inner_pos.z));
-
- {
- vec3 local_ref_vec = (refprobe2_local_matrix * vec4(ref_normal, 0.0)).xyz;
- refprobe2_reflection_normal_blend.xyz = local_ref_vec;
- refprobe2_reflection_normal_blend.a = blend;
- }
-#ifndef USE_LIGHTMAP
-
- refprobe2_ambient_normal = (refprobe2_local_matrix * vec4(normal_interp, 0.0)).xyz;
#endif
- }
-#endif //USE_REFLECTION_PROBE2
-
-#if defined(FOG_DEPTH_ENABLED) || defined(FOG_HEIGHT_ENABLED)
+ position_interp = gl_Position;
+}
- float fog_amount = 0.0;
+/* clang-format off */
+#[fragment]
-#ifdef LIGHT_MODE_DIRECTIONAL
- vec3 fog_color = mix(fog_color_base.rgb, fog_sun_color_amount.rgb, fog_sun_color_amount.a * pow(max(dot(normalize(vertex_interp), light_direction), 0.0), 8.0));
-#else
- vec3 fog_color = fog_color_base.rgb;
+// Default to SPECULAR_SCHLICK_GGX.
+#if !defined(SPECULAR_DISABLED) && !defined(SPECULAR_SCHLICK_GGX) && !defined(SPECULAR_TOON)
+#define SPECULAR_SCHLICK_GGX
#endif
-#ifdef FOG_DEPTH_ENABLED
-
- {
- float fog_z = smoothstep(fog_depth_begin, fog_max_distance, length(vertex));
-
- fog_amount = pow(fog_z, fog_depth_curve) * fog_color_base.a;
- }
+#if !defined(MODE_RENDER_DEPTH) || defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED) ||defined(LIGHT_CLEARCOAT_USED)
+#ifndef NORMAL_USED
+#define NORMAL_USED
#endif
-
-#ifdef FOG_HEIGHT_ENABLED
- {
- float y = (inv_view_matrix * vec4(vertex_interp, 1.0)).y;
- fog_amount = max(fog_amount, pow(smoothstep(fog_height_min, fog_height_max, y), fog_height_curve));
- }
#endif
- fog_interp = vec4(fog_color, fog_amount);
-#endif //fog
+#include "tonemap_inc.glsl"
+#include "stdlib_inc.glsl"
-#endif //use vertex lighting
+/* texture unit usage, N is max_texture_unity-N
-#if defined(OVERRIDE_POSITION)
- gl_Position = position;
-#else
- gl_Position = projection_matrix * vec4(vertex_interp, 1.0);
-#endif
+1-color correction // In tonemap_inc.glsl
+2-radiance
+3-directional_shadow
+4-positional_shadow
+5-screen
+6-depth
-#if defined(RENDER_DEPTH) && defined(USE_RGBA_SHADOWS)
- position_interp = gl_Position;
-#endif
-}
-
-/* clang-format off */
-[fragment]
+*/
-#ifdef USE_GLES_OVER_GL
-#define lowp
-#define mediump
-#define highp
-#else
-#if defined(USE_HIGHP_PRECISION)
-precision highp float;
-precision highp int;
-#else
-precision mediump float;
-precision mediump int;
-#endif
-#endif
+uniform highp mat4 world_transform;
+/* clang-format on */
#define M_PI 3.14159265359
#define SHADER_IS_SRGB true
-//
-// uniforms
-//
-
-uniform highp mat4 inv_view_matrix;
-/* clang-format on */
-uniform highp mat4 view_matrix;
-uniform highp mat4 projection_matrix;
-uniform highp mat4 projection_inverse_matrix;
-
-uniform highp mat4 world_transform;
-
-uniform highp float time;
+/* Varyings */
-uniform highp vec2 viewport_size;
-
-#if defined(SCREEN_UV_USED)
-uniform vec2 screen_pixel_size;
-#endif
-
-#if defined(SCREEN_TEXTURE_USED)
-uniform highp sampler2D screen_texture; //texunit:-4
-#endif
-#if defined(DEPTH_TEXTURE_USED)
-uniform highp sampler2D depth_texture; //texunit:-4
+#if defined(COLOR_USED)
+in vec4 color_interp;
#endif
-#ifdef USE_REFLECTION_PROBE1
-
-#ifdef USE_VERTEX_LIGHTING
-
-in mediump vec4 refprobe1_reflection_normal_blend;
-#ifndef USE_LIGHTMAP
-in mediump vec3 refprobe1_ambient_normal;
+#if defined(UV_USED)
+in vec2 uv_interp;
#endif
+#if defined(UV2_USED)
+in vec2 uv2_interp;
#else
-
-uniform bool refprobe1_use_box_project;
-uniform highp vec3 refprobe1_box_extents;
-uniform vec3 refprobe1_box_offset;
-uniform highp mat4 refprobe1_local_matrix;
-
-#endif //use vertex lighting
-
-uniform bool refprobe1_exterior;
-
-uniform highp samplerCube reflection_probe1; //texunit:-5
-
-uniform float refprobe1_intensity;
-uniform vec4 refprobe1_ambient;
-
-#endif //USE_REFLECTION_PROBE1
-
-#ifdef USE_REFLECTION_PROBE2
-
-#ifdef USE_VERTEX_LIGHTING
-
-in mediump vec4 refprobe2_reflection_normal_blend;
-#ifndef USE_LIGHTMAP
-in mediump vec3 refprobe2_ambient_normal;
+#ifdef USE_LIGHTMAP
+in vec2 uv2_interp;
#endif
-
-#else
-
-uniform bool refprobe2_use_box_project;
-uniform highp vec3 refprobe2_box_extents;
-uniform vec3 refprobe2_box_offset;
-uniform highp mat4 refprobe2_local_matrix;
-
-#endif //use vertex lighting
-
-uniform bool refprobe2_exterior;
-
-uniform highp samplerCube reflection_probe2; //texunit:-6
-
-uniform float refprobe2_intensity;
-uniform vec4 refprobe2_ambient;
-
-#endif //USE_REFLECTION_PROBE2
-
-#define RADIANCE_MAX_LOD 6.0
-
-#if defined(USE_REFLECTION_PROBE1) || defined(USE_REFLECTION_PROBE2)
-
-void reflection_process(samplerCube reflection_map,
-#ifdef USE_VERTEX_LIGHTING
- vec3 ref_normal,
-#ifndef USE_LIGHTMAP
- vec3 amb_normal,
#endif
- float ref_blend,
-
-#else //no vertex lighting
- vec3 normal, vec3 vertex,
- mat4 local_matrix,
- bool use_box_project, vec3 box_extents, vec3 box_offset,
-#endif //vertex lighting
- bool exterior, float intensity, vec4 ref_ambient, float roughness, vec3 ambient, vec3 skybox, inout highp vec4 reflection_accum, inout highp vec4 ambient_accum) {
- vec4 reflection;
-
-#ifdef USE_VERTEX_LIGHTING
-
- reflection.rgb = textureCubeLod(reflection_map, ref_normal, roughness * RADIANCE_MAX_LOD).rgb;
-
- float blend = ref_blend; //crappier blend formula for vertex
- blend *= blend;
- blend = max(0.0, 1.0 - blend);
-
-#else //fragment lighting
- vec3 local_pos = (local_matrix * vec4(vertex, 1.0)).xyz;
-
- if (any(greaterThan(abs(local_pos), box_extents))) { //out of the reflection box
- return;
- }
-
- vec3 inner_pos = abs(local_pos / box_extents);
- float blend = max(inner_pos.x, max(inner_pos.y, inner_pos.z));
- blend = mix(length(inner_pos), blend, blend);
- blend *= blend;
- blend = max(0.0, 1.0 - blend);
-
- //reflect and make local
- vec3 ref_normal = normalize(reflect(vertex, normal));
- ref_normal = (local_matrix * vec4(ref_normal, 0.0)).xyz;
-
- if (use_box_project) { //box project
-
- vec3 nrdir = normalize(ref_normal);
- vec3 rbmax = (box_extents - local_pos) / nrdir;
- vec3 rbmin = (-box_extents - local_pos) / nrdir;
-
- vec3 rbminmax = mix(rbmin, rbmax, vec3(greaterThan(nrdir, vec3(0.0, 0.0, 0.0))));
-
- float fa = min(min(rbminmax.x, rbminmax.y), rbminmax.z);
- vec3 posonbox = local_pos + nrdir * fa;
- ref_normal = posonbox - box_offset.xyz;
- }
+#if defined(TANGENT_USED) || defined(ENABLE_NORMALMAP) || defined(LIGHT_USE_ANISOTROPY)
+in vec3 tangent_interp;
+in vec3 binormal_interp;
+#endif
- reflection.rgb = textureCubeLod(reflection_map, ref_normal, roughness * RADIANCE_MAX_LOD).rgb;
+#ifdef NORMAL_USED
+in vec3 normal_interp;
#endif
- if (exterior) {
- reflection.rgb = mix(skybox, reflection.rgb, blend);
- }
- reflection.rgb *= intensity;
- reflection.a = blend;
- reflection.rgb *= blend;
+in highp vec3 vertex_interp;
- reflection_accum += reflection;
+/* PBR CHANNELS */
-#ifndef USE_LIGHTMAP
+#ifdef USE_RADIANCE_MAP
- vec4 ambient_out;
-#ifndef USE_VERTEX_LIGHTING
+layout(std140) uniform Radiance { // ubo:4
- vec3 amb_normal = (local_matrix * vec4(normal, 0.0)).xyz;
-#endif
+ mat4 radiance_inverse_xform;
+ float radiance_ambient_contribution;
+};
- ambient_out.rgb = textureCubeLod(reflection_map, amb_normal, RADIANCE_MAX_LOD).rgb;
- ambient_out.rgb = mix(ref_ambient.rgb, ambient_out.rgb, ref_ambient.a);
- if (exterior) {
- ambient_out.rgb = mix(ambient, ambient_out.rgb, blend);
- }
+#define RADIANCE_MAX_LOD 5.0
- ambient_out.a = blend;
- ambient_out.rgb *= blend;
- ambient_accum += ambient_out;
+uniform sampler2D radiance_map; // texunit:-2
-#endif
+vec3 textureDualParaboloid(sampler2D p_tex, vec3 p_vec, float p_roughness) {
+ vec3 norm = normalize(p_vec);
+ norm.xy /= 1.0 + abs(norm.z);
+ norm.xy = norm.xy * vec2(0.5, 0.25) + vec2(0.5, 0.25);
+ if (norm.z > 0.0) {
+ norm.y = 0.5 - norm.y + 0.5;
+ }
+ return textureLod(p_tex, norm.xy, p_roughness * RADIANCE_MAX_LOD).xyz;
}
-#endif //use refprobe 1 or 2
-
-#ifdef USE_LIGHTMAP
-uniform mediump sampler2D lightmap; //texunit:-4
-uniform mediump float lightmap_energy;
#endif
-#ifdef USE_LIGHTMAP_CAPTURE
-uniform mediump vec4[12] lightmap_captures;
-uniform bool lightmap_capture_sky;
+layout(std140) uniform GlobalVariableData { //ubo:1
+ vec4 global_variables[MAX_GLOBAL_VARIABLES];
+};
-#endif
+ /* Material Uniforms */
-#ifdef USE_RADIANCE_MAP
+#if defined(MATERIAL_UNIFORMS_USED)
-uniform samplerCube radiance_map; // texunit:-2
+/* clang-format off */
+layout(std140) uniform MaterialUniforms { // ubo:3
-uniform mat4 radiance_inverse_xform;
+#MATERIAL_UNIFORMS
-#endif
+};
+/* clang-format on */
-uniform vec4 bg_color;
-uniform float bg_energy;
+#endif
-uniform float ambient_sky_contribution;
-uniform vec4 ambient_color;
-uniform float ambient_energy;
+layout(std140) uniform SceneData { // ubo:2
+ highp mat4 projection_matrix;
+ highp mat4 inv_projection_matrix;
+ highp mat4 inv_view_matrix;
+ highp mat4 view_matrix;
-#ifdef USE_LIGHTING
+ vec2 viewport_size;
+ vec2 screen_pixel_size;
-uniform highp vec4 shadow_color;
+ mediump vec4 ambient_light_color_energy;
-#ifdef USE_VERTEX_LIGHTING
+ mediump float ambient_color_sky_mix;
+ uint ambient_flags;
+ bool material_uv2_mode;
+ float opaque_prepass_threshold;
+ //bool use_ambient_light;
+ //bool use_ambient_cubemap;
+ //bool use_reflection_cubemap;
-//get from vertex
-in highp vec3 diffuse_interp;
-in highp vec3 specular_interp;
+ mat3 radiance_inverse_xform;
-uniform highp vec3 light_direction; //may be used by fog, so leave here
+ uint directional_light_count;
+ float z_far;
+ float z_near;
+ float pad;
-#else
-//done in fragment
-// general for all lights
-uniform highp vec4 light_color;
+ bool fog_enabled;
+ float fog_density;
+ float fog_height;
+ float fog_height_density;
-uniform highp float light_specular;
+ vec3 fog_light_color;
+ float fog_sun_scatter;
-// directional
-uniform highp vec3 light_direction;
-// omni
-uniform highp vec3 light_position;
+ float fog_aerial_perspective;
-uniform highp float light_attenuation;
+ float time;
+ float reflection_multiplier; // one normally, zero when rendering reflections
-// spot
-uniform highp float light_spot_attenuation;
-uniform highp float light_spot_range;
-uniform highp float light_spot_angle;
-#endif
+ bool pancake_shadows;
+}
+scene_data;
-//this is needed outside above if because dual paraboloid wants it
-uniform highp float light_range;
+/* clang-format off */
-#ifdef USE_SHADOW
+#GLOBALS
-uniform highp vec2 shadow_pixel_size;
+/* clang-format on */
-#if defined(LIGHT_MODE_OMNI) || defined(LIGHT_MODE_SPOT)
-uniform highp sampler2D light_shadow_atlas; //texunit:-3
-#endif
+//directional light data
-#ifdef LIGHT_MODE_DIRECTIONAL
-uniform highp sampler2D light_directional_shadow; // texunit:-3
-uniform highp vec4 light_split_offsets;
-#endif
+#ifdef USE_LIGHT_DIRECTIONAL
-in highp vec4 shadow_coord;
+struct DirectionalLightData {
+ mediump vec3 direction;
+ mediump float energy;
+ mediump vec3 color;
+ mediump float size;
+ mediump vec3 pad;
+ mediump float specular;
+};
-#if defined(LIGHT_USE_PSSM2) || defined(LIGHT_USE_PSSM4)
-in highp vec4 shadow_coord2;
#endif
-#if defined(LIGHT_USE_PSSM4)
+// omni and spot
+#ifdef USE_LIGHT_POSITIONAL
+struct LightData { //this structure needs to be as packed as possible
+ highp vec3 position;
+ highp float inv_radius;
-in highp vec4 shadow_coord3;
-in highp vec4 shadow_coord4;
-
-#endif
+ mediump vec3 direction;
+ highp float size;
-uniform vec4 light_clamp;
+ mediump vec3 color;
+ mediump float attenuation;
-#endif // light shadow
+ mediump float cone_attenuation;
+ mediump float cone_angle;
+ mediump float specular_amount;
+ bool shadow_enabled;
+};
-// directional shadow
+layout(std140) uniform OmniLightData { // ubo:5
-#endif
+ LightData omni_lights[MAX_LIGHT_DATA_STRUCTS];
+};
-//
-// varyings
-//
+layout(std140) uniform SpotLightData { // ubo:6
-#if defined(RENDER_DEPTH) && defined(USE_RGBA_SHADOWS)
-in highp vec4 position_interp;
-#endif
+ LightData spot_lights[MAX_LIGHT_DATA_STRUCTS];
+};
-in highp vec3 vertex_interp;
-in vec3 normal_interp;
+uniform highp samplerCubeShadow positional_shadow; // texunit:-6
-#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP)
-in vec3 tangent_interp;
-in vec3 binormal_interp;
-#endif
+uniform int omni_light_indices[MAX_FORWARD_LIGHTS];
+uniform int omni_light_count;
-#if defined(ENABLE_COLOR_INTERP)
-in vec4 color_interp;
-#endif
+uniform int spot_light_indices[MAX_FORWARD_LIGHTS];
+uniform int spot_light_count;
-#if defined(ENABLE_UV_INTERP)
-in vec2 uv_interp;
-#endif
+uniform int reflection_indices[MAX_FORWARD_LIGHTS];
+uniform int reflection_count;
-#if defined(ENABLE_UV2_INTERP) || defined(USE_LIGHTMAP)
-in vec2 uv2_interp;
#endif
-in vec3 view_interp;
+uniform highp sampler2D screen_texture; // texunit:-5
+uniform highp sampler2D depth_buffer; // texunit:-6
layout(location = 0) out vec4 frag_color;
+in highp vec4 position_interp;
+
vec3 F0(float metallic, float specular, vec3 albedo) {
float dielectric = 0.16 * specular * specular;
// use albedo * metallic as colored specular reflectance at 0 angle for metallic materials;
@@ -985,95 +555,31 @@ vec3 F0(float metallic, float specular, vec3 albedo) {
return mix(vec3(dielectric), albedo, vec3(metallic));
}
-/* clang-format off */
-
-FRAGMENT_SHADER_GLOBALS
-
-/* clang-format on */
-
-#ifdef RENDER_DEPTH_DUAL_PARABOLOID
-
-in highp float dp_clip;
-
-#endif
-
-#ifdef USE_LIGHTING
-
-// This returns the G_GGX function divided by 2 cos_theta_m, where in practice cos_theta_m is either N.L or N.V.
-// We're dividing this factor off because the overall term we'll end up looks like
-// (see, for example, the first unnumbered equation in B. Burley, "Physically Based Shading at Disney", SIGGRAPH 2012):
-//
-// F(L.V) D(N.H) G(N.L) G(N.V) / (4 N.L N.V)
-//
-// We're basically regouping this as
-//
-// F(L.V) D(N.H) [G(N.L)/(2 N.L)] [G(N.V) / (2 N.V)]
-//
-// and thus, this function implements the [G(N.m)/(2 N.m)] part with m = L or V.
-//
-// The contents of the D and G (G1) functions (GGX) are taken from
-// E. Heitz, "Understanding the Masking-Shadowing Function in Microfacet-Based BRDFs", J. Comp. Graph. Tech. 3 (2) (2014).
-// Eqns 71-72 and 85-86 (see also Eqns 43 and 80).
-
-/*
-float G_GGX_2cos(float cos_theta_m, float alpha) {
- // Schlick's approximation
- // C. Schlick, "An Inexpensive BRDF Model for Physically-based Rendering", Computer Graphics Forum. 13 (3): 233 (1994)
- // Eq. (19), although see Heitz (2014) the about the problems with his derivation.
- // It nevertheless approximates GGX well with k = alpha/2.
- float k = 0.5 * alpha;
- return 0.5 / (cos_theta_m * (1.0 - k) + k);
-
- // float cos2 = cos_theta_m * cos_theta_m;
- // float sin2 = (1.0 - cos2);
- // return 1.0 / (cos_theta_m + sqrt(cos2 + alpha * alpha * sin2));
-}
-*/
-
-// This approximates G_GGX_2cos(cos_theta_l, alpha) * G_GGX_2cos(cos_theta_v, alpha)
-// See Filament docs, Specular G section.
-float V_GGX(float cos_theta_l, float cos_theta_v, float alpha) {
- return 0.5 / mix(2.0 * cos_theta_l * cos_theta_v, cos_theta_l + cos_theta_v, alpha);
-}
-
+#if defined(USE_LIGHT_DIRECTIONAL) || defined(USE_LIGHT_POSITIONAL)
float D_GGX(float cos_theta_m, float alpha) {
- float alpha2 = alpha * alpha;
- float d = 1.0 + (alpha2 - 1.0) * cos_theta_m * cos_theta_m;
- return alpha2 / (M_PI * d * d);
+ float a = cos_theta_m * alpha;
+ float k = alpha / (1.0 - cos_theta_m * cos_theta_m + a * a);
+ return k * k * (1.0 / M_PI);
}
-/*
-float G_GGX_anisotropic_2cos(float cos_theta_m, float alpha_x, float alpha_y, float cos_phi, float sin_phi) {
- float cos2 = cos_theta_m * cos_theta_m;
- float sin2 = (1.0 - cos2);
- float s_x = alpha_x * cos_phi;
- float s_y = alpha_y * sin_phi;
- return 1.0 / max(cos_theta_m + sqrt(cos2 + (s_x * s_x + s_y * s_y) * sin2), 0.001);
-}
-*/
-
-// This approximates G_GGX_anisotropic_2cos(cos_theta_l, ...) * G_GGX_anisotropic_2cos(cos_theta_v, ...)
-// See Filament docs, Anisotropic specular BRDF section.
-float V_GGX_anisotropic(float alpha_x, float alpha_y, float TdotV, float TdotL, float BdotV, float BdotL, float NdotV, float NdotL) {
- float Lambda_V = NdotL * length(vec3(alpha_x * TdotV, alpha_y * BdotV, NdotV));
- float Lambda_L = NdotV * length(vec3(alpha_x * TdotL, alpha_y * BdotL, NdotL));
- return 0.5 / (Lambda_V + Lambda_L);
+// From Earl Hammon, Jr. "PBR Diffuse Lighting for GGX+Smith Microsurfaces" https://www.gdcvault.com/play/1024478/PBR-Diffuse-Lighting-for-GGX
+float V_GGX(float NdotL, float NdotV, float alpha) {
+ return 0.5 / mix(2.0 * NdotL * NdotV, NdotL + NdotV, alpha);
}
-float D_GGX_anisotropic(float cos_theta_m, float alpha_x, float alpha_y, float cos_phi, float sin_phi, float NdotH) {
+float D_GGX_anisotropic(float cos_theta_m, float alpha_x, float alpha_y, float cos_phi, float sin_phi) {
float alpha2 = alpha_x * alpha_y;
- highp vec3 v = vec3(alpha_y * cos_phi, alpha_x * sin_phi, alpha2 * NdotH);
+ highp vec3 v = vec3(alpha_y * cos_phi, alpha_x * sin_phi, alpha2 * cos_theta_m);
highp float v2 = dot(v, v);
float w2 = alpha2 / v2;
float D = alpha2 * w2 * w2 * (1.0 / M_PI);
return D;
+}
- /* float cos2 = cos_theta_m * cos_theta_m;
- float sin2 = (1.0 - cos2);
- float r_x = cos_phi / alpha_x;
- float r_y = sin_phi / alpha_y;
- float d = cos2 + sin2 * (r_x * r_x + r_y * r_y);
- return 1.0 / max(M_PI * alpha_x * alpha_y * d * d, 0.001); */
+float V_GGX_anisotropic(float alpha_x, float alpha_y, float TdotV, float TdotL, float BdotV, float BdotL, float NdotV, float NdotL) {
+ float Lambda_V = NdotL * length(vec3(alpha_x * TdotV, alpha_y * BdotV, NdotV));
+ float Lambda_L = NdotV * length(vec3(alpha_x * TdotL, alpha_y * BdotL, NdotL));
+ return 0.5 / (Lambda_V + Lambda_L);
}
float SchlickFresnel(float u) {
@@ -1082,109 +588,64 @@ float SchlickFresnel(float u) {
return m2 * m2 * m; // pow(m,5)
}
-float GTR1(float NdotH, float a) {
- if (a >= 1.0)
- return 1.0 / M_PI;
- float a2 = a * a;
- float t = 1.0 + (a2 - 1.0) * NdotH * NdotH;
- return (a2 - 1.0) / (M_PI * log(a2) * t);
-}
+void light_compute(vec3 N, vec3 L, vec3 V, float A, vec3 light_color, float attenuation, vec3 f0, uint orms, float specular_amount, vec3 albedo, inout float alpha,
+#ifdef LIGHT_BACKLIGHT_USED
+ vec3 backlight,
+#endif
+#ifdef LIGHT_RIM_USED
+ float rim, float rim_tint,
+#endif
+#ifdef LIGHT_CLEARCOAT_USED
+ float clearcoat, float clearcoat_roughness, vec3 vertex_normal,
+#endif
+#ifdef LIGHT_ANISOTROPY_USED
+ vec3 B, vec3 T, float anisotropy,
+#endif
+ inout vec3 diffuse_light, inout vec3 specular_light) {
-void light_compute(
- vec3 N,
- vec3 L,
- vec3 V,
- vec3 B,
- vec3 T,
- vec3 light_color,
- vec3 attenuation,
- vec3 diffuse_color,
- vec3 transmission,
- float specular_blob_intensity,
- float roughness,
- float metallic,
- float specular,
- float rim,
- float rim_tint,
- float clearcoat,
- float clearcoat_roughness,
- float anisotropy,
- inout vec3 diffuse_light,
- inout vec3 specular_light,
- inout float alpha) {
-//this makes lights behave closer to linear, but then addition of lights looks bad
-//better left disabled
+ vec4 orms_unpacked = unpackUnorm4x8(orms);
-//#define SRGB_APPROX(m_var) m_var = pow(m_var,0.4545454545);
-/*
-#define SRGB_APPROX(m_var) {\
- float S1 = sqrt(m_var);\
- float S2 = sqrt(S1);\
- float S3 = sqrt(S2);\
- m_var = 0.662002687 * S1 + 0.684122060 * S2 - 0.323583601 * S3 - 0.0225411470 * m_var;\
- }
-*/
-#define SRGB_APPROX(m_var)
+ float roughness = orms_unpacked.y;
+ float metallic = orms_unpacked.z;
#if defined(USE_LIGHT_SHADER_CODE)
// light is written by the light shader
vec3 normal = N;
- vec3 albedo = diffuse_color;
vec3 light = L;
vec3 view = V;
/* clang-format off */
-LIGHT_SHADER_CODE
+
+#CODE : LIGHT
/* clang-format on */
#else
- float NdotL = dot(N, L);
+ float NdotL = min(A + dot(N, L), 1.0);
float cNdotL = max(NdotL, 0.0); // clamped NdotL
float NdotV = dot(N, V);
- float cNdotV = max(abs(NdotV), 1e-6);
+ float cNdotV = max(NdotV, 0.0);
-#if defined(DIFFUSE_BURLEY) || defined(SPECULAR_BLINN) || defined(SPECULAR_SCHLICK_GGX) || defined(LIGHT_USE_CLEARCOAT)
+#if defined(DIFFUSE_BURLEY) || defined(SPECULAR_SCHLICK_GGX) || defined(LIGHT_CLEARCOAT_USED)
vec3 H = normalize(V + L);
#endif
-#if defined(SPECULAR_BLINN) || defined(SPECULAR_SCHLICK_GGX) || defined(LIGHT_USE_CLEARCOAT)
- float cNdotH = max(dot(N, H), 0.0);
+#if defined(SPECULAR_SCHLICK_GGX)
+ float cNdotH = clamp(A + dot(N, H), 0.0, 1.0);
#endif
-#if defined(DIFFUSE_BURLEY) || defined(SPECULAR_SCHLICK_GGX) || defined(LIGHT_USE_CLEARCOAT)
- float cLdotH = max(dot(L, H), 0.0);
+#if defined(DIFFUSE_BURLEY) || defined(SPECULAR_SCHLICK_GGX) || defined(LIGHT_CLEARCOAT_USED)
+ float cLdotH = clamp(A + dot(L, H), 0.0, 1.0);
#endif
if (metallic < 1.0) {
-#if defined(DIFFUSE_OREN_NAYAR)
- vec3 diffuse_brdf_NL;
-#else
float diffuse_brdf_NL; // BRDF times N.L for calculating diffuse radiance
-#endif
#if defined(DIFFUSE_LAMBERT_WRAP)
// energy conserving lambert wrap shader
diffuse_brdf_NL = max(0.0, (NdotL + roughness) / ((1.0 + roughness) * (1.0 + roughness)));
-
-#elif defined(DIFFUSE_OREN_NAYAR)
-
- {
- // see http://mimosa-pudica.net/improved-oren-nayar.html
- float LdotV = dot(L, V);
-
- float s = LdotV - NdotL * NdotV;
- float t = mix(1.0, max(NdotL, NdotV), step(0.0, s));
-
- float sigma2 = roughness * roughness; // TODO: this needs checking
- vec3 A = 1.0 + sigma2 * (-0.5 / (sigma2 + 0.33) + 0.17 * diffuse_color / (sigma2 + 0.13));
- float B = 0.45 * sigma2 / (sigma2 + 0.09);
-
- diffuse_brdf_NL = cNdotL * (A + vec3(B) * s / t) * (1.0 / M_PI);
- }
-
#elif defined(DIFFUSE_TOON)
diffuse_brdf_NL = smoothstep(-roughness, max(roughness, 0.01), NdotL);
@@ -1196,230 +657,208 @@ LIGHT_SHADER_CODE
float FdV = 1.0 + FD90_minus_1 * SchlickFresnel(cNdotV);
float FdL = 1.0 + FD90_minus_1 * SchlickFresnel(cNdotL);
diffuse_brdf_NL = (1.0 / M_PI) * FdV * FdL * cNdotL;
- /*
- float energyBias = mix(roughness, 0.0, 0.5);
- float energyFactor = mix(roughness, 1.0, 1.0 / 1.51);
- float fd90 = energyBias + 2.0 * VoH * VoH * roughness;
- float f0 = 1.0;
- float lightScatter = f0 + (fd90 - f0) * pow(1.0 - cNdotL, 5.0);
- float viewScatter = f0 + (fd90 - f0) * pow(1.0 - cNdotV, 5.0);
-
- diffuse_brdf_NL = lightScatter * viewScatter * energyFactor;
- */
}
#else
// lambert
diffuse_brdf_NL = cNdotL * (1.0 / M_PI);
#endif
- SRGB_APPROX(diffuse_brdf_NL)
+ diffuse_light += light_color * diffuse_brdf_NL * attenuation;
- diffuse_light += light_color * diffuse_color * diffuse_brdf_NL * attenuation;
-
-#if defined(TRANSMISSION_USED)
- diffuse_light += light_color * diffuse_color * (vec3(1.0 / M_PI) - diffuse_brdf_NL) * transmission * attenuation;
+#if defined(LIGHT_BACKLIGHT_USED)
+ diffuse_light += light_color * (vec3(1.0 / M_PI) - diffuse_brdf_NL) * backlight * attenuation;
#endif
-#if defined(LIGHT_USE_RIM)
+#if defined(LIGHT_RIM_USED)
float rim_light = pow(max(0.0, 1.0 - cNdotV), max(0.0, (1.0 - roughness) * 16.0));
- diffuse_light += rim_light * rim * mix(vec3(1.0), diffuse_color, rim_tint) * light_color;
+ diffuse_light += rim_light * rim * mix(vec3(1.0), albedo, rim_tint) * light_color;
#endif
}
- if (roughness > 0.0) {
+ if (roughness > 0.0) { // FIXME: roughness == 0 should not disable specular light entirely
-#if defined(SPECULAR_SCHLICK_GGX)
- vec3 specular_brdf_NL = vec3(0.0);
-#else
- float specular_brdf_NL = 0.0;
-#endif
-
-#if defined(SPECULAR_BLINN)
-
- //normalized blinn
- float shininess = exp2(15.0 * (1.0 - roughness) + 1.0) * 0.25;
- float blinn = pow(cNdotH, shininess) * cNdotL;
- blinn *= (shininess + 8.0) * (1.0 / (8.0 * M_PI));
- specular_brdf_NL = blinn;
-
-#elif defined(SPECULAR_PHONG)
-
- vec3 R = normalize(-reflect(L, N));
- float cRdotV = max(0.0, dot(R, V));
- float shininess = exp2(15.0 * (1.0 - roughness) + 1.0) * 0.25;
- float phong = pow(cRdotV, shininess);
- phong *= (shininess + 8.0) * (1.0 / (8.0 * M_PI));
- specular_brdf_NL = (phong) / max(4.0 * cNdotV * cNdotL, 0.75);
+ // D
-#elif defined(SPECULAR_TOON)
+#if defined(SPECULAR_TOON)
vec3 R = normalize(-reflect(L, N));
float RdotV = dot(R, V);
float mid = 1.0 - roughness;
mid *= mid;
- specular_brdf_NL = smoothstep(mid - roughness * 0.5, mid + roughness * 0.5, RdotV) * mid;
+ float intensity = smoothstep(mid - roughness * 0.5, mid + roughness * 0.5, RdotV) * mid;
+ diffuse_light += light_color * intensity * attenuation * specular_amount; // write to diffuse_light, as in toon shading you generally want no reflection
#elif defined(SPECULAR_DISABLED)
// none..
+
#elif defined(SPECULAR_SCHLICK_GGX)
// shlick+ggx as default
-
-#if defined(LIGHT_USE_ANISOTROPY)
float alpha_ggx = roughness * roughness;
+#if defined(LIGHT_ANISOTROPY_USED)
+
float aspect = sqrt(1.0 - anisotropy * 0.9);
float ax = alpha_ggx / aspect;
float ay = alpha_ggx * aspect;
float XdotH = dot(T, H);
float YdotH = dot(B, H);
- float D = D_GGX_anisotropic(cNdotH, ax, ay, XdotH, YdotH, cNdotH);
- //float G = G_GGX_anisotropic_2cos(cNdotL, ax, ay, XdotH, YdotH) * G_GGX_anisotropic_2cos(cNdotV, ax, ay, XdotH, YdotH);
+ float D = D_GGX_anisotropic(cNdotH, ax, ay, XdotH, YdotH);
float G = V_GGX_anisotropic(ax, ay, dot(T, V), dot(T, L), dot(B, V), dot(B, L), cNdotV, cNdotL);
-
-#else
- float alpha_ggx = roughness * roughness;
+#else // LIGHT_ANISOTROPY_USED
float D = D_GGX(cNdotH, alpha_ggx);
- //float G = G_GGX_2cos(cNdotL, alpha_ggx) * G_GGX_2cos(cNdotV, alpha_ggx);
float G = V_GGX(cNdotL, cNdotV, alpha_ggx);
-#endif
- // F
- vec3 f0 = F0(metallic, specular, diffuse_color);
+#endif // LIGHT_ANISOTROPY_USED
+ // F
float cLdotH5 = SchlickFresnel(cLdotH);
vec3 F = mix(vec3(cLdotH5), vec3(1.0), f0);
- specular_brdf_NL = cNdotL * D * F * G;
+ vec3 specular_brdf_NL = cNdotL * D * F * G;
+ specular_light += specular_brdf_NL * light_color * attenuation * specular_amount;
#endif
- SRGB_APPROX(specular_brdf_NL)
- specular_light += specular_brdf_NL * light_color * specular_blob_intensity * attenuation;
-
-#if defined(LIGHT_USE_CLEARCOAT)
+#if defined(LIGHT_CLEARCOAT_USED)
+ // Clearcoat ignores normal_map, use vertex normal instead
+ float ccNdotL = max(min(A + dot(vertex_normal, L), 1.0), 0.0);
+ float ccNdotH = clamp(A + dot(vertex_normal, H), 0.0, 1.0);
+ float ccNdotV = max(dot(vertex_normal, V), 1e-4);
#if !defined(SPECULAR_SCHLICK_GGX)
float cLdotH5 = SchlickFresnel(cLdotH);
#endif
- float Dr = GTR1(cNdotH, mix(.1, .001, clearcoat_roughness));
+ float Dr = D_GGX(ccNdotH, mix(0.001, 0.1, clearcoat_roughness));
+ float Gr = 0.25 / (cLdotH * cLdotH);
float Fr = mix(.04, 1.0, cLdotH5);
- //float Gr = G_GGX_2cos(cNdotL, .25) * G_GGX_2cos(cNdotV, .25);
- float Gr = V_GGX(cNdotL, cNdotV, 0.25);
-
- float clearcoat_specular_brdf_NL = 0.25 * clearcoat * Gr * Fr * Dr * cNdotL;
+ float clearcoat_specular_brdf_NL = clearcoat * Gr * Fr * Dr * cNdotL;
- specular_light += clearcoat_specular_brdf_NL * light_color * specular_blob_intensity * attenuation;
-#endif
+ specular_light += clearcoat_specular_brdf_NL * light_color * attenuation * specular_amount;
+ // TODO: Clearcoat adds light to the scene right now (it is non-energy conserving), both diffuse and specular need to be scaled by (1.0 - FR)
+ // but to do so we need to rearrange this entire function
+#endif // LIGHT_CLEARCOAT_USED
}
#ifdef USE_SHADOW_TO_OPACITY
- alpha = min(alpha, clamp(1.0 - length(attenuation), 0.0, 1.0));
+ alpha = min(alpha, clamp(1.0 - attenuation, 0.0, 1.0));
#endif
-#endif //defined(USE_LIGHT_SHADER_CODE)
+#endif //defined(LIGHT_CODE_USED)
}
-#endif
-// shadows
-
-#ifdef USE_SHADOW
-
-#ifdef USE_RGBA_SHADOWS
-
-#define SHADOW_DEPTH(m_val) dot(m_val, vec4(1.0 / (255.0 * 255.0 * 255.0), 1.0 / (255.0 * 255.0), 1.0 / 255.0, 1.0))
-
-#else
-
-#define SHADOW_DEPTH(m_val) (m_val).r
+float get_omni_attenuation(float distance, float inv_range, float decay) {
+ float nd = distance * inv_range;
+ nd *= nd;
+ nd *= nd; // nd^4
+ nd = max(1.0 - nd, 0.0);
+ nd *= nd; // nd^2
+ return nd * pow(max(distance, 0.0001), -decay);
+}
+void light_process_omni(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 f0, uint orms, float shadow, vec3 albedo, inout float alpha,
+#ifdef LIGHT_BACKLIGHT_USED
+ vec3 backlight,
#endif
-
-#define SAMPLE_SHADOW_TEXEL(p_shadow, p_pos, p_depth) step(p_depth, SHADOW_DEPTH(texture(p_shadow, p_pos)))
-#define SAMPLE_SHADOW_TEXEL_PROJ(p_shadow, p_pos) step(p_pos.z, SHADOW_DEPTH(textureProj(p_shadow, p_pos)))
-
-float sample_shadow(highp sampler2D shadow, highp vec4 spos) {
-#ifdef SHADOW_MODE_PCF_13
-
- spos.xyz /= spos.w;
- vec2 pos = spos.xy;
- float depth = spos.z;
-
- float avg = SAMPLE_SHADOW_TEXEL(shadow, pos, depth);
- avg += SAMPLE_SHADOW_TEXEL(shadow, pos + vec2(shadow_pixel_size.x, 0.0), depth);
- avg += SAMPLE_SHADOW_TEXEL(shadow, pos + vec2(-shadow_pixel_size.x, 0.0), depth);
- avg += SAMPLE_SHADOW_TEXEL(shadow, pos + vec2(0.0, shadow_pixel_size.y), depth);
- avg += SAMPLE_SHADOW_TEXEL(shadow, pos + vec2(0.0, -shadow_pixel_size.y), depth);
- avg += SAMPLE_SHADOW_TEXEL(shadow, pos + vec2(shadow_pixel_size.x, shadow_pixel_size.y), depth);
- avg += SAMPLE_SHADOW_TEXEL(shadow, pos + vec2(-shadow_pixel_size.x, shadow_pixel_size.y), depth);
- avg += SAMPLE_SHADOW_TEXEL(shadow, pos + vec2(shadow_pixel_size.x, -shadow_pixel_size.y), depth);
- avg += SAMPLE_SHADOW_TEXEL(shadow, pos + vec2(-shadow_pixel_size.x, -shadow_pixel_size.y), depth);
- avg += SAMPLE_SHADOW_TEXEL(shadow, pos + vec2(shadow_pixel_size.x * 2.0, 0.0), depth);
- avg += SAMPLE_SHADOW_TEXEL(shadow, pos + vec2(-shadow_pixel_size.x * 2.0, 0.0), depth);
- avg += SAMPLE_SHADOW_TEXEL(shadow, pos + vec2(0.0, shadow_pixel_size.y * 2.0), depth);
- avg += SAMPLE_SHADOW_TEXEL(shadow, pos + vec2(0.0, -shadow_pixel_size.y * 2.0), depth);
- return avg * (1.0 / 13.0);
+#ifdef LIGHT_TRANSMITTANCE_USED
+ vec4 transmittance_color,
+ float transmittance_depth,
+ float transmittance_boost,
#endif
-
-#ifdef SHADOW_MODE_PCF_5
-
- spos.xyz /= spos.w;
- vec2 pos = spos.xy;
- float depth = spos.z;
-
- float avg = SAMPLE_SHADOW_TEXEL(shadow, pos, depth);
- avg += SAMPLE_SHADOW_TEXEL(shadow, pos + vec2(shadow_pixel_size.x, 0.0), depth);
- avg += SAMPLE_SHADOW_TEXEL(shadow, pos + vec2(-shadow_pixel_size.x, 0.0), depth);
- avg += SAMPLE_SHADOW_TEXEL(shadow, pos + vec2(0.0, shadow_pixel_size.y), depth);
- avg += SAMPLE_SHADOW_TEXEL(shadow, pos + vec2(0.0, -shadow_pixel_size.y), depth);
- return avg * (1.0 / 5.0);
-
+#ifdef LIGHT_RIM_USED
+ float rim, float rim_tint,
#endif
+#ifdef LIGHT_CLEARCOAT_USED
+ float clearcoat, float clearcoat_roughness, vec3 vertex_normal,
+#endif
+#ifdef LIGHT_ANISOTROPY_USED
+ vec3 binormal, vec3 tangent, float anisotropy,
+#endif
+ inout vec3 diffuse_light, inout vec3 specular_light) {
+ vec3 light_rel_vec = omni_lights[idx].position - vertex;
+ float light_length = length(light_rel_vec);
+ float omni_attenuation = get_omni_attenuation(light_length, omni_lights[idx].inv_radius, omni_lights[idx].attenuation);
+ vec3 light_attenuation = vec3(omni_attenuation);
+ vec3 color = omni_lights[idx].color;
+ float size_A = 0.0;
+
+ if (omni_lights.data[idx].size > 0.0) {
+ float t = omni_lights[idx].size / max(0.001, light_length);
+ size_A = max(0.0, 1.0 - 1 / sqrt(1 + t * t));
+ }
-#if !defined(SHADOW_MODE_PCF_5) || !defined(SHADOW_MODE_PCF_13)
-
- return SAMPLE_SHADOW_TEXEL_PROJ(shadow, spos);
+ light_compute(normal, normalize(light_rel_vec), eye_vec, size_A, color, light_attenuation, f0, orms, omni_lights[idx].specular_amount, albedo, alpha,
+#ifdef LIGHT_BACKLIGHT_USED
+ backlight,
+#endif
+#ifdef LIGHT_RIM_USED
+ rim * omni_attenuation, rim_tint,
+#endif
+#ifdef LIGHT_CLEARCOAT_USED
+ clearcoat, clearcoat_roughness, vertex_normal,
#endif
+#ifdef LIGHT_ANISOTROPY_USED
+ binormal, tangent, anisotropy,
+#endif
+ diffuse_light,
+ specular_light);
}
+void light_process_spot(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 f0, uint orms, float shadow, vec3 albedo, inout float alpha,
+#ifdef LIGHT_BACKLIGHT_USED
+ vec3 backlight,
#endif
-
-#if defined(FOG_DEPTH_ENABLED) || defined(FOG_HEIGHT_ENABLED)
-
-#if defined(USE_VERTEX_LIGHTING)
-
-in vec4 fog_interp;
-
-#else
-uniform mediump vec4 fog_color_base;
-#ifdef LIGHT_MODE_DIRECTIONAL
-uniform mediump vec4 fog_sun_color_amount;
+#ifdef LIGHT_RIM_USED
+ float rim, float rim_tint,
+#endif
+#ifdef LIGHT_CLEARCOAT_USED
+ float clearcoat, float clearcoat_roughness, vec3 vertex_normal,
+#endif
+#ifdef LIGHT_ANISOTROPY_USED
+ vec3 binormal, vec3 tangent, float anisotropy,
#endif
+ inout vec3 diffuse_light,
+ inout vec3 specular_light) {
-uniform bool fog_transmit_enabled;
-uniform mediump float fog_transmit_curve;
+ vec3 light_rel_vec = spot_lights[idx].position - vertex;
+ float light_length = length(light_rel_vec);
+ float spot_attenuation = get_omni_attenuation(light_length, spot_lights[idx].inv_radius, spot_lights[idx].attenuation);
+ vec3 spot_dir = spot_lights[idx].direction;
+ float scos = max(dot(-normalize(light_rel_vec), spot_dir), spot_lights[idx].cone_angle);
+ float spot_rim = max(0.0001, (1.0 - scos) / (1.0 - spot_lights[idx].cone_angle));
+ spot_attenuation *= 1.0 - pow(spot_rim, spot_lights[idx].cone_attenuation);
+ float light_attenuation = spot_attenuation;
+ vec3 color = spot_lights[idx].color;
+
+ float size_A = 0.0;
+
+ if (spot_lights.data[idx].size > 0.0) {
+ float t = spot_lights.data[idx].size / max(0.001, light_length);
+ size_A = max(0.0, 1.0 - 1 / sqrt(1 + t * t));
+ }
-#ifdef FOG_DEPTH_ENABLED
-uniform highp float fog_depth_begin;
-uniform mediump float fog_depth_curve;
-uniform mediump float fog_max_distance;
+ light_compute(normal, normalize(light_rel_vec), eye_vec, size_A, color, light_attenuation, f0, orms, spot_lights[idx].specular_amount, albedo, alpha,
+#ifdef LIGHT_BACKLIGHT_USED
+ backlight,
#endif
-
-#ifdef FOG_HEIGHT_ENABLED
-uniform highp float fog_height_min;
-uniform highp float fog_height_max;
-uniform mediump float fog_height_curve;
+#ifdef LIGHT_RIM_USED
+ rim * spot_attenuation, rim_tint,
#endif
-
-#endif //vertex lit
-#endif //fog
+#ifdef LIGHT_CLEARCOAT_USED
+ clearcoat, clearcoat_roughness, vertex_normal,
+#endif
+#ifdef LIGHT_ANISOTROPY_USED
+ binormal, tangent, anisotropy,
+#endif
+ diffuse_light, specular_light);
+}
+#endif // defined(USE_LIGHT_DIRECTIONAL) || defined(USE_LIGHT_POSITIONAL)
void main() {
-#ifdef RENDER_DEPTH_DUAL_PARABOLOID
-
- if (dp_clip > 0.0)
- discard;
-#endif
- highp vec3 vertex = vertex_interp;
+ //lay out everything, whatever is unused is optimized away anyway
+ vec3 vertex = vertex_interp;
vec3 view = -normalize(vertex_interp);
vec3 albedo = vec3(1.0);
- vec3 transmission = vec3(0.0);
+ vec3 backlight = vec3(0.0);
+ vec4 transmittance_color = vec4(0.0, 0.0, 0.0, 1.0);
+ float transmittance_depth = 0.0;
+ float transmittance_boost = 0.0;
float metallic = 0.0;
float specular = 0.5;
vec3 emission = vec3(0.0);
@@ -1430,617 +869,235 @@ void main() {
float clearcoat_roughness = 0.0;
float anisotropy = 0.0;
vec2 anisotropy_flow = vec2(1.0, 0.0);
- float sss_strength = 0.0; //unused
- // gl_FragDepth is not available in GLES2, so writing to DEPTH is not converted to gl_FragDepth by Godot compiler resulting in a
- // compile error because DEPTH is not a variable.
- float m_DEPTH = 0.0;
-
- float alpha = 1.0;
- float side = 1.0;
-
- float specular_blob_intensity = 1.0;
-#if defined(SPECULAR_TOON)
- specular_blob_intensity *= specular * 2.0;
+ vec4 fog = vec4(0.0);
+#if defined(CUSTOM_RADIANCE_USED)
+ vec4 custom_radiance = vec4(0.0);
+#endif
+#if defined(CUSTOM_IRRADIANCE_USED)
+ vec4 custom_irradiance = vec4(0.0);
#endif
-#if defined(ENABLE_AO)
float ao = 1.0;
float ao_light_affect = 0.0;
-#endif
-#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP)
- vec3 binormal = normalize(binormal_interp) * side;
- vec3 tangent = normalize(tangent_interp) * side;
+ float alpha = 1.0;
+
+#if defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED)
+ vec3 binormal = normalize(binormal_interp);
+ vec3 tangent = normalize(tangent_interp);
#else
vec3 binormal = vec3(0.0);
vec3 tangent = vec3(0.0);
#endif
- vec3 normal = normalize(normal_interp) * side;
-#if defined(ENABLE_NORMALMAP)
- vec3 normalmap = vec3(0.5);
+#ifdef NORMAL_USED
+ vec3 normal = normalize(normal_interp);
+
+#if defined(DO_SIDE_CHECK)
+ if (!gl_FrontFacing) {
+ normal = -normal;
+ }
#endif
- float normaldepth = 1.0;
-#if defined(ALPHA_SCISSOR_USED)
- float alpha_scissor = 0.5;
+#endif //NORMAL_USED
+
+#ifdef UV_USED
+ vec2 uv = uv_interp;
#endif
-#if defined(SCREEN_UV_USED)
- vec2 screen_uv = gl_FragCoord.xy * screen_pixel_size;
+#if defined(UV2_USED) || defined(USE_LIGHTMAP)
+ vec2 uv2 = uv2_interp;
#endif
- {
- /* clang-format off */
+#if defined(COLOR_USED)
+ vec4 color = color_interp;
+#endif
-FRAGMENT_SHADER_CODE
+#if defined(NORMAL_MAP_USED)
- /* clang-format on */
- }
+ vec3 normal_map = vec3(0.5);
+#endif
-#if defined(ENABLE_NORMALMAP)
- normalmap.xy = normalmap.xy * 2.0 - 1.0;
- normalmap.z = sqrt(max(0.0, 1.0 - dot(normalmap.xy, normalmap.xy)));
+ float normal_map_depth = 1.0;
- normal = normalize(mix(normal_interp, tangent * normalmap.x + binormal * normalmap.y + normal * normalmap.z, normaldepth)) * side;
- //normal = normalmap;
-#endif
+ vec2 screen_uv = gl_FragCoord.xy * scene_data.screen_pixel_size + scene_data.screen_pixel_size * 0.5; //account for center
- normal = normalize(normal);
+ float sss_strength = 0.0;
- vec3 N = normal;
+#ifdef ALPHA_SCISSOR_USED
+ float alpha_scissor_threshold = 1.0;
+#endif // ALPHA_SCISSOR_USED
- vec3 specular_light = vec3(0.0, 0.0, 0.0);
- vec3 diffuse_light = vec3(0.0, 0.0, 0.0);
- vec3 ambient_light = vec3(0.0, 0.0, 0.0);
+#ifdef ALPHA_HASH_USED
+ float alpha_hash_scale = 1.0;
+#endif // ALPHA_HASH_USED
- vec3 eye_position = view;
+#ifdef ALPHA_ANTIALIASING_EDGE_USED
+ float alpha_antialiasing_edge = 0.0;
+ vec2 alpha_texture_coordinate = vec2(0.0, 0.0);
+#endif // ALPHA_ANTIALIASING_EDGE_USED
+ {
+#CODE : FRAGMENT
+ }
-#if !defined(USE_SHADOW_TO_OPACITY)
+#ifndef USE_SHADOW_TO_OPACITY
#if defined(ALPHA_SCISSOR_USED)
- if (alpha < alpha_scissor) {
+ if (alpha < alpha_scissor_threshold) {
discard;
}
#endif // ALPHA_SCISSOR_USED
-#ifdef USE_DEPTH_PREPASS
- if (alpha < 0.1) {
+#ifdef USE_OPAQUE_PREPASS
+#if !defined(ALPHA_SCISSOR_USED)
+
+ if (alpha < scene_data.opaque_prepass_threshold) {
discard;
}
-#endif // USE_DEPTH_PREPASS
-#endif // !USE_SHADOW_TO_OPACITY
+#endif // not ALPHA_SCISSOR_USED
+#endif // USE_OPAQUE_PREPASS
-#ifdef BASE_PASS
-
- // IBL precalculations
- float ndotv = clamp(dot(normal, eye_position), 0.0, 1.0);
- vec3 f0 = F0(metallic, specular, albedo);
- vec3 F = f0 + (max(vec3(1.0 - roughness), f0) - f0) * pow(1.0 - ndotv, 5.0);
-
-#ifdef AMBIENT_LIGHT_DISABLED
- ambient_light = vec3(0.0, 0.0, 0.0);
-#else
-
-#ifdef USE_RADIANCE_MAP
-
- vec3 ref_vec = reflect(-eye_position, N);
- ref_vec = normalize((radiance_inverse_xform * vec4(ref_vec, 0.0)).xyz);
-
- ref_vec.z *= -1.0;
-
- specular_light = textureCubeLod(radiance_map, ref_vec, roughness * RADIANCE_MAX_LOD).xyz * bg_energy;
-#ifndef USE_LIGHTMAP
- {
- vec3 ambient_dir = normalize((radiance_inverse_xform * vec4(normal, 0.0)).xyz);
- vec3 env_ambient = textureCubeLod(radiance_map, ambient_dir, 4.0).xyz * bg_energy;
- env_ambient *= 1.0 - F;
+#endif // !USE_SHADOW_TO_OPACITY
- ambient_light = mix(ambient_color.rgb, env_ambient, ambient_sky_contribution);
- }
-#endif
+#ifdef NORMAL_MAP_USED
-#else
+ normal_map.xy = normal_map.xy * 2.0 - 1.0;
+ normal_map.z = sqrt(max(0.0, 1.0 - dot(normal_map.xy, normal_map.xy))); //always ignore Z, as it can be RG packed, Z may be pos/neg, etc.
- ambient_light = ambient_color.rgb;
- specular_light = bg_color.rgb * bg_energy;
+ normal = normalize(mix(normal, tangent * normal_map.x + binormal * normal_map.y + normal * normal_map.z, normal_map_depth));
#endif
-#endif // AMBIENT_LIGHT_DISABLED
- ambient_light *= ambient_energy;
-
-#if defined(USE_REFLECTION_PROBE1) || defined(USE_REFLECTION_PROBE2)
- vec4 ambient_accum = vec4(0.0);
- vec4 reflection_accum = vec4(0.0);
+#ifdef LIGHT_ANISOTROPY_USED
-#ifdef USE_REFLECTION_PROBE1
+ if (anisotropy > 0.01) {
+ //rotation matrix
+ mat3 rot = mat3(tangent, binormal, normal);
+ //make local to space
+ tangent = normalize(rot * vec3(anisotropy_flow.x, anisotropy_flow.y, 0.0));
+ binormal = normalize(rot * vec3(-anisotropy_flow.y, anisotropy_flow.x, 0.0));
+ }
- reflection_process(reflection_probe1,
-#ifdef USE_VERTEX_LIGHTING
- refprobe1_reflection_normal_blend.rgb,
-#ifndef USE_LIGHTMAP
- refprobe1_ambient_normal,
-#endif
- refprobe1_reflection_normal_blend.a,
-#else
- normal_interp, vertex_interp, refprobe1_local_matrix,
- refprobe1_use_box_project, refprobe1_box_extents, refprobe1_box_offset,
#endif
- refprobe1_exterior, refprobe1_intensity, refprobe1_ambient, roughness,
- ambient_light, specular_light, reflection_accum, ambient_accum);
-#endif // USE_REFLECTION_PROBE1
+#ifndef MODE_RENDER_DEPTH
+ vec3 f0 = F0(metallic, specular, albedo);
+ // Convert albedo to linear. Approximation from: http://chilliant.blogspot.com/2012/08/srgb-approximations-for-hlsl.html
+ albedo = albedo * (albedo * (albedo * 0.305306011 + 0.682171111) + 0.012522878);
+ vec3 specular_light = vec3(0.0, 0.0, 0.0);
+ vec3 diffuse_light = vec3(0.0, 0.0, 0.0);
+ vec3 ambient_light = vec3(0.0, 0.0, 0.0);
-#ifdef USE_REFLECTION_PROBE2
+#ifdef BASE_PASS
+ /////////////////////// LIGHTING //////////////////////////////
- reflection_process(reflection_probe2,
-#ifdef USE_VERTEX_LIGHTING
- refprobe2_reflection_normal_blend.rgb,
-#ifndef USE_LIGHTMAP
- refprobe2_ambient_normal,
-#endif
- refprobe2_reflection_normal_blend.a,
-#else
- normal_interp, vertex_interp, refprobe2_local_matrix,
- refprobe2_use_box_project, refprobe2_box_extents, refprobe2_box_offset,
-#endif
- refprobe2_exterior, refprobe2_intensity, refprobe2_ambient, roughness,
- ambient_light, specular_light, reflection_accum, ambient_accum);
+ // IBL precalculations
+ float ndotv = clamp(dot(normal, view), 0.0, 1.0);
+ vec3 F = f0 + (max(vec3(1.0 - roughness), f0) - f0) * pow(1.0 - ndotv, 5.0);
-#endif // USE_REFLECTION_PROBE2
+ // Calculate IBL
+ // Calculate Reflection probes
+ // Caclculate Lightmaps
- if (reflection_accum.a > 0.0) {
- specular_light = reflection_accum.rgb / reflection_accum.a;
- }
+ float specular_blob_intensity = 1.0;
-#ifndef USE_LIGHTMAP
- if (ambient_accum.a > 0.0) {
- ambient_light = ambient_accum.rgb / ambient_accum.a;
- }
+#if defined(SPECULAR_TOON)
+ specular_blob_intensity *= specular * 2.0;
#endif
-#endif // defined(USE_REFLECTION_PROBE1) || defined(USE_REFLECTION_PROBE2)
-
- // environment BRDF approximation
{
#if defined(DIFFUSE_TOON)
//simplify for toon, as
specular_light *= specular * metallic * albedo * 2.0;
#else
- // scales the specular reflections, needs to be computed before lighting happens,
- // but after environment and reflection probes are added
- //TODO: this curve is not really designed for gammaspace, should be adjusted
+ // scales the specular reflections, needs to be be computed before lighting happens,
+ // but after environment, GI, and reflection probes are added
+ // Environment brdf approximation (Lazarov 2013)
+ // see https://www.unrealengine.com/en-US/blog/physically-based-shading-on-mobile
const vec4 c0 = vec4(-1.0, -0.0275, -0.572, 0.022);
const vec4 c1 = vec4(1.0, 0.0425, 1.04, -0.04);
vec4 r = roughness * c0 + c1;
+ float ndotv = clamp(dot(normal, view), 0.0, 1.0);
+
float a004 = min(r.x * r.x, exp2(-9.28 * ndotv)) * r.x + r.y;
vec2 env = vec2(-1.04, 1.04) * a004 + r.zw;
- specular_light *= env.x * F + env.y;
-
-#endif
- }
-
-#ifdef USE_LIGHTMAP
- //ambient light will come entirely from lightmap is lightmap is used
- ambient_light = texture(lightmap, uv2_interp).rgb * lightmap_energy;
-#endif
-
-#ifdef USE_LIGHTMAP_CAPTURE
- {
- vec3 cone_dirs[12];
- cone_dirs[0] = vec3(0.0, 0.0, 1.0);
- cone_dirs[1] = vec3(0.866025, 0.0, 0.5);
- cone_dirs[2] = vec3(0.267617, 0.823639, 0.5);
- cone_dirs[3] = vec3(-0.700629, 0.509037, 0.5);
- cone_dirs[4] = vec3(-0.700629, -0.509037, 0.5);
- cone_dirs[5] = vec3(0.267617, -0.823639, 0.5);
- cone_dirs[6] = vec3(0.0, 0.0, -1.0);
- cone_dirs[7] = vec3(0.866025, 0.0, -0.5);
- cone_dirs[8] = vec3(0.267617, 0.823639, -0.5);
- cone_dirs[9] = vec3(-0.700629, 0.509037, -0.5);
- cone_dirs[10] = vec3(-0.700629, -0.509037, -0.5);
- cone_dirs[11] = vec3(0.267617, -0.823639, -0.5);
-
- vec3 local_normal = normalize(inv_view_matrix * vec4(normal, 0.0)).xyz;
- vec4 captured = vec4(0.0);
- float sum = 0.0;
- for (int i = 0; i < 12; i++) {
- float amount = max(0.0, dot(local_normal, cone_dirs[i])); //not correct, but creates a nice wrap around effect
- captured += lightmap_captures[i] * amount;
- sum += amount;
- }
-
- captured /= sum;
-
- if (lightmap_capture_sky) {
- ambient_light = mix(ambient_light, captured.rgb, captured.a);
- } else {
- ambient_light = captured.rgb;
- }
- }
-#endif
-
-#endif //BASE PASS
-
-//
-// Lighting
-//
-#ifdef USE_LIGHTING
-
-#ifndef USE_VERTEX_LIGHTING
- vec3 L;
-#endif
- vec3 light_att = vec3(1.0);
-
-#ifdef LIGHT_MODE_OMNI
-
-#ifndef USE_VERTEX_LIGHTING
- vec3 light_vec = light_position - vertex;
- float light_length = length(light_vec);
-
- float normalized_distance = light_length / light_range;
- if (normalized_distance < 1.0) {
- float omni_attenuation = pow(1.0 - normalized_distance, light_attenuation);
-
- light_att = vec3(omni_attenuation);
- } else {
- light_att = vec3(0.0);
- }
- L = normalize(light_vec);
-
+ specular_light *= env.x * f0 + env.y;
#endif
-
-#if !defined(SHADOWS_DISABLED)
-
-#ifdef USE_SHADOW
- {
- highp vec4 splane = shadow_coord;
- float shadow_len = length(splane.xyz);
-
- splane.xyz = normalize(splane.xyz);
-
- vec4 clamp_rect = light_clamp;
-
- if (splane.z >= 0.0) {
- splane.z += 1.0;
-
- clamp_rect.y += clamp_rect.w;
- } else {
- splane.z = 1.0 - splane.z;
- }
-
- splane.xy /= splane.z;
- splane.xy = splane.xy * 0.5 + 0.5;
- splane.z = shadow_len / light_range;
-
- splane.xy = clamp_rect.xy + splane.xy * clamp_rect.zw;
- splane.w = 1.0;
-
- float shadow = sample_shadow(light_shadow_atlas, splane);
-
- light_att *= mix(shadow_color.rgb, vec3(1.0), shadow);
}
-#endif
-
-#endif //SHADOWS_DISABLED
-
-#endif //type omni
-
-#ifdef LIGHT_MODE_DIRECTIONAL
-#ifndef USE_VERTEX_LIGHTING
- vec3 light_vec = -light_direction;
- L = normalize(light_vec);
-#endif
- float depth_z = -vertex.z;
-
-#if !defined(SHADOWS_DISABLED)
+#endif // BASE_PASS
-#ifdef USE_SHADOW
+ //this saves some VGPRs
+ uint orms = packUnorm4x8(vec4(ao, roughness, metallic, specular));
-#ifdef USE_VERTEX_LIGHTING
- //compute shadows in a mobile friendly way
+#ifdef USE_LIGHT_DIRECTIONAL
-#ifdef LIGHT_USE_PSSM4
- //take advantage of prefetch
- float shadow1 = sample_shadow(light_directional_shadow, shadow_coord);
- float shadow2 = sample_shadow(light_directional_shadow, shadow_coord2);
- float shadow3 = sample_shadow(light_directional_shadow, shadow_coord3);
- float shadow4 = sample_shadow(light_directional_shadow, shadow_coord4);
+ float size_A = directional_lights[i].size;
- if (depth_z < light_split_offsets.w) {
- float pssm_fade = 0.0;
- float shadow_att = 1.0;
-#ifdef LIGHT_USE_PSSM_BLEND
- float shadow_att2 = 1.0;
- float pssm_blend = 0.0;
- bool use_blend = true;
+ light_compute(normal, directional_lights[i].direction, normalize(view), size_A, directional_lights[i].color * directional_lights[i].energy, shadow, f0, orms, 1.0, albedo, alpha,
+#ifdef LIGHT_BACKLIGHT_USED
+ backlight,
#endif
- if (depth_z < light_split_offsets.y) {
- if (depth_z < light_split_offsets.x) {
- shadow_att = shadow1;
-
-#ifdef LIGHT_USE_PSSM_BLEND
- shadow_att2 = shadow2;
-
- pssm_blend = smoothstep(0.0, light_split_offsets.x, depth_z);
+#ifdef LIGHT_RIM_USED
+ rim, rim_tint,
#endif
- } else {
- shadow_att = shadow2;
-
-#ifdef LIGHT_USE_PSSM_BLEND
- shadow_att2 = shadow3;
-
- pssm_blend = smoothstep(light_split_offsets.x, light_split_offsets.y, depth_z);
-#endif
- }
- } else {
- if (depth_z < light_split_offsets.z) {
- shadow_att = shadow3;
-
-#if defined(LIGHT_USE_PSSM_BLEND)
- shadow_att2 = shadow4;
- pssm_blend = smoothstep(light_split_offsets.y, light_split_offsets.z, depth_z);
+#ifdef LIGHT_CLEARCOAT_USED
+ clearcoat, clearcoat_roughness, normalize(normal_interp),
#endif
-
- } else {
- shadow_att = shadow4;
- pssm_fade = smoothstep(light_split_offsets.z, light_split_offsets.w, depth_z);
-
-#if defined(LIGHT_USE_PSSM_BLEND)
- use_blend = false;
-#endif
- }
- }
-#if defined(LIGHT_USE_PSSM_BLEND)
- if (use_blend) {
- shadow_att = mix(shadow_att, shadow_att2, pssm_blend);
- }
+#ifdef LIGHT_ANISOTROPY_USED
+ binormal,
+ tangent, anisotropy,
#endif
- light_att *= mix(shadow_color.rgb, vec3(1.0), shadow_att);
- }
-
-#endif //LIGHT_USE_PSSM4
-
-#ifdef LIGHT_USE_PSSM2
-
- //take advantage of prefetch
- float shadow1 = sample_shadow(light_directional_shadow, shadow_coord);
- float shadow2 = sample_shadow(light_directional_shadow, shadow_coord2);
+ diffuse_light,
+ specular_light);
- if (depth_z < light_split_offsets.y) {
- float shadow_att = 1.0;
- float pssm_fade = 0.0;
+#endif //#USE_LIGHT_DIRECTIONAL
-#ifdef LIGHT_USE_PSSM_BLEND
- float shadow_att2 = 1.0;
- float pssm_blend = 0.0;
- bool use_blend = true;
+#ifdef USE_LIGHT_POSITIONAL
+ float shadow = 0.0;
+ for (int i = 0; i < omni_light_count; i++) {
+ light_process_omni(omni_light_indices[i], vertex, view, normal, f0, orms, shadow, albedo, alpha,
+#ifdef LIGHT_BACKLIGHT_USED
+ backlight,
#endif
- if (depth_z < light_split_offsets.x) {
- float pssm_fade = 0.0;
- shadow_att = shadow1;
-
-#ifdef LIGHT_USE_PSSM_BLEND
- shadow_att2 = shadow2;
- pssm_blend = smoothstep(0.0, light_split_offsets.x, depth_z);
+#ifdef LIGHT_RIM_USED
+ rim,
+ rim_tint,
#endif
- } else {
- shadow_att = shadow2;
- pssm_fade = smoothstep(light_split_offsets.x, light_split_offsets.y, depth_z);
-#ifdef LIGHT_USE_PSSM_BLEND
- use_blend = false;
+#ifdef LIGHT_CLEARCOAT_USED
+ clearcoat, clearcoat_roughness, normalize(normal_interp),
#endif
- }
-#ifdef LIGHT_USE_PSSM_BLEND
- if (use_blend) {
- shadow_att = mix(shadow_att, shadow_att2, pssm_blend);
- }
+#ifdef LIGHT_ANISOTROPY_USED
+ tangent, binormal, anisotropy,
#endif
- light_att *= mix(shadow_color.rgb, vec3(1.0), shadow_att);
+ diffuse_light, specular_light);
}
-#endif //LIGHT_USE_PSSM2
-
-#if !defined(LIGHT_USE_PSSM4) && !defined(LIGHT_USE_PSSM2)
-
- light_att *= mix(shadow_color.rgb, vec3(1.0), sample_shadow(light_directional_shadow, shadow_coord));
-#endif //orthogonal
-
-#else //fragment version of pssm
-
- {
-#ifdef LIGHT_USE_PSSM4
- if (depth_z < light_split_offsets.w) {
-#elif defined(LIGHT_USE_PSSM2)
- if (depth_z < light_split_offsets.y) {
-#else
- if (depth_z < light_split_offsets.x) {
-#endif //pssm2
-
- highp vec4 pssm_coord;
- float pssm_fade = 0.0;
-
-#ifdef LIGHT_USE_PSSM_BLEND
- float pssm_blend;
- highp vec4 pssm_coord2;
- bool use_blend = true;
-#endif
-
-#ifdef LIGHT_USE_PSSM4
-
- if (depth_z < light_split_offsets.y) {
- if (depth_z < light_split_offsets.x) {
- pssm_coord = shadow_coord;
-
-#ifdef LIGHT_USE_PSSM_BLEND
- pssm_coord2 = shadow_coord2;
-
- pssm_blend = smoothstep(0.0, light_split_offsets.x, depth_z);
-#endif
- } else {
- pssm_coord = shadow_coord2;
-
-#ifdef LIGHT_USE_PSSM_BLEND
- pssm_coord2 = shadow_coord3;
-
- pssm_blend = smoothstep(light_split_offsets.x, light_split_offsets.y, depth_z);
-#endif
- }
- } else {
- if (depth_z < light_split_offsets.z) {
- pssm_coord = shadow_coord3;
-
-#if defined(LIGHT_USE_PSSM_BLEND)
- pssm_coord2 = shadow_coord4;
- pssm_blend = smoothstep(light_split_offsets.y, light_split_offsets.z, depth_z);
-#endif
-
- } else {
- pssm_coord = shadow_coord4;
- pssm_fade = smoothstep(light_split_offsets.z, light_split_offsets.w, depth_z);
-
-#if defined(LIGHT_USE_PSSM_BLEND)
- use_blend = false;
-#endif
- }
- }
-
-#endif // LIGHT_USE_PSSM4
-
-#ifdef LIGHT_USE_PSSM2
- if (depth_z < light_split_offsets.x) {
- pssm_coord = shadow_coord;
-
-#ifdef LIGHT_USE_PSSM_BLEND
- pssm_coord2 = shadow_coord2;
- pssm_blend = smoothstep(0.0, light_split_offsets.x, depth_z);
+ for (int i = 0; i < spot_light_count; i++) {
+ light_process_spot(spot_light_indices[i], vertex, view, normal, f0, orms, shadow, albedo, alpha,
+#ifdef LIGHT_BACKLIGHT_USED
+ backlight,
#endif
- } else {
- pssm_coord = shadow_coord2;
- pssm_fade = smoothstep(light_split_offsets.x, light_split_offsets.y, depth_z);
-#ifdef LIGHT_USE_PSSM_BLEND
- use_blend = false;
+#ifdef LIGHT_RIM_USED
+ rim,
+ rim_tint,
#endif
- }
-
-#endif // LIGHT_USE_PSSM2
-
-#if !defined(LIGHT_USE_PSSM4) && !defined(LIGHT_USE_PSSM2)
- {
- pssm_coord = shadow_coord;
- }
-#endif
-
- float shadow = sample_shadow(light_directional_shadow, pssm_coord);
-
-#ifdef LIGHT_USE_PSSM_BLEND
- if (use_blend) {
- shadow = mix(shadow, sample_shadow(light_directional_shadow, pssm_coord2), pssm_blend);
- }
+#ifdef LIGHT_CLEARCOAT_USED
+ clearcoat, clearcoat_roughness, normalize(normal_interp),
#endif
-
- light_att *= mix(shadow_color.rgb, vec3(1.0), shadow);
- }
- }
-#endif //use vertex lighting
-
-#endif //use shadow
-
-#endif // SHADOWS_DISABLED
-
-#endif
-
-#ifdef LIGHT_MODE_SPOT
-
- light_att = vec3(1.0);
-
-#ifndef USE_VERTEX_LIGHTING
-
- vec3 light_rel_vec = light_position - vertex;
- float light_length = length(light_rel_vec);
- float normalized_distance = light_length / light_range;
-
- if (normalized_distance < 1.0) {
- float spot_attenuation = pow(1.0 - normalized_distance, light_attenuation);
- vec3 spot_dir = light_direction;
-
- float spot_cutoff = light_spot_angle;
- float angle = dot(-normalize(light_rel_vec), spot_dir);
-
- if (angle > spot_cutoff) {
- float scos = max(angle, spot_cutoff);
- float spot_rim = max(0.0001, (1.0 - scos) / (1.0 - spot_cutoff));
- spot_attenuation *= 1.0 - pow(spot_rim, light_spot_attenuation);
-
- light_att = vec3(spot_attenuation);
- } else {
- light_att = vec3(0.0);
- }
- } else {
- light_att = vec3(0.0);
- }
-
- L = normalize(light_rel_vec);
-
+#ifdef LIGHT_ANISOTROPY_USED
+ tangent,
+ binormal, anisotropy,
#endif
-
-#if !defined(SHADOWS_DISABLED)
-
-#ifdef USE_SHADOW
- {
- highp vec4 splane = shadow_coord;
-
- float shadow = sample_shadow(light_shadow_atlas, splane);
- light_att *= mix(shadow_color.rgb, vec3(1.0), shadow);
+ diffuse_light, specular_light);
}
-#endif
-
-#endif // SHADOWS_DISABLED
-
-#endif // LIGHT_MODE_SPOT
-
-#ifdef USE_VERTEX_LIGHTING
- //vertex lighting
-
- specular_light += specular_interp * specular_blob_intensity * light_att;
- diffuse_light += diffuse_interp * albedo * light_att;
-
-#else
- //fragment lighting
- light_compute(
- normal,
- L,
- eye_position,
- binormal,
- tangent,
- light_color.xyz,
- light_att,
- albedo,
- transmission,
- specular_blob_intensity * light_specular,
- roughness,
- metallic,
- specular,
- rim,
- rim_tint,
- clearcoat,
- clearcoat_roughness,
- anisotropy,
- diffuse_light,
- specular_light,
- alpha);
-
-#endif //vertex lighting
-#endif //USE_LIGHTING
- //compute and merge
-
-#ifdef USE_SHADOW_TO_OPACITY
+#endif // USE_LIGHT_POSITIONAL
+#endif //!MODE_RENDER_DEPTH
+#if defined(USE_SHADOW_TO_OPACITY)
alpha = min(alpha, clamp(length(ambient_light), 0.0, 1.0));
#if defined(ALPHA_SCISSOR_USED)
@@ -2049,105 +1106,50 @@ FRAGMENT_SHADER_CODE
}
#endif // ALPHA_SCISSOR_USED
-#ifdef USE_DEPTH_PREPASS
- if (alpha < 0.1) {
+#ifdef USE_OPAQUE_PREPASS
+#if !defined(ALPHA_SCISSOR_USED)
+
+ if (alpha < opaque_prepass_threshold) {
discard;
}
-#endif // USE_DEPTH_PREPASS
-#endif // !USE_SHADOW_TO_OPACITY
+#endif // not ALPHA_SCISSOR_USED
+#endif // USE_OPAQUE_PREPASS
-#ifndef RENDER_DEPTH
+#endif // USE_SHADOW_TO_OPACITY
-#ifdef SHADELESS
+#ifdef MODE_RENDER_DEPTH
+//nothing happens, so a tree-ssa optimizer will result in no fragment shader :)
+#else // !MODE_RENDER_DEPTH
- frag_color = vec4(albedo, alpha);
-#else
-
- ambient_light *= albedo;
-
-#if defined(ENABLE_AO)
- ambient_light *= ao;
- ao_light_affect = mix(1.0, ao, ao_light_affect);
- specular_light *= ao_light_affect;
- diffuse_light *= ao_light_affect;
-#endif
+ specular_light *= scene_data.reflection_multiplier;
+ ambient_light *= albedo; //ambient must be multiplied by albedo at the end
+ // base color remapping
diffuse_light *= 1.0 - metallic;
ambient_light *= 1.0 - metallic;
+#ifdef MODE_UNSHADED
+ frag_color = vec4(albedo, alpha);
+#else
frag_color = vec4(ambient_light + diffuse_light + specular_light, alpha);
-
- //add emission if in base pass
#ifdef BASE_PASS
frag_color.rgb += emission;
#endif
- // frag_color = vec4(normal, 1.0);
-
-//apply fog
-#if defined(FOG_DEPTH_ENABLED) || defined(FOG_HEIGHT_ENABLED)
-
-#if defined(USE_VERTEX_LIGHTING)
-
-#if defined(BASE_PASS)
- frag_color.rgb = mix(frag_color.rgb, fog_interp.rgb, fog_interp.a);
-#else
- frag_color.rgb *= (1.0 - fog_interp.a);
-#endif // BASE_PASS
-
-#else //pixel based fog
- float fog_amount = 0.0;
-
-#ifdef LIGHT_MODE_DIRECTIONAL
-
- vec3 fog_color = mix(fog_color_base.rgb, fog_sun_color_amount.rgb, fog_sun_color_amount.a * pow(max(dot(eye_position, light_direction), 0.0), 8.0));
-#else
- vec3 fog_color = fog_color_base.rgb;
-#endif
-
-#ifdef FOG_DEPTH_ENABLED
+#endif //MODE_UNSHADED
- {
- float fog_z = smoothstep(fog_depth_begin, fog_max_distance, length(vertex));
+ // Tonemap before writing as we are writing to an sRGB framebuffer
+ frag_color.rgb *= exposure;
+ frag_color.rgb = apply_tonemapping(frag_color.rgb, white);
+ frag_color.rgb = linear_to_srgb(frag_color.rgb);
- fog_amount = pow(fog_z, fog_depth_curve) * fog_color_base.a;
-
- if (fog_transmit_enabled) {
- vec3 total_light = frag_color.rgb;
- float transmit = pow(fog_z, fog_transmit_curve);
- fog_color = mix(max(total_light, fog_color), fog_color, transmit);
- }
- }
+#ifdef USE_BCS
+ frag_color.rgb = apply_bcs(frag_color.rgb, bcs);
#endif
-#ifdef FOG_HEIGHT_ENABLED
- {
- float y = (inv_view_matrix * vec4(vertex, 1.0)).y;
- fog_amount = max(fog_amount, pow(smoothstep(fog_height_min, fog_height_max, y), fog_height_curve));
- }
+#ifdef USE_COLOR_CORRECTION
+ frag_color.rgb = apply_color_correction(frag_color.rgb, color_correction);
#endif
-#if defined(BASE_PASS)
- frag_color.rgb = mix(frag_color.rgb, fog_color, fog_amount);
-#else
- frag_color.rgb *= (1.0 - fog_amount);
-#endif // BASE_PASS
-
-#endif //use vertex lit
-
-#endif // defined(FOG_DEPTH_ENABLED) || defined(FOG_HEIGHT_ENABLED)
-
-#endif //unshaded
-
-#else // not RENDER_DEPTH
-//depth render
-#ifdef USE_RGBA_SHADOWS
-
- highp float depth = ((position_interp.z / position_interp.w) + 1.0) * 0.5 + 0.0; // bias
- highp vec4 comp = fract(depth * vec4(255.0 * 255.0 * 255.0, 255.0 * 255.0, 255.0, 1.0));
- comp -= comp.xxyz * vec4(0.0, 1.0 / 255.0, 1.0 / 255.0, 1.0 / 255.0);
- frag_color = comp;
-
-#endif
-#endif
+#endif //!MODE_RENDER_DEPTH
}
diff --git a/drivers/gles3/shaders/sky.glsl b/drivers/gles3/shaders/sky.glsl
index 0faa3eb70c..3a1bcd3b28 100644
--- a/drivers/gles3/shaders/sky.glsl
+++ b/drivers/gles3/shaders/sky.glsl
@@ -12,21 +12,12 @@ mode_cubemap_quarter_res = #define USE_CUBEMAP_PASS \n#define USE_QUARTER_RES_PA
#[vertex]
-#ifdef USE_GLES_OVER_GL
-#define lowp
-#define mediump
-#define highp
-#else
-precision highp float;
-precision highp int;
-#endif
-
out vec2 uv_interp;
/* clang-format on */
void main() {
// One big triangle to cover the whole screen
- vec2 base_arr[3] = vec2[](vec2(-1.0, -2.0), vec2(-1.0, 2.0), vec2(2.0, 2.0));
+ vec2 base_arr[3] = vec2[](vec2(-1.0, -1.0), vec2(3.0, -1.0), vec2(-1.0, 3.0));
uv_interp = base_arr[gl_VertexID];
gl_Position = vec4(uv_interp, 1.0, 1.0);
}
@@ -36,19 +27,7 @@ void main() {
#define M_PI 3.14159265359
-#ifdef USE_GLES_OVER_GL
-#define lowp
-#define mediump
-#define highp
-#else
-#if defined(USE_HIGHP_PRECISION)
-precision highp float;
-precision highp int;
-#else
-precision mediump float;
-precision mediump int;
-#endif
-#endif
+#include "tonemap_inc.glsl"
in vec2 uv_interp;
@@ -63,40 +42,36 @@ uniform sampler2D half_res; //texunit:-2
uniform sampler2D quarter_res; //texunit:-3
#endif
-layout(std140) uniform CanvasData { //ubo:0
- mat3 orientation;
- vec4 projection;
- vec4 position_multiplier;
- float time;
- float luminance_multiplier;
- float pad1;
- float pad2;
-};
-
layout(std140) uniform GlobalVariableData { //ubo:1
vec4 global_variables[MAX_GLOBAL_VARIABLES];
};
+layout(std140) uniform SceneData { //ubo:2
+ float pad1;
+ float pad2;
+};
+
struct DirectionalLightData {
vec4 direction_energy;
vec4 color_size;
bool enabled;
};
-layout(std140) uniform DirectionalLights { //ubo:2
+layout(std140) uniform DirectionalLights { //ubo:3
DirectionalLightData data[MAX_DIRECTIONAL_LIGHT_DATA_STRUCTS];
}
directional_lights;
+/* clang-format off */
+
#ifdef MATERIAL_UNIFORMS_USED
-layout(std140) uniform MaterialUniforms{
-//ubo:3
+layout(std140) uniform MaterialUniforms{ //ubo:4
#MATERIAL_UNIFORMS
-} material;
+};
#endif
-
+/* clang-format on */
#GLOBALS
#ifdef USE_CUBEMAP_PASS
@@ -117,6 +92,12 @@ layout(std140) uniform MaterialUniforms{
#define AT_QUARTER_RES_PASS false
#endif
+// mat4 is a waste of space, but we don't have an easy way to set a mat3 uniform for now
+uniform mat4 orientation;
+uniform vec4 projection;
+uniform vec3 position;
+uniform float time;
+
layout(location = 0) out vec4 frag_color;
void main() {
@@ -128,7 +109,7 @@ void main() {
cube_normal.z = -cube_normal.z;
cube_normal = normalize(cube_normal);
- vec2 uv = uv_interp * 0.5 + 0.5;
+ vec2 uv = gl_FragCoord.xy; // uv_interp * 0.5 + 0.5;
vec2 panorama_coords = vec2(atan(cube_normal.x, cube_normal.z), acos(cube_normal.y));
@@ -148,17 +129,17 @@ void main() {
vec3 inverted_cube_normal = cube_normal;
inverted_cube_normal.z *= -1.0;
#ifdef USES_HALF_RES_COLOR
- half_res_color = texture(samplerCube(half_res, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), inverted_cube_normal) * luminance_multiplier;
+ half_res_color = texture(samplerCube(half_res, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), inverted_cube_normal);
#endif
#ifdef USES_QUARTER_RES_COLOR
- quarter_res_color = texture(samplerCube(quarter_res, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), inverted_cube_normal) * luminance_multiplier;
+ quarter_res_color = texture(samplerCube(quarter_res, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), inverted_cube_normal);
#endif
#else
#ifdef USES_HALF_RES_COLOR
- half_res_color = textureLod(sampler2D(half_res, material_samplers[SAMPLER_LINEAR_CLAMP]), uv, 0.0) * luminance_multiplier;
+ half_res_color = textureLod(sampler2D(half_res, material_samplers[SAMPLER_LINEAR_CLAMP]), uv, 0.0);
#endif
#ifdef USES_QUARTER_RES_COLOR
- quarter_res_color = textureLod(sampler2D(quarter_res, material_samplers[SAMPLER_LINEAR_CLAMP]), uv, 0.0) * luminance_multiplier;
+ quarter_res_color = textureLod(sampler2D(quarter_res, material_samplers[SAMPLER_LINEAR_CLAMP]), uv, 0.0);
#endif
#endif
@@ -168,12 +149,19 @@ void main() {
}
- frag_color.rgb = color * position_multiplier.w / luminance_multiplier;
- frag_color.a = alpha;
+ // Tonemap before writing as we are writing to an sRGB framebuffer
+ color *= exposure;
+ color = apply_tonemapping(color, white);
+ color = linear_to_srgb(color);
- // Blending is disabled for Sky, so alpha doesn't blend
- // alpha is used for subsurface scattering so make sure it doesn't get applied to Sky
- if (!AT_CUBEMAP_PASS && !AT_HALF_RES_PASS && !AT_QUARTER_RES_PASS) {
- frag_color.a = 0.0;
- }
+#ifdef USE_BCS
+ color = apply_bcs(color, bcs);
+#endif
+
+#ifdef USE_COLOR_CORRECTION
+ color = apply_color_correction(color, color_correction);
+#endif
+
+ frag_color.rgb = color;
+ frag_color.a = alpha;
}
diff --git a/drivers/gles3/shaders/stdlib_inc.glsl b/drivers/gles3/shaders/stdlib_inc.glsl
index 2eddf9d479..6cce6c12bd 100644
--- a/drivers/gles3/shaders/stdlib_inc.glsl
+++ b/drivers/gles3/shaders/stdlib_inc.glsl
@@ -1,5 +1,6 @@
-//TODO: only needed by GLES_OVER_GL
+#ifdef USE_GLES_OVER_GL
+// Floating point pack/unpack functions are part of the GLSL ES 300 specification used by web and mobile.
uint float2half(uint f) {
return ((f >> uint(16)) & uint(0x8000)) |
((((f & uint(0x7f800000)) - uint(0x38000000)) >> uint(13)) & uint(0x7c00)) |
@@ -37,6 +38,7 @@ vec2 unpackSnorm2x16(uint p) {
vec2 v = vec2(float(p & uint(0xffff)), float(p >> uint(16)));
return clamp((v - 32767.0) * vec2(0.00003051851), vec2(-1.0), vec2(1.0));
}
+#endif
uint packUnorm4x8(vec4 v) {
uvec4 uv = uvec4(round(clamp(v, vec4(0.0), vec4(1.0)) * 255.0));
diff --git a/drivers/gles3/shaders/tonemap_inc.glsl b/drivers/gles3/shaders/tonemap_inc.glsl
new file mode 100644
index 0000000000..ea15c05359
--- /dev/null
+++ b/drivers/gles3/shaders/tonemap_inc.glsl
@@ -0,0 +1,119 @@
+#ifdef USE_BCS
+uniform vec3 bcs;
+#endif
+
+#ifdef USE_COLOR_CORRECTION
+#ifdef USE_1D_LUT
+uniform sampler2D source_color_correction; //texunit:-1
+#else
+uniform sampler3D source_color_correction; //texunit:-1
+#endif
+#endif
+
+layout(std140) uniform TonemapData { //ubo:0
+ float exposure;
+ float white;
+ int tonemapper;
+ int pad;
+};
+
+vec3 apply_bcs(vec3 color, vec3 bcs) {
+ color = mix(vec3(0.0), color, bcs.x);
+ color = mix(vec3(0.5), color, bcs.y);
+ color = mix(vec3(dot(vec3(1.0), color) * 0.33333), color, bcs.z);
+
+ return color;
+}
+#ifdef USE_COLOR_CORRECTION
+#ifdef USE_1D_LUT
+vec3 apply_color_correction(vec3 color) {
+ color.r = texture(source_color_correction, vec2(color.r, 0.0f)).r;
+ color.g = texture(source_color_correction, vec2(color.g, 0.0f)).g;
+ color.b = texture(source_color_correction, vec2(color.b, 0.0f)).b;
+ return color;
+}
+#else
+vec3 apply_color_correction(vec3 color) {
+ return textureLod(source_color_correction, color, 0.0).rgb;
+}
+#endif
+#endif
+
+vec3 tonemap_filmic(vec3 color, float p_white) {
+ // exposure bias: input scale (color *= bias, white *= bias) to make the brightness consistent with other tonemappers
+ // also useful to scale the input to the range that the tonemapper is designed for (some require very high input values)
+ // has no effect on the curve's general shape or visual properties
+ const float exposure_bias = 2.0f;
+ const float A = 0.22f * exposure_bias * exposure_bias; // bias baked into constants for performance
+ const float B = 0.30f * exposure_bias;
+ const float C = 0.10f;
+ const float D = 0.20f;
+ const float E = 0.01f;
+ const float F = 0.30f;
+
+ vec3 color_tonemapped = ((color * (A * color + C * B) + D * E) / (color * (A * color + B) + D * F)) - E / F;
+ float p_white_tonemapped = ((p_white * (A * p_white + C * B) + D * E) / (p_white * (A * p_white + B) + D * F)) - E / F;
+
+ return color_tonemapped / p_white_tonemapped;
+}
+
+// Adapted from https://github.com/TheRealMJP/BakingLab/blob/master/BakingLab/ACES.hlsl
+// (MIT License).
+vec3 tonemap_aces(vec3 color, float p_white) {
+ const float exposure_bias = 1.8f;
+ const float A = 0.0245786f;
+ const float B = 0.000090537f;
+ const float C = 0.983729f;
+ const float D = 0.432951f;
+ const float E = 0.238081f;
+
+ // Exposure bias baked into transform to save shader instructions. Equivalent to `color *= exposure_bias`
+ const mat3 rgb_to_rrt = mat3(
+ vec3(0.59719f * exposure_bias, 0.35458f * exposure_bias, 0.04823f * exposure_bias),
+ vec3(0.07600f * exposure_bias, 0.90834f * exposure_bias, 0.01566f * exposure_bias),
+ vec3(0.02840f * exposure_bias, 0.13383f * exposure_bias, 0.83777f * exposure_bias));
+
+ const mat3 odt_to_rgb = mat3(
+ vec3(1.60475f, -0.53108f, -0.07367f),
+ vec3(-0.10208f, 1.10813f, -0.00605f),
+ vec3(-0.00327f, -0.07276f, 1.07602f));
+
+ color *= rgb_to_rrt;
+ vec3 color_tonemapped = (color * (color + A) - B) / (color * (C * color + D) + E);
+ color_tonemapped *= odt_to_rgb;
+
+ p_white *= exposure_bias;
+ float p_white_tonemapped = (p_white * (p_white + A) - B) / (p_white * (C * p_white + D) + E);
+
+ return color_tonemapped / p_white_tonemapped;
+}
+
+vec3 tonemap_reinhard(vec3 color, float p_white) {
+ return (p_white * color + color) / (color * p_white + p_white);
+}
+
+vec3 linear_to_srgb(vec3 color) {
+ //if going to srgb, clamp from 0 to 1.
+ color = clamp(color, vec3(0.0), vec3(1.0));
+ const vec3 a = vec3(0.055f);
+ return mix((vec3(1.0f) + a) * pow(color.rgb, vec3(1.0f / 2.4f)) - a, 12.92f * color.rgb, lessThan(color.rgb, vec3(0.0031308f)));
+}
+
+#define TONEMAPPER_LINEAR 0
+#define TONEMAPPER_REINHARD 1
+#define TONEMAPPER_FILMIC 2
+#define TONEMAPPER_ACES 3
+
+vec3 apply_tonemapping(vec3 color, float p_white) { // inputs are LINEAR, always outputs clamped [0;1] color
+ // Ensure color values passed to tonemappers are positive.
+ // They can be negative in the case of negative lights, which leads to undesired behavior.
+ if (tonemapper == TONEMAPPER_LINEAR) {
+ return color;
+ } else if (tonemapper == TONEMAPPER_REINHARD) {
+ return tonemap_reinhard(max(vec3(0.0f), color), p_white);
+ } else if (tonemapper == TONEMAPPER_FILMIC) {
+ return tonemap_filmic(max(vec3(0.0f), color), p_white);
+ } else { // TONEMAPPER_ACES
+ return tonemap_aces(max(vec3(0.0f), color), p_white);
+ }
+}
diff --git a/drivers/gles3/storage/config.cpp b/drivers/gles3/storage/config.cpp
index 96f98a5ef2..49a2a79cb2 100644
--- a/drivers/gles3/storage/config.cpp
+++ b/drivers/gles3/storage/config.cpp
@@ -120,16 +120,6 @@ Config::Config() {
support_write_depth = extensions.has("GL_EXT_frag_depth");
#endif
- support_half_float_vertices = true;
-//every platform should support this except web, iOS has issues with their support, so add option to disable
-#ifdef JAVASCRIPT_ENABLED
- support_half_float_vertices = false;
-#endif
- bool disable_half_float = false; //GLOBAL_GET("rendering/opengl/compatibility/disable_half_float");
- if (disable_half_float) {
- support_half_float_vertices = false;
- }
-
//picky requirements for these
support_shadow_cubemaps = support_write_depth && support_depth_cubemaps;
// the use skeleton software path should be used if either float texture is not supported,
@@ -149,6 +139,27 @@ Config::Config() {
force_vertex_shading = false; //GLOBAL_GET("rendering/quality/shading/force_vertex_shading");
use_nearest_mip_filter = GLOBAL_GET("rendering/textures/default_filters/use_nearest_mipmap_filter");
+
+ use_depth_prepass = bool(GLOBAL_GET("rendering/driver/depth_prepass/enable"));
+ if (use_depth_prepass) {
+ String vendors = GLOBAL_GET("rendering/driver/depth_prepass/disable_for_vendors");
+ Vector<String> vendor_match = vendors.split(",");
+ String renderer = (const char *)glGetString(GL_RENDERER);
+ for (int i = 0; i < vendor_match.size(); i++) {
+ String v = vendor_match[i].strip_edges();
+ if (v == String()) {
+ continue;
+ }
+
+ if (renderer.findn(v) != -1) {
+ use_depth_prepass = false;
+ }
+ }
+ }
+
+ max_renderable_elements = GLOBAL_GET("rendering/limits/opengl/max_renderable_elements");
+ max_renderable_lights = GLOBAL_GET("rendering/limits/opengl/max_renderable_lights");
+ max_lights_per_object = GLOBAL_GET("rendering/limits/opengl/max_lights_per_object");
}
Config::~Config() {
diff --git a/drivers/gles3/storage/config.h b/drivers/gles3/storage/config.h
index 0646881b72..7e143c1c1e 100644
--- a/drivers/gles3/storage/config.h
+++ b/drivers/gles3/storage/config.h
@@ -58,6 +58,9 @@ public:
int max_texture_image_units = 0;
int max_texture_size = 0;
int max_uniform_buffer_size = 0;
+ int max_renderable_elements = 0;
+ int max_renderable_lights = 0;
+ int max_lights_per_object = 0;
// TODO implement wireframe in OpenGL
// bool generate_wireframes;
@@ -82,7 +85,6 @@ public:
bool support_32_bits_indices = false;
bool support_write_depth = false;
- bool support_half_float_vertices = false;
bool support_npot_repeat_mipmap = false;
bool support_depth_cubemaps = false;
bool support_shadow_cubemaps = false;
@@ -97,6 +99,8 @@ public:
// so the user can switch orphaning off for them.
bool should_orphan = true;
+ bool use_depth_prepass = true;
+
static Config *get_singleton() { return singleton; };
Config();
diff --git a/drivers/gles3/storage/light_storage.cpp b/drivers/gles3/storage/light_storage.cpp
index 7395611d71..954aa11c0d 100644
--- a/drivers/gles3/storage/light_storage.cpp
+++ b/drivers/gles3/storage/light_storage.cpp
@@ -32,6 +32,7 @@
#include "light_storage.h"
#include "config.h"
+#include "texture_storage.h"
using namespace GLES3;
@@ -51,122 +52,284 @@ LightStorage::~LightStorage() {
/* Light API */
+void LightStorage::_light_initialize(RID p_light, RS::LightType p_type) {
+ Light light;
+ light.type = p_type;
+
+ light.param[RS::LIGHT_PARAM_ENERGY] = 1.0;
+ light.param[RS::LIGHT_PARAM_INDIRECT_ENERGY] = 1.0;
+ light.param[RS::LIGHT_PARAM_SPECULAR] = 0.5;
+ light.param[RS::LIGHT_PARAM_RANGE] = 1.0;
+ light.param[RS::LIGHT_PARAM_SIZE] = 0.0;
+ light.param[RS::LIGHT_PARAM_ATTENUATION] = 1.0;
+ light.param[RS::LIGHT_PARAM_SPOT_ANGLE] = 45;
+ light.param[RS::LIGHT_PARAM_SPOT_ATTENUATION] = 1.0;
+ light.param[RS::LIGHT_PARAM_SHADOW_MAX_DISTANCE] = 0;
+ light.param[RS::LIGHT_PARAM_SHADOW_SPLIT_1_OFFSET] = 0.1;
+ light.param[RS::LIGHT_PARAM_SHADOW_SPLIT_2_OFFSET] = 0.3;
+ light.param[RS::LIGHT_PARAM_SHADOW_SPLIT_3_OFFSET] = 0.6;
+ light.param[RS::LIGHT_PARAM_SHADOW_FADE_START] = 0.8;
+ light.param[RS::LIGHT_PARAM_SHADOW_NORMAL_BIAS] = 1.0;
+ light.param[RS::LIGHT_PARAM_SHADOW_BIAS] = 0.02;
+ light.param[RS::LIGHT_PARAM_SHADOW_BLUR] = 0;
+ light.param[RS::LIGHT_PARAM_SHADOW_PANCAKE_SIZE] = 20.0;
+ light.param[RS::LIGHT_PARAM_SHADOW_VOLUMETRIC_FOG_FADE] = 0.1;
+ light.param[RS::LIGHT_PARAM_TRANSMITTANCE_BIAS] = 0.05;
+
+ light_owner.initialize_rid(p_light, light);
+}
+
RID LightStorage::directional_light_allocate() {
- return RID();
+ return light_owner.allocate_rid();
}
void LightStorage::directional_light_initialize(RID p_rid) {
+ _light_initialize(p_rid, RS::LIGHT_DIRECTIONAL);
}
RID LightStorage::omni_light_allocate() {
- return RID();
+ return light_owner.allocate_rid();
}
void LightStorage::omni_light_initialize(RID p_rid) {
+ _light_initialize(p_rid, RS::LIGHT_OMNI);
}
RID LightStorage::spot_light_allocate() {
- return RID();
+ return light_owner.allocate_rid();
}
void LightStorage::spot_light_initialize(RID p_rid) {
+ _light_initialize(p_rid, RS::LIGHT_SPOT);
}
void LightStorage::light_free(RID p_rid) {
+ light_set_projector(p_rid, RID()); //clear projector
+
+ // delete the texture
+ Light *light = light_owner.get_or_null(p_rid);
+ light->dependency.deleted_notify(p_rid);
+ light_owner.free(p_rid);
}
void LightStorage::light_set_color(RID p_light, const Color &p_color) {
+ Light *light = light_owner.get_or_null(p_light);
+ ERR_FAIL_COND(!light);
+
+ light->color = p_color;
}
void LightStorage::light_set_param(RID p_light, RS::LightParam p_param, float p_value) {
+ Light *light = light_owner.get_or_null(p_light);
+ ERR_FAIL_COND(!light);
+ ERR_FAIL_INDEX(p_param, RS::LIGHT_PARAM_MAX);
+
+ if (light->param[p_param] == p_value) {
+ return;
+ }
+
+ switch (p_param) {
+ case RS::LIGHT_PARAM_RANGE:
+ case RS::LIGHT_PARAM_SPOT_ANGLE:
+ case RS::LIGHT_PARAM_SHADOW_MAX_DISTANCE:
+ case RS::LIGHT_PARAM_SHADOW_SPLIT_1_OFFSET:
+ case RS::LIGHT_PARAM_SHADOW_SPLIT_2_OFFSET:
+ case RS::LIGHT_PARAM_SHADOW_SPLIT_3_OFFSET:
+ case RS::LIGHT_PARAM_SHADOW_NORMAL_BIAS:
+ case RS::LIGHT_PARAM_SHADOW_PANCAKE_SIZE:
+ case RS::LIGHT_PARAM_SHADOW_BIAS: {
+ light->version++;
+ light->dependency.changed_notify(RendererStorage::DEPENDENCY_CHANGED_LIGHT);
+ } break;
+ case RS::LIGHT_PARAM_SIZE: {
+ if ((light->param[p_param] > CMP_EPSILON) != (p_value > CMP_EPSILON)) {
+ //changing from no size to size and the opposite
+ light->dependency.changed_notify(RendererStorage::DEPENDENCY_CHANGED_LIGHT_SOFT_SHADOW_AND_PROJECTOR);
+ }
+ } break;
+ default: {
+ }
+ }
+
+ light->param[p_param] = p_value;
}
void LightStorage::light_set_shadow(RID p_light, bool p_enabled) {
+ Light *light = light_owner.get_or_null(p_light);
+ ERR_FAIL_COND(!light);
+ light->shadow = p_enabled;
+
+ light->version++;
+ light->dependency.changed_notify(RendererStorage::DEPENDENCY_CHANGED_LIGHT);
}
void LightStorage::light_set_projector(RID p_light, RID p_texture) {
+ GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton();
+ Light *light = light_owner.get_or_null(p_light);
+ ERR_FAIL_COND(!light);
+
+ if (light->projector == p_texture) {
+ return;
+ }
+
+ if (light->type != RS::LIGHT_DIRECTIONAL && light->projector.is_valid()) {
+ texture_storage->texture_remove_from_decal_atlas(light->projector, light->type == RS::LIGHT_OMNI);
+ }
+
+ light->projector = p_texture;
+
+ if (light->type != RS::LIGHT_DIRECTIONAL) {
+ if (light->projector.is_valid()) {
+ texture_storage->texture_add_to_decal_atlas(light->projector, light->type == RS::LIGHT_OMNI);
+ }
+ light->dependency.changed_notify(RendererStorage::DEPENDENCY_CHANGED_LIGHT_SOFT_SHADOW_AND_PROJECTOR);
+ }
}
void LightStorage::light_set_negative(RID p_light, bool p_enable) {
+ Light *light = light_owner.get_or_null(p_light);
+ ERR_FAIL_COND(!light);
+
+ light->negative = p_enable;
}
void LightStorage::light_set_cull_mask(RID p_light, uint32_t p_mask) {
+ Light *light = light_owner.get_or_null(p_light);
+ ERR_FAIL_COND(!light);
+
+ light->cull_mask = p_mask;
+
+ light->version++;
+ light->dependency.changed_notify(RendererStorage::DEPENDENCY_CHANGED_LIGHT);
}
void LightStorage::light_set_distance_fade(RID p_light, bool p_enabled, float p_begin, float p_shadow, float p_length) {
+ Light *light = light_owner.get_or_null(p_light);
+ ERR_FAIL_COND(!light);
+
+ light->distance_fade = p_enabled;
+ light->distance_fade_begin = p_begin;
+ light->distance_fade_shadow = p_shadow;
+ light->distance_fade_length = p_length;
}
void LightStorage::light_set_reverse_cull_face_mode(RID p_light, bool p_enabled) {
+ Light *light = light_owner.get_or_null(p_light);
+ ERR_FAIL_COND(!light);
+
+ light->reverse_cull = p_enabled;
+
+ light->version++;
+ light->dependency.changed_notify(RendererStorage::DEPENDENCY_CHANGED_LIGHT);
}
void LightStorage::light_set_bake_mode(RID p_light, RS::LightBakeMode p_bake_mode) {
-}
+ Light *light = light_owner.get_or_null(p_light);
+ ERR_FAIL_COND(!light);
+
+ light->bake_mode = p_bake_mode;
-void LightStorage::light_set_max_sdfgi_cascade(RID p_light, uint32_t p_cascade) {
+ light->version++;
+ light->dependency.changed_notify(RendererStorage::DEPENDENCY_CHANGED_LIGHT);
}
void LightStorage::light_omni_set_shadow_mode(RID p_light, RS::LightOmniShadowMode p_mode) {
-}
+ Light *light = light_owner.get_or_null(p_light);
+ ERR_FAIL_COND(!light);
-void LightStorage::light_directional_set_shadow_mode(RID p_light, RS::LightDirectionalShadowMode p_mode) {
-}
+ light->omni_shadow_mode = p_mode;
-void LightStorage::light_directional_set_blend_splits(RID p_light, bool p_enable) {
+ light->version++;
+ light->dependency.changed_notify(RendererStorage::DEPENDENCY_CHANGED_LIGHT);
}
-bool LightStorage::light_directional_get_blend_splits(RID p_light) const {
- return false;
-}
+RS::LightOmniShadowMode LightStorage::light_omni_get_shadow_mode(RID p_light) {
+ const Light *light = light_owner.get_or_null(p_light);
+ ERR_FAIL_COND_V(!light, RS::LIGHT_OMNI_SHADOW_CUBE);
-void LightStorage::light_directional_set_sky_mode(RID p_light, RS::LightDirectionalSkyMode p_mode) {
+ return light->omni_shadow_mode;
}
-RS::LightDirectionalSkyMode LightStorage::light_directional_get_sky_mode(RID p_light) const {
- return RS::LIGHT_DIRECTIONAL_SKY_MODE_LIGHT_AND_SKY;
-}
+void LightStorage::light_directional_set_shadow_mode(RID p_light, RS::LightDirectionalShadowMode p_mode) {
+ Light *light = light_owner.get_or_null(p_light);
+ ERR_FAIL_COND(!light);
-RS::LightDirectionalShadowMode LightStorage::light_directional_get_shadow_mode(RID p_light) {
- return RS::LIGHT_DIRECTIONAL_SHADOW_ORTHOGONAL;
+ light->directional_shadow_mode = p_mode;
+ light->version++;
+ light->dependency.changed_notify(RendererStorage::DEPENDENCY_CHANGED_LIGHT);
}
-RS::LightOmniShadowMode LightStorage::light_omni_get_shadow_mode(RID p_light) {
- return RS::LIGHT_OMNI_SHADOW_DUAL_PARABOLOID;
-}
+void LightStorage::light_directional_set_blend_splits(RID p_light, bool p_enable) {
+ Light *light = light_owner.get_or_null(p_light);
+ ERR_FAIL_COND(!light);
-bool LightStorage::light_has_shadow(RID p_light) const {
- return false;
+ light->directional_blend_splits = p_enable;
+ light->version++;
+ light->dependency.changed_notify(RendererStorage::DEPENDENCY_CHANGED_LIGHT);
}
-bool LightStorage::light_has_projector(RID p_light) const {
- return false;
-}
+bool LightStorage::light_directional_get_blend_splits(RID p_light) const {
+ const Light *light = light_owner.get_or_null(p_light);
+ ERR_FAIL_COND_V(!light, false);
-RS::LightType LightStorage::light_get_type(RID p_light) const {
- return RS::LIGHT_OMNI;
+ return light->directional_blend_splits;
}
-AABB LightStorage::light_get_aabb(RID p_light) const {
- return AABB();
+void LightStorage::light_directional_set_sky_mode(RID p_light, RS::LightDirectionalSkyMode p_mode) {
+ Light *light = light_owner.get_or_null(p_light);
+ ERR_FAIL_COND(!light);
+
+ light->directional_sky_mode = p_mode;
}
-float LightStorage::light_get_param(RID p_light, RS::LightParam p_param) {
- return 0.0;
+RS::LightDirectionalSkyMode LightStorage::light_directional_get_sky_mode(RID p_light) const {
+ const Light *light = light_owner.get_or_null(p_light);
+ ERR_FAIL_COND_V(!light, RS::LIGHT_DIRECTIONAL_SKY_MODE_LIGHT_AND_SKY);
+
+ return light->directional_sky_mode;
}
-Color LightStorage::light_get_color(RID p_light) {
- return Color();
+RS::LightDirectionalShadowMode LightStorage::light_directional_get_shadow_mode(RID p_light) {
+ const Light *light = light_owner.get_or_null(p_light);
+ ERR_FAIL_COND_V(!light, RS::LIGHT_DIRECTIONAL_SHADOW_ORTHOGONAL);
+
+ return light->directional_shadow_mode;
}
RS::LightBakeMode LightStorage::light_get_bake_mode(RID p_light) {
- return RS::LIGHT_BAKE_DISABLED;
-}
+ const Light *light = light_owner.get_or_null(p_light);
+ ERR_FAIL_COND_V(!light, RS::LIGHT_BAKE_DISABLED);
-uint32_t LightStorage::light_get_max_sdfgi_cascade(RID p_light) {
- return 0;
+ return light->bake_mode;
}
uint64_t LightStorage::light_get_version(RID p_light) const {
- return 0;
+ const Light *light = light_owner.get_or_null(p_light);
+ ERR_FAIL_COND_V(!light, 0);
+
+ return light->version;
+}
+
+AABB LightStorage::light_get_aabb(RID p_light) const {
+ const Light *light = light_owner.get_or_null(p_light);
+ ERR_FAIL_COND_V(!light, AABB());
+
+ switch (light->type) {
+ case RS::LIGHT_SPOT: {
+ float len = light->param[RS::LIGHT_PARAM_RANGE];
+ float size = Math::tan(Math::deg2rad(light->param[RS::LIGHT_PARAM_SPOT_ANGLE])) * len;
+ return AABB(Vector3(-size, -size, -len), Vector3(size * 2, size * 2, len));
+ };
+ case RS::LIGHT_OMNI: {
+ float r = light->param[RS::LIGHT_PARAM_RANGE];
+ return AABB(-Vector3(r, r, r), Vector3(r, r, r) * 2);
+ };
+ case RS::LIGHT_DIRECTIONAL: {
+ return AABB();
+ };
+ }
+
+ ERR_FAIL_V(AABB());
}
/* PROBE API */
diff --git a/drivers/gles3/storage/light_storage.h b/drivers/gles3/storage/light_storage.h
index 6f24e467bc..5acaf45aa3 100644
--- a/drivers/gles3/storage/light_storage.h
+++ b/drivers/gles3/storage/light_storage.h
@@ -40,12 +40,100 @@
#include "servers/rendering/renderer_storage.h"
#include "servers/rendering/storage/light_storage.h"
+#include "platform_config.h"
+#ifndef OPENGL_INCLUDE_H
+#include <GLES3/gl3.h>
+#else
+#include OPENGL_INCLUDE_H
+#endif
+
namespace GLES3 {
+/* LIGHT */
+
+struct Light {
+ RS::LightType type;
+ float param[RS::LIGHT_PARAM_MAX];
+ Color color = Color(1, 1, 1, 1);
+ RID projector;
+ bool shadow = false;
+ bool negative = false;
+ bool reverse_cull = false;
+ RS::LightBakeMode bake_mode = RS::LIGHT_BAKE_DYNAMIC;
+ uint32_t max_sdfgi_cascade = 2;
+ uint32_t cull_mask = 0xFFFFFFFF;
+ bool distance_fade = false;
+ real_t distance_fade_begin = 40.0;
+ real_t distance_fade_shadow = 50.0;
+ real_t distance_fade_length = 10.0;
+ RS::LightOmniShadowMode omni_shadow_mode = RS::LIGHT_OMNI_SHADOW_DUAL_PARABOLOID;
+ RS::LightDirectionalShadowMode directional_shadow_mode = RS::LIGHT_DIRECTIONAL_SHADOW_ORTHOGONAL;
+ bool directional_blend_splits = false;
+ RS::LightDirectionalSkyMode directional_sky_mode = RS::LIGHT_DIRECTIONAL_SKY_MODE_LIGHT_AND_SKY;
+ uint64_t version = 0;
+
+ RendererStorage::Dependency dependency;
+};
+
+/* REFLECTION PROBE */
+
+struct ReflectionProbe {
+ RS::ReflectionProbeUpdateMode update_mode = RS::REFLECTION_PROBE_UPDATE_ONCE;
+ int resolution = 256;
+ float intensity = 1.0;
+ RS::ReflectionProbeAmbientMode ambient_mode = RS::REFLECTION_PROBE_AMBIENT_ENVIRONMENT;
+ Color ambient_color;
+ float ambient_color_energy = 1.0;
+ float max_distance = 0;
+ Vector3 extents = Vector3(1, 1, 1);
+ Vector3 origin_offset;
+ bool interior = false;
+ bool box_projection = false;
+ bool enable_shadows = false;
+ uint32_t cull_mask = (1 << 20) - 1;
+ float mesh_lod_threshold = 0.01;
+
+ RendererStorage::Dependency dependency;
+};
+
+/* LIGHTMAP */
+
+struct Lightmap {
+ RID light_texture;
+ bool uses_spherical_harmonics = false;
+ bool interior = false;
+ AABB bounds = AABB(Vector3(), Vector3(1, 1, 1));
+ int32_t array_index = -1; //unassigned
+ PackedVector3Array points;
+ PackedColorArray point_sh;
+ PackedInt32Array tetrahedra;
+ PackedInt32Array bsp_tree;
+
+ struct BSP {
+ static const int32_t EMPTY_LEAF = INT32_MIN;
+ float plane[4];
+ int32_t over = EMPTY_LEAF, under = EMPTY_LEAF;
+ };
+
+ RendererStorage::Dependency dependency;
+};
+
class LightStorage : public RendererLightStorage {
private:
static LightStorage *singleton;
+ /* LIGHT */
+ mutable RID_Owner<Light, true> light_owner;
+
+ /* REFLECTION PROBE */
+ mutable RID_Owner<ReflectionProbe, true> reflection_probe_owner;
+
+ /* LIGHTMAP */
+
+ Vector<RID> lightmap_textures;
+
+ mutable RID_Owner<Lightmap, true> lightmap_owner;
+
public:
static LightStorage *get_singleton();
@@ -54,6 +142,11 @@ public:
/* Light API */
+ Light *get_light(RID p_rid) { return light_owner.get_or_null(p_rid); };
+ bool owns_light(RID p_rid) { return light_owner.owns(p_rid); };
+
+ void _light_initialize(RID p_rid, RS::LightType p_type);
+
virtual RID directional_light_allocate() override;
virtual void directional_light_initialize(RID p_rid) override;
virtual RID omni_light_allocate() override;
@@ -72,7 +165,7 @@ public:
virtual void light_set_distance_fade(RID p_light, bool p_enabled, float p_begin, float p_shadow, float p_length) override;
virtual void light_set_reverse_cull_face_mode(RID p_light, bool p_enabled) override;
virtual void light_set_bake_mode(RID p_light, RS::LightBakeMode p_bake_mode) override;
- virtual void light_set_max_sdfgi_cascade(RID p_light, uint32_t p_cascade) override;
+ virtual void light_set_max_sdfgi_cascade(RID p_light, uint32_t p_cascade) override {}
virtual void light_omni_set_shadow_mode(RID p_light, RS::LightOmniShadowMode p_mode) override;
@@ -84,16 +177,99 @@ public:
virtual RS::LightDirectionalShadowMode light_directional_get_shadow_mode(RID p_light) override;
virtual RS::LightOmniShadowMode light_omni_get_shadow_mode(RID p_light) override;
+ virtual RS::LightType light_get_type(RID p_light) const override {
+ const Light *light = light_owner.get_or_null(p_light);
+ ERR_FAIL_COND_V(!light, RS::LIGHT_DIRECTIONAL);
- virtual bool light_has_shadow(RID p_light) const override;
- virtual bool light_has_projector(RID p_light) const override;
-
- virtual RS::LightType light_get_type(RID p_light) const override;
+ return light->type;
+ }
virtual AABB light_get_aabb(RID p_light) const override;
- virtual float light_get_param(RID p_light, RS::LightParam p_param) override;
- virtual Color light_get_color(RID p_light) override;
+
+ virtual float light_get_param(RID p_light, RS::LightParam p_param) override {
+ const Light *light = light_owner.get_or_null(p_light);
+ ERR_FAIL_COND_V(!light, 0);
+
+ return light->param[p_param];
+ }
+
+ _FORCE_INLINE_ RID light_get_projector(RID p_light) {
+ const Light *light = light_owner.get_or_null(p_light);
+ ERR_FAIL_COND_V(!light, RID());
+
+ return light->projector;
+ }
+
+ virtual Color light_get_color(RID p_light) override {
+ const Light *light = light_owner.get_or_null(p_light);
+ ERR_FAIL_COND_V(!light, Color());
+
+ return light->color;
+ }
+
+ _FORCE_INLINE_ uint32_t light_get_cull_mask(RID p_light) {
+ const Light *light = light_owner.get_or_null(p_light);
+ ERR_FAIL_COND_V(!light, 0);
+
+ return light->cull_mask;
+ }
+
+ _FORCE_INLINE_ bool light_is_distance_fade_enabled(RID p_light) {
+ const Light *light = light_owner.get_or_null(p_light);
+ return light->distance_fade;
+ }
+
+ _FORCE_INLINE_ float light_get_distance_fade_begin(RID p_light) {
+ const Light *light = light_owner.get_or_null(p_light);
+ return light->distance_fade_begin;
+ }
+
+ _FORCE_INLINE_ float light_get_distance_fade_shadow(RID p_light) {
+ const Light *light = light_owner.get_or_null(p_light);
+ return light->distance_fade_shadow;
+ }
+
+ _FORCE_INLINE_ float light_get_distance_fade_length(RID p_light) {
+ const Light *light = light_owner.get_or_null(p_light);
+ return light->distance_fade_length;
+ }
+
+ virtual bool light_has_shadow(RID p_light) const override {
+ const Light *light = light_owner.get_or_null(p_light);
+ ERR_FAIL_COND_V(!light, RS::LIGHT_DIRECTIONAL);
+
+ return light->shadow;
+ }
+
+ virtual bool light_has_projector(RID p_light) const override {
+ const Light *light = light_owner.get_or_null(p_light);
+ ERR_FAIL_COND_V(!light, RS::LIGHT_DIRECTIONAL);
+
+ return light_owner.owns(light->projector);
+ }
+
+ _FORCE_INLINE_ bool light_is_negative(RID p_light) const {
+ const Light *light = light_owner.get_or_null(p_light);
+ ERR_FAIL_COND_V(!light, RS::LIGHT_DIRECTIONAL);
+
+ return light->negative;
+ }
+
+ _FORCE_INLINE_ float light_get_transmittance_bias(RID p_light) const {
+ const Light *light = light_owner.get_or_null(p_light);
+ ERR_FAIL_COND_V(!light, 0.0);
+
+ return light->param[RS::LIGHT_PARAM_TRANSMITTANCE_BIAS];
+ }
+
+ _FORCE_INLINE_ float light_get_shadow_volumetric_fog_fade(RID p_light) const {
+ const Light *light = light_owner.get_or_null(p_light);
+ ERR_FAIL_COND_V(!light, 0.0);
+
+ return light->param[RS::LIGHT_PARAM_SHADOW_VOLUMETRIC_FOG_FADE];
+ }
+
virtual RS::LightBakeMode light_get_bake_mode(RID p_light) override;
- virtual uint32_t light_get_max_sdfgi_cascade(RID p_light) override;
+ virtual uint32_t light_get_max_sdfgi_cascade(RID p_light) override { return 0; }
virtual uint64_t light_get_version(RID p_light) const override;
/* PROBE API */
diff --git a/drivers/gles3/storage/material_storage.cpp b/drivers/gles3/storage/material_storage.cpp
index bc9409425c..c1122ccc07 100644
--- a/drivers/gles3/storage/material_storage.cpp
+++ b/drivers/gles3/storage/material_storage.cpp
@@ -43,7 +43,7 @@ using namespace GLES3;
///////////////////////////////////////////////////////////////////////////
// UBI helper functions
-_FORCE_INLINE_ static void _fill_std140_variant_ubo_value(ShaderLanguage::DataType type, int p_array_size, const Variant &value, uint8_t *data, bool p_linear_color) {
+_FORCE_INLINE_ static void _fill_std140_variant_ubo_value(ShaderLanguage::DataType type, int p_array_size, const Variant &value, uint8_t *data) {
switch (type) {
case ShaderLanguage::TYPE_BOOL: {
uint32_t *gui = (uint32_t *)data;
@@ -399,9 +399,6 @@ _FORCE_INLINE_ static void _fill_std140_variant_ubo_value(ShaderLanguage::DataTy
for (int i = 0, j = 0; i < p_array_size; i++, j += 4) {
if (i < s) {
Color color = a[i];
- if (p_linear_color) {
- color = color.srgb_to_linear();
- }
gui[j] = color.r;
gui[j + 1] = color.g;
gui[j + 2] = color.b;
@@ -433,10 +430,6 @@ _FORCE_INLINE_ static void _fill_std140_variant_ubo_value(ShaderLanguage::DataTy
if (value.get_type() == Variant::COLOR) {
Color v = value;
- if (p_linear_color) {
- v = v.srgb_to_linear();
- }
-
gui[0] = v.r;
gui[1] = v.g;
gui[2] = v.b;
@@ -459,9 +452,6 @@ _FORCE_INLINE_ static void _fill_std140_variant_ubo_value(ShaderLanguage::DataTy
for (int i = 0, j = 0; i < p_array_size; i++, j += 4) {
if (i < s) {
Color color = a[i];
- if (p_linear_color) {
- color = color.srgb_to_linear();
- }
gui[j] = color.r;
gui[j + 1] = color.g;
gui[j + 2] = color.b;
@@ -496,10 +486,6 @@ _FORCE_INLINE_ static void _fill_std140_variant_ubo_value(ShaderLanguage::DataTy
if (value.get_type() == Variant::COLOR) {
Color v = value;
- if (p_linear_color) {
- v = v.srgb_to_linear();
- }
-
gui[0] = v.r;
gui[1] = v.g;
gui[2] = v.b;
@@ -900,6 +886,42 @@ _FORCE_INLINE_ static void _fill_std140_ubo_empty(ShaderLanguage::DataType type,
///////////////////////////////////////////////////////////////////////////
// MaterialData
+// Look up table to translate ShaderLanguage::DataType to GL_TEXTURE_*
+static const GLenum target_from_type[ShaderLanguage::TYPE_MAX] = {
+ GL_TEXTURE_2D, // TYPE_VOID,
+ GL_TEXTURE_2D, // TYPE_BOOL,
+ GL_TEXTURE_2D, // TYPE_BVEC2,
+ GL_TEXTURE_2D, // TYPE_BVEC3,
+ GL_TEXTURE_2D, // TYPE_BVEC4,
+ GL_TEXTURE_2D, // TYPE_INT,
+ GL_TEXTURE_2D, // TYPE_IVEC2,
+ GL_TEXTURE_2D, // TYPE_IVEC3,
+ GL_TEXTURE_2D, // TYPE_IVEC4,
+ GL_TEXTURE_2D, // TYPE_UINT,
+ GL_TEXTURE_2D, // TYPE_UVEC2,
+ GL_TEXTURE_2D, // TYPE_UVEC3,
+ GL_TEXTURE_2D, // TYPE_UVEC4,
+ GL_TEXTURE_2D, // TYPE_FLOAT,
+ GL_TEXTURE_2D, // TYPE_VEC2,
+ GL_TEXTURE_2D, // TYPE_VEC3,
+ GL_TEXTURE_2D, // TYPE_VEC4,
+ GL_TEXTURE_2D, // TYPE_MAT2,
+ GL_TEXTURE_2D, // TYPE_MAT3,
+ GL_TEXTURE_2D, // TYPE_MAT4,
+ GL_TEXTURE_2D, // TYPE_SAMPLER2D,
+ GL_TEXTURE_2D, // TYPE_ISAMPLER2D,
+ GL_TEXTURE_2D, // TYPE_USAMPLER2D,
+ GL_TEXTURE_2D_ARRAY, // TYPE_SAMPLER2DARRAY,
+ GL_TEXTURE_2D_ARRAY, // TYPE_ISAMPLER2DARRAY,
+ GL_TEXTURE_2D_ARRAY, // TYPE_USAMPLER2DARRAY,
+ GL_TEXTURE_3D, // TYPE_SAMPLER3D,
+ GL_TEXTURE_3D, // TYPE_ISAMPLER3D,
+ GL_TEXTURE_3D, // TYPE_USAMPLER3D,
+ GL_TEXTURE_CUBE_MAP, // TYPE_SAMPLERCUBE,
+ GL_TEXTURE_CUBE_MAP, // TYPE_SAMPLERCUBEARRAY,
+ GL_TEXTURE_2D, // TYPE_STRUCT
+};
+
void MaterialData::update_uniform_buffer(const Map<StringName, ShaderLanguage::ShaderNode::Uniform> &p_uniforms, const uint32_t *p_uniform_offsets, const Map<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;
@@ -951,7 +973,7 @@ void MaterialData::update_uniform_buffer(const Map<StringName, ShaderLanguage::S
if (V) {
//user provided
- _fill_std140_variant_ubo_value(E.value.type, E.value.array_size, V->get(), data, p_use_linear_color);
+ _fill_std140_variant_ubo_value(E.value.type, E.value.array_size, V->get(), data);
} else if (E.value.default_value.size()) {
//default value
@@ -961,7 +983,7 @@ void MaterialData::update_uniform_buffer(const Map<StringName, ShaderLanguage::S
//zero because it was not provided
if ((E.value.type == ShaderLanguage::TYPE_VEC3 || E.value.type == ShaderLanguage::TYPE_VEC4) && E.value.hint == ShaderLanguage::ShaderNode::Uniform::HINT_COLOR) {
//colors must be set as black, with alpha as 1.0
- _fill_std140_variant_ubo_value(E.value.type, E.value.array_size, Color(0, 0, 0, 1), data, p_use_linear_color);
+ _fill_std140_variant_ubo_value(E.value.type, E.value.array_size, Color(0, 0, 0, 1), data);
} else {
//else just zero it out
_fill_std140_ubo_empty(E.value.type, E.value.array_size, data);
@@ -1275,16 +1297,16 @@ MaterialStorage *MaterialStorage::get_singleton() {
MaterialStorage::MaterialStorage() {
singleton = this;
- shader_data_request_func[RS::SHADER_SPATIAL] = nullptr;
+ 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_SKY] = nullptr;
+ 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] = 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_SKY] = nullptr;
+ material_data_request_func[RS::SHADER_SKY] = _create_sky_material_func;
material_data_request_func[RS::SHADER_FOG] = nullptr;
static_assert(sizeof(GlobalVariables::Value) == 16);
@@ -1365,16 +1387,12 @@ MaterialStorage::MaterialStorage() {
actions.render_mode_defines["unshaded"] = "#define MODE_UNSHADED\n";
actions.render_mode_defines["light_only"] = "#define MODE_LIGHT_ONLY\n";
- actions.base_texture_binding_index = 1;
- actions.base_uniform_string = "";
- actions.global_buffer_array_variable = "";
-
shaders.compiler_canvas.initialize(actions);
}
{
// Setup Scene compiler
- /*
+
//shader compiler
ShaderCompiler::DefaultIdentifierActions actions;
@@ -1529,11 +1547,6 @@ MaterialStorage::MaterialStorage() {
actions.render_mode_defines["sss_mode_skin"] = "#define SSS_MODE_SKIN\n";
actions.render_mode_defines["specular_schlick_ggx"] = "#define SPECULAR_SCHLICK_GGX\n";
-
- actions.custom_samplers["SCREEN_TEXTURE"] = "material_samplers[3]"; // linear filter with mipmaps
- actions.custom_samplers["DEPTH_TEXTURE"] = "material_samplers[3]";
- actions.custom_samplers["NORMAL_ROUGHNESS_TEXTURE"] = "material_samplers[1]"; // linear filter
-
actions.render_mode_defines["specular_toon"] = "#define SPECULAR_TOON\n";
actions.render_mode_defines["specular_disabled"] = "#define SPECULAR_DISABLED\n";
actions.render_mode_defines["shadows_disabled"] = "#define SHADOWS_DISABLED\n";
@@ -1541,19 +1554,10 @@ MaterialStorage::MaterialStorage() {
actions.render_mode_defines["shadow_to_opacity"] = "#define USE_SHADOW_TO_OPACITY\n";
actions.render_mode_defines["unshaded"] = "#define MODE_UNSHADED\n";
- actions.sampler_array_name = "material_samplers";
- actions.base_texture_binding_index = 1;
- actions.texture_layout_set = RenderForwardClustered::MATERIAL_UNIFORM_SET;
- 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_variables.data";
- actions.instance_uniform_index_variable = "instances.data[instance_index].instance_uniforms_ofs";
- compiler.initialize(actions);
- */
+ shaders.compiler_scene.initialize(actions);
}
{
@@ -1626,10 +1630,10 @@ ShaderCompiler::DefaultIdentifierActions actions;
actions.renames["COLOR"] = "color";
actions.renames["ALPHA"] = "alpha";
actions.renames["EYEDIR"] = "cube_normal";
- actions.renames["POSITION"] = "params.position_multiplier.xyz";
+ actions.renames["POSITION"] = "position";
actions.renames["SKY_COORDS"] = "panorama_coords";
actions.renames["SCREEN_UV"] = "uv";
- actions.renames["TIME"] = "params.time";
+ actions.renames["TIME"] = "time";
actions.renames["PI"] = _MKSTR(Math_PI);
actions.renames["TAU"] = _MKSTR(Math_TAU);
actions.renames["E"] = _MKSTR(Math_E);
@@ -1660,20 +1664,12 @@ ShaderCompiler::DefaultIdentifierActions actions;
actions.renames["AT_CUBEMAP_PASS"] = "AT_CUBEMAP_PASS";
actions.renames["AT_HALF_RES_PASS"] = "AT_HALF_RES_PASS";
actions.renames["AT_QUARTER_RES_PASS"] = "AT_QUARTER_RES_PASS";
- actions.custom_samplers["RADIANCE"] = "material_samplers[3]";
actions.usage_defines["HALF_RES_COLOR"] = "\n#define USES_HALF_RES_COLOR\n";
actions.usage_defines["QUARTER_RES_COLOR"] = "\n#define USES_QUARTER_RES_COLOR\n";
actions.render_mode_defines["disable_fog"] = "#define DISABLE_FOG\n";
- actions.sampler_array_name = "material_samplers";
- actions.base_texture_binding_index = 1;
- actions.texture_layout_set = 1;
- 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_variables";
shaders.compiler_sky.initialize(actions);
}
@@ -1873,7 +1869,7 @@ void MaterialStorage::_global_variable_store_in_buffer(int32_t p_index, RS::Glob
bv.w = v.a;
GlobalVariables::Value &bv_linear = global_variables.buffer_values[p_index + 1];
- v = v.srgb_to_linear();
+ //v = v.srgb_to_linear();
bv_linear.x = v.r;
bv_linear.y = v.g;
bv_linear.z = v.b;
@@ -2077,10 +2073,9 @@ Vector<StringName> MaterialStorage::global_variable_get_list() const {
ERR_FAIL_V_MSG(Vector<StringName>(), "This function should never be used outside the editor, it can severely damage performance.");
}
- const StringName *K = nullptr;
Vector<StringName> names;
- while ((K = global_variables.variables.next(K))) {
- names.push_back(*K);
+ for (const KeyValue<StringName, GlobalVariables::Variable> &E : global_variables.variables) {
+ names.push_back(E.key);
}
names.sort_custom<StringName::AlphCompare>();
return names;
@@ -2312,7 +2307,7 @@ void MaterialStorage::global_variables_instance_update(RID p_instance, int p_ind
pos += p_index;
- _fill_std140_variant_ubo_value(datatype, 0, p_value, (uint8_t *)&global_variables.buffer_values[pos], true); //instances always use linear color in this renderer
+ _fill_std140_variant_ubo_value(datatype, 0, p_value, (uint8_t *)&global_variables.buffer_values[pos]);
_global_variable_mark_buffer_dirty(pos, 1);
}
@@ -2739,7 +2734,8 @@ void MaterialStorage::material_update_dependency(RID p_material, RendererStorage
}
}
-// Canvas Shader Data
+/* Canvas Shader Data */
+
void CanvasShaderData::set_code(const String &p_code) {
// compile the shader
@@ -2916,51 +2912,15 @@ void CanvasMaterialData::update_parameters(const Map<StringName, Variant> &p_par
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);
}
-// Look up table to translate ShaderLanguage::DataType to GL_TEXTURE_*
-static const GLenum target_from_type[ShaderLanguage::TYPE_MAX] = {
- GL_TEXTURE_2D, // TYPE_VOID,
- GL_TEXTURE_2D, // TYPE_BOOL,
- GL_TEXTURE_2D, // TYPE_BVEC2,
- GL_TEXTURE_2D, // TYPE_BVEC3,
- GL_TEXTURE_2D, // TYPE_BVEC4,
- GL_TEXTURE_2D, // TYPE_INT,
- GL_TEXTURE_2D, // TYPE_IVEC2,
- GL_TEXTURE_2D, // TYPE_IVEC3,
- GL_TEXTURE_2D, // TYPE_IVEC4,
- GL_TEXTURE_2D, // TYPE_UINT,
- GL_TEXTURE_2D, // TYPE_UVEC2,
- GL_TEXTURE_2D, // TYPE_UVEC3,
- GL_TEXTURE_2D, // TYPE_UVEC4,
- GL_TEXTURE_2D, // TYPE_FLOAT,
- GL_TEXTURE_2D, // TYPE_VEC2,
- GL_TEXTURE_2D, // TYPE_VEC3,
- GL_TEXTURE_2D, // TYPE_VEC4,
- GL_TEXTURE_2D, // TYPE_MAT2,
- GL_TEXTURE_2D, // TYPE_MAT3,
- GL_TEXTURE_2D, // TYPE_MAT4,
- GL_TEXTURE_2D, // TYPE_SAMPLER2D,
- GL_TEXTURE_2D, // TYPE_ISAMPLER2D,
- GL_TEXTURE_2D, // TYPE_USAMPLER2D,
- GL_TEXTURE_2D_ARRAY, // TYPE_SAMPLER2DARRAY,
- GL_TEXTURE_2D_ARRAY, // TYPE_ISAMPLER2DARRAY,
- GL_TEXTURE_2D_ARRAY, // TYPE_USAMPLER2DARRAY,
- GL_TEXTURE_3D, // TYPE_SAMPLER3D,
- GL_TEXTURE_3D, // TYPE_ISAMPLER3D,
- GL_TEXTURE_3D, // TYPE_USAMPLER3D,
- GL_TEXTURE_CUBE_MAP, // TYPE_SAMPLERCUBE,
- GL_TEXTURE_CUBE_MAP, // TYPE_SAMPLERCUBEARRAY,
- GL_TEXTURE_2D, // TYPE_STRUCT
-};
-
void CanvasMaterialData::bind_uniforms() {
// Bind Material Uniforms
- glBindBufferBase(GL_UNIFORM_BUFFER, RasterizerCanvasGLES3::MATERIAL_UNIFORM_BUFFER_OBJECT, uniform_buffer);
+ glBindBufferBase(GL_UNIFORM_BUFFER, RasterizerCanvasGLES3::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);
+ 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);
// Set sampler state here as the same texture can be used in multiple places with different flags
@@ -2982,4 +2942,543 @@ GLES3::MaterialData *GLES3::_create_canvas_material_func(ShaderData *p_shader) {
return material_data;
}
+////////////////////////////////////////////////////////////////////////////////
+// SKY SHADER
+
+void SkyShaderData::set_code(const String &p_code) {
+ //compile
+
+ code = p_code;
+ valid = false;
+ ubo_size = 0;
+ uniforms.clear();
+
+ if (code.is_empty()) {
+ return; //just invalid, but no error
+ }
+
+ ShaderCompiler::GeneratedCode gen_code;
+ ShaderCompiler::IdentifierActions actions;
+ actions.entry_point_stages["sky"] = ShaderCompiler::STAGE_FRAGMENT;
+
+ uses_time = false;
+ uses_half_res = false;
+ uses_quarter_res = false;
+ uses_position = false;
+ uses_light = false;
+
+ actions.render_mode_flags["use_half_res_pass"] = &uses_half_res;
+ actions.render_mode_flags["use_quarter_res_pass"] = &uses_quarter_res;
+
+ actions.usage_flag_pointers["TIME"] = &uses_time;
+ actions.usage_flag_pointers["POSITION"] = &uses_position;
+ actions.usage_flag_pointers["LIGHT0_ENABLED"] = &uses_light;
+ actions.usage_flag_pointers["LIGHT0_ENERGY"] = &uses_light;
+ actions.usage_flag_pointers["LIGHT0_DIRECTION"] = &uses_light;
+ actions.usage_flag_pointers["LIGHT0_COLOR"] = &uses_light;
+ actions.usage_flag_pointers["LIGHT0_SIZE"] = &uses_light;
+ actions.usage_flag_pointers["LIGHT1_ENABLED"] = &uses_light;
+ actions.usage_flag_pointers["LIGHT1_ENERGY"] = &uses_light;
+ actions.usage_flag_pointers["LIGHT1_DIRECTION"] = &uses_light;
+ actions.usage_flag_pointers["LIGHT1_COLOR"] = &uses_light;
+ actions.usage_flag_pointers["LIGHT1_SIZE"] = &uses_light;
+ actions.usage_flag_pointers["LIGHT2_ENABLED"] = &uses_light;
+ actions.usage_flag_pointers["LIGHT2_ENERGY"] = &uses_light;
+ actions.usage_flag_pointers["LIGHT2_DIRECTION"] = &uses_light;
+ actions.usage_flag_pointers["LIGHT2_COLOR"] = &uses_light;
+ actions.usage_flag_pointers["LIGHT2_SIZE"] = &uses_light;
+ actions.usage_flag_pointers["LIGHT3_ENABLED"] = &uses_light;
+ actions.usage_flag_pointers["LIGHT3_ENERGY"] = &uses_light;
+ actions.usage_flag_pointers["LIGHT3_DIRECTION"] = &uses_light;
+ actions.usage_flag_pointers["LIGHT3_COLOR"] = &uses_light;
+ actions.usage_flag_pointers["LIGHT3_SIZE"] = &uses_light;
+
+ actions.uniforms = &uniforms;
+
+ Error err = MaterialStorage::get_singleton()->shaders.compiler_sky.compile(RS::SHADER_SKY, code, &actions, path, gen_code);
+ ERR_FAIL_COND_MSG(err != OK, "Shader compilation failed.");
+
+ if (version.is_null()) {
+ version = MaterialStorage::get_singleton()->shaders.sky_shader.version_create();
+ }
+
+#if 0
+ print_line("**compiling shader:");
+ print_line("**defines:\n");
+ for (int i = 0; i < gen_code.defines.size(); i++) {
+ print_line(gen_code.defines[i]);
+ }
+ print_line("\n**uniforms:\n" + gen_code.uniforms);
+ // print_line("\n**vertex_globals:\n" + gen_code.vertex_global);
+ // print_line("\n**vertex_code:\n" + gen_code.vertex);
+ print_line("\n**fragment_globals:\n" + gen_code.fragment_global);
+ print_line("\n**fragment_code:\n" + gen_code.fragment);
+ print_line("\n**light_code:\n" + gen_code.light);
+#endif
+
+ 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.sky_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.sky_shader.version_is_valid(version));
+
+ ubo_size = gen_code.uniform_total_size;
+ ubo_offsets = gen_code.uniform_offsets;
+ texture_uniforms = gen_code.texture_uniforms;
+
+ valid = true;
+}
+
+void SkyShaderData::set_default_texture_param(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] = Map<int, RID>();
+ }
+ default_texture_params[p_name][p_index] = p_texture;
+ }
+}
+
+void SkyShaderData::get_param_list(List<PropertyInfo> *p_param_list) const {
+ Map<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;
+ }
+ }
+
+ for (const KeyValue<int, StringName> &E : order) {
+ 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_param_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;
+}
+
+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);
+}
+
+SkyShaderData::SkyShaderData() {
+ valid = false;
+}
+
+SkyShaderData::~SkyShaderData() {
+ if (version.is_valid()) {
+ MaterialStorage::get_singleton()->shaders.sky_shader.version_free(version);
+ }
+}
+
+GLES3::ShaderData *GLES3::_create_sky_shader_func() {
+ SkyShaderData *shader_data = memnew(SkyShaderData);
+ return shader_data;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Sky material
+
+void SkyMaterialData::update_parameters(const Map<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);
+}
+
+SkyMaterialData::~SkyMaterialData() {
+}
+GLES3::MaterialData *GLES3::_create_sky_material_func(ShaderData *p_shader) {
+ SkyMaterialData *material_data = memnew(SkyMaterialData);
+ material_data->shader_data = static_cast<SkyShaderData *>(p_shader);
+ //update will happen later anyway so do nothing.
+ return material_data;
+}
+
+void SkyMaterialData::bind_uniforms() {
+ // Bind Material Uniforms
+ glBindBufferBase(GL_UNIFORM_BUFFER, SKY_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_TEXTURE0 + ti);
+ glBindTexture(target_from_type[texture_uniforms[ti].type], texture->tex_id);
+
+ // Set sampler state here as the same texture can be used in multiple places with different flags
+ // Need to convert sampler state from ShaderLanguage::Texture* to RS::CanvasItemTexture*
+ RS::CanvasItemTextureFilter filter = RS::CanvasItemTextureFilter((int(texture_uniforms[ti].filter) + 1) % RS::CANVAS_ITEM_TEXTURE_FILTER_MAX);
+ RS::CanvasItemTextureRepeat repeat = RS::CanvasItemTextureRepeat((int(texture_uniforms[ti].repeat) + 1) % RS::CANVAS_ITEM_TEXTURE_REPEAT_MIRROR);
+ texture->gl_set_filter(filter);
+ texture->gl_set_repeat(repeat);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Scene SHADER
+
+void SceneShaderData::set_code(const String &p_code) {
+ //compile
+
+ code = p_code;
+ valid = false;
+ ubo_size = 0;
+ uniforms.clear();
+ uses_screen_texture = false;
+
+ if (code.is_empty()) {
+ return; //just invalid, but no error
+ }
+
+ ShaderCompiler::GeneratedCode gen_code;
+
+ int blend_modei = BLEND_MODE_MIX;
+ int depth_testi = DEPTH_TEST_ENABLED;
+ int alpha_antialiasing_modei = ALPHA_ANTIALIASING_OFF;
+ int cull_modei = CULL_BACK;
+ int depth_drawi = DEPTH_DRAW_OPAQUE;
+
+ uses_point_size = false;
+ uses_alpha = false;
+ uses_alpha_clip = false;
+ uses_blend_alpha = false;
+ uses_depth_pre_pass = false;
+ uses_discard = false;
+ uses_roughness = false;
+ uses_normal = false;
+ wireframe = false;
+
+ unshaded = false;
+ uses_vertex = false;
+ uses_position = false;
+ uses_sss = false;
+ uses_transmittance = false;
+ uses_screen_texture = false;
+ uses_depth_texture = false;
+ uses_normal_texture = false;
+ uses_time = false;
+ writes_modelview_or_projection = false;
+ uses_world_coordinates = false;
+ uses_particle_trails = false;
+
+ ShaderCompiler::IdentifierActions actions;
+ actions.entry_point_stages["vertex"] = ShaderCompiler::STAGE_VERTEX;
+ actions.entry_point_stages["fragment"] = ShaderCompiler::STAGE_FRAGMENT;
+ actions.entry_point_stages["light"] = ShaderCompiler::STAGE_FRAGMENT;
+
+ actions.render_mode_values["blend_add"] = Pair<int *, int>(&blend_modei, BLEND_MODE_ADD);
+ actions.render_mode_values["blend_mix"] = Pair<int *, int>(&blend_modei, BLEND_MODE_MIX);
+ actions.render_mode_values["blend_sub"] = Pair<int *, int>(&blend_modei, BLEND_MODE_SUB);
+ actions.render_mode_values["blend_mul"] = Pair<int *, int>(&blend_modei, BLEND_MODE_MUL);
+
+ actions.render_mode_values["alpha_to_coverage"] = Pair<int *, int>(&alpha_antialiasing_modei, ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE);
+ actions.render_mode_values["alpha_to_coverage_and_one"] = Pair<int *, int>(&alpha_antialiasing_modei, ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE_AND_TO_ONE);
+
+ actions.render_mode_values["depth_draw_never"] = Pair<int *, int>(&depth_drawi, DEPTH_DRAW_DISABLED);
+ actions.render_mode_values["depth_draw_opaque"] = Pair<int *, int>(&depth_drawi, DEPTH_DRAW_OPAQUE);
+ actions.render_mode_values["depth_draw_always"] = Pair<int *, int>(&depth_drawi, DEPTH_DRAW_ALWAYS);
+
+ actions.render_mode_values["depth_test_disabled"] = Pair<int *, int>(&depth_testi, DEPTH_TEST_DISABLED);
+
+ actions.render_mode_values["cull_disabled"] = Pair<int *, int>(&cull_modei, CULL_DISABLED);
+ actions.render_mode_values["cull_front"] = Pair<int *, int>(&cull_modei, CULL_FRONT);
+ actions.render_mode_values["cull_back"] = Pair<int *, int>(&cull_modei, CULL_BACK);
+
+ actions.render_mode_flags["unshaded"] = &unshaded;
+ actions.render_mode_flags["wireframe"] = &wireframe;
+ actions.render_mode_flags["particle_trails"] = &uses_particle_trails;
+
+ 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;
+
+ 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;
+ actions.usage_flag_pointers["NORMAL"] = &uses_normal;
+ actions.usage_flag_pointers["NORMAL_MAP"] = &uses_normal;
+
+ actions.usage_flag_pointers["POINT_SIZE"] = &uses_point_size;
+ actions.usage_flag_pointers["POINT_COORD"] = &uses_point_size;
+
+ actions.write_flag_pointers["MODELVIEW_MATRIX"] = &writes_modelview_or_projection;
+ actions.write_flag_pointers["PROJECTION_MATRIX"] = &writes_modelview_or_projection;
+ actions.write_flag_pointers["VERTEX"] = &uses_vertex;
+ actions.write_flag_pointers["POSITION"] = &uses_position;
+
+ actions.usage_flag_pointers["TANGENT"] = &uses_tangent;
+ actions.usage_flag_pointers["BINORMAL"] = &uses_tangent;
+ actions.usage_flag_pointers["COLOR"] = &uses_color;
+ actions.usage_flag_pointers["UV"] = &uses_uv;
+ actions.usage_flag_pointers["UV2"] = &uses_uv2;
+ actions.usage_flag_pointers["CUSTOM0"] = &uses_custom0;
+ actions.usage_flag_pointers["CUSTOM1"] = &uses_custom1;
+ actions.usage_flag_pointers["CUSTOM2"] = &uses_custom2;
+ actions.usage_flag_pointers["CUSTOM3"] = &uses_custom3;
+ actions.usage_flag_pointers["BONE_INDICES"] = &uses_bones;
+ actions.usage_flag_pointers["BONE_WEIGHTS"] = &uses_weights;
+
+ actions.uniforms = &uniforms;
+
+ Error err = MaterialStorage::get_singleton()->shaders.compiler_scene.compile(RS::SHADER_SPATIAL, code, &actions, path, gen_code);
+ ERR_FAIL_COND_MSG(err != OK, "Shader compilation failed.");
+
+ if (version.is_null()) {
+ version = MaterialStorage::get_singleton()->shaders.scene_shader.version_create();
+ }
+
+ depth_draw = DepthDraw(depth_drawi);
+ depth_test = DepthTest(depth_testi);
+ cull_mode = Cull(cull_modei);
+ blend_mode = BlendMode(blend_modei);
+ alpha_antialiasing_mode = AlphaAntiAliasing(alpha_antialiasing_modei);
+ vertex_input_mask = uint32_t(uses_normal);
+ vertex_input_mask |= uses_tangent << 1;
+ vertex_input_mask |= uses_color << 2;
+ vertex_input_mask |= uses_uv << 3;
+ vertex_input_mask |= uses_uv2 << 4;
+ vertex_input_mask |= uses_custom0 << 5;
+ vertex_input_mask |= uses_custom1 << 6;
+ vertex_input_mask |= uses_custom2 << 7;
+ vertex_input_mask |= uses_custom3 << 8;
+ vertex_input_mask |= uses_bones << 9;
+ vertex_input_mask |= uses_weights << 10;
+
+#if 0
+ print_line("**compiling shader:");
+ print_line("**defines:\n");
+ for (int i = 0; i < gen_code.defines.size(); i++) {
+ print_line(gen_code.defines[i]);
+ }
+
+ Map<String, String>::Element *el = gen_code.code.front();
+ while (el) {
+ print_line("\n**code " + el->key() + ":\n" + el->value());
+
+ el = el->next();
+ }
+
+ print_line("\n**uniforms:\n" + gen_code.uniforms);
+ print_line("\n**vertex_globals:\n" + gen_code.stage_globals[ShaderCompiler::STAGE_VERTEX]);
+ print_line("\n**fragment_globals:\n" + gen_code.stage_globals[ShaderCompiler::STAGE_FRAGMENT]);
+#endif
+
+ 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.scene_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.scene_shader.version_is_valid(version));
+
+ ubo_size = gen_code.uniform_total_size;
+ ubo_offsets = gen_code.uniform_offsets;
+ texture_uniforms = gen_code.texture_uniforms;
+
+ // if any form of Alpha Antialiasing is enabled, set the blend mode to alpha to coverage
+ if (alpha_antialiasing_mode != ALPHA_ANTIALIASING_OFF) {
+ blend_mode = BLEND_MODE_ALPHA_TO_COVERAGE;
+ }
+
+ valid = true;
+}
+
+void SceneShaderData::set_default_texture_param(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] = Map<int, RID>();
+ }
+ default_texture_params[p_name][p_index] = p_texture;
+ }
+}
+
+void SceneShaderData::get_param_list(List<PropertyInfo> *p_param_list) const {
+ Map<int, StringName> order;
+
+ 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) {
+ order[E.value.texture_order + 100000] = E.key;
+ } else {
+ order[E.value.order] = E.key;
+ }
+ }
+
+ for (const KeyValue<int, StringName> &E : order) {
+ 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_param_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;
+}
+
+bool SceneShaderData::casts_shadows() const {
+ return false;
+}
+
+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();
+}
+
+RS::ShaderNativeSourceCode SceneShaderData::get_native_source_code() const {
+ return MaterialStorage::get_singleton()->shaders.scene_shader.version_get_native_source_code(version);
+}
+
+SceneShaderData::SceneShaderData() {
+ valid = false;
+ uses_screen_texture = false;
+}
+
+SceneShaderData::~SceneShaderData() {
+ if (version.is_valid()) {
+ MaterialStorage::get_singleton()->shaders.scene_shader.version_free(version);
+ }
+}
+
+GLES3::ShaderData *GLES3::_create_scene_shader_func() {
+ SceneShaderData *shader_data = memnew(SceneShaderData);
+ return shader_data;
+}
+
+void SceneMaterialData::set_render_priority(int p_priority) {
+ priority = p_priority - RS::MATERIAL_RENDER_PRIORITY_MIN; //8 bits
+}
+
+void SceneMaterialData::set_next_pass(RID p_pass) {
+ next_pass = p_pass;
+}
+
+void SceneMaterialData::update_parameters(const Map<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);
+}
+
+SceneMaterialData::~SceneMaterialData() {
+}
+
+GLES3::MaterialData *GLES3::_create_scene_material_func(ShaderData *p_shader) {
+ SceneMaterialData *material_data = memnew(SceneMaterialData);
+ material_data->shader_data = static_cast<SceneShaderData *>(p_shader);
+ //update will happen later anyway so do nothing.
+ return material_data;
+}
+
+void SceneMaterialData::bind_uniforms() {
+ // Bind Material Uniforms
+ glBindBufferBase(GL_UNIFORM_BUFFER, SCENE_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_TEXTURE0 + ti);
+ glBindTexture(target_from_type[texture_uniforms[ti].type], texture->tex_id);
+
+ // Set sampler state here as the same texture can be used in multiple places with different flags
+ // Need to convert sampler state from ShaderLanguage::Texture* to RS::CanvasItemTexture*
+ RS::CanvasItemTextureFilter filter = RS::CanvasItemTextureFilter((int(texture_uniforms[ti].filter) + 1) % RS::CANVAS_ITEM_TEXTURE_FILTER_MAX);
+ RS::CanvasItemTextureRepeat repeat = RS::CanvasItemTextureRepeat((int(texture_uniforms[ti].repeat) + 1) % RS::CANVAS_ITEM_TEXTURE_REPEAT_MIRROR);
+ texture->gl_set_filter(filter);
+ texture->gl_set_repeat(repeat);
+ }
+}
+
#endif // !GLES3_ENABLED
diff --git a/drivers/gles3/storage/material_storage.h b/drivers/gles3/storage/material_storage.h
index cc6cbdc152..aa36dda4e6 100644
--- a/drivers/gles3/storage/material_storage.h
+++ b/drivers/gles3/storage/material_storage.h
@@ -45,15 +45,17 @@
#include "drivers/gles3/shaders/copy.glsl.gen.h"
#include "../shaders/canvas.glsl.gen.h"
+#include "../shaders/scene.glsl.gen.h"
#include "../shaders/sky.glsl.gen.h"
namespace GLES3 {
-/* SHADER Structs */
+/* Shader Structs */
struct Shaders {
CanvasShaderGLES3 canvas_shader;
SkyShaderGLES3 sky_shader;
+ SceneShaderGLES3 scene_shader;
ShaderCompiler compiler_canvas;
ShaderCompiler compiler_scene;
@@ -141,7 +143,7 @@ struct Material {
update_element(this) {}
};
-// CanvasItem Materials
+/* CanvasItem Materials */
struct CanvasShaderData : public ShaderData {
enum BlendMode { //used internally
@@ -200,6 +202,179 @@ struct CanvasMaterialData : public MaterialData {
MaterialData *_create_canvas_material_func(ShaderData *p_shader);
+/* Sky Materials */
+
+struct SkyShaderData : public ShaderData {
+ bool valid;
+ RID version;
+
+ Map<StringName, ShaderLanguage::ShaderNode::Uniform> uniforms;
+ Vector<ShaderCompiler::GeneratedCode::Texture> texture_uniforms;
+
+ Vector<uint32_t> ubo_offsets;
+ uint32_t ubo_size;
+
+ String path;
+ String code;
+ Map<StringName, Map<int, RID>> default_texture_params;
+
+ bool uses_time;
+ bool uses_position;
+ bool uses_half_res;
+ bool uses_quarter_res;
+ bool uses_light;
+
+ virtual void set_code(const String &p_Code);
+ virtual void set_default_texture_param(const StringName &p_name, RID p_texture, int p_index);
+ virtual void get_param_list(List<PropertyInfo> *p_param_list) const;
+ virtual void get_instance_param_list(List<RendererMaterialStorage::InstanceShaderParam> *p_param_list) const;
+ virtual bool is_param_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();
+};
+
+ShaderData *_create_sky_shader_func();
+
+struct SkyMaterialData : public MaterialData {
+ SkyShaderData *shader_data = nullptr;
+
+ virtual void set_render_priority(int p_priority) {}
+ virtual void set_next_pass(RID p_pass) {}
+ virtual void update_parameters(const Map<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty);
+ virtual void bind_uniforms();
+ virtual ~SkyMaterialData();
+};
+
+MaterialData *_create_sky_material_func(ShaderData *p_shader);
+
+/* Scene Materials */
+
+struct SceneShaderData : public ShaderData {
+ enum BlendMode { //used internally
+ BLEND_MODE_MIX,
+ BLEND_MODE_ADD,
+ BLEND_MODE_SUB,
+ BLEND_MODE_MUL,
+ BLEND_MODE_ALPHA_TO_COVERAGE
+ };
+
+ enum DepthDraw {
+ DEPTH_DRAW_DISABLED,
+ DEPTH_DRAW_OPAQUE,
+ DEPTH_DRAW_ALWAYS
+ };
+
+ enum DepthTest {
+ DEPTH_TEST_DISABLED,
+ DEPTH_TEST_ENABLED
+ };
+
+ enum Cull {
+ CULL_DISABLED,
+ CULL_FRONT,
+ CULL_BACK
+ };
+
+ enum AlphaAntiAliasing {
+ ALPHA_ANTIALIASING_OFF,
+ ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE,
+ ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE_AND_TO_ONE
+ };
+
+ bool valid;
+ RID version;
+
+ String path;
+
+ Map<StringName, ShaderLanguage::ShaderNode::Uniform> uniforms;
+ Vector<ShaderCompiler::GeneratedCode::Texture> texture_uniforms;
+
+ Vector<uint32_t> ubo_offsets;
+ uint32_t ubo_size;
+
+ String code;
+ Map<StringName, Map<int, RID>> default_texture_params;
+
+ BlendMode blend_mode;
+ AlphaAntiAliasing alpha_antialiasing_mode;
+ DepthDraw depth_draw;
+ DepthTest depth_test;
+ Cull cull_mode;
+
+ bool uses_point_size;
+ bool uses_alpha;
+ bool uses_blend_alpha;
+ bool uses_alpha_clip;
+ bool uses_depth_pre_pass;
+ bool uses_discard;
+ bool uses_roughness;
+ bool uses_normal;
+ bool uses_particle_trails;
+ bool wireframe;
+
+ bool unshaded;
+ bool uses_vertex;
+ bool uses_position;
+ bool uses_sss;
+ bool uses_transmittance;
+ bool uses_screen_texture;
+ bool uses_depth_texture;
+ bool uses_normal_texture;
+ bool uses_time;
+ bool writes_modelview_or_projection;
+ bool uses_world_coordinates;
+ bool uses_tangent;
+ bool uses_color;
+ bool uses_uv;
+ bool uses_uv2;
+ bool uses_custom0;
+ bool uses_custom1;
+ bool uses_custom2;
+ bool uses_custom3;
+ bool uses_bones;
+ bool uses_weights;
+
+ uint32_t vertex_input_mask = 0;
+
+ uint64_t last_pass = 0;
+ uint32_t index = 0;
+
+ virtual void set_code(const String &p_Code);
+ virtual void set_default_texture_param(const StringName &p_name, RID p_texture, int p_index);
+ virtual void get_param_list(List<PropertyInfo> *p_param_list) const;
+ virtual void get_instance_param_list(List<RendererMaterialStorage::InstanceShaderParam> *p_param_list) const;
+
+ virtual bool is_param_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();
+ virtual ~SceneShaderData();
+};
+
+ShaderData *_create_scene_shader_func();
+
+struct SceneMaterialData : public MaterialData {
+ SceneShaderData *shader_data = nullptr;
+ uint64_t last_pass = 0;
+ uint32_t index = 0;
+ RID next_pass;
+ uint8_t priority = 0;
+ virtual void set_render_priority(int p_priority);
+ virtual void set_next_pass(RID p_pass);
+ virtual void update_parameters(const Map<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty);
+ virtual void bind_uniforms();
+ virtual ~SceneMaterialData();
+};
+
+MaterialData *_create_scene_material_func(ShaderData *p_shader);
+
/* Global variable structs */
struct GlobalVariables {
enum {
diff --git a/drivers/gles3/storage/mesh_storage.cpp b/drivers/gles3/storage/mesh_storage.cpp
index 41b5107b6c..a2b9cb6a62 100644
--- a/drivers/gles3/storage/mesh_storage.cpp
+++ b/drivers/gles3/storage/mesh_storage.cpp
@@ -194,6 +194,7 @@ void MeshStorage::mesh_add_surface(RID p_mesh, const RS::SurfaceData &p_surface)
glBindBuffer(GL_ARRAY_BUFFER, s->attribute_buffer);
glBufferData(GL_ARRAY_BUFFER, p_surface.attribute_data.size(), p_surface.attribute_data.ptr(), (s->format & RS::ARRAY_FLAG_USE_DYNAMIC_UPDATE) ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind
+ s->attribute_buffer_size = p_surface.attribute_data.size();
}
if (p_surface.skin_data.size()) {
glGenBuffers(1, &s->skin_buffer);
@@ -216,6 +217,7 @@ void MeshStorage::mesh_add_surface(RID p_mesh, const RS::SurfaceData &p_surface)
glBufferData(GL_ELEMENT_ARRAY_BUFFER, p_surface.index_data.size(), p_surface.index_data.ptr(), GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); //unbind
s->index_count = p_surface.index_count;
+ s->index_buffer_size = p_surface.index_data.size();
if (p_surface.lods.size()) {
s->lods = memnew_arr(Mesh::Surface::LOD, p_surface.lods.size());
@@ -323,7 +325,97 @@ RID MeshStorage::mesh_surface_get_material(RID p_mesh, int p_surface) const {
}
RS::SurfaceData MeshStorage::mesh_get_surface(RID p_mesh, int p_surface) const {
- return RS::SurfaceData();
+ Mesh *mesh = mesh_owner.get_or_null(p_mesh);
+ ERR_FAIL_COND_V(!mesh, RS::SurfaceData());
+ ERR_FAIL_UNSIGNED_INDEX_V((uint32_t)p_surface, mesh->surface_count, RS::SurfaceData());
+
+ Mesh::Surface &s = *mesh->surfaces[p_surface];
+
+ RS::SurfaceData sd;
+ sd.format = s.format;
+ {
+ Vector<uint8_t> ret;
+ ret.resize(s.vertex_buffer_size);
+ glBindBuffer(GL_ARRAY_BUFFER, s.vertex_buffer);
+
+#if defined(__EMSCRIPTEN__)
+ {
+ uint8_t *w = ret.ptrw();
+ glGetBufferSubData(GL_ARRAY_BUFFER, 0, s.vertex_buffer_size, w);
+ }
+#else
+ void *data = glMapBufferRange(GL_ARRAY_BUFFER, 0, s.vertex_buffer_size, GL_MAP_READ_BIT);
+ ERR_FAIL_NULL_V(data, RS::SurfaceData());
+ {
+ uint8_t *w = ret.ptrw();
+ memcpy(w, data, s.vertex_buffer_size);
+ }
+ glUnmapBuffer(GL_ARRAY_BUFFER);
+#endif
+ sd.vertex_data = ret;
+ }
+
+ if (s.attribute_buffer != 0) {
+ Vector<uint8_t> ret;
+ ret.resize(s.attribute_buffer_size);
+ glBindBuffer(GL_ARRAY_BUFFER, s.attribute_buffer);
+
+#if defined(__EMSCRIPTEN__)
+ {
+ uint8_t *w = ret.ptrw();
+ glGetBufferSubData(GL_ARRAY_BUFFER, 0, s.attribute_buffer_size, w);
+ }
+#else
+ void *data = glMapBufferRange(GL_ARRAY_BUFFER, 0, s.attribute_buffer_size, GL_MAP_READ_BIT);
+ ERR_FAIL_NULL_V(data, RS::SurfaceData());
+ {
+ uint8_t *w = ret.ptrw();
+ memcpy(w, data, s.attribute_buffer_size);
+ }
+ glUnmapBuffer(GL_ARRAY_BUFFER);
+#endif
+ sd.attribute_data = ret;
+ }
+
+ sd.vertex_count = s.vertex_count;
+ sd.index_count = s.index_count;
+ sd.primitive = s.primitive;
+
+ if (sd.index_count) {
+ Vector<uint8_t> ret;
+ ret.resize(s.index_buffer_size);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, s.index_buffer);
+
+#if defined(__EMSCRIPTEN__)
+ {
+ uint8_t *w = ret.ptrw();
+ glGetBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, s.index_buffer_size, w);
+ }
+#else
+ void *data = glMapBufferRange(GL_ELEMENT_ARRAY_BUFFER, 0, s.index_buffer_size, GL_MAP_READ_BIT);
+ ERR_FAIL_NULL_V(data, RS::SurfaceData());
+ {
+ uint8_t *w = ret.ptrw();
+ memcpy(w, data, s.index_buffer_size);
+ }
+ glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER);
+#endif
+ sd.index_data = ret;
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+ }
+
+ sd.aabb = s.aabb;
+ for (uint32_t i = 0; i < s.lod_count; i++) {
+ RS::SurfaceData::LOD lod;
+ lod.edge_length = s.lods[i].edge_length;
+ //lod.index_data = RD::get_singleton()->buffer_get_data(s.lods[i].index_buffer);
+ sd.lods.push_back(lod);
+ }
+
+ sd.bone_aabbs = s.bone_aabbs;
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+ return sd;
}
int MeshStorage::mesh_get_surface_count(RID p_mesh) const {
@@ -496,7 +588,6 @@ void MeshStorage::mesh_clear(RID p_mesh) {
if (s.index_buffer != 0) {
glDeleteBuffers(1, &s.index_buffer);
- glDeleteVertexArrays(1, &s.index_array);
}
memdelete(mesh->surfaces[i]);
}
@@ -553,14 +644,14 @@ void MeshStorage::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::V
case RS::ARRAY_NORMAL: {
attribs[i].offset = vertex_stride;
// Will need to change to accommodate octahedral compression
- attribs[i].size = 1;
+ attribs[i].size = 4;
attribs[i].type = GL_UNSIGNED_INT_2_10_10_10_REV;
vertex_stride += sizeof(float);
attribs[i].normalized = GL_TRUE;
} break;
case RS::ARRAY_TANGENT: {
attribs[i].offset = vertex_stride;
- attribs[i].size = 1;
+ attribs[i].size = 4;
attribs[i].type = GL_UNSIGNED_INT_2_10_10_10_REV;
vertex_stride += sizeof(float);
attribs[i].normalized = GL_TRUE;
@@ -629,14 +720,17 @@ void MeshStorage::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::V
continue;
}
if (i <= RS::ARRAY_TANGENT) {
+ attribs[i].stride = vertex_stride;
if (mis) {
glBindBuffer(GL_ARRAY_BUFFER, mis->vertex_buffer);
} else {
glBindBuffer(GL_ARRAY_BUFFER, s->vertex_buffer);
}
} else if (i <= RS::ARRAY_CUSTOM3) {
+ attribs[i].stride = attributes_stride;
glBindBuffer(GL_ARRAY_BUFFER, s->attribute_buffer);
} else {
+ attribs[i].stride = skin_stride;
glBindBuffer(GL_ARRAY_BUFFER, s->skin_buffer);
}
@@ -645,7 +739,7 @@ void MeshStorage::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::V
} else {
glVertexAttribPointer(i, attribs[i].size, attribs[i].type, attribs[i].normalized, attribs[i].stride, CAST_INT_TO_UCHAR_PTR(attribs[i].offset));
}
- glEnableVertexAttribArray(attribs[i].index);
+ glEnableVertexAttribArray(i);
}
// Do not bind index here as we want to switch between index buffers for LOD
diff --git a/drivers/gles3/storage/mesh_storage.h b/drivers/gles3/storage/mesh_storage.h
index f51ec6edbe..dfb9046e7b 100644
--- a/drivers/gles3/storage/mesh_storage.h
+++ b/drivers/gles3/storage/mesh_storage.h
@@ -54,7 +54,6 @@ struct Mesh {
struct Attrib {
bool enabled;
bool integer;
- GLuint index;
GLint size;
GLenum type;
GLboolean normalized;
@@ -69,6 +68,7 @@ struct Mesh {
GLuint skin_buffer = 0;
uint32_t vertex_count = 0;
uint32_t vertex_buffer_size = 0;
+ uint32_t attribute_buffer_size = 0;
uint32_t skin_buffer_size = 0;
// Cache vertex arrays so they can be created
@@ -84,8 +84,8 @@ struct Mesh {
uint32_t version_count = 0;
GLuint index_buffer = 0;
- GLuint index_array = 0;
uint32_t index_count = 0;
+ uint32_t index_buffer_size = 0;
struct LOD {
float edge_length = 0.0;
@@ -357,6 +357,12 @@ public:
}
}
+ _FORCE_INLINE_ GLenum mesh_surface_get_index_type(void *p_surface) const {
+ Mesh::Surface *s = reinterpret_cast<Mesh::Surface *>(p_surface);
+
+ return s->vertex_count <= 65536 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT;
+ }
+
// Use this to cache Vertex Array Objects so they are only generated once
_FORCE_INLINE_ void mesh_surface_get_vertex_arrays_and_format(void *p_surface, uint32_t p_input_mask, GLuint &r_vertex_array_gl) {
Mesh::Surface *s = reinterpret_cast<Mesh::Surface *>(p_surface);
@@ -388,6 +394,9 @@ public:
/* MESH INSTANCE API */
+ MeshInstance *get_mesh_instance(RID p_rid) { return mesh_instance_owner.get_or_null(p_rid); };
+ bool owns_mesh_instance(RID p_rid) { return mesh_instance_owner.owns(p_rid); };
+
virtual RID mesh_instance_create(RID p_base) override;
virtual void mesh_instance_free(RID p_rid) override;
virtual void mesh_instance_set_skeleton(RID p_mesh_instance, RID p_skeleton) override;
@@ -431,6 +440,9 @@ public:
/* MULTIMESH API */
+ MultiMesh *get_multimesh(RID p_rid) { return multimesh_owner.get_or_null(p_rid); };
+ bool owns_multimesh(RID p_rid) { return multimesh_owner.owns(p_rid); };
+
virtual RID multimesh_allocate() override;
virtual void multimesh_initialize(RID p_rid) override;
virtual void multimesh_free(RID p_rid) override;
@@ -483,6 +495,9 @@ public:
/* SKELETON API */
+ Skeleton *get_skeleton(RID p_rid) { return skeleton_owner.get_or_null(p_rid); };
+ bool owns_skeleton(RID p_rid) { return skeleton_owner.owns(p_rid); };
+
virtual RID skeleton_allocate() override;
virtual void skeleton_initialize(RID p_rid) override;
virtual void skeleton_free(RID p_rid) override;
diff --git a/drivers/gles3/storage/texture_storage.cpp b/drivers/gles3/storage/texture_storage.cpp
index 6f2dc391d8..4396ca4f93 100644
--- a/drivers/gles3/storage/texture_storage.cpp
+++ b/drivers/gles3/storage/texture_storage.cpp
@@ -62,8 +62,9 @@ TextureStorage::TextureStorage() {
Ref<Image> image;
image.instantiate();
- image->create(4, 4, false, Image::FORMAT_RGBA8);
+ image->create(4, 4, true, Image::FORMAT_RGBA8);
image->fill(Color(1, 1, 1, 1));
+ image->generate_mipmaps();
default_gl_textures[DEFAULT_GL_TEXTURE_WHITE] = texture_allocate();
texture_2d_initialize(default_gl_textures[DEFAULT_GL_TEXTURE_WHITE], image);
@@ -92,8 +93,9 @@ TextureStorage::TextureStorage() {
{ // black
Ref<Image> image;
image.instantiate();
- image->create(4, 4, false, Image::FORMAT_RGBA8);
+ image->create(4, 4, true, Image::FORMAT_RGBA8);
image->fill(Color(0, 0, 0, 1));
+ image->generate_mipmaps();
default_gl_textures[DEFAULT_GL_TEXTURE_BLACK] = texture_allocate();
texture_2d_initialize(default_gl_textures[DEFAULT_GL_TEXTURE_BLACK], image);
@@ -117,8 +119,9 @@ TextureStorage::TextureStorage() {
{
Ref<Image> image;
image.instantiate();
- image->create(4, 4, false, Image::FORMAT_RGBA8);
+ image->create(4, 4, true, Image::FORMAT_RGBA8);
image->fill(Color(0.5, 0.5, 1, 1));
+ image->generate_mipmaps();
default_gl_textures[DEFAULT_GL_TEXTURE_NORMAL] = texture_allocate();
texture_2d_initialize(default_gl_textures[DEFAULT_GL_TEXTURE_NORMAL], image);
@@ -127,8 +130,9 @@ TextureStorage::TextureStorage() {
{
Ref<Image> image;
image.instantiate();
- image->create(4, 4, false, Image::FORMAT_RGBA8);
+ image->create(4, 4, true, Image::FORMAT_RGBA8);
image->fill(Color(1.0, 0.5, 1, 1));
+ image->generate_mipmaps();
default_gl_textures[DEFAULT_GL_TEXTURE_ANISO] = texture_allocate();
texture_2d_initialize(default_gl_textures[DEFAULT_GL_TEXTURE_ANISO], image);
@@ -189,18 +193,7 @@ TextureStorage::~TextureStorage() {
}
}
-void TextureStorage::set_main_thread_id(Thread::ID p_id) {
- _main_thread_id = p_id;
-}
-
-bool TextureStorage::_is_main_thread() {
- //#if defined DEBUG_ENABLED && defined TOOLS_ENABLED
- // must be called from main thread in OpenGL
- bool is_main_thread = _main_thread_id == Thread::get_caller_id();
- //#endif
- return is_main_thread;
-}
-
+//TODO, move back to storage
bool TextureStorage::can_create_resources_async() const {
return false;
}
@@ -644,10 +637,14 @@ void TextureStorage::texture_2d_initialize(RID p_texture, const Ref<Image> &p_im
Texture texture;
texture.width = p_image->get_width();
texture.height = p_image->get_height();
+ texture.alloc_width = texture.width;
+ texture.alloc_height = texture.height;
+ texture.mipmaps = p_image->get_mipmap_count();
texture.format = p_image->get_format();
texture.type = Texture::TYPE_2D;
texture.target = GL_TEXTURE_2D;
- texture.image_cache_2d = p_image; //TODO, remove this once texture_2d_get is implemented
+ _get_gl_image_and_format(Ref<Image>(), texture.format, 0, texture.real_format, texture.gl_format_cache, texture.gl_internal_format_cache, texture.gl_type_cache, texture.compressed, false);
+ //texture.total_data_size = p_image->get_image_data_size(); // verify that this returns size in bytes
texture.active = true;
glGenTextures(1, &texture.tex_id);
texture_owner.initialize_rid(p_texture, texture);
@@ -740,49 +737,66 @@ void TextureStorage::texture_3d_placeholder_initialize(RID p_texture) {
}
Ref<Image> TextureStorage::texture_2d_get(RID p_texture) const {
- Texture *tex = texture_owner.get_or_null(p_texture);
- ERR_FAIL_COND_V(!tex, Ref<Image>());
+ Texture *texture = texture_owner.get_or_null(p_texture);
+ ERR_FAIL_COND_V(!texture, Ref<Image>());
#ifdef TOOLS_ENABLED
- if (tex->image_cache_2d.is_valid() && !tex->is_render_target) {
- return tex->image_cache_2d;
+ if (texture->image_cache_2d.is_valid() && !texture->is_render_target) {
+ return texture->image_cache_2d;
}
#endif
- /*
-#ifdef TOOLS_ENABLED
- if (tex->image_cache_2d.is_valid()) {
- return tex->image_cache_2d;
+#ifdef GLES_OVER_GL
+ // OpenGL 3.3 supports glGetTexImage which is faster and simpler than glReadPixels.
+ Vector<uint8_t> data;
+
+ int data_size = Image::get_image_data_size(texture->alloc_width, texture->alloc_height, texture->real_format, texture->mipmaps > 1);
+
+ data.resize(data_size * 2); //add some memory at the end, just in case for buggy drivers
+ uint8_t *w = data.ptrw();
+
+ glActiveTexture(GL_TEXTURE0);
+
+ glBindTexture(texture->target, texture->tex_id);
+
+ glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
+
+ for (int i = 0; i < texture->mipmaps; i++) {
+ int ofs = Image::get_image_mipmap_offset(texture->alloc_width, texture->alloc_height, texture->real_format, i);
+
+ if (texture->compressed) {
+ glPixelStorei(GL_PACK_ALIGNMENT, 4);
+ glGetCompressedTexImage(texture->target, i, &w[ofs]);
+
+ } else {
+ glPixelStorei(GL_PACK_ALIGNMENT, 1);
+
+ glGetTexImage(texture->target, i, texture->gl_format_cache, texture->gl_type_cache, &w[ofs]);
+ }
}
-#endif
- Vector<uint8_t> data = RD::get_singleton()->texture_get_data(tex->rd_texture, 0);
+
+ data.resize(data_size);
+
ERR_FAIL_COND_V(data.size() == 0, Ref<Image>());
Ref<Image> image;
- image.instance();
- image->create(tex->width, tex->height, tex->mipmaps > 1, tex->validated_format, data);
- ERR_FAIL_COND_V(image->empty(), Ref<Image>());
- if (tex->format != tex->validated_format) {
- image->convert(tex->format);
+ image.instantiate();
+ image->create(texture->width, texture->height, texture->mipmaps > 1, texture->real_format, data);
+ ERR_FAIL_COND_V(image->is_empty(), Ref<Image>());
+ if (texture->format != texture->real_format) {
+ image->convert(texture->format);
}
+#else
+ // Support for Web and Mobile will come later.
+ Ref<Image> image;
+#endif
#ifdef TOOLS_ENABLED
- if (Engine::get_singleton()->is_editor_hint()) {
- tex->image_cache_2d = image;
+ if (Engine::get_singleton()->is_editor_hint() && !texture->is_render_target) {
+ texture->image_cache_2d = image;
}
#endif
-*/
-
- /*
- #ifdef TOOLS_ENABLED
- if (Engine::get_singleton()->is_editor_hint() && !tex->is_render_target) {
- tex->image_cache_2d = image;
- }
- #endif
- */
-
- // return image;
- return Ref<Image>();
+ return image;
}
void TextureStorage::texture_replace(RID p_texture, RID p_by_texture) {
@@ -1357,6 +1371,9 @@ void TextureStorage::_update_render_target(RenderTarget *rt) {
}
texture->format = rt->image_format;
+ texture->real_format = rt->image_format;
+ texture->type = Texture::TYPE_2D;
+ texture->target = GL_TEXTURE_2D;
texture->gl_format_cache = rt->color_format;
texture->gl_type_cache = GL_UNSIGNED_BYTE;
texture->gl_internal_format_cache = rt->color_internal_format;
diff --git a/drivers/gles3/storage/texture_storage.h b/drivers/gles3/storage/texture_storage.h
index 1e1cd3f9bf..8281b8c596 100644
--- a/drivers/gles3/storage/texture_storage.h
+++ b/drivers/gles3/storage/texture_storage.h
@@ -141,6 +141,7 @@ struct Texture {
int alloc_width = 0;
int alloc_height = 0;
Image::Format format = Image::FORMAT_R8;
+ Image::Format real_format = Image::FORMAT_R8;
enum Type {
TYPE_2D,
@@ -370,9 +371,6 @@ private:
RID default_gl_textures[DEFAULT_GL_TEXTURE_MAX];
- Thread::ID _main_thread_id = 0;
- bool _is_main_thread();
-
/* Canvas Texture API */
RID_Owner<CanvasTexture, true> canvas_texture_owner;
@@ -440,8 +438,6 @@ public:
};
bool owns_texture(RID p_rid) { return texture_owner.owns(p_rid); };
- void set_main_thread_id(Thread::ID p_id);
-
virtual bool can_create_resources_async() const override;
RID texture_create();
@@ -574,7 +570,7 @@ public:
};
inline String TextureStorage::get_framebuffer_error(GLenum p_status) {
-#ifdef DEBUG_ENABLED
+#if defined(DEBUG_ENABLED) && defined(GLES_OVER_GL)
if (p_status == GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT) {
return "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT";
} else if (p_status == GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT) {
diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp
index c425613262..a70e7011fd 100644
--- a/editor/animation_track_editor.cpp
+++ b/editor/animation_track_editor.cpp
@@ -3421,7 +3421,7 @@ Ref<Animation> AnimationTrackEditor::get_current_animation() const {
return animation;
}
-void AnimationTrackEditor::_root_removed(Node *p_root) {
+void AnimationTrackEditor::_root_removed() {
root = nullptr;
}
diff --git a/editor/animation_track_editor.h b/editor/animation_track_editor.h
index bd66a4b2df..2773b48082 100644
--- a/editor/animation_track_editor.h
+++ b/editor/animation_track_editor.h
@@ -378,7 +378,7 @@ class AnimationTrackEditor : public VBoxContainer {
TrackIndices _confirm_insert(InsertData p_id, TrackIndices p_next_tracks, bool p_create_reset, Ref<Animation> p_reset_anim, bool p_create_beziers);
void _insert_track(bool p_create_reset, bool p_create_beziers);
- void _root_removed(Node *p_root);
+ void _root_removed();
PropertyInfo _find_hint_for_track(int p_idx, NodePath &r_base_path, Variant *r_current_val = nullptr);
diff --git a/editor/create_dialog.cpp b/editor/create_dialog.cpp
index fbb61a1614..b064f6aec7 100644
--- a/editor/create_dialog.cpp
+++ b/editor/create_dialog.cpp
@@ -125,7 +125,7 @@ bool CreateDialog::_should_hide_type(const String &p_type) const {
return true; // Do not show editor nodes.
}
- if (p_type == base_type) {
+ if (p_type == base_type && !EditorNode::get_editor_data().get_custom_types().has(p_type)) {
return true; // Root is already added.
}
diff --git a/editor/debugger/editor_performance_profiler.cpp b/editor/debugger/editor_performance_profiler.cpp
index c821561ca6..87cbd9423c 100644
--- a/editor/debugger/editor_performance_profiler.cpp
+++ b/editor/debugger/editor_performance_profiler.cpp
@@ -97,9 +97,9 @@ void EditorPerformanceProfiler::_monitor_select() {
void EditorPerformanceProfiler::_monitor_draw() {
Vector<StringName> active;
- for (OrderedHashMap<StringName, Monitor>::Element i = monitors.front(); i; i = i.next()) {
- if (i.value().item->is_checked(0)) {
- active.push_back(i.key());
+ for (const KeyValue<StringName, Monitor> &E : monitors) {
+ if (E.value.item->is_checked(0)) {
+ active.push_back(E.key);
}
}
@@ -204,22 +204,22 @@ void EditorPerformanceProfiler::_monitor_draw() {
void EditorPerformanceProfiler::_build_monitor_tree() {
Set<StringName> monitor_checked;
- for (OrderedHashMap<StringName, Monitor>::Element i = monitors.front(); i; i = i.next()) {
- if (i.value().item && i.value().item->is_checked(0)) {
- monitor_checked.insert(i.key());
+ for (KeyValue<StringName, Monitor> &E : monitors) {
+ if (E.value.item && E.value.item->is_checked(0)) {
+ monitor_checked.insert(E.key);
}
}
base_map.clear();
monitor_tree->get_root()->clear_children();
- for (OrderedHashMap<StringName, Monitor>::Element i = monitors.front(); i; i = i.next()) {
- TreeItem *base = _get_monitor_base(i.value().base);
- TreeItem *item = _create_monitor_item(i.value().name, base);
- item->set_checked(0, monitor_checked.has(i.key()));
- i.value().item = item;
- if (!i.value().history.is_empty()) {
- i.value().update_value(i.value().history.front()->get());
+ for (KeyValue<StringName, Monitor> &E : monitors) {
+ TreeItem *base = _get_monitor_base(E.value.base);
+ TreeItem *item = _create_monitor_item(E.value.name, base);
+ item->set_checked(0, monitor_checked.has(E.key));
+ E.value.item = item;
+ if (!E.value.history.is_empty()) {
+ E.value.update_value(E.value.history.front()->get());
}
}
}
@@ -252,9 +252,9 @@ void EditorPerformanceProfiler::_marker_input(const Ref<InputEvent> &p_event) {
Ref<InputEventMouseButton> mb = p_event;
if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
Vector<StringName> active;
- for (OrderedHashMap<StringName, Monitor>::Element i = monitors.front(); i; i = i.next()) {
- if (i.value().item->is_checked(0)) {
- active.push_back(i.key());
+ for (KeyValue<StringName, Monitor> &E : monitors) {
+ if (E.value.item->is_checked(0)) {
+ active.push_back(E.key);
}
}
if (active.size() > 0) {
@@ -293,12 +293,16 @@ void EditorPerformanceProfiler::_marker_input(const Ref<InputEvent> &p_event) {
}
void EditorPerformanceProfiler::reset() {
- for (OrderedHashMap<StringName, Monitor>::Element i = monitors.front(); i; i = i.next()) {
- if (String(i.key()).begins_with("custom:")) {
- monitors.erase(i);
+ HashMap<StringName, Monitor>::Iterator E = monitors.begin();
+ while (E != monitors.end()) {
+ HashMap<StringName, Monitor>::Iterator N = E;
+ ++N;
+ if (String(E->key).begins_with("custom:")) {
+ monitors.remove(E);
} else {
- i.value().reset();
+ E->value.reset();
}
+ E = N;
}
_build_monitor_tree();
@@ -308,43 +312,49 @@ void EditorPerformanceProfiler::reset() {
}
void EditorPerformanceProfiler::update_monitors(const Vector<StringName> &p_names) {
- OrderedHashMap<StringName, int> names;
+ HashMap<StringName, int> names;
for (int i = 0; i < p_names.size(); i++) {
names.insert("custom:" + p_names[i], Performance::MONITOR_MAX + i);
}
- for (OrderedHashMap<StringName, Monitor>::Element i = monitors.front(); i; i = i.next()) {
- if (String(i.key()).begins_with("custom:")) {
- if (!names.has(i.key())) {
- monitors.erase(i);
- } else {
- i.value().frame_index = names[i.key()];
- names.erase(i.key());
+ {
+ HashMap<StringName, Monitor>::Iterator E = monitors.begin();
+ while (E != monitors.end()) {
+ HashMap<StringName, Monitor>::Iterator N = E;
+ ++N;
+ if (String(E->key).begins_with("custom:")) {
+ if (!names.has(E->key)) {
+ monitors.remove(E);
+ } else {
+ E->value.frame_index = names[E->key];
+ names.erase(E->key);
+ }
}
+ E = N;
}
}
- for (OrderedHashMap<StringName, int>::Element i = names.front(); i; i = i.next()) {
- String name = String(i.key()).replace_first("custom:", "");
+ for (const KeyValue<StringName, int> &E : names) {
+ String name = String(E.key).replace_first("custom:", "");
String base = "Custom";
if (name.get_slice_count("/") == 2) {
base = name.get_slicec('/', 0);
name = name.get_slicec('/', 1);
}
- monitors.insert(i.key(), Monitor(name, base, i.value(), Performance::MONITOR_TYPE_QUANTITY, nullptr));
+ monitors.insert(E.key, Monitor(name, base, E.value, Performance::MONITOR_TYPE_QUANTITY, nullptr));
}
_build_monitor_tree();
}
void EditorPerformanceProfiler::add_profile_frame(const Vector<float> &p_values) {
- for (OrderedHashMap<StringName, Monitor>::Element i = monitors.front(); i; i = i.next()) {
+ for (KeyValue<StringName, Monitor> &E : monitors) {
float data = 0.0f;
- if (i.value().frame_index >= 0 && i.value().frame_index < p_values.size()) {
- data = p_values[i.value().frame_index];
+ if (E.value.frame_index >= 0 && E.value.frame_index < p_values.size()) {
+ data = p_values[E.value.frame_index];
}
- i.value().history.push_front(data);
- i.value().update_value(data);
+ E.value.history.push_front(data);
+ E.value.update_value(data);
}
marker_frame++;
monitor_draw->update();
diff --git a/editor/debugger/editor_performance_profiler.h b/editor/debugger/editor_performance_profiler.h
index ab0e43de2f..21d2a52820 100644
--- a/editor/debugger/editor_performance_profiler.h
+++ b/editor/debugger/editor_performance_profiler.h
@@ -31,8 +31,8 @@
#ifndef EDITOR_PERFORMANCE_PROFILER_H
#define EDITOR_PERFORMANCE_PROFILER_H
+#include "core/templates/hash_map.h"
#include "core/templates/map.h"
-#include "core/templates/ordered_hash_map.h"
#include "main/performance.h"
#include "scene/gui/control.h"
#include "scene/gui/label.h"
@@ -59,7 +59,7 @@ private:
void reset();
};
- OrderedHashMap<StringName, Monitor> monitors;
+ HashMap<StringName, Monitor> monitors;
Map<StringName, TreeItem *> base_map;
Tree *monitor_tree = nullptr;
diff --git a/editor/editor_asset_installer.cpp b/editor/editor_asset_installer.cpp
index 7dcb9a4088..f60dcade82 100644
--- a/editor/editor_asset_installer.cpp
+++ b/editor/editor_asset_installer.cpp
@@ -64,7 +64,8 @@ void EditorAssetInstaller::open(const String &p_path, int p_depth) {
package_path = p_path;
Set<String> files_sorted;
- zlib_filefunc_def io = zipio_create_io();
+ Ref<FileAccess> io_fa;
+ zlib_filefunc_def io = zipio_create_io(&io_fa);
unzFile pkg = unzOpen2(p_path.utf8().get_data(), &io);
if (!pkg) {
@@ -237,7 +238,8 @@ void EditorAssetInstaller::open(const String &p_path, int p_depth) {
}
void EditorAssetInstaller::ok_pressed() {
- zlib_filefunc_def io = zipio_create_io();
+ Ref<FileAccess> io_fa;
+ zlib_filefunc_def io = zipio_create_io(&io_fa);
unzFile pkg = unzOpen2(package_path.utf8().get_data(), &io);
if (!pkg) {
diff --git a/editor/editor_command_palette.cpp b/editor/editor_command_palette.cpp
index d13d1a6c68..bb82da6666 100644
--- a/editor/editor_command_palette.cpp
+++ b/editor/editor_command_palette.cpp
@@ -57,20 +57,19 @@ float EditorCommandPalette::_score_path(const String &p_search, const String &p_
}
void EditorCommandPalette::_update_command_search(const String &search_text) {
- commands.get_key_list(&command_keys);
- ERR_FAIL_COND(command_keys.is_empty());
+ ERR_FAIL_COND(commands.size() == 0);
Map<String, TreeItem *> sections;
TreeItem *first_section = nullptr;
// Filter possible candidates.
Vector<CommandEntry> entries;
- for (int i = 0; i < command_keys.size(); i++) {
+ for (const KeyValue<String, Command> &E : commands) {
CommandEntry r;
- r.key_name = command_keys[i];
- r.display_name = commands[r.key_name].name;
- r.shortcut_text = commands[r.key_name].shortcut;
- r.last_used = commands[r.key_name].last_used;
+ r.key_name = E.key;
+ r.display_name = E.value.name;
+ r.shortcut_text = E.value.shortcut;
+ r.last_used = E.value.last_used;
if (search_text.is_subsequence_ofn(r.display_name)) {
if (!search_text.is_empty()) {
@@ -180,7 +179,9 @@ void EditorCommandPalette::open_popup() {
}
void EditorCommandPalette::get_actions_list(List<String> *p_list) const {
- commands.get_key_list(p_list);
+ for (const KeyValue<String, Command> &E : commands) {
+ p_list->push_back(E.key);
+ }
}
void EditorCommandPalette::remove_command(String p_key_name) {
@@ -229,17 +230,14 @@ void EditorCommandPalette::execute_command(String &p_command_key) {
}
void EditorCommandPalette::register_shortcuts_as_command() {
- const String *key = nullptr;
- key = unregistered_shortcuts.next(key);
- while (key != nullptr) {
- String command_name = unregistered_shortcuts[*key].first;
- Ref<Shortcut> shortcut = unregistered_shortcuts[*key].second;
+ for (const KeyValue<String, Pair<String, Ref<Shortcut>>> &E : unregistered_shortcuts) {
+ String command_name = E.value.first;
+ Ref<Shortcut> shortcut = E.value.second;
Ref<InputEventShortcut> ev;
ev.instantiate();
ev->set_shortcut(shortcut);
String shortcut_text = String(shortcut->get_as_text());
- add_command(command_name, *key, callable_mp(EditorNode::get_singleton()->get_viewport(), &Viewport::push_unhandled_input), varray(ev, false), shortcut_text);
- key = unregistered_shortcuts.next(key);
+ add_command(command_name, E.key, callable_mp(EditorNode::get_singleton()->get_viewport(), &Viewport::push_unhandled_input), varray(ev, false), shortcut_text);
}
unregistered_shortcuts.clear();
@@ -276,12 +274,10 @@ void EditorCommandPalette::_theme_changed() {
void EditorCommandPalette::_save_history() const {
Dictionary command_history;
- List<String> command_keys;
- commands.get_key_list(&command_keys);
- for (const String &key : command_keys) {
- if (commands[key].last_used > 0) {
- command_history[key] = commands[key].last_used;
+ for (const KeyValue<String, Command> &E : commands) {
+ if (E.value.last_used > 0) {
+ command_history[E.key] = E.value.last_used;
}
}
EditorSettings::get_singleton()->set_project_metadata("command_palette", "command_history", command_history);
diff --git a/editor/editor_data.cpp b/editor/editor_data.cpp
index 7ce483d788..f770af8100 100644
--- a/editor/editor_data.cpp
+++ b/editor/editor_data.cpp
@@ -945,13 +945,10 @@ void EditorData::script_class_set_name(const String &p_path, const StringName &p
}
void EditorData::script_class_save_icon_paths() {
- List<StringName> keys;
- _script_class_icon_paths.get_key_list(&keys);
-
Dictionary d;
- for (const StringName &E : keys) {
- if (ScriptServer::is_global_class(E)) {
- d[E] = _script_class_icon_paths[E];
+ for (const KeyValue<StringName, String> &E : _script_class_icon_paths) {
+ if (ScriptServer::is_global_class(E.key)) {
+ d[E.key] = E.value;
}
}
diff --git a/editor/editor_export.cpp b/editor/editor_export.cpp
index a21ee46818..ef99425f68 100644
--- a/editor/editor_export.cpp
+++ b/editor/editor_export.cpp
@@ -1334,7 +1334,8 @@ Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, b
Error EditorExportPlatform::save_zip(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path) {
EditorProgress ep("savezip", TTR("Packing"), 102, true);
- zlib_filefunc_def io = zipio_create_io();
+ Ref<FileAccess> io_fa;
+ zlib_filefunc_def io = zipio_create_io(&io_fa);
zipFile zip = zipOpen2(p_path.utf8().get_data(), APPEND_STATUS_CREATE, nullptr, &io);
ZipData zd;
diff --git a/editor/editor_file_dialog.cpp b/editor/editor_file_dialog.cpp
index 84d656fc20..13084e2aab 100644
--- a/editor/editor_file_dialog.cpp
+++ b/editor/editor_file_dialog.cpp
@@ -529,7 +529,7 @@ void EditorFileDialog::_multi_selected(int p_item, bool p_selected) {
get_ok_button()->set_disabled(_is_open_should_be_disabled());
}
-void EditorFileDialog::_items_clear_selection(int p_item, const Vector2 &p_pos, MouseButton p_mouse_button_index) {
+void EditorFileDialog::_items_clear_selection(const Vector2 &p_pos, MouseButton p_mouse_button_index) {
if (p_mouse_button_index != MouseButton::LEFT) {
return;
}
diff --git a/editor/editor_file_dialog.h b/editor/editor_file_dialog.h
index d9a58a5b7f..5f2e29b690 100644
--- a/editor/editor_file_dialog.h
+++ b/editor/editor_file_dialog.h
@@ -165,7 +165,7 @@ private:
void _item_selected(int p_item);
void _multi_selected(int p_item, bool p_selected);
- void _items_clear_selection(int p_item, const Vector2 &p_pos, MouseButton p_mouse_button_index);
+ void _items_clear_selection(const Vector2 &p_pos, MouseButton p_mouse_button_index);
void _item_dc_selected(int p_item);
void _item_list_item_rmb_clicked(int p_item, const Vector2 &p_pos, MouseButton p_mouse_button_index);
diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp
index dfd768d0d0..b24268ade7 100644
--- a/editor/editor_help.cpp
+++ b/editor/editor_help.cpp
@@ -123,37 +123,34 @@ void EditorHelp::_class_desc_select(const String &p_select) {
return;
}
- if (link.contains(".")) {
- emit_signal(SNAME("go_to_help"), topic + ":" + link.get_slice(".", 0) + ":" + link.get_slice(".", 1));
- } else {
- if (table->has(link)) {
- // Found in the current page
- class_desc->scroll_to_paragraph((*table)[link]);
- } else {
- if (topic == "class_enum") {
- // Try to find the enum in @GlobalScope
- const DocData::ClassDoc &cd = doc->class_list["@GlobalScope"];
-
- for (int i = 0; i < cd.constants.size(); i++) {
- if (cd.constants[i].enumeration == link) {
- // Found in @GlobalScope
- emit_signal(SNAME("go_to_help"), topic + ":@GlobalScope:" + link);
- break;
- }
- }
- } else if (topic == "class_constant") {
- // Try to find the constant in @GlobalScope
- const DocData::ClassDoc &cd = doc->class_list["@GlobalScope"];
-
- for (int i = 0; i < cd.constants.size(); i++) {
- if (cd.constants[i].name == link) {
- // Found in @GlobalScope
- emit_signal(SNAME("go_to_help"), topic + ":@GlobalScope:" + link);
- break;
- }
- }
+ // Case order is important here to correctly handle edge cases like Variant.Type in @GlobalScope.
+ if (table->has(link)) {
+ // Found in the current page.
+ class_desc->scroll_to_paragraph((*table)[link]);
+ } else if (topic == "class_enum") {
+ // Try to find the enum in @GlobalScope.
+ const DocData::ClassDoc &cd = doc->class_list["@GlobalScope"];
+
+ for (int i = 0; i < cd.constants.size(); i++) {
+ if (cd.constants[i].enumeration == link) {
+ // Found in @GlobalScope.
+ emit_signal(SNAME("go_to_help"), topic + ":@GlobalScope:" + link);
+ break;
}
}
+ } else if (topic == "class_constant") {
+ // Try to find the constant in @GlobalScope.
+ const DocData::ClassDoc &cd = doc->class_list["@GlobalScope"];
+
+ for (int i = 0; i < cd.constants.size(); i++) {
+ if (cd.constants[i].name == link) {
+ // Found in @GlobalScope.
+ emit_signal(SNAME("go_to_help"), topic + ":@GlobalScope:" + link);
+ break;
+ }
+ }
+ } else if (link.contains(".")) {
+ emit_signal(SNAME("go_to_help"), topic + ":" + link.get_slice(".", 0) + ":" + link.get_slice(".", 1));
}
} else if (p_select.begins_with("http")) {
OS::get_singleton()->shell_open(p_select);
@@ -339,7 +336,7 @@ Error EditorHelp::_goto_desc(const String &p_class, int p_vscr) {
description_line = 0;
if (p_class == edited_class) {
- return OK; //already there
+ return OK; // Already there.
}
edited_class = p_class;
@@ -885,7 +882,7 @@ void EditorHelp::_update_doc() {
data_type_names["style"] = TTR("Styles");
for (int i = 0; i < cd.theme_properties.size(); i++) {
- theme_property_line[cd.theme_properties[i].name] = class_desc->get_line_count() - 2; //gets overridden if description
+ theme_property_line[cd.theme_properties[i].name] = class_desc->get_line_count() - 2; // Gets overridden if description.
if (theme_data_type != cd.theme_properties[i].data_type) {
theme_data_type = cd.theme_properties[i].data_type;
@@ -970,7 +967,7 @@ void EditorHelp::_update_doc() {
class_desc->push_indent(1);
for (int i = 0; i < cd.signals.size(); i++) {
- signal_line[cd.signals[i].name] = class_desc->get_line_count() - 2; //gets overridden if description
+ signal_line[cd.signals[i].name] = class_desc->get_line_count() - 2; // Gets overridden if description.
class_desc->push_font(doc_code_font); // monofont
class_desc->push_color(headline_color);
@@ -1100,7 +1097,7 @@ void EditorHelp::_update_doc() {
enumValuesContainer[enum_list[i].name] = enumStartingLine;
}
- // Add the enum constant line to the constant_line map so we can locate it as a constant
+ // Add the enum constant line to the constant_line map so we can locate it as a constant.
constant_line[enum_list[i].name] = class_desc->get_line_count() - 2;
class_desc->push_font(doc_code_font);
@@ -1417,7 +1414,7 @@ void EditorHelp::_help_callback(const String &p_topic) {
name = p_topic.get_slice(":", 2);
}
- _request_help(clss); //first go to class
+ _request_help(clss); // First go to class.
int line = 0;
@@ -1486,7 +1483,7 @@ static void _add_text_to_rt(const String &p_bbcode, RichTextLabel *p_rt) {
String bbcode = p_bbcode.dedent().replace("\t", "").replace("\r", "").strip_edges();
- // Select the correct code examples
+ // Select the correct code examples.
switch ((int)EDITOR_GET("text_editor/help/class_reference_examples")) {
case 0: // GDScript
bbcode = bbcode.replace("[gdscript]", "[codeblock]");
@@ -1531,13 +1528,13 @@ static void _add_text_to_rt(const String &p_bbcode, RichTextLabel *p_rt) {
break;
}
- // Remove codeblocks (they would be printed otherwise)
+ // Remove codeblocks (they would be printed otherwise).
bbcode = bbcode.replace("[codeblocks]\n", "");
bbcode = bbcode.replace("\n[/codeblocks]", "");
bbcode = bbcode.replace("[codeblocks]", "");
bbcode = bbcode.replace("[/codeblocks]", "");
- // remove extra new lines around code blocks
+ // Remove extra new lines around code blocks.
bbcode = bbcode.replace("[codeblock]\n", "[codeblock]");
bbcode = bbcode.replace("\n[/codeblock]", "[/codeblock]");
@@ -1561,7 +1558,7 @@ static void _add_text_to_rt(const String &p_bbcode, RichTextLabel *p_rt) {
}
if (brk_pos == bbcode.length()) {
- break; //nothing else to add
+ break; // Nothing else to add.
}
int brk_end = bbcode.find("]", brk_pos + 1);
@@ -1627,45 +1624,45 @@ static void _add_text_to_rt(const String &p_bbcode, RichTextLabel *p_rt) {
pos = brk_end + 1;
} else if (tag == "b") {
- //use bold font
+ // Use bold font.
p_rt->push_font(doc_bold_font);
pos = brk_end + 1;
tag_stack.push_front(tag);
} else if (tag == "i") {
- //use italics font
+ // Use italics font.
p_rt->push_font(doc_italic_font);
pos = brk_end + 1;
tag_stack.push_front(tag);
} else if (tag == "code" || tag == "codeblock") {
- //use monospace font
+ // Use monospace font.
p_rt->push_font(doc_code_font);
p_rt->push_color(code_color);
code_tag = true;
pos = brk_end + 1;
tag_stack.push_front(tag);
} else if (tag == "kbd") {
- //use keyboard font with custom color
+ // Use keyboard font with custom color.
p_rt->push_font(doc_kbd_font);
p_rt->push_color(kbd_color);
- code_tag = true; // though not strictly a code tag, logic is similar
+ code_tag = true; // Though not strictly a code tag, logic is similar.
pos = brk_end + 1;
tag_stack.push_front(tag);
} else if (tag == "center") {
- //align to center
+ // Align to center.
p_rt->push_paragraph(HORIZONTAL_ALIGNMENT_CENTER, Control::TEXT_DIRECTION_AUTO, "");
pos = brk_end + 1;
tag_stack.push_front(tag);
} else if (tag == "br") {
- //force a line break
+ // Force a line break.
p_rt->add_newline();
pos = brk_end + 1;
} else if (tag == "u") {
- //use underline
+ // Use underline.
p_rt->push_underline();
pos = brk_end + 1;
tag_stack.push_front(tag);
} else if (tag == "s") {
- //use strikethrough
+ // Use strikethrough.
p_rt->push_strikethrough();
pos = brk_end + 1;
tag_stack.push_front(tag);
@@ -1741,7 +1738,7 @@ void EditorHelp::_wait_for_thread() {
void EditorHelp::_gen_doc_thread(void *p_udata) {
DocTools compdoc;
compdoc.load_compressed(_doc_data_compressed, _doc_data_compressed_size, _doc_data_uncompressed_size);
- doc->merge_from(compdoc); //ensure all is up to date
+ doc->merge_from(compdoc); // Ensure all is up to date.
}
static bool doc_gen_use_threads = true;
@@ -1935,7 +1932,7 @@ void EditorHelpBit::_meta_clicked(String p_select) {
String m = p_select.substr(1, p_select.length());
if (m.contains(".")) {
- _go_to_help("class_method:" + m.get_slice(".", 0) + ":" + m.get_slice(".", 0)); //must go somewhere else
+ _go_to_help("class_method:" + m.get_slice(".", 0) + ":" + m.get_slice(".", 0)); // Must go somewhere else.
}
}
}
diff --git a/editor/editor_help_search.h b/editor/editor_help_search.h
index d89bb0959c..14a8c46a79 100644
--- a/editor/editor_help_search.h
+++ b/editor/editor_help_search.h
@@ -31,7 +31,7 @@
#ifndef EDITOR_HELP_SEARCH_H
#define EDITOR_HELP_SEARCH_H
-#include "core/templates/ordered_hash_map.h"
+#include "core/templates/map.h"
#include "editor/code_editor.h"
#include "editor/editor_help.h"
#include "editor/editor_plugin.h"
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 194021669f..3b71c85422 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -623,8 +623,6 @@ void EditorNode::_notification(int p_what) {
ResourceImporterTexture::get_singleton()->update_imports();
- // if using a main thread only renderer, we need to update the resource previews
- EditorResourcePreview::get_singleton()->update();
} break;
case NOTIFICATION_ENTER_TREE: {
diff --git a/editor/editor_plugin_settings.cpp b/editor/editor_plugin_settings.cpp
index b728ce64c9..85a906ef51 100644
--- a/editor/editor_plugin_settings.cpp
+++ b/editor/editor_plugin_settings.cpp
@@ -200,12 +200,9 @@ EditorPluginSettings::EditorPluginSettings() {
l->set_theme_type_variation("HeaderSmall");
title_hb->add_child(l);
title_hb->add_spacer();
- create_plugin = memnew(Button(TTR("Create")));
+ Button *create_plugin = memnew(Button(TTR("Create New Plugin")));
create_plugin->connect("pressed", callable_mp(this, &EditorPluginSettings::_create_clicked));
title_hb->add_child(create_plugin);
- update_list = memnew(Button(TTR("Update")));
- update_list->connect("pressed", callable_mp(this, &EditorPluginSettings::update_plugins));
- title_hb->add_child(update_list);
add_child(title_hb);
plugin_list = memnew(Tree);
diff --git a/editor/editor_plugin_settings.h b/editor/editor_plugin_settings.h
index 826bb8c00f..121534b613 100644
--- a/editor/editor_plugin_settings.h
+++ b/editor/editor_plugin_settings.h
@@ -45,8 +45,6 @@ class EditorPluginSettings : public VBoxContainer {
};
PluginConfigDialog *plugin_config_dialog = nullptr;
- Button *create_plugin = nullptr;
- Button *update_list = nullptr;
Tree *plugin_list = nullptr;
bool updating = false;
diff --git a/editor/editor_resource_preview.cpp b/editor/editor_resource_preview.cpp
index 6d5b20e591..dffb378408 100644
--- a/editor/editor_resource_preview.cpp
+++ b/editor/editor_resource_preview.cpp
@@ -430,12 +430,8 @@ void EditorResourcePreview::check_for_invalidation(const String &p_path) {
}
void EditorResourcePreview::start() {
- if (OS::get_singleton()->get_render_main_thread_mode() == OS::RENDER_ANY_THREAD) {
- ERR_FAIL_COND_MSG(thread.is_started(), "Thread already started.");
- thread.start(_thread_func, this);
- } else {
- _mainthread_only = true;
- }
+ ERR_FAIL_COND_MSG(thread.is_started(), "Thread already started.");
+ thread.start(_thread_func, this);
}
void EditorResourcePreview::stop() {
@@ -458,18 +454,3 @@ EditorResourcePreview::EditorResourcePreview() {
EditorResourcePreview::~EditorResourcePreview() {
stop();
}
-
-void EditorResourcePreview::update() {
- if (!_mainthread_only) {
- return;
- }
-
- if (!exit.is_set()) {
- // no need to even lock the mutex if the size is zero
- // there is no problem if queue.size() is wrong, even if
- // there was a race condition.
- if (queue.size()) {
- _iterate();
- }
- }
-}
diff --git a/editor/editor_resource_preview.h b/editor/editor_resource_preview.h
index 769340c36f..4e91df8e08 100644
--- a/editor/editor_resource_preview.h
+++ b/editor/editor_resource_preview.h
@@ -81,11 +81,6 @@ class EditorResourcePreview : public Node {
SafeFlag exit;
SafeFlag exited;
- // when running from GLES, we want to run the previews
- // in the main thread using an update, rather than create
- // a separate thread
- bool _mainthread_only = false;
-
struct Item {
Ref<Texture2D> preview;
Ref<Texture2D> small_preview;
@@ -125,9 +120,6 @@ public:
void start();
void stop();
- // for single threaded mode
- void update();
-
EditorResourcePreview();
~EditorResourcePreview();
};
diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp
index bdabff20f9..5d846028c5 100644
--- a/editor/editor_settings.cpp
+++ b/editor/editor_settings.cpp
@@ -141,7 +141,7 @@ bool EditorSettings::_get(const StringName &p_name, Variant &r_ret) const {
if (p_name == "shortcuts") {
Array save_array;
- const OrderedHashMap<String, List<Ref<InputEvent>>> &builtin_list = InputMap::get_singleton()->get_builtins();
+ const HashMap<String, List<Ref<InputEvent>>> &builtin_list = InputMap::get_singleton()->get_builtins();
for (const KeyValue<String, Ref<Shortcut>> &shortcut_definition : shortcuts) {
Ref<Shortcut> sc = shortcut_definition.value;
@@ -244,18 +244,17 @@ struct _EVCSort {
void EditorSettings::_get_property_list(List<PropertyInfo> *p_list) const {
_THREAD_SAFE_METHOD_
- const String *k = nullptr;
Set<_EVCSort> vclist;
- while ((k = props.next(k))) {
- const VariantContainer *v = props.getptr(*k);
+ for (const KeyValue<String, VariantContainer> &E : props) {
+ const VariantContainer *v = &E.value;
if (v->hide_from_editor) {
continue;
}
_EVCSort vc;
- vc.name = *k;
+ vc.name = E.key;
vc.order = v->order;
vc.type = v->variant.get_type();
vc.save = v->save;
@@ -789,7 +788,11 @@ bool EditorSettings::_save_text_editor_theme(String p_file) {
Ref<ConfigFile> cf = memnew(ConfigFile); // hex is better?
List<String> keys;
- props.get_key_list(&keys);
+
+ for (const KeyValue<String, VariantContainer> &E : props) {
+ keys.push_back(E.key);
+ }
+
keys.sort();
for (const String &key : keys) {
@@ -1421,10 +1424,10 @@ Ref<Shortcut> EditorSettings::get_shortcut(const String &p_name) const {
// If there was no override, check the default builtins to see if it has an InputEvent for the provided name.
if (sc.is_null()) {
- const OrderedHashMap<String, List<Ref<InputEvent>>>::ConstElement builtin_default = InputMap::get_singleton()->get_builtins_with_feature_overrides_applied().find(p_name);
+ const HashMap<String, List<Ref<InputEvent>>>::ConstIterator builtin_default = InputMap::get_singleton()->get_builtins_with_feature_overrides_applied().find(p_name);
if (builtin_default) {
sc.instantiate();
- sc->set_events_list(&builtin_default.get());
+ sc->set_events_list(&builtin_default->value);
sc->set_name(InputMap::get_singleton()->get_builtin_display_name(p_name));
}
}
@@ -1562,9 +1565,9 @@ void EditorSettings::set_builtin_action_override(const String &p_name, const Arr
// Check if the provided event array is same as built-in. If it is, it does not need to be added to the overrides.
// Note that event order must also be the same.
bool same_as_builtin = true;
- OrderedHashMap<String, List<Ref<InputEvent>>>::ConstElement builtin_default = InputMap::get_singleton()->get_builtins_with_feature_overrides_applied().find(p_name);
+ HashMap<String, List<Ref<InputEvent>>>::ConstIterator builtin_default = InputMap::get_singleton()->get_builtins_with_feature_overrides_applied().find(p_name);
if (builtin_default) {
- List<Ref<InputEvent>> builtin_events = builtin_default.get();
+ const List<Ref<InputEvent>> &builtin_events = builtin_default->value;
// In the editor we only care about key events.
List<Ref<InputEventKey>> builtin_key_events;
diff --git a/editor/editor_settings_dialog.cpp b/editor/editor_settings_dialog.cpp
index ab029c1d30..94775f8c76 100644
--- a/editor/editor_settings_dialog.cpp
+++ b/editor/editor_settings_dialog.cpp
@@ -248,11 +248,11 @@ void EditorSettingsDialog::_update_shortcut_events(const String &p_path, const A
undo_redo->commit_action();
}
-Array EditorSettingsDialog::_event_list_to_array_helper(List<Ref<InputEvent>> &p_events) {
+Array EditorSettingsDialog::_event_list_to_array_helper(const List<Ref<InputEvent>> &p_events) {
Array events;
// Convert the list to an array, and only keep key events as this is for the editor.
- for (List<Ref<InputEvent>>::Element *E = p_events.front(); E; E = E->next()) {
+ for (const List<Ref<InputEvent>>::Element *E = p_events.front(); E; E = E->next()) {
Ref<InputEventKey> k = E->get();
if (k.is_valid()) {
events.append(E->get());
@@ -374,10 +374,9 @@ void EditorSettingsDialog::_update_shortcuts() {
common_section->set_custom_bg_color(1, shortcuts->get_theme_color(SNAME("prop_subsection"), SNAME("Editor")));
// Get the action map for the editor, and add each item to the "Common" section.
- OrderedHashMap<StringName, InputMap::Action> action_map = InputMap::get_singleton()->get_action_map();
- for (OrderedHashMap<StringName, InputMap::Action>::Element E = action_map.front(); E; E = E.next()) {
- String action_name = E.key();
- InputMap::Action action = E.get();
+ for (const KeyValue<StringName, InputMap::Action> &E : InputMap::get_singleton()->get_action_map()) {
+ const String &action_name = E.key;
+ const InputMap::Action &action = E.value;
Array events; // Need to get the list of events into an array so it can be set as metadata on the item.
Vector<String> event_strings;
@@ -387,10 +386,10 @@ void EditorSettingsDialog::_update_shortcuts() {
continue;
}
- List<Ref<InputEvent>> all_default_events = InputMap::get_singleton()->get_builtins_with_feature_overrides_applied().find(action_name).value();
+ const List<Ref<InputEvent>> &all_default_events = InputMap::get_singleton()->get_builtins_with_feature_overrides_applied().find(action_name)->value;
List<Ref<InputEventKey>> key_default_events;
// Remove all non-key events from the defaults. Only check keys, since we are in the editor.
- for (List<Ref<InputEvent>>::Element *I = all_default_events.front(); I; I = I->next()) {
+ for (const List<Ref<InputEvent>>::Element *I = all_default_events.front(); I; I = I->next()) {
Ref<InputEventKey> k = I->get();
if (k.is_valid()) {
key_default_events.push_back(k);
diff --git a/editor/editor_settings_dialog.h b/editor/editor_settings_dialog.h
index 9a34eac7ef..294186a509 100644
--- a/editor/editor_settings_dialog.h
+++ b/editor/editor_settings_dialog.h
@@ -89,7 +89,7 @@ class EditorSettingsDialog : public AcceptDialog {
void _event_config_confirmed();
void _create_shortcut_treeitem(TreeItem *p_parent, const String &p_shortcut_identifier, const String &p_display, Array &p_events, bool p_allow_revert, bool p_is_common, bool p_is_collapsed);
- Array _event_list_to_array_helper(List<Ref<InputEvent>> &p_events);
+ Array _event_list_to_array_helper(const List<Ref<InputEvent>> &p_events);
void _update_builtin_action(const String &p_name, const Array &p_events);
void _update_shortcut_events(const String &p_path, const Array &p_events);
diff --git a/editor/export_template_manager.cpp b/editor/export_template_manager.cpp
index 3526b4ce4c..68796683d2 100644
--- a/editor/export_template_manager.cpp
+++ b/editor/export_template_manager.cpp
@@ -374,7 +374,8 @@ void ExportTemplateManager::_install_file() {
}
bool ExportTemplateManager::_install_file_selected(const String &p_file, bool p_skip_progress) {
- zlib_filefunc_def io = zipio_create_io();
+ Ref<FileAccess> io_fa;
+ zlib_filefunc_def io = zipio_create_io(&io_fa);
unzFile pkg = unzOpen2(p_file.utf8().get_data(), &io);
if (!pkg) {
@@ -676,7 +677,8 @@ Error ExportTemplateManager::install_android_template_from_file(const String &p_
// Uncompress source template.
- zlib_filefunc_def io = zipio_create_io();
+ Ref<FileAccess> io_fa;
+ zlib_filefunc_def io = zipio_create_io(&io_fa);
unzFile pkg = unzOpen2(p_file.utf8().get_data(), &io);
ERR_FAIL_COND_V_MSG(!pkg, ERR_CANT_OPEN, "Android sources not in ZIP format.");
diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp
index 47c016803c..aae8cf25b6 100644
--- a/editor/filesystem_dock.cpp
+++ b/editor/filesystem_dock.cpp
@@ -2637,7 +2637,7 @@ void FileSystemDock::_tree_empty_selected() {
tree->deselect_all();
}
-void FileSystemDock::_file_list_rmb_clicked(int p_item, const Vector2 &p_pos, MouseButton p_mouse_button_index) {
+void FileSystemDock::_file_list_item_clicked(int p_item, const Vector2 &p_pos, MouseButton p_mouse_button_index) {
if (p_mouse_button_index != MouseButton::RIGHT) {
return;
}
@@ -2665,7 +2665,11 @@ void FileSystemDock::_file_list_rmb_clicked(int p_item, const Vector2 &p_pos, Mo
}
}
-void FileSystemDock::_file_list_rmb_pressed(const Vector2 &p_pos) {
+void FileSystemDock::_file_list_empty_clicked(const Vector2 &p_pos, MouseButton p_mouse_button_index) {
+ if (p_mouse_button_index != MouseButton::RIGHT) {
+ return;
+ }
+
// Right click on empty space for file list.
if (searched_string.length() > 0) {
return;
@@ -3132,10 +3136,10 @@ FileSystemDock::FileSystemDock() {
files->set_v_size_flags(SIZE_EXPAND_FILL);
files->set_select_mode(ItemList::SELECT_MULTI);
files->set_drag_forwarding(this);
- files->connect("item_clicked", callable_mp(this, &FileSystemDock::_file_list_rmb_clicked));
+ files->connect("item_clicked", callable_mp(this, &FileSystemDock::_file_list_item_clicked));
files->connect("gui_input", callable_mp(this, &FileSystemDock::_file_list_gui_input));
files->connect("multi_selected", callable_mp(this, &FileSystemDock::_file_multi_selected));
- files->connect("rmb_clicked", callable_mp(this, &FileSystemDock::_file_list_rmb_pressed));
+ files->connect("empty_clicked", callable_mp(this, &FileSystemDock::_file_list_empty_clicked));
files->set_custom_minimum_size(Size2(0, 15 * EDSCALE));
files->set_allow_rmb_select(true);
file_list_vb->add_child(files);
diff --git a/editor/filesystem_dock.h b/editor/filesystem_dock.h
index f6c4e271bf..fc24b3e9fd 100644
--- a/editor/filesystem_dock.h
+++ b/editor/filesystem_dock.h
@@ -260,8 +260,8 @@ private:
void _file_and_folders_fill_popup(PopupMenu *p_popup, Vector<String> p_paths, bool p_display_path_dependent_options = true);
void _tree_rmb_select(const Vector2 &p_pos);
void _tree_rmb_empty(const Vector2 &p_pos);
- void _file_list_rmb_clicked(int p_item, const Vector2 &p_pos, MouseButton p_mouse_button_index);
- void _file_list_rmb_pressed(const Vector2 &p_pos);
+ void _file_list_item_clicked(int p_item, const Vector2 &p_pos, MouseButton p_mouse_button_index);
+ void _file_list_empty_clicked(const Vector2 &p_pos, MouseButton p_mouse_button_index);
void _tree_empty_selected();
struct FileInfo {
diff --git a/editor/import/resource_importer_scene.cpp b/editor/import/resource_importer_scene.cpp
index 06a84ff23c..43b52177c6 100644
--- a/editor/import/resource_importer_scene.cpp
+++ b/editor/import/resource_importer_scene.cpp
@@ -1531,7 +1531,7 @@ void ResourceImporterScene::get_import_options(const String &p_path, List<Import
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "meshes/generate_lods"), true));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "meshes/create_shadow_meshes"), true));
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "meshes/light_baking", PROPERTY_HINT_ENUM, "Disabled,Static (VoxelGI/SDFGI),Static Lightmaps (VoxelGI/SDFGI/LightmapGI),Dynamic (VoxelGI only)", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 1));
- r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "meshes/lightmap_texel_size", PROPERTY_HINT_RANGE, "0.001,100,0.001"), 0.1));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "meshes/lightmap_texel_size", PROPERTY_HINT_RANGE, "0.001,100,0.001"), 0.2));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "skins/use_named_skins"), true));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/import"), true));
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "animation/fps", PROPERTY_HINT_RANGE, "1,120,1"), 30));
@@ -1737,7 +1737,7 @@ void ResourceImporterScene::_optimize_track_usage(AnimationPlayer *p_player, Ani
p_player->get_animation_list(&anims);
Node *parent = p_player->get_parent();
ERR_FAIL_COND(parent == nullptr);
- OrderedHashMap<NodePath, uint32_t> used_tracks[TRACK_CHANNEL_MAX];
+ HashMap<NodePath, uint32_t> used_tracks[TRACK_CHANNEL_MAX];
bool tracks_to_add = false;
static const Animation::TrackType track_types[TRACK_CHANNEL_MAX] = { Animation::TYPE_POSITION_3D, Animation::TYPE_ROTATION_3D, Animation::TYPE_SCALE_3D, Animation::TYPE_BLEND_SHAPE };
for (const StringName &I : anims) {
@@ -1790,12 +1790,12 @@ void ResourceImporterScene::_optimize_track_usage(AnimationPlayer *p_player, Ani
used_tracks[j][path] = pass;
}
- for (OrderedHashMap<NodePath, uint32_t>::Element J = used_tracks[j].front(); J; J = J.next()) {
- if (J.get() == pass) {
+ for (const KeyValue<NodePath, uint32_t> &J : used_tracks[j]) {
+ if (J.value == pass) {
continue;
}
- NodePath path = J.key();
+ NodePath path = J.key;
Node *n = parent->get_node(path);
if (j == TRACK_CHANNEL_BLEND_SHAPE) {
diff --git a/editor/plugin_config_dialog.cpp b/editor/plugin_config_dialog.cpp
index 755bf7ce07..77e4905341 100644
--- a/editor/plugin_config_dialog.cpp
+++ b/editor/plugin_config_dialog.cpp
@@ -47,7 +47,7 @@ void PluginConfigDialog::_clear_fields() {
}
void PluginConfigDialog::_on_confirmed() {
- String path = "res://addons/" + subfolder_edit->get_text();
+ String path = "res://addons/" + _get_subfolder();
if (!_edit_mode) {
Ref<DirAccess> d = DirAccess::create(DirAccess::ACCESS_RESOURCES);
@@ -56,32 +56,35 @@ void PluginConfigDialog::_on_confirmed() {
}
}
+ int lang_idx = script_option_edit->get_selected();
+ String ext = ScriptServer::get_language(lang_idx)->get_extension();
+ String script_name = script_edit->get_text().is_empty() ? _get_subfolder() : script_edit->get_text();
+ if (script_name.get_extension().is_empty()) {
+ script_name += "." + ext;
+ }
+ String script_path = path.plus_file(script_name);
+
Ref<ConfigFile> cf = memnew(ConfigFile);
cf->set_value("plugin", "name", name_edit->get_text());
cf->set_value("plugin", "description", desc_edit->get_text());
cf->set_value("plugin", "author", author_edit->get_text());
cf->set_value("plugin", "version", version_edit->get_text());
- cf->set_value("plugin", "script", script_edit->get_text());
+ cf->set_value("plugin", "script", script_name);
cf->save(path.plus_file("plugin.cfg"));
if (!_edit_mode) {
- int lang_idx = script_option_edit->get_selected();
- String lang_name = ScriptServer::get_language(lang_idx)->get_name();
-
- Ref<Script> script;
- String script_path = path.plus_file(script_edit->get_text());
- String class_name = script_path.get_file().get_basename();
+ String class_name = script_name.get_basename();
String template_content = "";
Vector<ScriptLanguage::ScriptTemplate> templates = ScriptServer::get_language(lang_idx)->get_built_in_templates("EditorPlugin");
- if (templates.size() > 0) {
- template_content = templates.get(0).content;
+ if (!templates.is_empty()) {
+ template_content = templates[0].content;
}
- script = ScriptServer::get_language(lang_idx)->make_template(template_content, class_name, "EditorPlugin");
+ Ref<Script> script = ScriptServer::get_language(lang_idx)->make_template(template_content, class_name, "EditorPlugin");
script->set_path(script_path);
ResourceSaver::save(script_path, script);
- emit_signal(SNAME("plugin_ready"), script.operator->(), active_edit->is_pressed() ? _to_absolute_plugin_path(subfolder_edit->get_text()) : "");
+ emit_signal(SNAME("plugin_ready"), script.ptr(), active_edit->is_pressed() ? _to_absolute_plugin_path(_get_subfolder()) : "");
} else {
EditorNode::get_singleton()->get_project_settings()->update_plugins();
}
@@ -119,27 +122,18 @@ void PluginConfigDialog::_on_required_text_changed(const String &) {
name_validation->set_texture(invalid_icon);
name_validation->set_tooltip(TTR("Plugin name cannot be blank."));
}
- if (script_edit->get_text().get_extension() != ext) {
+ if ((!script_edit->get_text().get_extension().is_empty() && script_edit->get_text().get_extension() != ext) || script_edit->get_text().ends_with(".")) {
is_valid = false;
script_validation->set_texture(invalid_icon);
script_validation->set_tooltip(vformat(TTR("Script extension must match chosen language extension (.%s)."), ext));
}
- if (script_edit->get_text().get_basename().is_empty()) {
+ if (!subfolder_edit->get_text().is_empty() && !subfolder_edit->get_text().is_valid_filename()) {
is_valid = false;
- script_validation->set_texture(invalid_icon);
- script_validation->set_tooltip(TTR("Script name cannot be blank."));
- }
- if (subfolder_edit->get_text().is_empty()) {
- is_valid = false;
- subfolder_validation->set_texture(invalid_icon);
- subfolder_validation->set_tooltip(TTR("Subfolder cannot be blank."));
- } else if (!subfolder_edit->get_text().is_valid_filename()) {
subfolder_validation->set_texture(invalid_icon);
subfolder_validation->set_tooltip(TTR("Subfolder name is not a valid folder name."));
} else {
- Ref<DirAccess> dir = DirAccess::create(DirAccess::ACCESS_RESOURCES);
- String path = "res://addons/" + subfolder_edit->get_text();
- if (dir->dir_exists(path) && !_edit_mode) { // Only show this error if in "create" mode.
+ String path = "res://addons/" + _get_subfolder();
+ if (!_edit_mode && DirAccess::exists(path)) { // Only show this error if in "create" mode.
is_valid = false;
subfolder_validation->set_texture(invalid_icon);
subfolder_validation->set_tooltip(TTR("Subfolder cannot be one which already exists."));
@@ -149,6 +143,10 @@ void PluginConfigDialog::_on_required_text_changed(const String &) {
get_ok_button()->set_disabled(!is_valid);
}
+String PluginConfigDialog::_get_subfolder() {
+ return subfolder_edit->get_text().is_empty() ? name_edit->get_text().replace(" ", "_").to_lower() : subfolder_edit->get_text();
+}
+
String PluginConfigDialog::_to_absolute_plugin_path(const String &p_plugin_name) {
return "res://addons/" + p_plugin_name + "/plugin.cfg";
}
@@ -225,6 +223,7 @@ PluginConfigDialog::PluginConfigDialog() {
// Plugin Name
Label *name_lb = memnew(Label);
name_lb->set_text(TTR("Plugin Name:"));
+ name_lb->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
grid->add_child(name_lb);
name_validation = memnew(TextureRect);
@@ -239,6 +238,7 @@ PluginConfigDialog::PluginConfigDialog() {
// Subfolder
Label *subfolder_lb = memnew(Label);
subfolder_lb->set_text(TTR("Subfolder:"));
+ subfolder_lb->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
grid->add_child(subfolder_lb);
subfolder_validation = memnew(TextureRect);
@@ -253,6 +253,7 @@ PluginConfigDialog::PluginConfigDialog() {
// Description
Label *desc_lb = memnew(Label);
desc_lb->set_text(TTR("Description:"));
+ desc_lb->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
grid->add_child(desc_lb);
Control *desc_spacer = memnew(Control);
@@ -266,6 +267,7 @@ PluginConfigDialog::PluginConfigDialog() {
// Author
Label *author_lb = memnew(Label);
author_lb->set_text(TTR("Author:"));
+ author_lb->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
grid->add_child(author_lb);
Control *author_spacer = memnew(Control);
@@ -278,6 +280,7 @@ PluginConfigDialog::PluginConfigDialog() {
// Version
Label *version_lb = memnew(Label);
version_lb->set_text(TTR("Version:"));
+ version_lb->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
grid->add_child(version_lb);
Control *version_spacer = memnew(Control);
@@ -290,6 +293,7 @@ PluginConfigDialog::PluginConfigDialog() {
// Language dropdown
Label *script_option_lb = memnew(Label);
script_option_lb->set_text(TTR("Language:"));
+ script_option_lb->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
grid->add_child(script_option_lb);
Control *script_opt_spacer = memnew(Control);
@@ -311,6 +315,7 @@ PluginConfigDialog::PluginConfigDialog() {
// Plugin Script Name
Label *script_lb = memnew(Label);
script_lb->set_text(TTR("Script Name:"));
+ script_lb->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
grid->add_child(script_lb);
script_validation = memnew(TextureRect);
@@ -326,6 +331,7 @@ PluginConfigDialog::PluginConfigDialog() {
// TODO Make this option work better with languages like C#. Right now, it does not work because the C# project must be compiled first.
Label *active_lb = memnew(Label);
active_lb->set_text(TTR("Activate now?"));
+ active_lb->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
grid->add_child(active_lb);
Control *active_spacer = memnew(Control);
diff --git a/editor/plugin_config_dialog.h b/editor/plugin_config_dialog.h
index 5c6043da12..6e0cbea378 100644
--- a/editor/plugin_config_dialog.h
+++ b/editor/plugin_config_dialog.h
@@ -61,6 +61,7 @@ class PluginConfigDialog : public ConfirmationDialog {
void _on_cancelled();
void _on_language_changed(const int p_language);
void _on_required_text_changed(const String &p_text);
+ String _get_subfolder();
static String _to_absolute_plugin_path(const String &p_plugin_name);
diff --git a/editor/plugins/mesh_instance_3d_editor_plugin.cpp b/editor/plugins/mesh_instance_3d_editor_plugin.cpp
index d33803213a..1c75591b34 100644
--- a/editor/plugins/mesh_instance_3d_editor_plugin.cpp
+++ b/editor/plugins/mesh_instance_3d_editor_plugin.cpp
@@ -77,12 +77,14 @@ void MeshInstance3DEditor::_menu_option(int p_option) {
StaticBody3D *body = memnew(StaticBody3D);
body->add_child(cshape, true);
- Node *owner = node == get_tree()->get_edited_scene_root() ? node : node->get_owner();
+ Node *owner = get_tree()->get_edited_scene_root();
ur->create_action(TTR("Create Static Trimesh Body"));
ur->add_do_method(node, "add_child", body, true);
ur->add_do_method(body, "set_owner", owner);
ur->add_do_method(cshape, "set_owner", owner);
+ ur->add_do_method(Node3DEditor::get_singleton(), "_request_gizmo", body);
+ ur->add_do_method(Node3DEditor::get_singleton(), "_request_gizmo", cshape);
ur->add_do_reference(body);
ur->add_undo_method(node, "remove_child", body);
ur->commit_action();
@@ -112,11 +114,13 @@ void MeshInstance3DEditor::_menu_option(int p_option) {
StaticBody3D *body = memnew(StaticBody3D);
body->add_child(cshape, true);
- Node *owner = instance == get_tree()->get_edited_scene_root() ? instance : instance->get_owner();
+ Node *owner = get_tree()->get_edited_scene_root();
ur->add_do_method(instance, "add_child", body, true);
ur->add_do_method(body, "set_owner", owner);
ur->add_do_method(cshape, "set_owner", owner);
+ ur->add_do_method(Node3DEditor::get_singleton(), "_request_gizmo", body);
+ ur->add_do_method(Node3DEditor::get_singleton(), "_request_gizmo", cshape);
ur->add_do_reference(body);
ur->add_undo_method(instance, "remove_child", body);
}
@@ -141,7 +145,7 @@ void MeshInstance3DEditor::_menu_option(int p_option) {
cshape->set_shape(shape);
cshape->set_transform(node->get_transform());
- Node *owner = node->get_owner();
+ Node *owner = get_tree()->get_edited_scene_root();
UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
@@ -150,6 +154,7 @@ void MeshInstance3DEditor::_menu_option(int p_option) {
ur->add_do_method(node->get_parent(), "add_child", cshape, true);
ur->add_do_method(node->get_parent(), "move_child", cshape, node->get_index() + 1);
ur->add_do_method(cshape, "set_owner", owner);
+ ur->add_do_method(Node3DEditor::get_singleton(), "_request_gizmo", cshape);
ur->add_do_reference(cshape);
ur->add_undo_method(node->get_parent(), "remove_child", cshape);
ur->commit_action();
@@ -184,11 +189,12 @@ void MeshInstance3DEditor::_menu_option(int p_option) {
cshape->set_shape(shape);
cshape->set_transform(node->get_transform());
- Node *owner = node->get_owner();
+ Node *owner = get_tree()->get_edited_scene_root();
ur->add_do_method(node->get_parent(), "add_child", cshape, true);
ur->add_do_method(node->get_parent(), "move_child", cshape, node->get_index() + 1);
ur->add_do_method(cshape, "set_owner", owner);
+ ur->add_do_method(Node3DEditor::get_singleton(), "_request_gizmo", cshape);
ur->add_do_reference(cshape);
ur->add_undo_method(node->get_parent(), "remove_child", cshape);
@@ -217,14 +223,17 @@ void MeshInstance3DEditor::_menu_option(int p_option) {
for (int i = 0; i < shapes.size(); i++) {
CollisionShape3D *cshape = memnew(CollisionShape3D);
+ cshape->set_name("CollisionShape3D");
+
cshape->set_shape(shapes[i]);
cshape->set_transform(node->get_transform());
- Node *owner = node->get_owner();
+ Node *owner = get_tree()->get_edited_scene_root();
ur->add_do_method(node->get_parent(), "add_child", cshape);
ur->add_do_method(node->get_parent(), "move_child", cshape, node->get_index() + 1);
ur->add_do_method(cshape, "set_owner", owner);
+ ur->add_do_method(Node3DEditor::get_singleton(), "_request_gizmo", cshape);
ur->add_do_reference(cshape);
ur->add_undo_method(node->get_parent(), "remove_child", cshape);
}
@@ -243,13 +252,14 @@ void MeshInstance3DEditor::_menu_option(int p_option) {
NavigationRegion3D *nmi = memnew(NavigationRegion3D);
nmi->set_navigation_mesh(nmesh);
- Node *owner = node == get_tree()->get_edited_scene_root() ? node : node->get_owner();
+ Node *owner = get_tree()->get_edited_scene_root();
UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
ur->create_action(TTR("Create Navigation Mesh"));
ur->add_do_method(node, "add_child", nmi, true);
ur->add_do_method(nmi, "set_owner", owner);
+ ur->add_do_method(Node3DEditor::get_singleton(), "_request_gizmo", nmi);
ur->add_do_reference(nmi);
ur->add_undo_method(node, "remove_child", nmi);
@@ -267,13 +277,52 @@ void MeshInstance3DEditor::_menu_option(int p_option) {
return;
}
- Error err = mesh2->lightmap_unwrap(node->get_global_transform());
+ String path = mesh2->get_path();
+ int srpos = path.find("::");
+ if (srpos != -1) {
+ String base = path.substr(0, srpos);
+ if (ResourceLoader::get_resource_type(base) == "PackedScene") {
+ if (!get_tree()->get_edited_scene_root() || get_tree()->get_edited_scene_root()->get_scene_file_path() != base) {
+ err_dialog->set_text(TTR("Mesh cannot unwrap UVs because it does not belong to the edited scene. Make it unique first."));
+ err_dialog->popup_centered();
+ return;
+ }
+ } else {
+ if (FileAccess::exists(path + ".import")) {
+ err_dialog->set_text(TTR("Mesh cannot unwrap UVs because it belongs to another resource which was imported from another file type. Make it unique first."));
+ err_dialog->popup_centered();
+ return;
+ }
+ }
+ } else {
+ if (FileAccess::exists(path + ".import")) {
+ err_dialog->set_text(TTR("Mesh cannot unwrap UVs because it was imported from another file type. Make it unique first."));
+ err_dialog->popup_centered();
+ return;
+ }
+ }
+
+ Ref<ArrayMesh> unwrapped_mesh = mesh2->duplicate(false);
+
+ Error err = unwrapped_mesh->lightmap_unwrap(node->get_global_transform());
if (err != OK) {
err_dialog->set_text(TTR("UV Unwrap failed, mesh may not be manifold?"));
err_dialog->popup_centered();
return;
}
+ UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Unwrap UV2"));
+
+ ur->add_do_method(node, "set_mesh", unwrapped_mesh);
+ ur->add_do_reference(node);
+ ur->add_do_reference(mesh2.ptr());
+
+ ur->add_undo_method(node, "set_mesh", mesh2);
+ ur->add_undo_reference(unwrapped_mesh.ptr());
+
+ ur->commit_action();
+
} break;
case MENU_OPTION_DEBUG_UV1: {
Ref<Mesh> mesh2 = node->get_mesh();
@@ -418,10 +467,7 @@ void MeshInstance3DEditor::_create_outline_mesh() {
MeshInstance3D *mi = memnew(MeshInstance3D);
mi->set_mesh(mesho);
- Node *owner = node->get_owner();
- if (get_tree()->get_edited_scene_root() == node) {
- owner = node;
- }
+ Node *owner = get_tree()->get_edited_scene_root();
UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
@@ -429,6 +475,7 @@ void MeshInstance3DEditor::_create_outline_mesh() {
ur->add_do_method(node, "add_child", mi, true);
ur->add_do_method(mi, "set_owner", owner);
+ ur->add_do_method(Node3DEditor::get_singleton(), "_request_gizmo", mi);
ur->add_do_reference(mi);
ur->add_undo_method(node, "remove_child", mi);
diff --git a/editor/plugins/node_3d_editor_gizmos.h b/editor/plugins/node_3d_editor_gizmos.h
index f859ceda3b..4df329a2c5 100644
--- a/editor/plugins/node_3d_editor_gizmos.h
+++ b/editor/plugins/node_3d_editor_gizmos.h
@@ -31,8 +31,8 @@
#ifndef NODE_3D_EDITOR_GIZMOS_H
#define NODE_3D_EDITOR_GIZMOS_H
+#include "core/templates/hash_map.h"
#include "core/templates/local_vector.h"
-#include "core/templates/ordered_hash_map.h"
#include "scene/3d/camera_3d.h"
#include "scene/3d/node_3d.h"
#include "scene/3d/skeleton_3d.h"
diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp
index 5240fdf836..8d29e04eec 100644
--- a/editor/plugins/script_editor_plugin.cpp
+++ b/editor/plugins/script_editor_plugin.cpp
@@ -119,9 +119,9 @@ void EditorStandardSyntaxHighlighter::_update_cache() {
}
/* Autoloads. */
- OrderedHashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
- for (OrderedHashMap<StringName, ProjectSettings::AutoloadInfo>::Element E = autoloads.front(); E; E = E.next()) {
- const ProjectSettings::AutoloadInfo &info = E.value();
+ HashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
+ for (const KeyValue<StringName, ProjectSettings::AutoloadInfo> &E : autoloads) {
+ const ProjectSettings::AutoloadInfo &info = E.value;
if (info.is_singleton) {
highlighter->add_keyword_color(info.name, usertype_color);
}
diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp
index 981881fb9b..bed7739239 100644
--- a/editor/plugins/script_text_editor.cpp
+++ b/editor/plugins/script_text_editor.cpp
@@ -855,6 +855,21 @@ void ScriptTextEditor::_lookup_symbol(const String &p_symbol, int p_row, int p_c
emit_signal(SNAME("go_to_help"), "class_method:" + result.class_name + ":" + result.class_member);
} break;
+ case ScriptLanguage::LOOKUP_RESULT_CLASS_SIGNAL: {
+ StringName cname = result.class_name;
+
+ while (true) {
+ if (ClassDB::has_signal(cname, result.class_member)) {
+ result.class_name = cname;
+ cname = ClassDB::get_parent_class(cname);
+ } else {
+ break;
+ }
+ }
+
+ emit_signal(SNAME("go_to_help"), "class_signal:" + result.class_name + ":" + result.class_member);
+
+ } break;
case ScriptLanguage::LOOKUP_RESULT_CLASS_ENUM: {
StringName cname = result.class_name;
StringName success;
diff --git a/editor/plugins/skeleton_3d_editor_plugin.cpp b/editor/plugins/skeleton_3d_editor_plugin.cpp
index 1b703a097c..26a982a091 100644
--- a/editor/plugins/skeleton_3d_editor_plugin.cpp
+++ b/editor/plugins/skeleton_3d_editor_plugin.cpp
@@ -357,7 +357,7 @@ void Skeleton3DEditor::pose_to_rest(const bool p_all_bones) {
void Skeleton3DEditor::create_physical_skeleton() {
UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
ERR_FAIL_COND(!get_tree());
- Node *owner = skeleton == get_tree()->get_edited_scene_root() ? skeleton : skeleton->get_owner();
+ Node *owner = get_tree()->get_edited_scene_root();
const int bc = skeleton->get_bone_count();
@@ -368,37 +368,47 @@ void Skeleton3DEditor::create_physical_skeleton() {
Vector<BoneInfo> bones_infos;
bones_infos.resize(bc);
- for (int bone_id = 0; bc > bone_id; ++bone_id) {
- const int parent = skeleton->get_bone_parent(bone_id);
+ if (bc > 0) {
+ ur->create_action(TTR("Create physical bones"), UndoRedo::MERGE_ALL);
+ for (int bone_id = 0; bc > bone_id; ++bone_id) {
+ const int parent = skeleton->get_bone_parent(bone_id);
- if (parent < 0) {
- bones_infos.write[bone_id].relative_rest = skeleton->get_bone_rest(bone_id);
-
- } else {
- const int parent_parent = skeleton->get_bone_parent(parent);
-
- bones_infos.write[bone_id].relative_rest = bones_infos[parent].relative_rest * skeleton->get_bone_rest(bone_id);
-
- // Create physical bone on parent.
- if (!bones_infos[parent].physical_bone) {
- bones_infos.write[parent].physical_bone = create_physical_bone(parent, bone_id, bones_infos);
-
- ur->create_action(TTR("Create physical bones"), UndoRedo::MERGE_ALL);
- ur->add_do_method(skeleton, "add_child", bones_infos[parent].physical_bone);
- ur->add_do_reference(bones_infos[parent].physical_bone);
- ur->add_undo_method(skeleton, "remove_child", bones_infos[parent].physical_bone);
- ur->commit_action();
+ if (parent < 0) {
+ bones_infos.write[bone_id].relative_rest = skeleton->get_bone_rest(bone_id);
+ } else {
+ const int parent_parent = skeleton->get_bone_parent(parent);
+
+ bones_infos.write[bone_id].relative_rest = bones_infos[parent].relative_rest * skeleton->get_bone_rest(bone_id);
+
+ // Create physical bone on parent.
+ if (!bones_infos[parent].physical_bone) {
+ PhysicalBone3D *physical_bone = create_physical_bone(parent, bone_id, bones_infos);
+ if (physical_bone && physical_bone->get_child(0)) {
+ CollisionShape3D *collision_shape = Object::cast_to<CollisionShape3D>(physical_bone->get_child(0));
+ if (collision_shape) {
+ bones_infos.write[parent].physical_bone = physical_bone;
+
+ ur->add_do_method(skeleton, "add_child", physical_bone);
+ ur->add_do_method(physical_bone, "set_owner", owner);
+ ur->add_do_method(collision_shape, "set_owner", owner);
+ ur->add_do_property(physical_bone, "bone_name", skeleton->get_bone_name(parent));
+
+ // Create joint between parent of parent.
+ if (parent_parent != -1) {
+ ur->add_do_method(physical_bone, "set_joint_type", PhysicalBone3D::JOINT_TYPE_PIN);
+ }
- bones_infos[parent].physical_bone->set_bone_name(skeleton->get_bone_name(parent));
- bones_infos[parent].physical_bone->set_owner(owner);
- bones_infos[parent].physical_bone->get_child(0)->set_owner(owner); // set shape owner
+ ur->add_do_method(Node3DEditor::get_singleton(), "_request_gizmo", physical_bone);
+ ur->add_do_method(Node3DEditor::get_singleton(), "_request_gizmo", collision_shape);
- // Create joint between parent of parent.
- if (-1 != parent_parent) {
- bones_infos[parent].physical_bone->set_joint_type(PhysicalBone3D::JOINT_TYPE_PIN);
+ ur->add_do_reference(physical_bone);
+ ur->add_undo_method(skeleton, "remove_child", physical_bone);
+ }
+ }
}
}
}
+ ur->commit_action();
}
}
@@ -414,6 +424,7 @@ PhysicalBone3D *Skeleton3DEditor::create_physical_bone(int bone_id, int bone_chi
CollisionShape3D *bone_shape = memnew(CollisionShape3D);
bone_shape->set_shape(bone_shape_capsule);
+ bone_shape->set_name("CollisionShape3D");
Transform3D capsule_transform;
capsule_transform.basis = Basis(Vector3(1, 0, 0), Vector3(0, 0, 1), Vector3(0, -1, 0));
diff --git a/editor/plugins/texture_region_editor_plugin.cpp b/editor/plugins/texture_region_editor_plugin.cpp
index 455592aa26..74c96e19d0 100644
--- a/editor/plugins/texture_region_editor_plugin.cpp
+++ b/editor/plugins/texture_region_editor_plugin.cpp
@@ -921,12 +921,6 @@ void TextureRegionEditor::edit(Object *p_obj) {
atlas_tex = Ref<AtlasTexture>(nullptr);
}
edit_draw->update();
- if ((node_sprite_2d && !node_sprite_2d->is_region_enabled()) || (node_sprite_3d && !node_sprite_3d->is_region_enabled())) {
- set_process(true);
- }
- if (!p_obj) {
- set_process(false);
- }
}
void TextureRegionEditor::_texture_changed() {
diff --git a/editor/plugins/theme_editor_plugin.cpp b/editor/plugins/theme_editor_plugin.cpp
index 87f8c4b165..41a599e933 100644
--- a/editor/plugins/theme_editor_plugin.cpp
+++ b/editor/plugins/theme_editor_plugin.cpp
@@ -2339,8 +2339,8 @@ void ThemeTypeEditor::_update_type_list_debounced() {
update_debounce_timer->start();
}
-OrderedHashMap<StringName, bool> ThemeTypeEditor::_get_type_items(String p_type_name, void (Theme::*get_list_func)(StringName, List<StringName> *) const, bool include_default) {
- OrderedHashMap<StringName, bool> items;
+HashMap<StringName, bool> ThemeTypeEditor::_get_type_items(String p_type_name, void (Theme::*get_list_func)(StringName, List<StringName> *) const, bool include_default) {
+ HashMap<StringName, bool> items;
List<StringName> names;
if (include_default) {
@@ -2367,12 +2367,12 @@ OrderedHashMap<StringName, bool> ThemeTypeEditor::_get_type_items(String p_type_
}
List<StringName> keys;
- for (OrderedHashMap<StringName, bool>::Element E = items.front(); E; E = E.next()) {
- keys.push_back(E.key());
+ for (const KeyValue<StringName, bool> &E : items) {
+ keys.push_back(E.key);
}
keys.sort_custom<StringName::AlphCompare>();
- OrderedHashMap<StringName, bool> ordered_items;
+ HashMap<StringName, bool> ordered_items;
for (const StringName &E : keys) {
ordered_items[E] = items[E];
}
@@ -2464,18 +2464,18 @@ void ThemeTypeEditor::_update_type_items() {
color_items_list->remove_child(node);
}
- OrderedHashMap<StringName, bool> color_items = _get_type_items(edited_type, &Theme::get_color_list, show_default);
- for (OrderedHashMap<StringName, bool>::Element E = color_items.front(); E; E = E.next()) {
- HBoxContainer *item_control = _create_property_control(Theme::DATA_TYPE_COLOR, E.key(), E.get());
+ HashMap<StringName, bool> color_items = _get_type_items(edited_type, &Theme::get_color_list, show_default);
+ for (const KeyValue<StringName, bool> &E : color_items) {
+ HBoxContainer *item_control = _create_property_control(Theme::DATA_TYPE_COLOR, E.key, E.value);
ColorPickerButton *item_editor = memnew(ColorPickerButton);
item_editor->set_h_size_flags(SIZE_EXPAND_FILL);
item_control->add_child(item_editor);
- if (E.get()) {
- item_editor->set_pick_color(edited_theme->get_color(E.key(), edited_type));
- item_editor->connect("color_changed", callable_mp(this, &ThemeTypeEditor::_color_item_changed), varray(E.key()));
+ if (E.value) {
+ item_editor->set_pick_color(edited_theme->get_color(E.key, edited_type));
+ item_editor->connect("color_changed", callable_mp(this, &ThemeTypeEditor::_color_item_changed), varray(E.key));
} else {
- item_editor->set_pick_color(Theme::get_default()->get_color(E.key(), edited_type));
+ item_editor->set_pick_color(Theme::get_default()->get_color(E.key, edited_type));
item_editor->set_disabled(true);
}
@@ -2492,9 +2492,9 @@ void ThemeTypeEditor::_update_type_items() {
constant_items_list->remove_child(node);
}
- OrderedHashMap<StringName, bool> constant_items = _get_type_items(edited_type, &Theme::get_constant_list, show_default);
- for (OrderedHashMap<StringName, bool>::Element E = constant_items.front(); E; E = E.next()) {
- HBoxContainer *item_control = _create_property_control(Theme::DATA_TYPE_CONSTANT, E.key(), E.get());
+ HashMap<StringName, bool> constant_items = _get_type_items(edited_type, &Theme::get_constant_list, show_default);
+ for (const KeyValue<StringName, bool> &E : constant_items) {
+ HBoxContainer *item_control = _create_property_control(Theme::DATA_TYPE_CONSTANT, E.key, E.value);
SpinBox *item_editor = memnew(SpinBox);
item_editor->set_h_size_flags(SIZE_EXPAND_FILL);
item_editor->set_min(-100000);
@@ -2504,11 +2504,11 @@ void ThemeTypeEditor::_update_type_items() {
item_editor->set_allow_greater(true);
item_control->add_child(item_editor);
- if (E.get()) {
- item_editor->set_value(edited_theme->get_constant(E.key(), edited_type));
- item_editor->connect("value_changed", callable_mp(this, &ThemeTypeEditor::_constant_item_changed), varray(E.key()));
+ if (E.value) {
+ item_editor->set_value(edited_theme->get_constant(E.key, edited_type));
+ item_editor->connect("value_changed", callable_mp(this, &ThemeTypeEditor::_constant_item_changed), varray(E.key));
} else {
- item_editor->set_value(Theme::get_default()->get_constant(E.key(), edited_type));
+ item_editor->set_value(Theme::get_default()->get_constant(E.key, edited_type));
item_editor->set_editable(false);
}
@@ -2525,25 +2525,25 @@ void ThemeTypeEditor::_update_type_items() {
font_items_list->remove_child(node);
}
- OrderedHashMap<StringName, bool> font_items = _get_type_items(edited_type, &Theme::get_font_list, show_default);
- for (OrderedHashMap<StringName, bool>::Element E = font_items.front(); E; E = E.next()) {
- HBoxContainer *item_control = _create_property_control(Theme::DATA_TYPE_FONT, E.key(), E.get());
+ HashMap<StringName, bool> font_items = _get_type_items(edited_type, &Theme::get_font_list, show_default);
+ for (const KeyValue<StringName, bool> &E : font_items) {
+ HBoxContainer *item_control = _create_property_control(Theme::DATA_TYPE_FONT, E.key, E.value);
EditorResourcePicker *item_editor = memnew(EditorResourcePicker);
item_editor->set_h_size_flags(SIZE_EXPAND_FILL);
item_editor->set_base_type("Font");
item_control->add_child(item_editor);
- if (E.get()) {
- if (edited_theme->has_font(E.key(), edited_type)) {
- item_editor->set_edited_resource(edited_theme->get_font(E.key(), edited_type));
+ if (E.value) {
+ if (edited_theme->has_font(E.key, edited_type)) {
+ item_editor->set_edited_resource(edited_theme->get_font(E.key, edited_type));
} else {
item_editor->set_edited_resource(Ref<Resource>());
}
item_editor->connect("resource_selected", callable_mp(this, &ThemeTypeEditor::_edit_resource_item));
- item_editor->connect("resource_changed", callable_mp(this, &ThemeTypeEditor::_font_item_changed), varray(E.key()));
+ item_editor->connect("resource_changed", callable_mp(this, &ThemeTypeEditor::_font_item_changed), varray(E.key));
} else {
- if (Theme::get_default()->has_font(E.key(), edited_type)) {
- item_editor->set_edited_resource(Theme::get_default()->get_font(E.key(), edited_type));
+ if (Theme::get_default()->has_font(E.key, edited_type)) {
+ item_editor->set_edited_resource(Theme::get_default()->get_font(E.key, edited_type));
} else {
item_editor->set_edited_resource(Ref<Resource>());
}
@@ -2563,9 +2563,9 @@ void ThemeTypeEditor::_update_type_items() {
font_size_items_list->remove_child(node);
}
- OrderedHashMap<StringName, bool> font_size_items = _get_type_items(edited_type, &Theme::get_font_size_list, show_default);
- for (OrderedHashMap<StringName, bool>::Element E = font_size_items.front(); E; E = E.next()) {
- HBoxContainer *item_control = _create_property_control(Theme::DATA_TYPE_FONT_SIZE, E.key(), E.get());
+ HashMap<StringName, bool> font_size_items = _get_type_items(edited_type, &Theme::get_font_size_list, show_default);
+ for (const KeyValue<StringName, bool> &E : font_size_items) {
+ HBoxContainer *item_control = _create_property_control(Theme::DATA_TYPE_FONT_SIZE, E.key, E.value);
SpinBox *item_editor = memnew(SpinBox);
item_editor->set_h_size_flags(SIZE_EXPAND_FILL);
item_editor->set_min(-100000);
@@ -2575,11 +2575,11 @@ void ThemeTypeEditor::_update_type_items() {
item_editor->set_allow_greater(true);
item_control->add_child(item_editor);
- if (E.get()) {
- item_editor->set_value(edited_theme->get_font_size(E.key(), edited_type));
- item_editor->connect("value_changed", callable_mp(this, &ThemeTypeEditor::_font_size_item_changed), varray(E.key()));
+ if (E.value) {
+ item_editor->set_value(edited_theme->get_font_size(E.key, edited_type));
+ item_editor->connect("value_changed", callable_mp(this, &ThemeTypeEditor::_font_size_item_changed), varray(E.key));
} else {
- item_editor->set_value(Theme::get_default()->get_font_size(E.key(), edited_type));
+ item_editor->set_value(Theme::get_default()->get_font_size(E.key, edited_type));
item_editor->set_editable(false);
}
@@ -2596,25 +2596,25 @@ void ThemeTypeEditor::_update_type_items() {
icon_items_list->remove_child(node);
}
- OrderedHashMap<StringName, bool> icon_items = _get_type_items(edited_type, &Theme::get_icon_list, show_default);
- for (OrderedHashMap<StringName, bool>::Element E = icon_items.front(); E; E = E.next()) {
- HBoxContainer *item_control = _create_property_control(Theme::DATA_TYPE_ICON, E.key(), E.get());
+ HashMap<StringName, bool> icon_items = _get_type_items(edited_type, &Theme::get_icon_list, show_default);
+ for (const KeyValue<StringName, bool> &E : icon_items) {
+ HBoxContainer *item_control = _create_property_control(Theme::DATA_TYPE_ICON, E.key, E.value);
EditorResourcePicker *item_editor = memnew(EditorResourcePicker);
item_editor->set_h_size_flags(SIZE_EXPAND_FILL);
item_editor->set_base_type("Texture2D");
item_control->add_child(item_editor);
- if (E.get()) {
- if (edited_theme->has_icon(E.key(), edited_type)) {
- item_editor->set_edited_resource(edited_theme->get_icon(E.key(), edited_type));
+ if (E.value) {
+ if (edited_theme->has_icon(E.key, edited_type)) {
+ item_editor->set_edited_resource(edited_theme->get_icon(E.key, edited_type));
} else {
item_editor->set_edited_resource(Ref<Resource>());
}
item_editor->connect("resource_selected", callable_mp(this, &ThemeTypeEditor::_edit_resource_item));
- item_editor->connect("resource_changed", callable_mp(this, &ThemeTypeEditor::_icon_item_changed), varray(E.key()));
+ item_editor->connect("resource_changed", callable_mp(this, &ThemeTypeEditor::_icon_item_changed), varray(E.key));
} else {
- if (Theme::get_default()->has_icon(E.key(), edited_type)) {
- item_editor->set_edited_resource(Theme::get_default()->get_icon(E.key(), edited_type));
+ if (Theme::get_default()->has_icon(E.key, edited_type)) {
+ item_editor->set_edited_resource(Theme::get_default()->get_icon(E.key, edited_type));
} else {
item_editor->set_edited_resource(Ref<Resource>());
}
@@ -2664,26 +2664,26 @@ void ThemeTypeEditor::_update_type_items() {
stylebox_items_list->add_child(memnew(HSeparator));
}
- OrderedHashMap<StringName, bool> stylebox_items = _get_type_items(edited_type, &Theme::get_stylebox_list, show_default);
- for (OrderedHashMap<StringName, bool>::Element E = stylebox_items.front(); E; E = E.next()) {
- if (leading_stylebox.pinned && leading_stylebox.item_name == E.key()) {
+ HashMap<StringName, bool> stylebox_items = _get_type_items(edited_type, &Theme::get_stylebox_list, show_default);
+ for (const KeyValue<StringName, bool> &E : stylebox_items) {
+ if (leading_stylebox.pinned && leading_stylebox.item_name == E.key) {
continue;
}
- HBoxContainer *item_control = _create_property_control(Theme::DATA_TYPE_STYLEBOX, E.key(), E.get());
+ HBoxContainer *item_control = _create_property_control(Theme::DATA_TYPE_STYLEBOX, E.key, E.value);
EditorResourcePicker *item_editor = memnew(EditorResourcePicker);
item_editor->set_h_size_flags(SIZE_EXPAND_FILL);
item_editor->set_stretch_ratio(1.5);
item_editor->set_base_type("StyleBox");
- if (E.get()) {
- if (edited_theme->has_stylebox(E.key(), edited_type)) {
- item_editor->set_edited_resource(edited_theme->get_stylebox(E.key(), edited_type));
+ if (E.value) {
+ if (edited_theme->has_stylebox(E.key, edited_type)) {
+ item_editor->set_edited_resource(edited_theme->get_stylebox(E.key, edited_type));
} else {
item_editor->set_edited_resource(Ref<Resource>());
}
item_editor->connect("resource_selected", callable_mp(this, &ThemeTypeEditor::_edit_resource_item));
- item_editor->connect("resource_changed", callable_mp(this, &ThemeTypeEditor::_stylebox_item_changed), varray(E.key()));
+ item_editor->connect("resource_changed", callable_mp(this, &ThemeTypeEditor::_stylebox_item_changed), varray(E.key));
Button *pin_leader_button = memnew(Button);
pin_leader_button->set_flat(true);
@@ -2691,10 +2691,10 @@ void ThemeTypeEditor::_update_type_items() {
pin_leader_button->set_icon(get_theme_icon(SNAME("Pin"), SNAME("EditorIcons")));
pin_leader_button->set_tooltip(TTR("Pin this StyleBox as a main style. Editing its properties will update the same properties in all other StyleBoxes of this type."));
item_control->add_child(pin_leader_button);
- pin_leader_button->connect("pressed", callable_mp(this, &ThemeTypeEditor::_on_pin_leader_button_pressed), varray(item_editor, E.key()));
+ pin_leader_button->connect("pressed", callable_mp(this, &ThemeTypeEditor::_on_pin_leader_button_pressed), varray(item_editor, E.key));
} else {
- if (Theme::get_default()->has_stylebox(E.key(), edited_type)) {
- item_editor->set_edited_resource(Theme::get_default()->get_stylebox(E.key(), edited_type));
+ if (Theme::get_default()->has_stylebox(E.key, edited_type)) {
+ item_editor->set_edited_resource(Theme::get_default()->get_stylebox(E.key, edited_type));
} else {
item_editor->set_edited_resource(Ref<Resource>());
}
diff --git a/editor/plugins/theme_editor_plugin.h b/editor/plugins/theme_editor_plugin.h
index 3894ca31e5..6debf00e90 100644
--- a/editor/plugins/theme_editor_plugin.h
+++ b/editor/plugins/theme_editor_plugin.h
@@ -363,7 +363,7 @@ class ThemeTypeEditor : public MarginContainer {
VBoxContainer *_create_item_list(Theme::DataType p_data_type);
void _update_type_list();
void _update_type_list_debounced();
- OrderedHashMap<StringName, bool> _get_type_items(String p_type_name, void (Theme::*get_list_func)(StringName, List<StringName> *) const, bool include_default);
+ HashMap<StringName, bool> _get_type_items(String p_type_name, void (Theme::*get_list_func)(StringName, List<StringName> *) const, bool include_default);
HBoxContainer *_create_property_control(Theme::DataType p_data_type, String p_item_name, bool p_editable);
void _add_focusable(Control *p_control);
void _update_type_items();
diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp
index f184049d41..ff35e3e6f3 100644
--- a/editor/plugins/visual_shader_editor_plugin.cpp
+++ b/editor/plugins/visual_shader_editor_plugin.cpp
@@ -4801,7 +4801,7 @@ VisualShaderEditor::VisualShaderEditor() {
///////////////////////////////////////
preview_window = memnew(Window);
- preview_window->set_title(TTR("Generated shader code"));
+ preview_window->set_title(TTR("Generated Shader Code"));
preview_window->set_visible(preview_showed);
preview_window->connect("close_requested", callable_mp(this, &VisualShaderEditor::_preview_close_requested));
preview_window->connect("size_changed", callable_mp(this, &VisualShaderEditor::_preview_size_changed));
@@ -5027,8 +5027,8 @@ VisualShaderEditor::VisualShaderEditor() {
add_options.push_back(AddOption("ColorOp", "Color", "Common", "VisualShaderNodeColorOp", TTR("Color operator."), {}, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("Grayscale", "Color", "Functions", "VisualShaderNodeColorFunc", TTR("Grayscale function."), { VisualShaderNodeColorFunc::FUNC_GRAYSCALE }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
- add_options.push_back(AddOption("HSV2RGB", "Color", "Functions", "VisualShaderNodeVectorFunc", TTR("Converts HSV vector to RGB equivalent."), { VisualShaderNodeVectorFunc::FUNC_HSV2RGB, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
- add_options.push_back(AddOption("RGB2HSV", "Color", "Functions", "VisualShaderNodeVectorFunc", TTR("Converts RGB vector to HSV equivalent."), { VisualShaderNodeVectorFunc::FUNC_RGB2HSV, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
+ add_options.push_back(AddOption("HSV2RGB", "Color", "Functions", "VisualShaderNodeColorFunc", TTR("Converts HSV vector to RGB equivalent."), { VisualShaderNodeColorFunc::FUNC_HSV2RGB, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
+ add_options.push_back(AddOption("RGB2HSV", "Color", "Functions", "VisualShaderNodeColorFunc", TTR("Converts RGB vector to HSV equivalent."), { VisualShaderNodeColorFunc::FUNC_RGB2HSV, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("Sepia", "Color", "Functions", "VisualShaderNodeColorFunc", TTR("Sepia function."), { VisualShaderNodeColorFunc::FUNC_SEPIA }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("Burn", "Color", "Operators", "VisualShaderNodeColorOp", TTR("Burn operator."), { VisualShaderNodeColorOp::OP_BURN }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
@@ -5041,8 +5041,8 @@ VisualShaderEditor::VisualShaderEditor() {
add_options.push_back(AddOption("Screen", "Color", "Operators", "VisualShaderNodeColorOp", TTR("Screen operator."), { VisualShaderNodeColorOp::OP_SCREEN }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("SoftLight", "Color", "Operators", "VisualShaderNodeColorOp", TTR("SoftLight operator."), { VisualShaderNodeColorOp::OP_SOFT_LIGHT }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
- add_options.push_back(AddOption("ColorConstant", "Color", "Variables", "VisualShaderNodeColorConstant", TTR("Color constant.")));
- add_options.push_back(AddOption("ColorUniform", "Color", "Variables", "VisualShaderNodeColorUniform", TTR("Color uniform.")));
+ add_options.push_back(AddOption("ColorConstant", "Color", "Variables", "VisualShaderNodeColorConstant", TTR("Color constant."), {}, VisualShaderNode::PORT_TYPE_VECTOR_4D));
+ add_options.push_back(AddOption("ColorUniform", "Color", "Variables", "VisualShaderNodeColorUniform", TTR("Color uniform."), {}, VisualShaderNode::PORT_TYPE_VECTOR_4D));
// COMMON
@@ -5368,25 +5368,25 @@ VisualShaderEditor::VisualShaderEditor() {
add_options.push_back(AddOption("UVFunc", "Textures", "Common", "VisualShaderNodeUVFunc", TTR("Function to be applied on texture coordinates."), {}, VisualShaderNode::PORT_TYPE_VECTOR_2D));
cubemap_node_option_idx = add_options.size();
- add_options.push_back(AddOption("CubeMap", "Textures", "Functions", "VisualShaderNodeCubemap", TTR("Perform the cubic texture lookup.")));
+ add_options.push_back(AddOption("CubeMap", "Textures", "Functions", "VisualShaderNodeCubemap", TTR("Perform the cubic texture lookup."), {}, VisualShaderNode::PORT_TYPE_VECTOR_4D));
curve_node_option_idx = add_options.size();
- add_options.push_back(AddOption("CurveTexture", "Textures", "Functions", "VisualShaderNodeCurveTexture", TTR("Perform the curve texture lookup.")));
+ add_options.push_back(AddOption("CurveTexture", "Textures", "Functions", "VisualShaderNodeCurveTexture", TTR("Perform the curve texture lookup."), {}, VisualShaderNode::PORT_TYPE_SCALAR));
curve_xyz_node_option_idx = add_options.size();
- add_options.push_back(AddOption("CurveXYZTexture", "Textures", "Functions", "VisualShaderNodeCurveXYZTexture", TTR("Perform the three components curve texture lookup.")));
+ add_options.push_back(AddOption("CurveXYZTexture", "Textures", "Functions", "VisualShaderNodeCurveXYZTexture", TTR("Perform the three components curve texture lookup."), {}, VisualShaderNode::PORT_TYPE_VECTOR_3D));
texture2d_node_option_idx = add_options.size();
- add_options.push_back(AddOption("Texture2D", "Textures", "Functions", "VisualShaderNodeTexture", TTR("Perform the 2D texture lookup.")));
+ add_options.push_back(AddOption("Texture2D", "Textures", "Functions", "VisualShaderNodeTexture", TTR("Perform the 2D texture lookup."), {}, VisualShaderNode::PORT_TYPE_VECTOR_4D));
texture2d_array_node_option_idx = add_options.size();
- add_options.push_back(AddOption("Texture2DArray", "Textures", "Functions", "VisualShaderNodeTexture2DArray", TTR("Perform the 2D-array texture lookup.")));
+ add_options.push_back(AddOption("Texture2DArray", "Textures", "Functions", "VisualShaderNodeTexture2DArray", TTR("Perform the 2D-array texture lookup."), {}, VisualShaderNode::PORT_TYPE_VECTOR_4D));
texture3d_node_option_idx = add_options.size();
- add_options.push_back(AddOption("Texture3D", "Textures", "Functions", "VisualShaderNodeTexture3D", TTR("Perform the 3D texture lookup.")));
+ add_options.push_back(AddOption("Texture3D", "Textures", "Functions", "VisualShaderNodeTexture3D", TTR("Perform the 3D texture lookup."), {}, VisualShaderNode::PORT_TYPE_VECTOR_4D));
add_options.push_back(AddOption("UVPanning", "Textures", "Functions", "VisualShaderNodeUVFunc", TTR("Apply panning function on texture coordinates."), { VisualShaderNodeUVFunc::FUNC_PANNING }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
add_options.push_back(AddOption("UVScaling", "Textures", "Functions", "VisualShaderNodeUVFunc", TTR("Apply scaling function on texture coordinates."), { VisualShaderNodeUVFunc::FUNC_SCALING }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
- add_options.push_back(AddOption("CubeMapUniform", "Textures", "Variables", "VisualShaderNodeCubemapUniform", TTR("Cubic texture uniform lookup.")));
- add_options.push_back(AddOption("TextureUniform", "Textures", "Variables", "VisualShaderNodeTextureUniform", TTR("2D texture uniform lookup.")));
+ add_options.push_back(AddOption("CubeMapUniform", "Textures", "Variables", "VisualShaderNodeCubemapUniform", TTR("Cubic texture uniform lookup."), {}, VisualShaderNode::PORT_TYPE_SAMPLER));
+ add_options.push_back(AddOption("TextureUniform", "Textures", "Variables", "VisualShaderNodeTextureUniform", TTR("2D texture uniform lookup."), {}, VisualShaderNode::PORT_TYPE_SAMPLER));
add_options.push_back(AddOption("TextureUniformTriplanar", "Textures", "Variables", "VisualShaderNodeTextureUniformTriplanar", TTR("2D texture uniform lookup with triplanar."), {}, -1, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, Shader::MODE_SPATIAL));
- add_options.push_back(AddOption("Texture2DArrayUniform", "Textures", "Variables", "VisualShaderNodeTexture2DArrayUniform", TTR("2D array of textures uniform lookup.")));
- add_options.push_back(AddOption("Texture3DUniform", "Textures", "Variables", "VisualShaderNodeTexture3DUniform", TTR("3D texture uniform lookup.")));
+ add_options.push_back(AddOption("Texture2DArrayUniform", "Textures", "Variables", "VisualShaderNodeTexture2DArrayUniform", TTR("2D array of textures uniform lookup."), {}, VisualShaderNode::PORT_TYPE_SAMPLER));
+ add_options.push_back(AddOption("Texture3DUniform", "Textures", "Variables", "VisualShaderNodeTexture3DUniform", TTR("3D texture uniform lookup."), {}, VisualShaderNode::PORT_TYPE_SAMPLER));
// TRANSFORM
diff --git a/editor/pot_generator.cpp b/editor/pot_generator.cpp
index 0835d0212f..e684d9a5e6 100644
--- a/editor/pot_generator.cpp
+++ b/editor/pot_generator.cpp
@@ -39,7 +39,7 @@ POTGenerator *POTGenerator::singleton = nullptr;
#ifdef DEBUG_POT
void POTGenerator::_print_all_translation_strings() {
- for (OrderedHashMap<String, Vector<POTGenerator::MsgidData>>::Element E = all_translation_strings.front(); E; E = E.next()) {
+ for (HashMap<String, Vector<POTGenerator::MsgidData>>::Element E = all_translation_strings.front(); E; E = E.next()) {
Vector<MsgidData> v_md = all_translation_strings[E.key()];
for (int i = 0; i < v_md.size(); i++) {
print_line("++++++");
@@ -121,9 +121,9 @@ void POTGenerator::_write_to_pot(const String &p_file) {
file->store_string(header);
- for (OrderedHashMap<String, Vector<MsgidData>>::Element E_pair = all_translation_strings.front(); E_pair; E_pair = E_pair.next()) {
- String msgid = E_pair.key();
- Vector<MsgidData> v_msgid_data = E_pair.value();
+ for (const KeyValue<String, Vector<MsgidData>> &E_pair : all_translation_strings) {
+ String msgid = E_pair.key;
+ const Vector<MsgidData> &v_msgid_data = E_pair.value;
for (int i = 0; i < v_msgid_data.size(); i++) {
String context = v_msgid_data[i].ctx;
String plural = v_msgid_data[i].plural;
diff --git a/editor/pot_generator.h b/editor/pot_generator.h
index e7a5f90cee..05d1903dd6 100644
--- a/editor/pot_generator.h
+++ b/editor/pot_generator.h
@@ -32,7 +32,7 @@
#define POT_GENERATOR_H
#include "core/io/file_access.h"
-#include "core/templates/ordered_hash_map.h"
+#include "core/templates/hash_map.h"
#include "core/templates/set.h"
//#define DEBUG_POT
@@ -46,7 +46,7 @@ class POTGenerator {
Set<String> locations;
};
// Store msgid as key and the additional data around the msgid - if it's under a context, has plurals and its file locations.
- OrderedHashMap<String, Vector<MsgidData>> all_translation_strings;
+ HashMap<String, Vector<MsgidData>> all_translation_strings;
void _write_to_pot(const String &p_file);
void _write_msgid(Ref<FileAccess> r_file, const String &p_id, bool p_plural);
diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp
index 9c745e6a41..b4cbb3b7f1 100644
--- a/editor/project_manager.cpp
+++ b/editor/project_manager.cpp
@@ -186,7 +186,8 @@ private:
if (mode == MODE_IMPORT || mode == MODE_RENAME) {
if (!valid_path.is_empty() && !d->file_exists("project.godot")) {
if (valid_path.ends_with(".zip")) {
- zlib_filefunc_def io = zipio_create_io();
+ Ref<FileAccess> io_fa;
+ zlib_filefunc_def io = zipio_create_io(&io_fa);
unzFile pkg = unzOpen2(valid_path.utf8().get_data(), &io);
if (!pkg) {
@@ -499,7 +500,8 @@ private:
zip_path = project_path->get_text();
}
- zlib_filefunc_def io = zipio_create_io();
+ Ref<FileAccess> io_fa;
+ zlib_filefunc_def io = zipio_create_io(&io_fa);
unzFile pkg = unzOpen2(zip_path.utf8().get_data(), &io);
if (!pkg) {
@@ -1401,7 +1403,7 @@ void ProjectList::create_project_item_control(int p_index) {
title->set_clip_text(true);
title_hb->add_child(title);
- String unsupported_features_str = Variant(item.unsupported_features).operator String().trim_prefix("[").trim_suffix("]");
+ String unsupported_features_str = String(", ").join(item.unsupported_features);
int length = unsupported_features_str.length();
if (length > 0) {
Label *unsupported_label = memnew(Label(unsupported_features_str));
@@ -2207,7 +2209,7 @@ void ProjectManager::_open_selected_projects_ask() {
}
}
if (!unsupported_features.is_empty()) {
- String unsupported_features_str = Variant(unsupported_features).operator String().trim_prefix("[").trim_suffix("]");
+ String unsupported_features_str = String(", ").join(unsupported_features);
warning_message += vformat(TTR("Warning: This project uses the following features not supported by this build of Godot:\n\n%s\n\n"), unsupported_features_str);
}
warning_message += TTR("Open anyway? Project will be modified.");
diff --git a/editor/project_settings_editor.cpp b/editor/project_settings_editor.cpp
index f684c0e0c9..48cd975715 100644
--- a/editor/project_settings_editor.cpp
+++ b/editor/project_settings_editor.cpp
@@ -420,7 +420,7 @@ void ProjectSettingsEditor::_action_reordered(const String &p_action_name, const
Variant target_value = ps->get(target_name);
List<PropertyInfo> props;
- OrderedHashMap<String, Variant> action_values;
+ HashMap<String, Variant> action_values;
ProjectSettings::get_singleton()->get_property_list(&props);
undo_redo->create_action(TTR("Update Input Action Order"));
@@ -437,9 +437,9 @@ void ProjectSettingsEditor::_action_reordered(const String &p_action_name, const
undo_redo->add_undo_method(ProjectSettings::get_singleton(), "clear", prop.name);
}
- for (OrderedHashMap<String, Variant>::Element E = action_values.front(); E; E = E.next()) {
- String name = E.key();
- Variant value = E.get();
+ for (const KeyValue<String, Variant> &E : action_values) {
+ String name = E.key;
+ const Variant &value = E.value;
if (name == target_name) {
if (p_before) {
diff --git a/editor/scene_tree_editor.cpp b/editor/scene_tree_editor.cpp
index b588c01bce..4efd22aa1e 100644
--- a/editor/scene_tree_editor.cpp
+++ b/editor/scene_tree_editor.cpp
@@ -279,15 +279,26 @@ bool SceneTreeEditor::_add_nodes(Node *p_node, TreeItem *p_parent, bool p_scroll
Array arr;
arr.push_back(num_connections);
msg_temp += TTRN("Node has one connection.", "Node has {num} connections.", num_connections).format(arr, "{num}");
- msg_temp += " ";
+ if (num_groups >= 1) {
+ msg_temp += "\n";
+ }
}
if (num_groups >= 1) {
- Array arr;
- arr.push_back(num_groups);
- msg_temp += TTRN("Node is in one group.", "Node is in {num} groups.", num_groups).format(arr, "{num}");
+ msg_temp += TTRN("Node is in this group:", "Node is in the following groups:", num_groups) + "\n";
+
+ List<GroupInfo> groups;
+ p_node->get_groups(&groups);
+ for (const GroupInfo &E : groups) {
+ if (E.persistent) {
+ msg_temp += String::utf8("• ") + String(E.name) + "\n";
+ }
+ }
}
if (num_connections >= 1 || num_groups >= 1) {
- msg_temp += "\n" + TTR("Click to show signals dock.");
+ if (num_groups < 1) {
+ msg_temp += "\n";
+ }
+ msg_temp += TTR("Click to show signals dock.");
}
Ref<Texture2D> icon_temp;
diff --git a/main/main.cpp b/main/main.cpp
index 609c865a1c..2134e5079d 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -1350,35 +1350,6 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
// always convert to lower case for consistency in the code
rendering_driver = rendering_driver.to_lower();
- GLOBAL_DEF_BASIC("display/window/size/viewport_width", 1024);
- ProjectSettings::get_singleton()->set_custom_property_info("display/window/size/viewport_width",
- PropertyInfo(Variant::INT, "display/window/size/viewport_width",
- PROPERTY_HINT_RANGE,
- "0,7680,1,or_greater")); // 8K resolution
-
- GLOBAL_DEF_BASIC("display/window/size/viewport_height", 600);
- ProjectSettings::get_singleton()->set_custom_property_info("display/window/size/viewport_height",
- PropertyInfo(Variant::INT, "display/window/size/viewport_height",
- PROPERTY_HINT_RANGE,
- "0,4320,1,or_greater")); // 8K resolution
-
- GLOBAL_DEF_BASIC("display/window/size/resizable", true);
- GLOBAL_DEF_BASIC("display/window/size/borderless", false);
- GLOBAL_DEF_BASIC("display/window/size/fullscreen", false);
- GLOBAL_DEF("display/window/size/always_on_top", false);
- GLOBAL_DEF("display/window/size/window_width_override", 0);
- ProjectSettings::get_singleton()->set_custom_property_info("display/window/size/window_width_override",
- PropertyInfo(Variant::INT,
- "display/window/size/window_width_override",
- PROPERTY_HINT_RANGE,
- "0,7680,1,or_greater")); // 8K resolution
- GLOBAL_DEF("display/window/size/window_height_override", 0);
- ProjectSettings::get_singleton()->set_custom_property_info("display/window/size/window_height_override",
- PropertyInfo(Variant::INT,
- "display/window/size/window_height_override",
- PROPERTY_HINT_RANGE,
- "0,4320,1,or_greater")); // 8K resolution
-
if (use_custom_res) {
if (!force_res) {
window_size.width = GLOBAL_GET("display/window/size/viewport_width");
@@ -2351,11 +2322,11 @@ bool Main::start() {
if (!project_manager && !editor) { // game
if (!game_path.is_empty() || !script.is_empty()) {
//autoload
- OrderedHashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
+ HashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
//first pass, add the constants so they exist before any script is loaded
- for (OrderedHashMap<StringName, ProjectSettings::AutoloadInfo>::Element E = autoloads.front(); E; E = E.next()) {
- const ProjectSettings::AutoloadInfo &info = E.get();
+ for (const KeyValue<StringName, ProjectSettings::AutoloadInfo> &E : autoloads) {
+ const ProjectSettings::AutoloadInfo &info = E.value;
if (info.is_singleton) {
for (int i = 0; i < ScriptServer::get_language_count(); i++) {
@@ -2366,8 +2337,8 @@ bool Main::start() {
//second pass, load into global constants
List<Node *> to_add;
- for (OrderedHashMap<StringName, ProjectSettings::AutoloadInfo>::Element E = autoloads.front(); E; E = E.next()) {
- const ProjectSettings::AutoloadInfo &info = E.get();
+ for (const KeyValue<StringName, ProjectSettings::AutoloadInfo> &E : autoloads) {
+ const ProjectSettings::AutoloadInfo &info = E.value;
Ref<Resource> res = ResourceLoader::load(info.path);
ERR_CONTINUE_MSG(res.is_null(), "Can't autoload: " + info.path);
diff --git a/main/performance.cpp b/main/performance.cpp
index e80906b4a7..25659b999f 100644
--- a/main/performance.cpp
+++ b/main/performance.cpp
@@ -247,8 +247,8 @@ Array Performance::get_custom_monitor_names() {
Array return_array;
return_array.resize(_monitor_map.size());
int index = 0;
- for (OrderedHashMap<StringName, MonitorCall>::Element i = _monitor_map.front(); i; i = i.next()) {
- return_array.set(index, i.key());
+ for (KeyValue<StringName, MonitorCall> i : _monitor_map) {
+ return_array.set(index, i.key);
index++;
}
return return_array;
diff --git a/main/performance.h b/main/performance.h
index 927b5b0389..2837d8f512 100644
--- a/main/performance.h
+++ b/main/performance.h
@@ -32,7 +32,7 @@
#define PERFORMANCE_H
#include "core/object/class_db.h"
-#include "core/templates/ordered_hash_map.h"
+#include "core/templates/hash_map.h"
#define PERF_WARN_OFFLINE_FUNCTION
#define PERF_WARN_PROCESS_SYNC
@@ -58,7 +58,7 @@ class Performance : public Object {
Variant call(bool &r_error, String &r_error_message);
};
- OrderedHashMap<StringName, MonitorCall> _monitor_map;
+ HashMap<StringName, MonitorCall> _monitor_map;
uint64_t _monitor_modification_time;
public:
diff --git a/modules/csg/csg.cpp b/modules/csg/csg.cpp
index e0cc0f8234..177014e5a7 100644
--- a/modules/csg/csg.cpp
+++ b/modules/csg/csg.cpp
@@ -1387,13 +1387,13 @@ void CSGBrushOperation::update_faces(const CSGBrush &p_brush_a, const int p_face
}
// Ensure B has points either side of or in the plane of A.
- int in_plane_count = 0, over_count = 0, under_count = 0;
+ int over_count = 0, under_count = 0;
Plane plane_a(vertices_a[0], vertices_a[1], vertices_a[2]);
ERR_FAIL_COND_MSG(plane_a.normal == Vector3(), "Couldn't form plane from Brush A face.");
for (int i = 0; i < 3; i++) {
if (plane_a.has_point(vertices_b[i])) {
- in_plane_count++;
+ // In plane.
} else if (plane_a.is_point_over(vertices_b[i])) {
over_count++;
} else {
@@ -1406,7 +1406,6 @@ void CSGBrushOperation::update_faces(const CSGBrush &p_brush_a, const int p_face
}
// Ensure A has points either side of or in the plane of B.
- in_plane_count = 0;
over_count = 0;
under_count = 0;
Plane plane_b(vertices_b[0], vertices_b[1], vertices_b[2]);
@@ -1414,7 +1413,7 @@ void CSGBrushOperation::update_faces(const CSGBrush &p_brush_a, const int p_face
for (int i = 0; i < 3; i++) {
if (plane_b.has_point(vertices_a[i])) {
- in_plane_count++;
+ // In plane.
} else if (plane_b.is_point_over(vertices_a[i])) {
over_count++;
} else {
diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml
index d0926d317b..70151c4d21 100644
--- a/modules/gdscript/doc_classes/@GDScript.xml
+++ b/modules/gdscript/doc_classes/@GDScript.xml
@@ -184,27 +184,24 @@
<method name="range" qualifiers="vararg">
<return type="Array" />
<description>
- Returns an array with the given range. Range can be 1 argument [code]N[/code] (0 to [code]N[/code] - 1), two arguments ([code]initial[/code], [code]final - 1[/code]) or three arguments ([code]initial[/code], [code]final - 1[/code], [code]increment[/code]). Returns an empty array if the range isn't valid (e.g. [code]range(2, 5, -1)[/code] or [code]range(5, 5, 1)[/code]).
- Returns an array with the given range. [code]range()[/code] can have 1 argument N ([code]0[/code] to [code]N - 1[/code]), two arguments ([code]initial[/code], [code]final - 1[/code]) or three arguments ([code]initial[/code], [code]final - 1[/code], [code]increment[/code]). [code]increment[/code] can be negative. If [code]increment[/code] is negative, [code]final - 1[/code] will become [code]final + 1[/code]. Also, the initial value must be greater than the final value for the loop to run.
- [code]range()[/code] converts all arguments to [int] before processing.
+ Returns an array with the given range. [method range] can be called in three ways:
+ [code]range(n: int)[/code]: Starts from 0, increases by steps of 1, and stops [i]before[/i] [code]n[/code]. The argument [code]n[/code] is [b]exclusive[/b].
+ [code]range(b: int, n: int)[/code]: Starts from [code]b[/code], increases by steps of 1, and stops [i]before[/i] [code]n[/code]. The arguments [code]b[/code] and [code]n[/code] are [b]inclusive[/b] and [b]exclusive[/b], respectively.
+ [code]range(b: int, n: int, s: int)[/code]: Starts from [code]b[/code], increases/decreases by steps of [code]s[/code], and stops [i]before[/i] [code]n[/code]. The arguments [code]b[/code] and [code]n[/code] are [b]inclusive[/b] and [b]exclusive[/b], respectively. The argument [code]s[/code] [b]can[/b] be negative, but not [code]0[/code]. If [code]s[/code] is [code]0[/code], an error message is printed.
+ [method range] converts all arguments to [int] before processing.
+ [b]Note:[/b] Returns an empty array if no value meets the value constraint (e.g. [code]range(2, 5, -1)[/code] or [code]range(5, 5, 1)[/code]).
+ Examples:
[codeblock]
- print(range(4))
- print(range(2, 5))
- print(range(0, 6, 2))
- [/codeblock]
- Output:
- [codeblock]
- [0, 1, 2, 3]
- [2, 3, 4]
- [0, 2, 4]
+ print(range(4)) # Prints [0, 1, 2, 3]
+ print(range(2, 5)) # Prints [2, 3, 4]
+ print(range(0, 6, 2)) # Prints [0, 2, 4]
+ print(range(4, 1, -1)) # Prints [4, 3, 2]
[/codeblock]
To iterate over an [Array] backwards, use:
[codeblock]
var array = [3, 6, 9]
- var i := array.size() - 1
- while i &gt;= 0:
- print(array[i])
- i -= 1
+ for i in range(array.size(), 0, -1):
+ print(array[i - 1])
[/codeblock]
Output:
[codeblock]
diff --git a/modules/gdscript/editor/gdscript_highlighter.cpp b/modules/gdscript/editor/gdscript_highlighter.cpp
index e3f0ddfc35..191568661d 100644
--- a/modules/gdscript/editor/gdscript_highlighter.cpp
+++ b/modules/gdscript/editor/gdscript_highlighter.cpp
@@ -510,9 +510,8 @@ void GDScriptSyntaxHighlighter::_update_cache() {
}
/* Autoloads. */
- OrderedHashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
- for (OrderedHashMap<StringName, ProjectSettings::AutoloadInfo>::Element E = autoloads.front(); E; E = E.next()) {
- const ProjectSettings::AutoloadInfo &info = E.value();
+ for (const KeyValue<StringName, ProjectSettings::AutoloadInfo> &E : ProjectSettings::get_singleton()->get_autoload_list()) {
+ const ProjectSettings::AutoloadInfo &info = E.value;
if (info.is_singleton) {
keywords[info.name] = usertype_color;
}
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index d346264933..85ad08ea4f 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -4218,13 +4218,11 @@ Error GDScriptAnalyzer::resolve_program() {
resolve_class_interface(parser->head);
resolve_class_body(parser->head);
- List<String> parser_keys;
- depended_parsers.get_key_list(&parser_keys);
- for (const String &E : parser_keys) {
- if (depended_parsers[E].is_null()) {
+ for (KeyValue<String, Ref<GDScriptParserRef>> &K : depended_parsers) {
+ if (K.value.is_null()) {
return ERR_PARSE_ERROR;
}
- depended_parsers[E]->raise_status(GDScriptParserRef::FULLY_SOLVED);
+ K.value->raise_status(GDScriptParserRef::FULLY_SOLVED);
}
return parser->errors.is_empty() ? OK : ERR_PARSE_ERROR;
}
diff --git a/modules/gdscript/gdscript_byte_codegen.cpp b/modules/gdscript/gdscript_byte_codegen.cpp
index e72069bcd5..3d5a39bf38 100644
--- a/modules/gdscript/gdscript_byte_codegen.cpp
+++ b/modules/gdscript/gdscript_byte_codegen.cpp
@@ -196,10 +196,8 @@ GDScriptFunction *GDScriptByteCodeGenerator::write_end() {
function->_constant_count = constant_map.size();
function->constants.resize(constant_map.size());
function->_constants_ptr = function->constants.ptrw();
- const Variant *K = nullptr;
- while ((K = constant_map.next(K))) {
- int idx = constant_map[*K];
- function->constants.write[idx] = *K;
+ for (const KeyValue<Variant, int> &K : constant_map) {
+ function->constants.write[K.value] = K.key;
}
} else {
function->_constants_ptr = nullptr;
diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp
index 37a988ee4c..c194fbf9b8 100644
--- a/modules/gdscript/gdscript_compiler.cpp
+++ b/modules/gdscript/gdscript_compiler.cpp
@@ -336,7 +336,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
if (GDScriptLanguage::get_singleton()->get_global_map().has(identifier)) {
// If it's an autoload singleton, we postpone to load it at runtime.
// This is so one autoload doesn't try to load another before it's compiled.
- OrderedHashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
+ HashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
if (autoloads.has(identifier) && autoloads[identifier].is_singleton) {
GDScriptCodeGenerator::Address global = codegen.add_temporary(_gdtype_from_datatype(in->get_datatype()));
int idx = GDScriptLanguage::get_singleton()->get_global_map()[identifier];
diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp
index 0197bf9ea3..226b4c24b1 100644
--- a/modules/gdscript/gdscript_editor.cpp
+++ b/modules/gdscript/gdscript_editor.cpp
@@ -851,9 +851,10 @@ static void _list_available_types(bool p_inherit_only, GDScriptParser::Completio
}
// Autoload singletons
- OrderedHashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
- for (OrderedHashMap<StringName, ProjectSettings::AutoloadInfo>::Element E = autoloads.front(); E; E = E.next()) {
- const ProjectSettings::AutoloadInfo &info = E.get();
+ HashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
+
+ for (const KeyValue<StringName, ProjectSettings::AutoloadInfo> &E : autoloads) {
+ const ProjectSettings::AutoloadInfo &info = E.value;
if (!info.is_singleton || info.path.get_extension().to_lower() != "gd") {
continue;
}
@@ -1056,6 +1057,14 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
r_result.insert(option.display, option);
}
+ List<MethodInfo> signals;
+ ClassDB::get_signal_list(type, &signals);
+ for (const MethodInfo &E : signals) {
+ int location = p_recursion_depth + _get_signal_location(type, StringName(E.name));
+ ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_SIGNAL, location);
+ r_result.insert(option.display, option);
+ }
+
if (!_static || Engine::get_singleton()->has_singleton(type)) {
List<PropertyInfo> pinfo;
ClassDB::get_property_list(type, &pinfo);
@@ -1219,12 +1228,11 @@ static void _find_identifiers(const GDScriptParser::CompletionContext &p_context
r_result.insert(option.display, option);
}
- OrderedHashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
- for (OrderedHashMap<StringName, ProjectSettings::AutoloadInfo>::Element E = autoloads.front(); E; E = E.next()) {
- if (!E.value().is_singleton) {
+ for (const KeyValue<StringName, ProjectSettings::AutoloadInfo> &E : ProjectSettings::get_singleton()->get_autoload_list()) {
+ if (!E.value.is_singleton) {
continue;
}
- ScriptLanguage::CodeCompletionOption option(E.key(), ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT);
+ ScriptLanguage::CodeCompletionOption option(E.key, ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT);
r_result.insert(option.display, option);
}
@@ -1517,12 +1525,10 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context,
r_type = _type_from_variant(GDScriptLanguage::get_singleton()->get_named_globals_map()[which]);
found = true;
} else {
- OrderedHashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
-
- for (OrderedHashMap<StringName, ProjectSettings::AutoloadInfo>::Element E = autoloads.front(); E; E = E.next()) {
- String name = E.key();
+ for (const KeyValue<StringName, ProjectSettings::AutoloadInfo> &E : ProjectSettings::get_singleton()->get_autoload_list()) {
+ String name = E.key;
if (name == which) {
- String script = E.value().path;
+ String script = E.value.path;
if (!script.begins_with("res://")) {
script = "res://" + script;
@@ -2882,10 +2888,8 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
}
// Get autoloads.
- OrderedHashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
-
- for (OrderedHashMap<StringName, ProjectSettings::AutoloadInfo>::Element E = autoloads.front(); E; E = E.next()) {
- String path = "/root/" + E.key();
+ for (const KeyValue<StringName, ProjectSettings::AutoloadInfo> &E : ProjectSettings::get_singleton()->get_autoload_list()) {
+ String path = "/root/" + E.key;
ScriptLanguage::CodeCompletionOption option(path.quote(quote_style), ScriptLanguage::CODE_COMPLETION_KIND_NODE_PATH);
options.insert(option.display, option);
}
@@ -3062,6 +3066,13 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co
}
}
+ if (ClassDB::has_signal(class_name, p_symbol, true)) {
+ r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_SIGNAL;
+ r_result.class_name = base_type.native_type;
+ r_result.class_member = p_symbol;
+ return OK;
+ }
+
StringName enum_name = ClassDB::get_integer_constant_enum(class_name, p_symbol, true);
if (enum_name != StringName()) {
r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_ENUM;
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index 67f6b61f14..aa1bfb312c 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -100,10 +100,8 @@ void GDScriptParser::cleanup() {
}
void GDScriptParser::get_annotation_list(List<MethodInfo> *r_annotations) const {
- List<StringName> keys;
- valid_annotations.get_key_list(&keys);
- for (const StringName &E : keys) {
- r_annotations->push_back(valid_annotations[E].info);
+ for (const KeyValue<StringName, AnnotationInfo> &E : valid_annotations) {
+ r_annotations->push_back(E.value.info);
}
}
@@ -1863,7 +1861,7 @@ GDScriptParser::MatchBranchNode *GDScriptParser::parse_match_branch() {
if (pattern == nullptr) {
continue;
}
- if (pattern->pattern_type == PatternNode::PT_BIND) {
+ if (pattern->binds.size() > 0) {
has_bind = true;
}
if (branch->patterns.size() > 0 && has_bind) {
@@ -1894,11 +1892,9 @@ GDScriptParser::MatchBranchNode *GDScriptParser::parse_match_branch() {
SuiteNode *suite = alloc_node<SuiteNode>();
if (branch->patterns.size() > 0) {
- List<StringName> binds;
- branch->patterns[0]->binds.get_key_list(&binds);
-
- for (const StringName &E : binds) {
- SuiteNode::Local local(branch->patterns[0]->binds[E], current_function);
+ for (const KeyValue<StringName, IdentifierNode *> &E : branch->patterns[0]->binds) {
+ SuiteNode::Local local(E.value, current_function);
+ local.type = SuiteNode::Local::PATTERN_BIND;
suite->add_local(local);
}
}
@@ -2319,6 +2315,10 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_binary_operator(Expression
operation->operation = BinaryOpNode::OP_MODULO;
operation->variant_op = Variant::OP_MODULE;
break;
+ case GDScriptTokenizer::Token::STAR_STAR:
+ operation->operation = BinaryOpNode::OP_POWER;
+ operation->variant_op = Variant::OP_POWER;
+ break;
case GDScriptTokenizer::Token::LESS_LESS:
operation->operation = BinaryOpNode::OP_BIT_LEFT_SHIFT;
operation->variant_op = Variant::OP_SHIFT_LEFT;
@@ -2482,6 +2482,10 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_assignment(ExpressionNode
assignment->operation = AssignmentNode::OP_MULTIPLICATION;
assignment->variant_op = Variant::OP_MULTIPLY;
break;
+ case GDScriptTokenizer::Token::STAR_STAR_EQUAL:
+ assignment->operation = AssignmentNode::OP_POWER;
+ assignment->variant_op = Variant::OP_POWER;
+ break;
case GDScriptTokenizer::Token::SLASH_EQUAL:
assignment->operation = AssignmentNode::OP_DIVISION;
assignment->variant_op = Variant::OP_DIVIDE;
@@ -3264,6 +3268,7 @@ GDScriptParser::ParseRule *GDScriptParser::get_rule(GDScriptTokenizer::Token::Ty
{ &GDScriptParser::parse_unary_operator, &GDScriptParser::parse_binary_operator, PREC_ADDITION_SUBTRACTION }, // PLUS,
{ &GDScriptParser::parse_unary_operator, &GDScriptParser::parse_binary_operator, PREC_ADDITION_SUBTRACTION }, // MINUS,
{ nullptr, &GDScriptParser::parse_binary_operator, PREC_FACTOR }, // STAR,
+ { nullptr, &GDScriptParser::parse_binary_operator, PREC_POWER }, // STAR_STAR,
{ nullptr, &GDScriptParser::parse_binary_operator, PREC_FACTOR }, // SLASH,
{ nullptr, &GDScriptParser::parse_binary_operator, PREC_FACTOR }, // PERCENT,
// Assignment
@@ -3271,6 +3276,7 @@ GDScriptParser::ParseRule *GDScriptParser::get_rule(GDScriptTokenizer::Token::Ty
{ nullptr, &GDScriptParser::parse_assignment, PREC_ASSIGNMENT }, // PLUS_EQUAL,
{ nullptr, &GDScriptParser::parse_assignment, PREC_ASSIGNMENT }, // MINUS_EQUAL,
{ nullptr, &GDScriptParser::parse_assignment, PREC_ASSIGNMENT }, // STAR_EQUAL,
+ { nullptr, &GDScriptParser::parse_assignment, PREC_ASSIGNMENT }, // STAR_STAR_EQUAL,
{ nullptr, &GDScriptParser::parse_assignment, PREC_ASSIGNMENT }, // SLASH_EQUAL,
{ nullptr, &GDScriptParser::parse_assignment, PREC_ASSIGNMENT }, // PERCENT_EQUAL,
{ nullptr, &GDScriptParser::parse_assignment, PREC_ASSIGNMENT }, // LESS_LESS_EQUAL,
@@ -3555,14 +3561,16 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
variable->export_info.hint = PROPERTY_HINT_ENUM;
String enum_hint_string;
- for (OrderedHashMap<StringName, int>::Element E = export_type.enum_values.front(); E; E = E.next()) {
- enum_hint_string += E.key().operator String().capitalize().xml_escape();
- enum_hint_string += ":";
- enum_hint_string += String::num_int64(E.value()).xml_escape();
-
- if (E.next()) {
+ bool first = true;
+ for (const KeyValue<StringName, int> &E : export_type.enum_values) {
+ if (!first) {
enum_hint_string += ",";
+ } else {
+ first = false;
}
+ enum_hint_string += E.key.operator String().capitalize().xml_escape();
+ enum_hint_string += ":";
+ enum_hint_string += String::num_int64(E.value).xml_escape();
}
variable->export_info.hint_string = enum_hint_string;
@@ -3895,6 +3903,9 @@ void GDScriptParser::TreePrinter::print_assignment(AssignmentNode *p_assignment)
case AssignmentNode::OP_MODULO:
push_text("%");
break;
+ case AssignmentNode::OP_POWER:
+ push_text("**");
+ break;
case AssignmentNode::OP_BIT_SHIFT_LEFT:
push_text("<<");
break;
@@ -3943,6 +3954,9 @@ void GDScriptParser::TreePrinter::print_binary_op(BinaryOpNode *p_binary_op) {
case BinaryOpNode::OP_MODULO:
push_text(" % ");
break;
+ case BinaryOpNode::OP_POWER:
+ push_text(" ** ");
+ break;
case BinaryOpNode::OP_BIT_LEFT_SHIFT:
push_text(" << ");
break;
diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h
index 10474db02f..857e06440c 100644
--- a/modules/gdscript/gdscript_parser.h
+++ b/modules/gdscript/gdscript_parser.h
@@ -132,7 +132,7 @@ public:
ClassNode *class_type = nullptr;
MethodInfo method_info; // For callable/signals.
- OrderedHashMap<StringName, int> enum_values; // For enums.
+ HashMap<StringName, int> enum_values; // For enums.
_FORCE_INLINE_ bool is_set() const { return kind != UNRESOLVED; }
_FORCE_INLINE_ bool has_no_type() const { return type_source == UNDETECTED; }
@@ -360,6 +360,7 @@ public:
OP_MULTIPLICATION,
OP_DIVISION,
OP_MODULO,
+ OP_POWER,
OP_BIT_SHIFT_LEFT,
OP_BIT_SHIFT_RIGHT,
OP_BIT_AND,
@@ -393,6 +394,7 @@ public:
OP_MULTIPLICATION,
OP_DIVISION,
OP_MODULO,
+ OP_POWER,
OP_BIT_LEFT_SHIFT,
OP_BIT_RIGHT_SHIFT,
OP_BIT_AND,
@@ -1263,6 +1265,7 @@ private:
PREC_FACTOR,
PREC_SIGN,
PREC_BIT_NOT,
+ PREC_POWER,
PREC_TYPE_TEST,
PREC_AWAIT,
PREC_CALL,
diff --git a/modules/gdscript/gdscript_tokenizer.cpp b/modules/gdscript/gdscript_tokenizer.cpp
index 63fad0d9bb..6c17afe939 100644
--- a/modules/gdscript/gdscript_tokenizer.cpp
+++ b/modules/gdscript/gdscript_tokenizer.cpp
@@ -67,6 +67,7 @@ static const char *token_names[] = {
"+", // PLUS,
"-", // MINUS,
"*", // STAR,
+ "**", // STAR_STAR,
"/", // SLASH,
"%", // PERCENT,
// Assignment
@@ -74,6 +75,7 @@ static const char *token_names[] = {
"+=", // PLUS_EQUAL,
"-=", // MINUS_EQUAL,
"*=", // STAR_EQUAL,
+ "**=", // STAR_STAR_EQUAL,
"/=", // SLASH_EQUAL,
"%=", // PERCENT_EQUAL,
"<<=", // LESS_LESS_EQUAL,
@@ -1403,6 +1405,14 @@ GDScriptTokenizer::Token GDScriptTokenizer::scan() {
if (_peek() == '=') {
_advance();
return make_token(Token::STAR_EQUAL);
+ } else if (_peek() == '*') {
+ if (_peek(1) == '=') {
+ _advance();
+ _advance(); // Advance both '*' and '='
+ return make_token(Token::STAR_STAR_EQUAL);
+ }
+ _advance();
+ return make_token(Token::STAR_STAR);
} else {
return make_token(Token::STAR);
}
diff --git a/modules/gdscript/gdscript_tokenizer.h b/modules/gdscript/gdscript_tokenizer.h
index abd090e381..75f9a7626e 100644
--- a/modules/gdscript/gdscript_tokenizer.h
+++ b/modules/gdscript/gdscript_tokenizer.h
@@ -78,6 +78,7 @@ public:
PLUS,
MINUS,
STAR,
+ STAR_STAR,
SLASH,
PERCENT,
// Assignment
@@ -85,6 +86,7 @@ public:
PLUS_EQUAL,
MINUS_EQUAL,
STAR_EQUAL,
+ STAR_STAR_EQUAL,
SLASH_EQUAL,
PERCENT_EQUAL,
LESS_LESS_EQUAL,
diff --git a/modules/gdscript/language_server/gdscript_extend_parser.cpp b/modules/gdscript/language_server/gdscript_extend_parser.cpp
index 5516f59fe9..bc1f001d86 100644
--- a/modules/gdscript/language_server/gdscript_extend_parser.cpp
+++ b/modules/gdscript/language_server/gdscript_extend_parser.cpp
@@ -89,16 +89,16 @@ void ExtendGDScriptParser::update_symbols() {
for (int i = 0; i < class_symbol.children.size(); i++) {
const lsp::DocumentSymbol &symbol = class_symbol.children[i];
- members.set(symbol.name, &symbol);
+ members.insert(symbol.name, &symbol);
// cache level one inner classes
if (symbol.kind == lsp::SymbolKind::Class) {
ClassMembers inner_class;
for (int j = 0; j < symbol.children.size(); j++) {
const lsp::DocumentSymbol &s = symbol.children[j];
- inner_class.set(s.name, &s);
+ inner_class.insert(s.name, &s);
}
- inner_classes.set(symbol.name, inner_class);
+ inner_classes.insert(symbol.name, inner_class);
}
}
}
@@ -661,30 +661,22 @@ const List<lsp::DocumentLink> &ExtendGDScriptParser::get_document_links() const
const Array &ExtendGDScriptParser::get_member_completions() {
if (member_completions.is_empty()) {
- const String *name = members.next(nullptr);
- while (name) {
- const lsp::DocumentSymbol *symbol = members.get(*name);
+ for (const KeyValue<String, const lsp::DocumentSymbol *> &E : members) {
+ const lsp::DocumentSymbol *symbol = E.value;
lsp::CompletionItem item = symbol->make_completion_item();
- item.data = JOIN_SYMBOLS(path, *name);
+ item.data = JOIN_SYMBOLS(path, E.key);
member_completions.push_back(item.to_json());
-
- name = members.next(name);
}
- const String *_class = inner_classes.next(nullptr);
- while (_class) {
- const ClassMembers *inner_class = inner_classes.getptr(*_class);
- const String *member_name = inner_class->next(nullptr);
- while (member_name) {
- const lsp::DocumentSymbol *symbol = inner_class->get(*member_name);
+ for (const KeyValue<String, ClassMembers> &E : inner_classes) {
+ const ClassMembers *inner_class = &E.value;
+
+ for (const KeyValue<String, const lsp::DocumentSymbol *> &F : *inner_class) {
+ const lsp::DocumentSymbol *symbol = F.value;
lsp::CompletionItem item = symbol->make_completion_item();
- item.data = JOIN_SYMBOLS(path, JOIN_SYMBOLS(*_class, *member_name));
+ item.data = JOIN_SYMBOLS(path, JOIN_SYMBOLS(E.key, F.key));
member_completions.push_back(item.to_json());
-
- member_name = inner_class->next(member_name);
}
-
- _class = inner_classes.next(_class);
}
}
diff --git a/modules/gdscript/language_server/gdscript_language_protocol.cpp b/modules/gdscript/language_server/gdscript_language_protocol.cpp
index cdddab319d..7460f8edff 100644
--- a/modules/gdscript/language_server/gdscript_language_protocol.cpp
+++ b/modules/gdscript/language_server/gdscript_language_protocol.cpp
@@ -126,7 +126,7 @@ Error GDScriptLanguageProtocol::on_client_connected() {
ERR_FAIL_COND_V_MSG(clients.size() >= LSP_MAX_CLIENTS, FAILED, "Max client limits reached");
Ref<LSPeer> peer = memnew(LSPeer);
peer->connection = tcp_peer;
- clients.set(next_client_id, peer);
+ clients.insert(next_client_id, peer);
next_client_id++;
EditorNode::get_log()->add_message("[LSP] Connection Taken", EditorLog::MSG_TYPE_EDITOR);
return OK;
@@ -229,28 +229,33 @@ void GDScriptLanguageProtocol::poll() {
if (server->is_connection_available()) {
on_client_connected();
}
- const int *id = nullptr;
- while ((id = clients.next(id))) {
- Ref<LSPeer> peer = clients.get(*id);
+
+ HashMap<int, Ref<LSPeer>>::Iterator E = clients.begin();
+ while (E != clients.end()) {
+ Ref<LSPeer> peer = E->value;
StreamPeerTCP::Status status = peer->connection->get_status();
if (status == StreamPeerTCP::STATUS_NONE || status == StreamPeerTCP::STATUS_ERROR) {
- on_client_disconnected(*id);
- id = nullptr;
+ on_client_disconnected(E->key);
+ E = clients.begin();
+ continue;
} else {
if (peer->connection->get_available_bytes() > 0) {
- latest_client_id = *id;
+ latest_client_id = E->key;
Error err = peer->handle_data();
if (err != OK && err != ERR_BUSY) {
- on_client_disconnected(*id);
- id = nullptr;
+ on_client_disconnected(E->key);
+ E = clients.begin();
+ continue;
}
}
Error err = peer->send_data();
if (err != OK && err != ERR_BUSY) {
- on_client_disconnected(*id);
- id = nullptr;
+ on_client_disconnected(E->key);
+ E = clients.begin();
+ continue;
}
}
+ ++E;
}
}
@@ -259,9 +264,8 @@ Error GDScriptLanguageProtocol::start(int p_port, const IPAddress &p_bind_ip) {
}
void GDScriptLanguageProtocol::stop() {
- const int *id = nullptr;
- while ((id = clients.next(id))) {
- Ref<LSPeer> peer = clients.get(*id);
+ for (const KeyValue<int, Ref<LSPeer>> &E : clients) {
+ Ref<LSPeer> peer = clients.get(E.key);
peer->connection->disconnect_from_host();
}
diff --git a/modules/gdscript/language_server/gdscript_text_document.cpp b/modules/gdscript/language_server/gdscript_text_document.cpp
index c42bd58aeb..1f02943480 100644
--- a/modules/gdscript/language_server/gdscript_text_document.cpp
+++ b/modules/gdscript/language_server/gdscript_text_document.cpp
@@ -109,23 +109,15 @@ void GDScriptTextDocument::notify_client_show_symbol(const lsp::DocumentSymbol *
void GDScriptTextDocument::initialize() {
if (GDScriptLanguageProtocol::get_singleton()->is_smart_resolve_enabled()) {
- const HashMap<StringName, ClassMembers> &native_members = GDScriptLanguageProtocol::get_singleton()->get_workspace()->native_members;
+ for (const KeyValue<StringName, ClassMembers> &E : GDScriptLanguageProtocol::get_singleton()->get_workspace()->native_members) {
+ const ClassMembers &members = E.value;
- const StringName *class_ptr = native_members.next(nullptr);
- while (class_ptr) {
- const ClassMembers &members = native_members.get(*class_ptr);
-
- const String *name = members.next(nullptr);
- while (name) {
- const lsp::DocumentSymbol *symbol = members.get(*name);
+ for (const KeyValue<String, const lsp::DocumentSymbol *> &F : members) {
+ const lsp::DocumentSymbol *symbol = members.get(F.key);
lsp::CompletionItem item = symbol->make_completion_item();
- item.data = JOIN_SYMBOLS(String(*class_ptr), *name);
+ item.data = JOIN_SYMBOLS(String(E.key), F.key);
native_member_completions.push_back(item.to_json());
-
- name = members.next(name);
}
-
- class_ptr = native_members.next(class_ptr);
}
}
}
diff --git a/modules/gdscript/language_server/gdscript_workspace.cpp b/modules/gdscript/language_server/gdscript_workspace.cpp
index 89ee6b35e5..378dc6d04b 100644
--- a/modules/gdscript/language_server/gdscript_workspace.cpp
+++ b/modules/gdscript/language_server/gdscript_workspace.cpp
@@ -404,9 +404,9 @@ Error GDScriptWorkspace::initialize() {
const lsp::DocumentSymbol &class_symbol = E.value;
for (int i = 0; i < class_symbol.children.size(); i++) {
const lsp::DocumentSymbol &symbol = class_symbol.children[i];
- members.set(symbol.name, &symbol);
+ members.insert(symbol.name, &symbol);
}
- native_members.set(E.key, members);
+ native_members.insert(E.key, members);
}
// cache member completions
@@ -682,13 +682,11 @@ void GDScriptWorkspace::resolve_related_symbols(const lsp::TextDocumentPositionP
Vector2i offset;
symbol_identifier = parser->get_identifier_under_position(p_doc_pos.position, offset);
- const StringName *class_ptr = native_members.next(nullptr);
- while (class_ptr) {
- const ClassMembers &members = native_members.get(*class_ptr);
+ for (const KeyValue<StringName, ClassMembers> &E : native_members) {
+ const ClassMembers &members = native_members.get(E.key);
if (const lsp::DocumentSymbol *const *symbol = members.getptr(symbol_identifier)) {
r_list.push_back(*symbol);
}
- class_ptr = native_members.next(class_ptr);
}
for (const KeyValue<String, ExtendGDScriptParser *> &E : scripts) {
@@ -698,15 +696,11 @@ void GDScriptWorkspace::resolve_related_symbols(const lsp::TextDocumentPositionP
r_list.push_back(*symbol);
}
- const HashMap<String, ClassMembers> &inner_classes = script->get_inner_classes();
- const String *_class = inner_classes.next(nullptr);
- while (_class) {
- const ClassMembers *inner_class = inner_classes.getptr(*_class);
+ for (const KeyValue<String, ClassMembers> &F : script->get_inner_classes()) {
+ const ClassMembers *inner_class = &F.value;
if (const lsp::DocumentSymbol *const *symbol = inner_class->getptr(symbol_identifier)) {
r_list.push_back(*symbol);
}
-
- _class = inner_classes.next(_class);
}
}
}
diff --git a/modules/gdscript/tests/gdscript_test_runner.cpp b/modules/gdscript/tests/gdscript_test_runner.cpp
index ea51990237..71dc5de7e4 100644
--- a/modules/gdscript/tests/gdscript_test_runner.cpp
+++ b/modules/gdscript/tests/gdscript_test_runner.cpp
@@ -48,11 +48,11 @@
namespace GDScriptTests {
void init_autoloads() {
- OrderedHashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
+ HashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
// First pass, add the constants so they exist before any script is loaded.
- for (OrderedHashMap<StringName, ProjectSettings::AutoloadInfo>::Element E = autoloads.front(); E; E = E.next()) {
- const ProjectSettings::AutoloadInfo &info = E.get();
+ for (const KeyValue<StringName, ProjectSettings::AutoloadInfo> &E : ProjectSettings::get_singleton()->get_autoload_list()) {
+ const ProjectSettings::AutoloadInfo &info = E.value;
if (info.is_singleton) {
for (int i = 0; i < ScriptServer::get_language_count(); i++) {
@@ -62,8 +62,8 @@ void init_autoloads() {
}
// Second pass, load into global constants.
- for (OrderedHashMap<StringName, ProjectSettings::AutoloadInfo>::Element E = autoloads.front(); E; E = E.next()) {
- const ProjectSettings::AutoloadInfo &info = E.get();
+ for (const KeyValue<StringName, ProjectSettings::AutoloadInfo> &E : ProjectSettings::get_singleton()->get_autoload_list()) {
+ const ProjectSettings::AutoloadInfo &info = E.value;
if (!info.is_singleton) {
// Skip non-singletons since we don't have a scene tree here anyway.
diff --git a/modules/gdscript/tests/scripts/analyzer/features/function_match_parent_signature_with_default_dict_void.gd b/modules/gdscript/tests/scripts/analyzer/features/function_match_parent_signature_with_default_dict_void.gd
new file mode 100644
index 0000000000..631e7be5ce
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/function_match_parent_signature_with_default_dict_void.gd
@@ -0,0 +1,14 @@
+func test():
+ var instance := Parent.new()
+ instance.my_function({"a": 1})
+ instance = Child.new()
+ instance.my_function({"a": 1})
+ print("No failure")
+
+class Parent:
+ func my_function(_par1: Dictionary = {}) -> void:
+ pass
+
+class Child extends Parent:
+ func my_function(_par1: Dictionary = {}) -> void:
+ pass
diff --git a/modules/gdscript/tests/scripts/analyzer/features/function_match_parent_signature_with_default_dict_void.out b/modules/gdscript/tests/scripts/analyzer/features/function_match_parent_signature_with_default_dict_void.out
new file mode 100644
index 0000000000..67f0045867
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/function_match_parent_signature_with_default_dict_void.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+No failure
diff --git a/modules/gdscript/tests/scripts/parser/errors/match_multiple_variable_binds_in_branch.gd b/modules/gdscript/tests/scripts/parser/errors/match_multiple_variable_binds_in_branch.gd
new file mode 100644
index 0000000000..4608c778aa
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/match_multiple_variable_binds_in_branch.gd
@@ -0,0 +1,4 @@
+func test():
+ match 1:
+ [[[var a]]], 2:
+ pass
diff --git a/modules/gdscript/tests/scripts/parser/errors/match_multiple_variable_binds_in_branch.out b/modules/gdscript/tests/scripts/parser/errors/match_multiple_variable_binds_in_branch.out
new file mode 100644
index 0000000000..1cdc24683b
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/match_multiple_variable_binds_in_branch.out
@@ -0,0 +1,2 @@
+GDTEST_PARSER_ERROR
+Cannot use a variable bind with multiple patterns.
diff --git a/modules/gdscript/tests/scripts/parser/features/match_multiple_variable_binds_in_pattern.gd b/modules/gdscript/tests/scripts/parser/features/match_multiple_variable_binds_in_pattern.gd
new file mode 100644
index 0000000000..a0ae7fb17c
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/match_multiple_variable_binds_in_pattern.gd
@@ -0,0 +1,6 @@
+func test():
+ match [1, 2, 3]:
+ [var a, var b, var c]:
+ print(a == 1)
+ print(b == 2)
+ print(c == 3)
diff --git a/modules/gdscript/tests/scripts/parser/features/match_multiple_variable_binds_in_pattern.out b/modules/gdscript/tests/scripts/parser/features/match_multiple_variable_binds_in_pattern.out
new file mode 100644
index 0000000000..316db6d748
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/match_multiple_variable_binds_in_pattern.out
@@ -0,0 +1,4 @@
+GDTEST_OK
+true
+true
+true
diff --git a/modules/mono/class_db_api_json.cpp b/modules/mono/class_db_api_json.cpp
index 9253f105bb..3afde1e8d3 100644
--- a/modules/mono/class_db_api_json.cpp
+++ b/modules/mono/class_db_api_json.cpp
@@ -40,17 +40,12 @@
void class_db_api_to_json(const String &p_output_file, ClassDB::APIType p_api) {
Dictionary classes_dict;
- List<StringName> names;
+ List<StringName> class_list;
+ ClassDB::get_class_list(&class_list);
+ // Must be alphabetically sorted for hash to compute.
+ class_list.sort_custom<StringName::AlphCompare>();
- const StringName *k = nullptr;
-
- while ((k = ClassDB::classes.next(k))) {
- names.push_back(*k);
- }
- //must be alphabetically sorted for hash to compute
- names.sort_custom<StringName::AlphCompare>();
-
- for (const StringName &E : names) {
+ for (const StringName &E : class_list) {
ClassDB::ClassInfo *t = ClassDB::classes.getptr(E);
ERR_FAIL_COND(!t);
if (t->api != p_api || !t->exposed) {
@@ -66,10 +61,8 @@ void class_db_api_to_json(const String &p_output_file, ClassDB::APIType p_api) {
List<StringName> snames;
- k = nullptr;
-
- while ((k = t->method_map.next(k))) {
- String name = k->operator String();
+ for (const KeyValue<StringName, MethodBind *> &F : t->method_map) {
+ String name = F.key.operator String();
ERR_CONTINUE(name.is_empty());
@@ -77,7 +70,7 @@ void class_db_api_to_json(const String &p_output_file, ClassDB::APIType p_api) {
continue; // Ignore non-virtual methods that start with an underscore
}
- snames.push_back(*k);
+ snames.push_back(F.key);
}
snames.sort_custom<StringName::AlphCompare>();
@@ -131,10 +124,8 @@ void class_db_api_to_json(const String &p_output_file, ClassDB::APIType p_api) {
List<StringName> snames;
- k = nullptr;
-
- while ((k = t->constant_map.next(k))) {
- snames.push_back(*k);
+ for (const KeyValue<StringName, int> &F : t->constant_map) {
+ snames.push_back(F.key);
}
snames.sort_custom<StringName::AlphCompare>();
@@ -158,10 +149,8 @@ void class_db_api_to_json(const String &p_output_file, ClassDB::APIType p_api) {
List<StringName> snames;
- k = nullptr;
-
- while ((k = t->signal_map.next(k))) {
- snames.push_back(*k);
+ for (const KeyValue<StringName, MethodInfo> &F : t->signal_map) {
+ snames.push_back(F.key);
}
snames.sort_custom<StringName::AlphCompare>();
@@ -193,10 +182,8 @@ void class_db_api_to_json(const String &p_output_file, ClassDB::APIType p_api) {
List<StringName> snames;
- k = nullptr;
-
- while ((k = t->property_setget.next(k))) {
- snames.push_back(*k);
+ for (const KeyValue<StringName, ClassDB::PropertySetGet> &F : t->property_setget) {
+ snames.push_back(F.key);
}
snames.sort_custom<StringName::AlphCompare>();
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index 5875a0fbd4..31257ac33a 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -1798,8 +1798,8 @@ void CSharpInstance::get_event_signals_state_for_reloading(List<Pair<StringName,
void CSharpInstance::get_property_list(List<PropertyInfo> *p_properties) const {
List<PropertyInfo> props;
- for (OrderedHashMap<StringName, PropertyInfo>::ConstElement E = script->member_info.front(); E; E = E.next()) {
- props.push_front(E.value());
+ for (const KeyValue<StringName, PropertyInfo> &E : script->member_info) {
+ props.push_front(E.value);
}
// Call _get_property_list
@@ -3491,8 +3491,8 @@ Ref<Script> CSharpScript::get_base_script() const {
void CSharpScript::get_script_property_list(List<PropertyInfo> *r_list) const {
List<PropertyInfo> props;
- for (OrderedHashMap<StringName, PropertyInfo>::ConstElement E = member_info.front(); E; E = E.next()) {
- props.push_front(E.value());
+ for (const KeyValue<StringName, PropertyInfo> &E : member_info) {
+ props.push_front(E.value);
}
for (const PropertyInfo &prop : props) {
diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h
index 41b54248a3..c69cba61a4 100644
--- a/modules/mono/csharp_script.h
+++ b/modules/mono/csharp_script.h
@@ -154,7 +154,7 @@ private:
Set<StringName> exported_members_names;
#endif
- OrderedHashMap<StringName, PropertyInfo> member_info;
+ HashMap<StringName, PropertyInfo> member_info;
void _clear();
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs
index bac7a2e6db..02e9d98647 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs
@@ -63,6 +63,7 @@ namespace GodotTools.Build
startInfo.RedirectStandardOutput = true;
startInfo.RedirectStandardError = true;
startInfo.UseShellExecute = false;
+ startInfo.CreateNoWindow = true;
if (UsingMonoMsBuildOnWindows)
{
diff --git a/modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs b/modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs
index 93a1360cb6..2db549c623 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs
@@ -184,7 +184,8 @@ namespace GodotTools.Utils
{
RedirectStandardOutput = true,
RedirectStandardError = true,
- UseShellExecute = false
+ UseShellExecute = false,
+ CreateNoWindow = true
};
using (Process process = Process.Start(startInfo))
diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp
index 54c65c21e8..168f0254ee 100644
--- a/modules/mono/editor/bindings_generator.cpp
+++ b/modules/mono/editor/bindings_generator.cpp
@@ -1078,8 +1078,8 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_proj_dir) {
compile_items.push_back(output_file);
}
- for (OrderedHashMap<StringName, TypeInterface>::Element E = obj_types.front(); E; E = E.next()) {
- const TypeInterface &itype = E.get();
+ for (const KeyValue<StringName, TypeInterface> &E : obj_types) {
+ const TypeInterface &itype = E.value;
if (itype.api_type == ClassDB::API_EDITOR) {
continue;
@@ -1187,8 +1187,8 @@ Error BindingsGenerator::generate_cs_editor_project(const String &p_proj_dir) {
Vector<String> compile_items;
- for (OrderedHashMap<StringName, TypeInterface>::Element E = obj_types.front(); E; E = E.next()) {
- const TypeInterface &itype = E.get();
+ for (const KeyValue<StringName, TypeInterface> &E : obj_types) {
+ const TypeInterface &itype = E.value;
if (itype.api_type != ClassDB::API_EDITOR) {
continue;
@@ -1573,9 +1573,9 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte
// Search it in base types too
const TypeInterface *current_type = &p_itype;
while (!setter && current_type->base_name != StringName()) {
- OrderedHashMap<StringName, TypeInterface>::Element base_match = obj_types.find(current_type->base_name);
+ HashMap<StringName, TypeInterface>::Iterator base_match = obj_types.find(current_type->base_name);
ERR_FAIL_COND_V_MSG(!base_match, ERR_BUG, "Type not found '" + current_type->base_name + "'. Inherited by '" + current_type->name + "'.");
- current_type = &base_match.get();
+ current_type = &base_match->value;
setter = current_type->find_method_by_name(p_iprop.setter);
}
@@ -1584,9 +1584,9 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte
// Search it in base types too
current_type = &p_itype;
while (!getter && current_type->base_name != StringName()) {
- OrderedHashMap<StringName, TypeInterface>::Element base_match = obj_types.find(current_type->base_name);
+ HashMap<StringName, TypeInterface>::Iterator base_match = obj_types.find(current_type->base_name);
ERR_FAIL_COND_V_MSG(!base_match, ERR_BUG, "Type not found '" + current_type->base_name + "'. Inherited by '" + current_type->name + "'.");
- current_type = &base_match.get();
+ current_type = &base_match->value;
getter = current_type->find_method_by_name(p_iprop.getter);
}
@@ -2096,8 +2096,8 @@ Error BindingsGenerator::generate_glue(const String &p_output_dir) {
generated_icall_funcs.clear();
- for (OrderedHashMap<StringName, TypeInterface>::Element type_elem = obj_types.front(); type_elem; type_elem = type_elem.next()) {
- const TypeInterface &itype = type_elem.get();
+ for (const KeyValue<StringName, TypeInterface> &type_elem : obj_types) {
+ const TypeInterface &itype = type_elem.value;
bool is_derived_type = itype.base_name != StringName();
@@ -2474,10 +2474,10 @@ const BindingsGenerator::TypeInterface *BindingsGenerator::_get_type_or_null(con
return &builtin_type_match->get();
}
- const OrderedHashMap<StringName, TypeInterface>::Element obj_type_match = obj_types.find(p_typeref.cname);
+ const HashMap<StringName, TypeInterface>::Iterator obj_type_match = obj_types.find(p_typeref.cname);
if (obj_type_match) {
- return &obj_type_match.get();
+ return &obj_type_match->value;
}
if (p_typeref.is_enum) {
@@ -2942,12 +2942,11 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
// Populate signals
const HashMap<StringName, MethodInfo> &signal_map = class_info->signal_map;
- const StringName *k = nullptr;
- while ((k = signal_map.next(k))) {
+ for (const KeyValue<StringName, MethodInfo> &E : signal_map) {
SignalInterface isignal;
- const MethodInfo &method_info = signal_map.get(*k);
+ const MethodInfo &method_info = E.value;
isignal.name = method_info.name;
isignal.cname = method_info.name;
@@ -3024,10 +3023,9 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
ClassDB::get_integer_constant_list(type_cname, &constants, true);
const HashMap<StringName, List<StringName>> &enum_map = class_info->enum_map;
- k = nullptr;
- while ((k = enum_map.next(k))) {
- StringName enum_proxy_cname = *k;
+ for (const KeyValue<StringName, List<StringName>> &E : enum_map) {
+ StringName enum_proxy_cname = E.key;
String enum_proxy_name = enum_proxy_cname.operator String();
if (itype.find_property_by_proxy_name(enum_proxy_cname)) {
// We have several conflicts between enums and PascalCase properties,
@@ -3036,7 +3034,7 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
enum_proxy_cname = StringName(enum_proxy_name);
}
EnumInterface ienum(enum_proxy_cname);
- const List<StringName> &enum_constants = enum_map.get(*k);
+ const List<StringName> &enum_constants = E.value;
for (const StringName &constant_cname : enum_constants) {
String constant_name = constant_cname.operator String();
int *value = class_info->constant_map.getptr(constant_cname);
@@ -3066,7 +3064,7 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
TypeInterface enum_itype;
enum_itype.is_enum = true;
- enum_itype.name = itype.name + "." + String(*k);
+ enum_itype.name = itype.name + "." + String(E.key);
enum_itype.cname = StringName(enum_itype.name);
enum_itype.proxy_name = itype.proxy_name + "." + enum_proxy_name;
TypeInterface::postsetup_enum_type(enum_itype);
@@ -3715,8 +3713,8 @@ void BindingsGenerator::_initialize() {
core_custom_icalls.clear();
editor_custom_icalls.clear();
- for (OrderedHashMap<StringName, TypeInterface>::Element E = obj_types.front(); E; E = E.next()) {
- _generate_method_icalls(E.get());
+ for (const KeyValue<StringName, TypeInterface> &E : obj_types) {
+ _generate_method_icalls(E.value);
}
initialized = true;
diff --git a/modules/mono/editor/bindings_generator.h b/modules/mono/editor/bindings_generator.h
index dec4fae8cd..ec57a34c2b 100644
--- a/modules/mono/editor/bindings_generator.h
+++ b/modules/mono/editor/bindings_generator.h
@@ -533,7 +533,7 @@ class BindingsGenerator {
bool log_print_enabled = true;
bool initialized = false;
- OrderedHashMap<StringName, TypeInterface> obj_types;
+ HashMap<StringName, TypeInterface> obj_types;
Map<StringName, TypeInterface> placeholder_types;
Map<StringName, TypeInterface> builtin_types;
diff --git a/modules/mono/editor/code_completion.cpp b/modules/mono/editor/code_completion.cpp
index 79015686c3..a1789412f4 100644
--- a/modules/mono/editor/code_completion.cpp
+++ b/modules/mono/editor/code_completion.cpp
@@ -121,10 +121,10 @@ PackedStringArray get_code_completion(CompletionKind p_kind, const String &p_scr
case CompletionKind::NODE_PATHS: {
{
// Autoloads.
- OrderedHashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
+ HashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
- for (OrderedHashMap<StringName, ProjectSettings::AutoloadInfo>::Element E = autoloads.front(); E; E = E.next()) {
- const ProjectSettings::AutoloadInfo &info = E.value();
+ for (const KeyValue<StringName, ProjectSettings::AutoloadInfo> &E : autoloads) {
+ const ProjectSettings::AutoloadInfo &info = E.value;
suggestions.push_back(quoted("/root/" + String(info.name)));
}
}
diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp
index e98ce8f6c1..39a8ef22b7 100644
--- a/modules/mono/mono_gd/gd_mono.cpp
+++ b/modules/mono/mono_gd/gd_mono.cpp
@@ -1167,9 +1167,8 @@ GDMonoClass *GDMono::get_class(MonoClass *p_raw_class) {
int32_t domain_id = mono_domain_get_id(mono_domain_get());
HashMap<String, GDMonoAssembly *> &domain_assemblies = assemblies[domain_id];
- const String *k = nullptr;
- while ((k = domain_assemblies.next(k))) {
- GDMonoAssembly *assembly = domain_assemblies.get(*k);
+ for (const KeyValue<String, GDMonoAssembly *> &E : domain_assemblies) {
+ GDMonoAssembly *assembly = E.value;
if (assembly->get_image() == image) {
GDMonoClass *klass = assembly->get_class(p_raw_class);
if (klass) {
@@ -1190,9 +1189,8 @@ GDMonoClass *GDMono::get_class(const StringName &p_namespace, const StringName &
int32_t domain_id = mono_domain_get_id(mono_domain_get());
HashMap<String, GDMonoAssembly *> &domain_assemblies = assemblies[domain_id];
- const String *k = nullptr;
- while ((k = domain_assemblies.next(k))) {
- GDMonoAssembly *assembly = domain_assemblies.get(*k);
+ for (const KeyValue<String, GDMonoAssembly *> &E : domain_assemblies) {
+ GDMonoAssembly *assembly = E.value;
klass = assembly->get_class(p_namespace, p_name);
if (klass) {
return klass;
@@ -1205,9 +1203,8 @@ GDMonoClass *GDMono::get_class(const StringName &p_namespace, const StringName &
void GDMono::_domain_assemblies_cleanup(int32_t p_domain_id) {
HashMap<String, GDMonoAssembly *> &domain_assemblies = assemblies[p_domain_id];
- const String *k = nullptr;
- while ((k = domain_assemblies.next(k))) {
- memdelete(domain_assemblies.get(*k));
+ for (const KeyValue<String, GDMonoAssembly *> &E : domain_assemblies) {
+ memdelete(E.value);
}
assemblies.erase(p_domain_id);
@@ -1298,13 +1295,11 @@ GDMono::~GDMono() {
// Leave the rest to 'mono_jit_cleanup'
#endif
- const int32_t *k = nullptr;
- while ((k = assemblies.next(k))) {
- HashMap<String, GDMonoAssembly *> &domain_assemblies = assemblies.get(*k);
+ for (const KeyValue<int32_t, HashMap<String, GDMonoAssembly *>> &E : assemblies) {
+ const HashMap<String, GDMonoAssembly *> &domain_assemblies = E.value;
- const String *kk = nullptr;
- while ((kk = domain_assemblies.next(kk))) {
- memdelete(domain_assemblies.get(*kk));
+ for (const KeyValue<String, GDMonoAssembly *> &F : domain_assemblies) {
+ memdelete(F.value);
}
}
assemblies.clear();
diff --git a/modules/mono/mono_gd/gd_mono_class.cpp b/modules/mono/mono_gd/gd_mono_class.cpp
index 3fc0f16e05..daf70443e9 100644
--- a/modules/mono/mono_gd/gd_mono_class.cpp
+++ b/modules/mono/mono_gd/gd_mono_class.cpp
@@ -247,7 +247,7 @@ void GDMonoClass::fetch_methods_with_godot_api_checks(GDMonoClass *p_native_base
if (existing_method) {
memdelete(*existing_method); // Must delete old one
}
- methods.set(key, method);
+ methods.insert(key, method);
break;
}
@@ -266,11 +266,9 @@ void GDMonoClass::fetch_methods_with_godot_api_checks(GDMonoClass *p_native_base
GDMonoMethod *GDMonoClass::get_fetched_method_unknown_params(const StringName &p_name) {
ERR_FAIL_COND_V(!methods_fetched, nullptr);
- const MethodKey *k = nullptr;
-
- while ((k = methods.next(k))) {
- if (k->name == p_name) {
- return methods.get(*k);
+ for (const KeyValue<MethodKey, GDMonoMethod *> &E : methods) {
+ if (E.key.name == p_name) {
+ return E.value;
}
}
@@ -307,7 +305,7 @@ GDMonoMethod *GDMonoClass::get_method(const StringName &p_name, uint16_t p_param
if (raw_method) {
GDMonoMethod *method = memnew(GDMonoMethod(p_name, raw_method));
- methods.set(key, method);
+ methods.insert(key, method);
return method;
}
@@ -342,7 +340,7 @@ GDMonoMethod *GDMonoClass::get_method(MonoMethod *p_raw_method, const StringName
}
GDMonoMethod *method = memnew(GDMonoMethod(p_name, p_raw_method));
- methods.set(key, method);
+ methods.insert(key, method);
return method;
}
@@ -549,9 +547,8 @@ GDMonoClass::~GDMonoClass() {
Vector<GDMonoMethod *> deleted_methods;
deleted_methods.resize(methods.size());
- const MethodKey *k = nullptr;
- while ((k = methods.next(k))) {
- GDMonoMethod *method = methods.get(*k);
+ for (const KeyValue<MethodKey, GDMonoMethod *> &E : methods) {
+ GDMonoMethod *method = E.value;
if (method) {
for (int i = 0; i < offset; i++) {
diff --git a/modules/mono/utils/osx_utils.cpp b/modules/mono/utils/osx_utils.cpp
index 835c611709..abb59420eb 100644
--- a/modules/mono/utils/osx_utils.cpp
+++ b/modules/mono/utils/osx_utils.cpp
@@ -34,8 +34,8 @@
#include "core/string/print_string.h"
-#include <CoreFoundation/CoreFoundation.h>
-#include <CoreServices/CoreServices.h>
+#import <CoreFoundation/CoreFoundation.h>
+#import <CoreServices/CoreServices.h>
bool osx_is_app_bundle_installed(const String &p_bundle_id) {
CFStringRef bundle_id = CFStringCreateWithCString(nullptr, p_bundle_id.utf8(), kCFStringEncodingUTF8);
diff --git a/modules/navigation/navigation_mesh_generator.cpp b/modules/navigation/navigation_mesh_generator.cpp
index 2d6c78f704..110a4f5138 100644
--- a/modules/navigation/navigation_mesh_generator.cpp
+++ b/modules/navigation/navigation_mesh_generator.cpp
@@ -462,6 +462,14 @@ void NavigationMeshGenerator::_build_recast_navigation_mesh(
#endif
rcCalcGridSize(cfg.bmin, cfg.bmax, cfg.cs, &cfg.width, &cfg.height);
+ // ~30000000 seems to be around sweetspot where Editor baking breaks
+ if ((cfg.width * cfg.height) > 30000000) {
+ WARN_PRINT("NavigationMesh baking process will likely fail."
+ "\nSource geometry is suspiciously big for the current Cell Size and Cell Height in the NavMesh Resource bake settings."
+ "\nIf baking does not fail, the resulting NavigationMesh will create serious pathfinding performance issues."
+ "\nIt is advised to increase Cell Size and/or Cell Height in the NavMesh Resource bake settings or reduce the size / scale of the source geometry.");
+ }
+
#ifdef TOOLS_ENABLED
if (ep) {
ep->step(TTR("Creating heightfield..."), 3);
diff --git a/modules/openxr/extensions/openxr_vulkan_extension.cpp b/modules/openxr/extensions/openxr_vulkan_extension.cpp
index 1eb7635a82..2ddf3b8a7d 100644
--- a/modules/openxr/extensions/openxr_vulkan_extension.cpp
+++ b/modules/openxr/extensions/openxr_vulkan_extension.cpp
@@ -33,6 +33,7 @@
#include "../extensions/openxr_vulkan_extension.h"
#include "../openxr_api.h"
#include "../openxr_util.h"
+#include "servers/rendering/renderer_rd/effects/copy_effects.h"
#include "servers/rendering/renderer_rd/renderer_storage_rd.h"
#include "servers/rendering/renderer_rd/storage_rd/texture_storage.h"
#include "servers/rendering/rendering_server_globals.h"
@@ -450,11 +451,9 @@ bool OpenXRVulkanExtension::copy_render_target_to_image(RID p_from_render_target
ERR_FAIL_COND_V(fb.is_null(), false);
// Our vulkan extension can only be used in conjunction with our vulkan renderer.
- // We need access to the effects object in order to have access to our copy logic.
- // Breaking all the rules but there is no nice way to do this.
- EffectsRD *effects = RendererStorageRD::base_singleton->get_effects();
- ERR_FAIL_NULL_V(effects, false);
- effects->copy_to_fb_rect(source_image, fb, Rect2i(), false, false, false, false, depth_image, data->is_multiview);
+ RendererRD::CopyEffects *copy_effects = RendererRD::CopyEffects::get_singleton();
+ ERR_FAIL_NULL_V(copy_effects, false);
+ copy_effects->copy_to_fb_rect(source_image, fb, Rect2i(), false, false, false, false, depth_image, data->is_multiview);
return true;
}
diff --git a/modules/openxr/openxr_api.cpp b/modules/openxr/openxr_api.cpp
index 2e9be48f01..d0f67b7f5d 100644
--- a/modules/openxr/openxr_api.cpp
+++ b/modules/openxr/openxr_api.cpp
@@ -1001,7 +1001,7 @@ bool OpenXRAPI::initialize(const String &p_rendering_driver) {
ERR_FAIL_V(false);
#endif
} else if (p_rendering_driver == "opengl3") {
-#ifdef OPENGL3_ENABLED
+#ifdef GLES3_ENABLED
// graphics_extension = memnew(OpenXROpenGLExtension(this));
// register_extension_wrapper(graphics_extension);
ERR_FAIL_V_MSG(false, "OpenXR: OpenGL is not supported at this time.");
diff --git a/modules/raycast/raycast_occlusion_cull.cpp b/modules/raycast/raycast_occlusion_cull.cpp
index 1550f0ef8b..f49cd08698 100644
--- a/modules/raycast/raycast_occlusion_cull.cpp
+++ b/modules/raycast/raycast_occlusion_cull.cpp
@@ -454,10 +454,9 @@ bool RaycastOcclusionCull::Scenario::update(ThreadWorkPool &p_thread_pool) {
next_scene = rtcNewScene(raycast_singleton->ebr_device);
rtcSetSceneBuildQuality(next_scene, RTCBuildQuality(raycast_singleton->build_quality));
- const RID *inst_rid = nullptr;
- while ((inst_rid = instances.next(inst_rid))) {
- OccluderInstance *occ_inst = instances.getptr(*inst_rid);
- Occluder *occ = raycast_singleton->occluder_owner.get_or_null(occ_inst->occluder);
+ for (const KeyValue<RID, OccluderInstance> &E : instances) {
+ const OccluderInstance *occ_inst = &E.value;
+ const Occluder *occ = raycast_singleton->occluder_owner.get_or_null(occ_inst->occluder);
if (!occ || !occ_inst->enabled) {
continue;
@@ -573,9 +572,8 @@ void RaycastOcclusionCull::set_build_quality(RS::ViewportOcclusionCullingBuildQu
build_quality = p_quality;
- const RID *scenario_rid = nullptr;
- while ((scenario_rid = scenarios.next(scenario_rid))) {
- scenarios[*scenario_rid].dirty = true;
+ for (KeyValue<RID, Scenario> &K : scenarios) {
+ K.value.dirty = true;
}
}
@@ -596,9 +594,8 @@ RaycastOcclusionCull::RaycastOcclusionCull() {
}
RaycastOcclusionCull::~RaycastOcclusionCull() {
- const RID *scenario_rid = nullptr;
- while ((scenario_rid = scenarios.next(scenario_rid))) {
- Scenario &scenario = scenarios[*scenario_rid];
+ for (KeyValue<RID, Scenario> &K : scenarios) {
+ Scenario &scenario = K.value;
if (scenario.commit_thread) {
scenario.commit_thread->wait_to_finish();
memdelete(scenario.commit_thread);
diff --git a/modules/svg/image_loader_svg.cpp b/modules/svg/image_loader_svg.cpp
index 7fe2e589b1..dcb1f1d744 100644
--- a/modules/svg/image_loader_svg.cpp
+++ b/modules/svg/image_loader_svg.cpp
@@ -94,7 +94,7 @@ void ImageLoaderSVG::create_image_from_string(Ref<Image> p_image, String p_strin
ERR_FAIL_MSG("ImageLoaderSVG can't create image.");
}
- res = sw_canvas->push(move(picture));
+ res = sw_canvas->push(std::move(picture));
if (res != tvg::Result::Success) {
memfree(buffer);
ERR_FAIL_MSG("ImageLoaderSVG can't create image.");
diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp
index ba249aff7a..fe3a43dc97 100644
--- a/modules/text_server_adv/text_server_adv.cpp
+++ b/modules/text_server_adv/text_server_adv.cpp
@@ -2172,6 +2172,11 @@ void TextServerAdvanced::font_set_scale(const RID &p_font_rid, int64_t p_size, d
Vector2i size = _get_size(fd, p_size);
ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+#ifdef MODULE_FREETYPE_ENABLED
+ if (fd->cache[size]->face) {
+ return; // Do not override scale for dynamic fonts, it's calculated automatically.
+ }
+#endif
fd->cache[size]->scale = p_scale;
}
@@ -2366,9 +2371,8 @@ Array TextServerAdvanced::font_get_glyph_list(const RID &p_font_rid, const Vecto
Array ret;
const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map;
- const int32_t *E = nullptr;
- while ((E = gl.next(E))) {
- ret.push_back(*E);
+ for (const KeyValue<int32_t, FontGlyph> &E : gl) {
+ ret.push_back(E.key);
}
return ret;
}
@@ -2765,7 +2769,7 @@ Vector2 TextServerAdvanced::font_get_kerning(const RID &p_font_rid, int64_t p_si
ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Vector2());
- const Map<Vector2i, Vector2> &kern = fd->cache[size]->kerning_map;
+ const HashMap<Vector2i, Vector2, VariantHasher, VariantComparator> &kern = fd->cache[size]->kerning_map;
if (kern.has(p_glyph_pair)) {
if (fd->msdf) {
@@ -2823,7 +2827,7 @@ bool TextServerAdvanced::font_has_char(const RID &p_font_rid, int64_t p_char) co
if (fd->cache.is_empty()) {
ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, fd->msdf ? Vector2i(fd->msdf_source_size, 0) : Vector2i(16, 0)), false);
}
- FontDataForSizeAdvanced *at_size = fd->cache.front()->get();
+ FontDataForSizeAdvanced *at_size = fd->cache.begin()->value;
#ifdef MODULE_FREETYPE_ENABLED
if (at_size && at_size->face) {
@@ -2841,7 +2845,7 @@ String TextServerAdvanced::font_get_supported_chars(const RID &p_font_rid) const
if (fd->cache.is_empty()) {
ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, fd->msdf ? Vector2i(fd->msdf_source_size, 0) : Vector2i(16, 0)), String());
}
- FontDataForSizeAdvanced *at_size = fd->cache.front()->get();
+ FontDataForSizeAdvanced *at_size = fd->cache.begin()->value;
String chars;
#ifdef MODULE_FREETYPE_ENABLED
@@ -2859,9 +2863,8 @@ String TextServerAdvanced::font_get_supported_chars(const RID &p_font_rid) const
#endif
if (at_size) {
const HashMap<int32_t, FontGlyph> &gl = at_size->glyph_map;
- const int32_t *E = nullptr;
- while ((E = gl.next(E))) {
- chars = chars + String::chr(*E);
+ for (const KeyValue<int32_t, FontGlyph> &E : gl) {
+ chars = chars + String::chr(E.key);
}
}
return chars;
@@ -4572,7 +4575,7 @@ bool TextServerAdvanced::shaped_text_update_justification_ops(const RID &p_shape
Glyph *sd_glyphs = sd->glyphs.ptrw();
int sd_size = sd->glyphs.size();
- if (sd->jstops.size() > 0) {
+ if (!sd->jstops.is_empty()) {
for (int i = 0; i < sd_size; i++) {
if (sd_glyphs[i].count > 0) {
char32_t c = sd->text[sd_glyphs[i].start - sd->start];
diff --git a/modules/text_server_adv/text_server_adv.h b/modules/text_server_adv/text_server_adv.h
index 1b4293aa72..c292e2e707 100644
--- a/modules/text_server_adv/text_server_adv.h
+++ b/modules/text_server_adv/text_server_adv.h
@@ -67,7 +67,6 @@
#include <godot_cpp/classes/ref.hpp>
#include <godot_cpp/templates/hash_map.hpp>
-#include <godot_cpp/templates/map.hpp>
#include <godot_cpp/templates/rid_owner.hpp>
#include <godot_cpp/templates/set.hpp>
#include <godot_cpp/templates/thread_work_pool.hpp>
@@ -78,6 +77,7 @@ using namespace godot;
#else
// Headers for building as built-in module.
+#include "core/templates/hash_map.h"
#include "core/templates/rid_owner.h"
#include "core/templates/thread_work_pool.h"
#include "scene/resources/texture.h"
@@ -133,8 +133,8 @@ class TextServerAdvanced : public TextServerExtension {
};
Vector<NumSystemData> num_systems;
- Map<StringName, int32_t> feature_sets;
- Map<int32_t, StringName> feature_sets_inv;
+ HashMap<StringName, int32_t> feature_sets;
+ HashMap<int32_t, StringName> feature_sets_inv;
void _insert_num_systems_lang();
void _insert_feature_sets();
@@ -191,7 +191,7 @@ class TextServerAdvanced : public TextServerExtension {
Vector<FontTexture> textures;
HashMap<int32_t, FontGlyph> glyph_map;
- Map<Vector2i, Vector2> kerning_map;
+ HashMap<Vector2i, Vector2, VariantHasher, VariantComparator> kerning_map;
hb_font_t *hb_handle = nullptr;
@@ -233,7 +233,7 @@ class TextServerAdvanced : public TextServerExtension {
String font_name;
String style_name;
- Map<Vector2i, FontDataForSizeAdvanced *> cache;
+ HashMap<Vector2i, FontDataForSizeAdvanced *, VariantHasher, VariantComparator> cache;
bool face_init = false;
Set<uint32_t> supported_scripts;
@@ -242,8 +242,8 @@ class TextServerAdvanced : public TextServerExtension {
Dictionary feature_overrides;
// Language/script support override.
- Map<String, bool> language_support_overrides;
- Map<String, bool> script_support_overrides;
+ HashMap<String, bool> language_support_overrides;
+ HashMap<String, bool> script_support_overrides;
PackedByteArray data;
const uint8_t *data_ptr;
@@ -334,7 +334,7 @@ class TextServerAdvanced : public TextServerExtension {
InlineAlignment inline_align = INLINE_ALIGNMENT_CENTER;
Rect2 rect;
};
- Map<Variant, EmbeddedObject> objects;
+ HashMap<Variant, EmbeddedObject, VariantHasher, VariantComparator> objects;
/* Shaped data */
TextServer::Direction para_direction = DIRECTION_LTR; // Detected text direction.
diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp
index 8ae56aa64d..4a4b51e5d3 100644
--- a/modules/text_server_fb/text_server_fb.cpp
+++ b/modules/text_server_fb/text_server_fb.cpp
@@ -1334,6 +1334,11 @@ void TextServerFallback::font_set_scale(const RID &p_font_rid, int64_t p_size, d
Vector2i size = _get_size(fd, p_size);
ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+#ifdef MODULE_FREETYPE_ENABLED
+ if (fd->cache[size]->face) {
+ return; // Do not override scale for dynamic fonts, it's calculated automatically.
+ }
+#endif
fd->cache[size]->scale = p_scale;
}
@@ -1528,9 +1533,8 @@ Array TextServerFallback::font_get_glyph_list(const RID &p_font_rid, const Vecto
Array ret;
const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map;
- const int32_t *E = nullptr;
- while ((E = gl.next(E))) {
- ret.push_back(*E);
+ for (const KeyValue<int32_t, FontGlyph> &E : gl) {
+ ret.push_back(E.key);
}
return ret;
}
@@ -1913,7 +1917,7 @@ Vector2 TextServerFallback::font_get_kerning(const RID &p_font_rid, int64_t p_si
ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Vector2());
- const Map<Vector2i, Vector2> &kern = fd->cache[size]->kerning_map;
+ const HashMap<Vector2i, Vector2, VariantHasher, VariantComparator> &kern = fd->cache[size]->kerning_map;
if (kern.has(p_glyph_pair)) {
if (fd->msdf) {
@@ -1953,7 +1957,7 @@ bool TextServerFallback::font_has_char(const RID &p_font_rid, int64_t p_char) co
if (fd->cache.is_empty()) {
ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, fd->msdf ? Vector2i(fd->msdf_source_size, 0) : Vector2i(16, 0)), false);
}
- FontDataForSizeFallback *at_size = fd->cache.front()->get();
+ FontDataForSizeFallback *at_size = fd->cache.begin()->value;
#ifdef MODULE_FREETYPE_ENABLED
if (at_size && at_size->face) {
@@ -1971,7 +1975,7 @@ String TextServerFallback::font_get_supported_chars(const RID &p_font_rid) const
if (fd->cache.is_empty()) {
ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, fd->msdf ? Vector2i(fd->msdf_source_size, 0) : Vector2i(16, 0)), String());
}
- FontDataForSizeFallback *at_size = fd->cache.front()->get();
+ FontDataForSizeFallback *at_size = fd->cache.begin()->value;
String chars;
#ifdef MODULE_FREETYPE_ENABLED
@@ -1989,9 +1993,8 @@ String TextServerFallback::font_get_supported_chars(const RID &p_font_rid) const
#endif
if (at_size) {
const HashMap<int32_t, FontGlyph> &gl = at_size->glyph_map;
- const int32_t *E = nullptr;
- while ((E = gl.next(E))) {
- chars = chars + String::chr(*E);
+ for (const KeyValue<int32_t, FontGlyph> &E : gl) {
+ chars = chars + String::chr(E.key);
}
}
return chars;
diff --git a/modules/text_server_fb/text_server_fb.h b/modules/text_server_fb/text_server_fb.h
index c837029623..0d2fc2628d 100644
--- a/modules/text_server_fb/text_server_fb.h
+++ b/modules/text_server_fb/text_server_fb.h
@@ -67,7 +67,6 @@
#include <godot_cpp/classes/ref.hpp>
#include <godot_cpp/templates/hash_map.hpp>
-#include <godot_cpp/templates/map.hpp>
#include <godot_cpp/templates/rid_owner.hpp>
#include <godot_cpp/templates/set.hpp>
#include <godot_cpp/templates/thread_work_pool.hpp>
@@ -80,6 +79,7 @@ using namespace godot;
#include "servers/text/text_server_extension.h"
+#include "core/templates/hash_map.h"
#include "core/templates/rid_owner.h"
#include "core/templates/thread_work_pool.h"
#include "scene/resources/texture.h"
@@ -106,8 +106,8 @@ class TextServerFallback : public TextServerExtension {
GDCLASS(TextServerFallback, TextServerExtension);
_THREAD_SAFE_CLASS_
- Map<StringName, int32_t> feature_sets;
- Map<int32_t, StringName> feature_sets_inv;
+ HashMap<StringName, int32_t> feature_sets;
+ HashMap<int32_t, StringName> feature_sets_inv;
void _insert_feature_sets();
_FORCE_INLINE_ void _insert_feature(const StringName &p_name, int32_t p_tag);
@@ -159,7 +159,7 @@ class TextServerFallback : public TextServerExtension {
Vector<FontTexture> textures;
HashMap<int32_t, FontGlyph> glyph_map;
- Map<Vector2i, Vector2> kerning_map;
+ HashMap<Vector2i, Vector2, VariantHasher, VariantComparator> kerning_map;
#ifdef MODULE_FREETYPE_ENABLED
FT_Face face = nullptr;
@@ -196,15 +196,15 @@ class TextServerFallback : public TextServerExtension {
String font_name;
String style_name;
- Map<Vector2i, FontDataForSizeFallback *> cache;
+ HashMap<Vector2i, FontDataForSizeFallback *, VariantHasher, VariantComparator> cache;
bool face_init = false;
Dictionary supported_varaitions;
Dictionary feature_overrides;
// Language/script support override.
- Map<String, bool> language_support_overrides;
- Map<String, bool> script_support_overrides;
+ HashMap<String, bool> language_support_overrides;
+ HashMap<String, bool> script_support_overrides;
PackedByteArray data;
const uint8_t *data_ptr;
@@ -294,7 +294,7 @@ class TextServerFallback : public TextServerExtension {
InlineAlignment inline_align = INLINE_ALIGNMENT_CENTER;
Rect2 rect;
};
- Map<Variant, EmbeddedObject> objects;
+ HashMap<Variant, EmbeddedObject, VariantHasher, VariantComparator> objects;
/* Shaped data */
TextServer::Direction para_direction = DIRECTION_LTR; // Detected text direction.
diff --git a/modules/visual_script/editor/visual_script_editor.cpp b/modules/visual_script/editor/visual_script_editor.cpp
index 3e6680d8d8..569bcad780 100644
--- a/modules/visual_script/editor/visual_script_editor.cpp
+++ b/modules/visual_script/editor/visual_script_editor.cpp
@@ -1608,7 +1608,7 @@ void VisualScriptEditor::_remove_output_port(int p_id, int p_port) {
if (E.from_node == p_id && E.from_port == p_port) {
// Push into the connections map.
if (!conn_map.has(E.to_node)) {
- conn_map.set(E.to_node, Set<int>());
+ conn_map.insert(E.to_node, Set<int>());
}
conn_map[E.to_node].insert(E.to_port);
}
@@ -1617,11 +1617,9 @@ void VisualScriptEditor::_remove_output_port(int p_id, int p_port) {
undo_redo->add_do_method(vsn.ptr(), "remove_output_data_port", p_port);
undo_redo->add_do_method(this, "_update_graph", p_id);
- List<int> keys;
- conn_map.get_key_list(&keys);
- for (const int &E : keys) {
- for (const Set<int>::Element *F = conn_map[E].front(); F; F = F->next()) {
- undo_redo->add_undo_method(script.ptr(), "data_connect", p_id, p_port, E, F->get());
+ for (const KeyValue<int, Set<int>> &E : conn_map) {
+ for (const Set<int>::Element *F = E.value.front(); F; F = F->next()) {
+ undo_redo->add_undo_method(script.ptr(), "data_connect", p_id, p_port, E.key, F->get());
}
}
@@ -1912,7 +1910,7 @@ void VisualScriptEditor::_on_nodes_duplicate() {
Ref<VisualScriptNode> dupe = node->duplicate(true);
int new_id = idc++;
- remap.set(F->get(), new_id);
+ remap.insert(F->get(), new_id);
to_select.insert(new_id);
undo_redo->add_do_method(script.ptr(), "add_node", new_id, dupe, script->get_node_position(F->get()) + Vector2(20, 20));
diff --git a/modules/visual_script/visual_script.cpp b/modules/visual_script/visual_script.cpp
index 7028fead9a..c140ceb0b4 100644
--- a/modules/visual_script/visual_script.cpp
+++ b/modules/visual_script/visual_script.cpp
@@ -209,8 +209,9 @@ Vector2 VisualScript::get_scroll() const {
}
void VisualScript::get_function_list(List<StringName> *r_functions) const {
- functions.get_key_list(r_functions);
- // r_functions->sort_custom<StringName::AlphCompare>(); // Don't force sorting.
+ for (const KeyValue<StringName, Function> &E : functions) {
+ r_functions->push_back(E.key);
+ }
}
int VisualScript::get_function_node_id(const StringName &p_name) const {
@@ -346,7 +347,9 @@ Point2 VisualScript::get_node_position(int p_id) const {
}
void VisualScript::get_node_list(List<int> *r_nodes) const {
- nodes.get_key_list(r_nodes);
+ for (const KeyValue<int, NodeData> &E : nodes) {
+ r_nodes->push_back(E.key);
+ }
}
void VisualScript::sequence_connect(int p_from_node, int p_from_output, int p_to_node) {
@@ -563,8 +566,9 @@ Dictionary VisualScript::_get_variable_info(const StringName &p_name) const {
}
void VisualScript::get_variable_list(List<StringName> *r_variables) const {
- variables.get_key_list(r_variables);
- // r_variables->sort_custom<StringName::AlphCompare>(); // Don't force it.
+ for (const KeyValue<StringName, Variable> &E : variables) {
+ r_variables->push_back(E.key);
+ }
}
void VisualScript::set_instance_base_type(const StringName &p_type) {
@@ -713,12 +717,11 @@ int VisualScript::get_available_id() const {
// This is infinitely increasing,
// so one might want to implement a better solution,
// if the there is a case for huge number of nodes to be added to visual script.
- List<int> nds;
- nodes.get_key_list(&nds);
+
int max = -1;
- for (const int &E : nds) {
- if (E > max) {
- max = E;
+ for (const KeyValue<int, NodeData> &E : nodes) {
+ if (E.key > max) {
+ max = E.key;
}
}
return (max + 1);
@@ -750,18 +753,15 @@ void VisualScript::_update_placeholders() {
List<PropertyInfo> pinfo;
Map<StringName, Variant> values;
- List<StringName> keys;
- variables.get_key_list(&keys);
-
- for (const StringName &E : keys) {
- if (!variables[E]._export) {
+ for (const KeyValue<StringName, Variable> &E : variables) {
+ if (!variables[E.key]._export) {
continue;
}
- PropertyInfo p = variables[E].info;
- p.name = String(E);
+ PropertyInfo p = variables[E.key].info;
+ p.name = String(E.key);
pinfo.push_back(p);
- values[p.name] = variables[E].default_value;
+ values[p.name] = variables[E.key].default_value;
}
for (Set<PlaceHolderScriptInstance *>::Element *E = placeholders.front(); E; E = E->next()) {
@@ -781,18 +781,15 @@ ScriptInstance *VisualScript::instance_create(Object *p_this) {
List<PropertyInfo> pinfo;
Map<StringName, Variant> values;
- List<StringName> keys;
- variables.get_key_list(&keys);
-
- for (const StringName &E : keys) {
- if (!variables[E]._export) {
+ for (const KeyValue<StringName, Variable> &E : variables) {
+ if (!variables[E.key]._export) {
continue;
}
- PropertyInfo p = variables[E].info;
- p.name = String(E);
+ PropertyInfo p = variables[E.key].info;
+ p.name = String(E.key);
pinfo.push_back(p);
- values[p.name] = variables[E].default_value;
+ values[p.name] = variables[E.key].default_value;
}
sins->update(pinfo, values);
@@ -872,14 +869,11 @@ bool VisualScript::get_property_default_value(const StringName &p_property, Vari
}
void VisualScript::get_script_method_list(List<MethodInfo> *p_list) const {
- List<StringName> funcs;
- functions.get_key_list(&funcs);
-
- for (const StringName &E : funcs) {
+ for (const KeyValue<StringName, Function> &E : functions) {
MethodInfo mi;
- mi.name = E;
- if (functions[E].func_id >= 0) {
- Ref<VisualScriptFunction> func = nodes[functions[E].func_id].node;
+ mi.name = E.key;
+ if (functions[E.key].func_id >= 0) {
+ Ref<VisualScriptFunction> func = nodes[functions[E.key].func_id].node;
if (func.is_valid()) {
for (int i = 0; i < func->get_argument_count(); i++) {
PropertyInfo arg;
@@ -945,10 +939,8 @@ int VisualScript::get_member_line(const StringName &p_member) const {
#ifdef TOOLS_ENABLED
bool VisualScript::are_subnodes_edited() const {
- List<int> keys;
- nodes.get_key_list(&keys);
- for (const int &F : keys) {
- if (nodes[F].node->is_edited()) {
+ for (const KeyValue<int, NodeData> &F : nodes) {
+ if (F.value.node->is_edited()) {
return true;
}
}
@@ -1017,15 +1009,13 @@ void VisualScript::_set_data(const Dictionary &p_data) {
// Takes all the rpc methods.
rpc_functions.clear();
- List<StringName> fns;
- functions.get_key_list(&fns);
- for (const StringName &E : fns) {
- if (functions[E].func_id >= 0 && nodes.has(functions[E].func_id)) {
- Ref<VisualScriptFunction> vsf = nodes[functions[E].func_id].node;
+ for (const KeyValue<StringName, Function> &E : functions) {
+ if (E.value.func_id >= 0 && nodes.has(E.value.func_id)) {
+ Ref<VisualScriptFunction> vsf = nodes[E.value.func_id].node;
if (vsf.is_valid()) {
if (vsf->get_rpc_mode() != Multiplayer::RPC_MODE_DISABLED) {
Multiplayer::RPCConfig nd;
- nd.name = E;
+ nd.name = E.key;
nd.rpc_mode = vsf->get_rpc_mode();
nd.transfer_mode = Multiplayer::TRANSFER_MODE_RELIABLE; // TODO
if (rpc_functions.find(nd) == -1) {
@@ -1045,13 +1035,11 @@ Dictionary VisualScript::_get_data() const {
d["base_type"] = base_type;
Array vars;
- List<StringName> var_names;
- variables.get_key_list(&var_names);
- for (const StringName &E : var_names) {
- Dictionary var = _get_variable_info(E);
- var["name"] = E; // Make sure it's the right one.
- var["default_value"] = variables[E].default_value;
- var["export"] = variables[E]._export;
+ for (const KeyValue<StringName, Variable> &E : variables) {
+ Dictionary var = _get_variable_info(E.key);
+ var["name"] = E.key; // Make sure it's the right one.
+ var["default_value"] = E.value.default_value;
+ var["export"] = E.value._export;
vars.push_back(var);
}
d["variables"] = vars;
@@ -1073,23 +1061,19 @@ Dictionary VisualScript::_get_data() const {
d["signals"] = sigs;
Array funcs;
- List<StringName> func_names;
- functions.get_key_list(&func_names);
- for (const StringName &E : func_names) {
+ for (const KeyValue<StringName, Function> &E : functions) {
Dictionary func;
- func["name"] = E;
- func["function_id"] = functions[E].func_id;
+ func["name"] = E.key;
+ func["function_id"] = E.value.func_id;
funcs.push_back(func);
}
d["functions"] = funcs;
Array nds;
- List<int> node_ids;
- nodes.get_key_list(&node_ids);
- for (const int &F : node_ids) {
- nds.push_back(F);
- nds.push_back(nodes[F].pos);
- nds.push_back(nodes[F].node);
+ for (const KeyValue<int, NodeData> &F : nodes) {
+ nds.push_back(F.key);
+ nds.push_back(F.value.pos);
+ nds.push_back(F.value.node);
}
d["nodes"] = nds;
@@ -1199,10 +1183,8 @@ Set<int> VisualScript::get_output_sequence_ports_connected(int from_node) {
VisualScript::~VisualScript() {
// Remove all nodes and stuff that hold data refs.
- List<int> nds;
- nodes.get_key_list(&nds);
- for (const int &E : nds) {
- remove_node(E);
+ for (const KeyValue<int, NodeData> &E : nodes) {
+ remove_node(E.key);
}
}
@@ -1230,14 +1212,12 @@ bool VisualScriptInstance::get(const StringName &p_name, Variant &r_ret) const {
}
void VisualScriptInstance::get_property_list(List<PropertyInfo> *p_properties) const {
- List<StringName> vars;
- script->variables.get_key_list(&vars);
- for (const StringName &E : vars) {
- if (!script->variables[E]._export) {
+ for (const KeyValue<StringName, VisualScript::Variable> &E : script->variables) {
+ if (!E.value._export) {
continue;
}
- PropertyInfo p = script->variables[E].info;
- p.name = String(E);
+ PropertyInfo p = E.value.info;
+ p.name = String(E.key);
p.usage |= PROPERTY_USAGE_SCRIPT_VARIABLE;
p_properties->push_back(p);
}
@@ -1259,13 +1239,11 @@ Variant::Type VisualScriptInstance::get_property_type(const StringName &p_name,
}
void VisualScriptInstance::get_method_list(List<MethodInfo> *p_list) const {
- List<StringName> fns;
- script->functions.get_key_list(&fns);
- for (const StringName &E : fns) {
+ for (const KeyValue<StringName, VisualScript::Function> &E : script->functions) {
MethodInfo mi;
- mi.name = E;
- if (script->functions[E].func_id >= 0 && script->nodes.has(script->functions[E].func_id)) {
- Ref<VisualScriptFunction> vsf = script->nodes[script->functions[E].func_id].node;
+ mi.name = E.key;
+ if (E.value.func_id >= 0 && script->nodes.has(E.value.func_id)) {
+ Ref<VisualScriptFunction> vsf = script->nodes[E.value.func_id].node;
if (vsf.is_valid()) {
for (int i = 0; i < vsf->get_argument_count(); i++) {
PropertyInfo arg;
@@ -1845,19 +1823,15 @@ void VisualScriptInstance::create(const Ref<VisualScript> &p_script, Object *p_o
// Setup variables.
{
- List<StringName> keys;
- script->variables.get_key_list(&keys);
- for (const StringName &E : keys) {
- variables[E] = script->variables[E].default_value;
+ for (const KeyValue<StringName, VisualScript::Variable> &E : script->variables) {
+ variables[E.key] = E.value.default_value;
}
}
// Setup functions from sequence trees.
{
- List<StringName> keys;
- script->functions.get_key_list(&keys);
- for (const StringName &E : keys) {
- const VisualScript::Function vsfn = p_script->functions[E];
+ for (const KeyValue<StringName, VisualScript::Function> &E : script->functions) {
+ const VisualScript::Function &vsfn = E.value;
Function function;
function.node = vsfn.func_id;
function.max_stack = 0;
@@ -1868,7 +1842,7 @@ void VisualScriptInstance::create(const Ref<VisualScript> &p_script, Object *p_o
Map<StringName, int> local_var_indices;
if (function.node < 0) {
- VisualScriptLanguage::singleton->debug_break_parse(get_script()->get_path(), 0, "No start node in function: " + String(E));
+ VisualScriptLanguage::singleton->debug_break_parse(get_script()->get_path(), 0, "No start node in function: " + String(E.key));
ERR_CONTINUE(function.node < 0);
}
@@ -1876,7 +1850,7 @@ void VisualScriptInstance::create(const Ref<VisualScript> &p_script, Object *p_o
Ref<VisualScriptFunction> func_node = script->get_node(vsfn.func_id);
if (func_node.is_null()) {
- VisualScriptLanguage::singleton->debug_break_parse(get_script()->get_path(), 0, "No VisualScriptFunction typed start node in function: " + String(E));
+ VisualScriptLanguage::singleton->debug_break_parse(get_script()->get_path(), 0, "No VisualScriptFunction typed start node in function: " + String(E.key));
}
ERR_CONTINUE(!func_node.is_valid());
@@ -1916,13 +1890,12 @@ void VisualScriptInstance::create(const Ref<VisualScript> &p_script, Object *p_o
List<int> dc_keys;
while (!nd_queue.is_empty()) {
int ky = nd_queue.front()->get();
- dc_lut[ky].get_key_list(&dc_keys);
- for (const int &F : dc_keys) {
+ for (const KeyValue<int, Pair<int, int>> &F : dc_lut[ky]) {
VisualScript::DataConnection dc;
- dc.from_node = dc_lut[ky][F].first;
- dc.from_port = dc_lut[ky][F].second;
+ dc.from_node = F.value.first;
+ dc.from_port = F.value.second;
dc.to_node = ky;
- dc.to_port = F;
+ dc.to_port = F.key;
dataconns.insert(dc);
nd_queue.push_back(dc.from_node);
node_ids.insert(dc.from_node);
@@ -2072,7 +2045,7 @@ void VisualScriptInstance::create(const Ref<VisualScript> &p_script, Object *p_o
}
}
- functions[E] = function;
+ functions[E.key] = function;
}
}
}
diff --git a/modules/vorbis/audio_stream_ogg_vorbis.cpp b/modules/vorbis/audio_stream_ogg_vorbis.cpp
index 5ff5b2339c..b645a48c88 100644
--- a/modules/vorbis/audio_stream_ogg_vorbis.cpp
+++ b/modules/vorbis/audio_stream_ogg_vorbis.cpp
@@ -350,7 +350,6 @@ void AudioStreamOGGVorbis::maybe_update_info() {
vorbis_info_init(&info);
vorbis_comment_init(&comment);
- int packet_count = 0;
Ref<OGGPacketSequencePlayback> packet_sequence_playback = packet_sequence->instance_playback();
for (int i = 0; i < 3; i++) {
@@ -369,8 +368,6 @@ void AudioStreamOGGVorbis::maybe_update_info() {
err = vorbis_synthesis_headerin(&info, &comment, packet);
ERR_FAIL_COND_MSG(err != 0, "Error parsing header packet " + itos(i) + ": " + itos(err));
-
- packet_count++;
}
packet_sequence->set_sampling_rate(info.rate);
diff --git a/platform/android/detect.py b/platform/android/detect.py
index 3319d5890c..0099ac7e0d 100644
--- a/platform/android/detect.py
+++ b/platform/android/detect.py
@@ -188,8 +188,11 @@ def configure(env):
if env["target"].startswith("release"):
if env["optimize"] == "speed": # optimize for speed (default)
- env.Append(LINKFLAGS=["-O2"])
- env.Append(CCFLAGS=["-O2", "-fomit-frame-pointer"])
+ # `-O2` is more friendly to debuggers than `-O3`, leading to better crash backtraces
+ # when using `target=release_debug`.
+ opt = "-O3" if env["target"] == "release" else "-O2"
+ env.Append(LINKFLAGS=[opt])
+ env.Append(CCFLAGS=[opt, "-fomit-frame-pointer"])
elif env["optimize"] == "size": # optimize for size
env.Append(CCFLAGS=["-Os"])
env.Append(LINKFLAGS=["-Os"])
diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp
index d357cd586e..5aef64943b 100644
--- a/platform/android/export/export_plugin.cpp
+++ b/platform/android/export/export_plugin.cpp
@@ -2775,7 +2775,8 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
return ERR_FILE_BAD_PATH;
}
- zlib_filefunc_def io = zipio_create_io();
+ Ref<FileAccess> io_fa;
+ zlib_filefunc_def io = zipio_create_io(&io_fa);
if (ep.step(TTR("Creating APK..."), 0)) {
return ERR_SKIP;
@@ -2789,7 +2790,8 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
int ret = unzGoToFirstFile(pkg);
- zlib_filefunc_def io2 = zipio_create_io();
+ Ref<FileAccess> io2_fa;
+ zlib_filefunc_def io2 = zipio_create_io(&io2_fa);
String tmp_unaligned_path = EditorPaths::get_singleton()->get_cache_dir().plus_file("tmpexport-unaligned." + uitos(OS::get_singleton()->get_unix_time()) + ".apk");
@@ -2985,7 +2987,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
ret = unzGoToFirstFile(tmp_unaligned);
- io2 = zipio_create_io();
+ io2 = zipio_create_io(&io2_fa);
zipFile final_apk = zipOpen2(p_path.utf8().get_data(), APPEND_STATUS_CREATE, nullptr, &io2);
// Take files from the unaligned APK and write them out to the aligned one
diff --git a/platform/iphone/detect.py b/platform/iphone/detect.py
index f442235e7c..392a0151be 100644
--- a/platform/iphone/detect.py
+++ b/platform/iphone/detect.py
@@ -47,8 +47,11 @@ def configure(env):
if env["target"].startswith("release"):
env.Append(CPPDEFINES=["NDEBUG", ("NS_BLOCK_ASSERTIONS", 1)])
if env["optimize"] == "speed": # optimize for speed (default)
- env.Append(CCFLAGS=["-O2", "-ftree-vectorize", "-fomit-frame-pointer"])
- env.Append(LINKFLAGS=["-O2"])
+ # `-O2` is more friendly to debuggers than `-O3`, leading to better crash backtraces
+ # when using `target=release_debug`.
+ opt = "-O3" if env["target"] == "release" else "-O2"
+ env.Append(CCFLAGS=[opt, "-ftree-vectorize", "-fomit-frame-pointer"])
+ env.Append(LINKFLAGS=[opt])
elif env["optimize"] == "size": # optimize for size
env.Append(CCFLAGS=["-Os", "-ftree-vectorize"])
env.Append(LINKFLAGS=["-Os"])
diff --git a/platform/iphone/export/export_plugin.cpp b/platform/iphone/export/export_plugin.cpp
index 57bee59523..bf7cf6ee81 100644
--- a/platform/iphone/export/export_plugin.cpp
+++ b/platform/iphone/export/export_plugin.cpp
@@ -107,16 +107,13 @@ void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options)
for (int i = 0; i < found_plugins.size(); i++) {
// Editable plugin plist values
PluginConfigIOS plugin = found_plugins[i];
- const String *K = nullptr;
- while ((K = plugin.plist.next(K))) {
- String key = *K;
- PluginConfigIOS::PlistItem item = plugin.plist[key];
- switch (item.type) {
+ for (const KeyValue<String, PluginConfigIOS::PlistItem> &E : plugin.plist) {
+ switch (E.value.type) {
case PluginConfigIOS::PlistItemType::STRING_INPUT: {
- String preset_name = "plugins_plist/" + key;
+ String preset_name = "plugins_plist/" + E.key;
if (!plist_keys.has(preset_name)) {
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, preset_name), item.value));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, preset_name), E.value.value));
plist_keys.insert(preset_name);
}
} break;
@@ -1258,11 +1255,10 @@ Error EditorExportPlatformIOS::_export_ios_plugins(const Ref<EditorExportPreset>
// Plist
// Using hash map container to remove duplicates
- const String *K = nullptr;
- while ((K = plugin.plist.next(K))) {
- String key = *K;
- PluginConfigIOS::PlistItem item = plugin.plist[key];
+ for (const KeyValue<String, PluginConfigIOS::PlistItem> &E : plugin.plist) {
+ String key = E.key;
+ const PluginConfigIOS::PlistItem &item = E.value;
String value;
@@ -1301,10 +1297,9 @@ Error EditorExportPlatformIOS::_export_ios_plugins(const Ref<EditorExportPreset>
// Updating `Info.plist`
{
- const String *K = nullptr;
- while ((K = plist_values.next(K))) {
- String key = *K;
- String value = plist_values[key];
+ for (const KeyValue<String, String> &E : plist_values) {
+ String key = E.key;
+ String value = E.value;
if (key.is_empty() || value.is_empty()) {
continue;
@@ -1488,7 +1483,8 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p
ERR_FAIL_COND_V(tmp_app_path.is_null(), ERR_CANT_CREATE);
print_line("Unzipping...");
- zlib_filefunc_def io = zipio_create_io();
+ Ref<FileAccess> io_fa;
+ zlib_filefunc_def io = zipio_create_io(&io_fa);
unzFile src_pkg_zip = unzOpen2(src_pkg_name.utf8().get_data(), &io);
if (!src_pkg_zip) {
EditorNode::add_io_error("Could not open export template (not a zip file?):\n" + src_pkg_name);
diff --git a/platform/iphone/tts_ios.h b/platform/iphone/tts_ios.h
index c7defeb98f..3fac762b62 100644
--- a/platform/iphone/tts_ios.h
+++ b/platform/iphone/tts_ios.h
@@ -31,7 +31,11 @@
#ifndef TTS_IOS_H
#define TTS_IOS_H
-#include <AVFAudio/AVSpeechSynthesis.h>
+#if __has_include(<AVFAudio/AVSpeechSynthesis.h>)
+#import <AVFAudio/AVSpeechSynthesis.h>
+#else
+#import <AVFoundation/AVFoundation.h>
+#endif
#include "core/string/ustring.h"
#include "core/templates/list.h"
diff --git a/platform/javascript/api/javascript_tools_editor_plugin.cpp b/platform/javascript/api/javascript_tools_editor_plugin.cpp
index 31ce71127d..1507f32375 100644
--- a/platform/javascript/api/javascript_tools_editor_plugin.cpp
+++ b/platform/javascript/api/javascript_tools_editor_plugin.cpp
@@ -64,7 +64,8 @@ void JavaScriptToolsEditorPlugin::_download_zip(Variant p_v) {
}
String resource_path = ProjectSettings::get_singleton()->get_resource_path();
- zlib_filefunc_def io = zipio_create_io();
+ Ref<FileAccess> io_fa;
+ zlib_filefunc_def io = zipio_create_io(&io_fa);
// Name the downloaded ZIP file to contain the project name and download date for easier organization.
// Replace characters not allowed (or risky) in Windows file names with safe characters.
@@ -122,7 +123,7 @@ void JavaScriptToolsEditorPlugin::_zip_file(String p_path, String p_base_path, z
void JavaScriptToolsEditorPlugin::_zip_recursive(String p_path, String p_base_path, zipFile p_zip) {
Ref<DirAccess> dir = DirAccess::open(p_path);
- if (!dir) {
+ if (dir.is_null()) {
WARN_PRINT("Unable to open directory for zipping: " + p_path);
return;
}
diff --git a/platform/javascript/export/export_plugin.cpp b/platform/javascript/export/export_plugin.cpp
index 66d93d7c49..9576256d03 100644
--- a/platform/javascript/export/export_plugin.cpp
+++ b/platform/javascript/export/export_plugin.cpp
@@ -33,7 +33,8 @@
#include "core/config/project_settings.h"
Error EditorExportPlatformJavaScript::_extract_template(const String &p_template, const String &p_dir, const String &p_name, bool pwa) {
- zlib_filefunc_def io = zipio_create_io();
+ Ref<FileAccess> io_fa;
+ zlib_filefunc_def io = zipio_create_io(&io_fa);
unzFile pkg = unzOpen2(p_template.utf8().get_data(), &io);
if (!pkg) {
diff --git a/platform/javascript/godot_webgl2.h b/platform/javascript/godot_webgl2.h
new file mode 100644
index 0000000000..7c357ff66d
--- /dev/null
+++ b/platform/javascript/godot_webgl2.h
@@ -0,0 +1,37 @@
+/*************************************************************************/
+/* godot_webgl2.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. */
+/*************************************************************************/
+
+#ifndef GODOT_WEBGL2_H
+#define GODOT_WEBGL2_H
+
+#include "GLES3/gl3.h"
+#include "webgl/webgl2.h"
+
+#endif
diff --git a/platform/javascript/js/libs/library_godot_os.js b/platform/javascript/js/libs/library_godot_os.js
index 12d06a8d51..377eec3234 100644
--- a/platform/javascript/js/libs/library_godot_os.js
+++ b/platform/javascript/js/libs/library_godot_os.js
@@ -305,7 +305,9 @@ const GodotOS = {
godot_js_os_hw_concurrency_get__sig: 'i',
godot_js_os_hw_concurrency_get: function () {
- return navigator.hardwareConcurrency || 1;
+ // TODO Godot core needs fixing to avoid spawning too many threads (> 24).
+ const concurrency = navigator.hardwareConcurrency || 1;
+ return concurrency < 2 ? concurrency : 2;
},
godot_js_os_download_buffer__sig: 'viiii',
diff --git a/platform/javascript/platform_config.h b/platform/javascript/platform_config.h
index ba1b0d459e..1970fe0fa0 100644
--- a/platform/javascript/platform_config.h
+++ b/platform/javascript/platform_config.h
@@ -29,3 +29,5 @@
/*************************************************************************/
#include <alloca.h>
+
+#define OPENGL_INCLUDE_H "platform/javascript/godot_webgl2.h"
diff --git a/platform/linuxbsd/display_server_x11.cpp b/platform/linuxbsd/display_server_x11.cpp
index b35f0daec6..9f53b31567 100644
--- a/platform/linuxbsd/display_server_x11.cpp
+++ b/platform/linuxbsd/display_server_x11.cpp
@@ -4112,13 +4112,13 @@ void DisplayServerX11::process_events() {
void DisplayServerX11::release_rendering_thread() {
#if defined(GLES3_ENABLED)
-// gl_manager->release_current();
+ gl_manager->release_current();
#endif
}
void DisplayServerX11::make_rendering_thread() {
#if defined(GLES3_ENABLED)
-// gl_manager->make_current();
+ gl_manager->make_current();
#endif
}
diff --git a/platform/osx/dir_access_osx.mm b/platform/osx/dir_access_osx.mm
index d26f35e847..6bafb9470d 100644
--- a/platform/osx/dir_access_osx.mm
+++ b/platform/osx/dir_access_osx.mm
@@ -34,8 +34,8 @@
#include <errno.h>
-#include <AppKit/NSWorkspace.h>
-#include <Foundation/Foundation.h>
+#import <AppKit/NSWorkspace.h>
+#import <Foundation/Foundation.h>
String DirAccessOSX::fix_unicode_name(const char *p_name) const {
String fname;
diff --git a/platform/osx/display_server_osx.h b/platform/osx/display_server_osx.h
index e1e5aea715..538a9bc04c 100644
--- a/platform/osx/display_server_osx.h
+++ b/platform/osx/display_server_osx.h
@@ -45,10 +45,11 @@
#include "platform/osx/vulkan_context_osx.h"
#endif // VULKAN_ENABLED
-#include <AppKit/AppKit.h>
-#include <AppKit/NSCursor.h>
-#include <ApplicationServices/ApplicationServices.h>
-#include <CoreVideo/CoreVideo.h>
+#import <AppKit/AppKit.h>
+#import <AppKit/NSCursor.h>
+#import <ApplicationServices/ApplicationServices.h>
+#import <CoreVideo/CoreVideo.h>
+#import <Foundation/Foundation.h>
#undef BitMap
#undef CursorShape
diff --git a/platform/osx/display_server_osx.mm b/platform/osx/display_server_osx.mm
index 548acba923..a16bd2e8de 100644
--- a/platform/osx/display_server_osx.mm
+++ b/platform/osx/display_server_osx.mm
@@ -45,12 +45,12 @@
#include "main/main.h"
#include "scene/resources/texture.h"
-#include <Carbon/Carbon.h>
-#include <Cocoa/Cocoa.h>
-#include <IOKit/IOCFPlugIn.h>
-#include <IOKit/IOKitLib.h>
-#include <IOKit/hid/IOHIDKeys.h>
-#include <IOKit/hid/IOHIDLib.h>
+#import <Carbon/Carbon.h>
+#import <Cocoa/Cocoa.h>
+#import <IOKit/IOCFPlugIn.h>
+#import <IOKit/IOKitLib.h>
+#import <IOKit/hid/IOHIDKeys.h>
+#import <IOKit/hid/IOHIDLib.h>
#if defined(GLES3_ENABLED)
#include "drivers/gles3/rasterizer_gles3.h"
@@ -146,7 +146,7 @@ DisplayServerOSX::WindowID DisplayServerOSX::_create_window(WindowMode p_mode, V
[wd.window_object setTabbingMode:NSWindowTabbingModeDisallowed];
}
- CALayer *layer = [wd.window_view layer];
+ CALayer *layer = [(NSView *)wd.window_view layer];
if (layer) {
layer.contentsScale = scale;
}
@@ -174,7 +174,7 @@ DisplayServerOSX::WindowID DisplayServerOSX::_create_window(WindowMode p_mode, V
wd.size.width = contentRect.size.width * scale;
wd.size.height = contentRect.size.height * scale;
- CALayer *layer = [wd.window_view layer];
+ CALayer *layer = [(NSView *)wd.window_view layer];
if (layer) {
layer.contentsScale = scale;
}
@@ -209,16 +209,16 @@ void DisplayServerOSX::_update_window_style(WindowData p_wd) {
if (borderless_full) {
// If the window covers up the screen set the level to above the main menu and hide on deactivate.
- [p_wd.window_object setLevel:NSMainMenuWindowLevel + 1];
- [p_wd.window_object setHidesOnDeactivate:YES];
+ [(NSWindow *)p_wd.window_object setLevel:NSMainMenuWindowLevel + 1];
+ [(NSWindow *)p_wd.window_object setHidesOnDeactivate:YES];
} else {
// Reset these when our window is not a borderless window that covers up the screen.
if (p_wd.on_top && !p_wd.fullscreen) {
- [p_wd.window_object setLevel:NSFloatingWindowLevel];
+ [(NSWindow *)p_wd.window_object setLevel:NSFloatingWindowLevel];
} else {
- [p_wd.window_object setLevel:NSNormalWindowLevel];
+ [(NSWindow *)p_wd.window_object setLevel:NSNormalWindowLevel];
}
- [p_wd.window_object setHidesOnDeactivate:NO];
+ [(NSWindow *)p_wd.window_object setHidesOnDeactivate:NO];
}
}
@@ -234,7 +234,7 @@ void DisplayServerOSX::_set_window_per_pixel_transparency_enabled(bool p_enabled
[wd.window_object setBackgroundColor:[NSColor clearColor]];
[wd.window_object setOpaque:NO];
[wd.window_object setHasShadow:NO];
- CALayer *layer = [wd.window_view layer];
+ CALayer *layer = [(NSView *)wd.window_view layer];
if (layer) {
[layer setBackgroundColor:[NSColor clearColor].CGColor];
[layer setOpaque:NO];
@@ -249,7 +249,7 @@ void DisplayServerOSX::_set_window_per_pixel_transparency_enabled(bool p_enabled
[wd.window_object setBackgroundColor:[NSColor colorWithCalibratedWhite:1 alpha:1]];
[wd.window_object setOpaque:YES];
[wd.window_object setHasShadow:YES];
- CALayer *layer = [wd.window_view layer];
+ CALayer *layer = [(NSView *)wd.window_view layer];
if (layer) {
[layer setBackgroundColor:[NSColor colorWithCalibratedWhite:1 alpha:1].CGColor];
[layer setOpaque:YES];
@@ -2256,7 +2256,7 @@ void DisplayServerOSX::window_set_mode(WindowMode p_mode, WindowID p_window) {
} break;
case WINDOW_MODE_EXCLUSIVE_FULLSCREEN:
case WINDOW_MODE_FULLSCREEN: {
- [wd.window_object setLevel:NSNormalWindowLevel];
+ [(NSWindow *)wd.window_object setLevel:NSNormalWindowLevel];
_set_window_per_pixel_transparency_enabled(true, p_window);
if (wd.resize_disabled) { // Restore resize disabled.
[wd.window_object setStyleMask:[wd.window_object styleMask] & ~NSWindowStyleMaskResizable];
@@ -2380,9 +2380,9 @@ void DisplayServerOSX::window_set_flag(WindowFlags p_flag, bool p_enabled, Windo
return;
}
if (p_enabled) {
- [wd.window_object setLevel:NSFloatingWindowLevel];
+ [(NSWindow *)wd.window_object setLevel:NSFloatingWindowLevel];
} else {
- [wd.window_object setLevel:NSNormalWindowLevel];
+ [(NSWindow *)wd.window_object setLevel:NSNormalWindowLevel];
}
} break;
case WINDOW_FLAG_TRANSPARENT: {
@@ -2423,7 +2423,7 @@ bool DisplayServerOSX::window_get_flag(WindowFlags p_flag, WindowID p_window) co
if (wd.fullscreen) {
return wd.on_top;
} else {
- return [wd.window_object level] == NSFloatingWindowLevel;
+ return [(NSWindow *)wd.window_object level] == NSFloatingWindowLevel;
}
} break;
case WINDOW_FLAG_TRANSPARENT: {
diff --git a/platform/osx/export/export_plugin.cpp b/platform/osx/export/export_plugin.cpp
index 94ef875072..465925524c 100644
--- a/platform/osx/export/export_plugin.cpp
+++ b/platform/osx/export/export_plugin.cpp
@@ -722,7 +722,8 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
return ERR_FILE_BAD_PATH;
}
- zlib_filefunc_def io = zipio_create_io();
+ Ref<FileAccess> io_fa;
+ zlib_filefunc_def io = zipio_create_io(&io_fa);
if (ep.step(TTR("Creating app bundle"), 0)) {
return ERR_SKIP;
@@ -1327,7 +1328,8 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
OS::get_singleton()->move_to_trash(p_path);
}
- zlib_filefunc_def io_dst = zipio_create_io();
+ Ref<FileAccess> io_fa_dst;
+ zlib_filefunc_def io_dst = zipio_create_io(&io_fa_dst);
zipFile zip = zipOpen2(p_path.utf8().get_data(), APPEND_STATUS_CREATE, nullptr, &io_dst);
_zip_folder_recursive(zip, tmp_base_path_name, "", pkg_name);
diff --git a/platform/osx/gl_manager_osx_legacy.h b/platform/osx/gl_manager_osx_legacy.h
index b5a1b9dd98..76d58de229 100644
--- a/platform/osx/gl_manager_osx_legacy.h
+++ b/platform/osx/gl_manager_osx_legacy.h
@@ -38,9 +38,9 @@
#include "core/templates/local_vector.h"
#include "servers/display_server.h"
-#include <AppKit/AppKit.h>
-#include <ApplicationServices/ApplicationServices.h>
-#include <CoreVideo/CoreVideo.h>
+#import <AppKit/AppKit.h>
+#import <ApplicationServices/ApplicationServices.h>
+#import <CoreVideo/CoreVideo.h>
class GLManager_OSX {
public:
diff --git a/platform/osx/joypad_osx.h b/platform/osx/joypad_osx.h
index b09d5ce34a..3f89048ce6 100644
--- a/platform/osx/joypad_osx.h
+++ b/platform/osx/joypad_osx.h
@@ -32,13 +32,13 @@
#define JOYPADOSX_H
#ifdef MACOS_10_0_4
-#include <IOKit/hidsystem/IOHIDUsageTables.h>
+#import <IOKit/hidsystem/IOHIDUsageTables.h>
#else
-#include <Kernel/IOKit/hidsystem/IOHIDUsageTables.h>
+#import <Kernel/IOKit/hidsystem/IOHIDUsageTables.h>
#endif
-#include <ForceFeedback/ForceFeedback.h>
-#include <ForceFeedback/ForceFeedbackConstants.h>
-#include <IOKit/hid/IOHIDLib.h>
+#import <ForceFeedback/ForceFeedback.h>
+#import <ForceFeedback/ForceFeedbackConstants.h>
+#import <IOKit/hid/IOHIDLib.h>
#include "core/input/input.h"
diff --git a/platform/osx/key_mapping_osx.mm b/platform/osx/key_mapping_osx.mm
index fde9206824..bfec45de58 100644
--- a/platform/osx/key_mapping_osx.mm
+++ b/platform/osx/key_mapping_osx.mm
@@ -30,8 +30,8 @@
#include "key_mapping_osx.h"
-#include <Carbon/Carbon.h>
-#include <Cocoa/Cocoa.h>
+#import <Carbon/Carbon.h>
+#import <Cocoa/Cocoa.h>
bool KeyMappingOSX::is_numpad_key(unsigned int key) {
static const unsigned int table[] = {
diff --git a/platform/osx/tts_osx.h b/platform/osx/tts_osx.h
index 2cf6d21c18..54d419e573 100644
--- a/platform/osx/tts_osx.h
+++ b/platform/osx/tts_osx.h
@@ -37,8 +37,13 @@
#include "core/variant/array.h"
#include "servers/display_server.h"
-#include <AVFAudio/AVSpeechSynthesis.h>
-#include <AppKit/AppKit.h>
+#import <AppKit/AppKit.h>
+
+#if __has_include(<AVFAudio/AVSpeechSynthesis.h>)
+#import <AVFAudio/AVSpeechSynthesis.h>
+#else
+#import <AVFoundation/AVFoundation.h>
+#endif
@interface TTS_OSX : NSObject <AVSpeechSynthesizerDelegate> {
// AVSpeechSynthesizer
diff --git a/platform/osx/vulkan_context_osx.h b/platform/osx/vulkan_context_osx.h
index b78b4eb141..ade0f4a4c9 100644
--- a/platform/osx/vulkan_context_osx.h
+++ b/platform/osx/vulkan_context_osx.h
@@ -32,7 +32,7 @@
#define VULKAN_DEVICE_OSX_H
#include "drivers/vulkan/vulkan_context.h"
-#include <AppKit/AppKit.h>
+#import <AppKit/AppKit.h>
class VulkanContextOSX : public VulkanContext {
virtual const char *_get_platform_surface_extension() const;
diff --git a/platform/uwp/export/export_plugin.cpp b/platform/uwp/export/export_plugin.cpp
index 7e06bf01e3..65a5ee7140 100644
--- a/platform/uwp/export/export_plugin.cpp
+++ b/platform/uwp/export/export_plugin.cpp
@@ -301,7 +301,8 @@ Error EditorExportPlatformUWP::export_project(const Ref<EditorExportPreset> &p_p
AppxPackager packager;
packager.init(fa_pack);
- zlib_filefunc_def io = zipio_create_io();
+ Ref<FileAccess> io_fa;
+ zlib_filefunc_def io = zipio_create_io(&io_fa);
if (ep.step("Creating package...", 0)) {
return ERR_SKIP;
diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp
index 7bfe9b64ea..341eb58f9f 100644
--- a/platform/windows/display_server_windows.cpp
+++ b/platform/windows/display_server_windows.cpp
@@ -1109,6 +1109,10 @@ void DisplayServerWindows::_update_window_style(WindowID p_window, bool p_repain
SetWindowLongPtr(wd.hWnd, GWL_STYLE, style);
SetWindowLongPtr(wd.hWnd, GWL_EXSTYLE, style_ex);
+ if (icon.is_valid()) {
+ set_icon(icon);
+ }
+
SetWindowPos(wd.hWnd, wd.always_on_top ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | ((wd.no_focus || wd.is_popup) ? SWP_NOACTIVATE : 0));
if (p_repaint) {
@@ -1900,9 +1904,11 @@ void DisplayServerWindows::set_icon(const Ref<Image> &p_icon) {
_THREAD_SAFE_METHOD_
ERR_FAIL_COND(!p_icon.is_valid());
- Ref<Image> icon = p_icon->duplicate();
- if (icon->get_format() != Image::FORMAT_RGBA8) {
- icon->convert(Image::FORMAT_RGBA8);
+ if (icon != p_icon) {
+ icon = p_icon->duplicate();
+ if (icon->get_format() != Image::FORMAT_RGBA8) {
+ icon->convert(Image::FORMAT_RGBA8);
+ }
}
int w = icon->get_width();
int h = icon->get_height();
diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h
index 253969c351..e8e207401e 100644
--- a/platform/windows/display_server_windows.h
+++ b/platform/windows/display_server_windows.h
@@ -399,6 +399,7 @@ class DisplayServerWindows : public DisplayServer {
HHOOK mouse_monitor = nullptr;
List<WindowID> popup_list;
uint64_t time_since_popup = 0;
+ Ref<Image> icon;
WindowID _create_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect);
WindowID window_id_counter = MAIN_WINDOW_ID;
diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp
index 2361729e24..55483cfc83 100644
--- a/platform/windows/os_windows.cpp
+++ b/platform/windows/os_windows.cpp
@@ -416,6 +416,31 @@ String OS_Windows::_quote_command_line_argument(const String &p_text) const {
return p_text;
}
+static void _append_to_pipe(char *p_bytes, int p_size, String *r_pipe, Mutex *p_pipe_mutex) {
+ // Try to convert from default ANSI code page to Unicode.
+ LocalVector<wchar_t> wchars;
+ int total_wchars = MultiByteToWideChar(CP_ACP, 0, p_bytes, p_size, nullptr, 0);
+ if (total_wchars > 0) {
+ wchars.resize(total_wchars);
+ if (MultiByteToWideChar(CP_ACP, 0, p_bytes, p_size, wchars.ptr(), total_wchars) == 0) {
+ wchars.clear();
+ }
+ }
+
+ if (p_pipe_mutex) {
+ p_pipe_mutex->lock();
+ }
+ if (wchars.is_empty()) {
+ // Let's hope it's compatible with UTF-8.
+ (*r_pipe) += String::utf8(p_bytes, p_size);
+ } else {
+ (*r_pipe) += String(wchars.ptr(), total_wchars);
+ }
+ if (p_pipe_mutex) {
+ p_pipe_mutex->unlock();
+ }
+}
+
Error OS_Windows::execute(const String &p_path, const List<String> &p_arguments, String *r_pipe, int *r_exitcode, bool read_stderr, Mutex *p_pipe_mutex, bool p_open_console) {
String path = p_path.replace("/", "\\");
String command = _quote_command_line_argument(path);
@@ -464,21 +489,44 @@ Error OS_Windows::execute(const String &p_path, const List<String> &p_arguments,
if (r_pipe) {
CloseHandle(pipe[1]); // Close pipe write handle (only child process is writing).
- char buf[4096];
+
+ LocalVector<char> bytes;
+ int bytes_in_buffer = 0;
+
+ const int CHUNK_SIZE = 4096;
DWORD read = 0;
for (;;) { // Read StdOut and StdErr from pipe.
- bool success = ReadFile(pipe[0], buf, 4096, &read, NULL);
+ bytes.resize(bytes_in_buffer + CHUNK_SIZE);
+ const bool success = ReadFile(pipe[0], bytes.ptr() + bytes_in_buffer, CHUNK_SIZE, &read, NULL);
if (!success || read == 0) {
break;
}
- if (p_pipe_mutex) {
- p_pipe_mutex->lock();
+
+ // Assume that all possible encodings are ASCII-compatible.
+ // Break at newline to allow receiving long output in portions.
+ int newline_index = -1;
+ for (int i = read - 1; i >= 0; i--) {
+ if (bytes[bytes_in_buffer + i] == '\n') {
+ newline_index = i;
+ break;
+ }
}
- (*r_pipe) += String::utf8(buf, read);
- if (p_pipe_mutex) {
- p_pipe_mutex->unlock();
+ if (newline_index == -1) {
+ bytes_in_buffer += read;
+ continue;
}
+
+ const int bytes_to_convert = bytes_in_buffer + (newline_index + 1);
+ _append_to_pipe(bytes.ptr(), bytes_to_convert, r_pipe, p_pipe_mutex);
+
+ bytes_in_buffer = read - (newline_index + 1);
+ memmove(bytes.ptr(), bytes.ptr() + bytes_to_convert, bytes_in_buffer);
+ }
+
+ if (bytes_in_buffer > 0) {
+ _append_to_pipe(bytes.ptr(), bytes_in_buffer, r_pipe, p_pipe_mutex);
}
+
CloseHandle(pipe[0]); // Close pipe read handle.
} else {
WaitForSingleObject(pi.pi.hProcess, INFINITE);
diff --git a/scene/2d/camera_2d.cpp b/scene/2d/camera_2d.cpp
index efde8d8a2b..f61dbc071d 100644
--- a/scene/2d/camera_2d.cpp
+++ b/scene/2d/camera_2d.cpp
@@ -57,7 +57,7 @@ void Camera2D::_update_scroll() {
Size2 screen_size = _get_camera_screen_size();
Point2 screen_offset = (anchor_mode == ANCHOR_MODE_DRAG_CENTER ? (screen_size * 0.5) : Point2());
- get_tree()->call_group_flags(SceneTree::GROUP_CALL_REALTIME, group_name, "_camera_moved", xform, screen_offset);
+ get_tree()->call_group(group_name, "_camera_moved", xform, screen_offset);
};
}
@@ -421,7 +421,7 @@ bool Camera2D::is_current() const {
void Camera2D::make_current() {
if (is_inside_tree()) {
- get_tree()->call_group_flags(SceneTree::GROUP_CALL_REALTIME, group_name, "_make_current", this);
+ get_tree()->call_group(group_name, "_make_current", this);
} else {
current = true;
}
@@ -430,7 +430,7 @@ void Camera2D::make_current() {
void Camera2D::clear_current() {
if (is_inside_tree()) {
- get_tree()->call_group_flags(SceneTree::GROUP_CALL_REALTIME, group_name, "_make_current", (Object *)nullptr);
+ get_tree()->call_group(group_name, "_make_current", (Object *)nullptr);
} else {
current = false;
}
diff --git a/scene/2d/collision_polygon_2d.cpp b/scene/2d/collision_polygon_2d.cpp
index c8986e3c94..20840f5aea 100644
--- a/scene/2d/collision_polygon_2d.cpp
+++ b/scene/2d/collision_polygon_2d.cpp
@@ -32,6 +32,7 @@
#include "collision_object_2d.h"
#include "core/math/geometry_2d.h"
+#include "scene/2d/area_2d.h"
#include "scene/resources/concave_polygon_shape_2d.h"
#include "scene/resources/convex_polygon_shape_2d.h"
@@ -254,6 +255,9 @@ TypedArray<String> CollisionPolygon2D::get_configuration_warnings() const {
warnings.push_back(RTR("Invalid polygon. At least 2 points are needed in 'Segments' build mode."));
}
}
+ if (one_way_collision && Object::cast_to<Area2D>(get_parent())) {
+ warnings.push_back(RTR("The One Way Collision property will be ignored when the parent is an Area2D."));
+ }
return warnings;
}
@@ -276,6 +280,7 @@ void CollisionPolygon2D::set_one_way_collision(bool p_enable) {
if (parent) {
parent->shape_owner_set_one_way_collision(owner_id, p_enable);
}
+ update_configuration_warnings();
}
bool CollisionPolygon2D::is_one_way_collision_enabled() const {
diff --git a/scene/2d/collision_shape_2d.cpp b/scene/2d/collision_shape_2d.cpp
index dd47ae6cb5..5a25b1705a 100644
--- a/scene/2d/collision_shape_2d.cpp
+++ b/scene/2d/collision_shape_2d.cpp
@@ -31,6 +31,7 @@
#include "collision_shape_2d.h"
#include "collision_object_2d.h"
+#include "scene/2d/area_2d.h"
#include "scene/resources/concave_polygon_shape_2d.h"
#include "scene/resources/convex_polygon_shape_2d.h"
@@ -176,6 +177,9 @@ TypedArray<String> CollisionShape2D::get_configuration_warnings() const {
if (!shape.is_valid()) {
warnings.push_back(RTR("A shape must be provided for CollisionShape2D to function. Please create a shape resource for it!"));
}
+ if (one_way_collision && Object::cast_to<Area2D>(get_parent())) {
+ warnings.push_back(RTR("The One Way Collision property will be ignored when the parent is an Area2D."));
+ }
Ref<ConvexPolygonShape2D> convex = shape;
Ref<ConcavePolygonShape2D> concave = shape;
@@ -204,6 +208,7 @@ void CollisionShape2D::set_one_way_collision(bool p_enable) {
if (parent) {
parent->shape_owner_set_one_way_collision(owner_id, p_enable);
}
+ update_configuration_warnings();
}
bool CollisionShape2D::is_one_way_collision_enabled() const {
diff --git a/scene/3d/camera_3d.cpp b/scene/3d/camera_3d.cpp
index 4f05e80377..4c53776bba 100644
--- a/scene/3d/camera_3d.cpp
+++ b/scene/3d/camera_3d.cpp
@@ -217,8 +217,6 @@ void Camera3D::make_current() {
}
get_viewport()->_camera_3d_set(this);
-
- //get_scene()->call_group(SceneMainLoop::GROUP_CALL_REALTIME,camera_group,"_camera_make_current",this);
}
void Camera3D::clear_current(bool p_enable_next) {
diff --git a/scene/3d/gpu_particles_collision_3d.cpp b/scene/3d/gpu_particles_collision_3d.cpp
index bedf0edf38..4552ed9537 100644
--- a/scene/3d/gpu_particles_collision_3d.cpp
+++ b/scene/3d/gpu_particles_collision_3d.cpp
@@ -640,18 +640,13 @@ void GPUParticlesCollisionHeightField3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_update_mode", "update_mode"), &GPUParticlesCollisionHeightField3D::set_update_mode);
ClassDB::bind_method(D_METHOD("get_update_mode"), &GPUParticlesCollisionHeightField3D::get_update_mode);
- ClassDB::bind_method(D_METHOD("set_follow_camera_mode", "enabled"), &GPUParticlesCollisionHeightField3D::set_follow_camera_mode);
- ClassDB::bind_method(D_METHOD("is_follow_camera_mode_enabled"), &GPUParticlesCollisionHeightField3D::is_follow_camera_mode_enabled);
-
- ClassDB::bind_method(D_METHOD("set_follow_camera_push_ratio", "ratio"), &GPUParticlesCollisionHeightField3D::set_follow_camera_push_ratio);
- ClassDB::bind_method(D_METHOD("get_follow_camera_push_ratio"), &GPUParticlesCollisionHeightField3D::get_follow_camera_push_ratio);
+ ClassDB::bind_method(D_METHOD("set_follow_camera_enabled", "enabled"), &GPUParticlesCollisionHeightField3D::set_follow_camera_enabled);
+ ClassDB::bind_method(D_METHOD("is_follow_camera_enabled"), &GPUParticlesCollisionHeightField3D::is_follow_camera_enabled);
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "extents", PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater"), "set_extents", "get_extents");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "resolution", PROPERTY_HINT_ENUM, "256,512,1024,2048,4096,8192"), "set_resolution", "get_resolution");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "update_mode", PROPERTY_HINT_ENUM, "WhenMoved,Always"), "set_update_mode", "get_update_mode");
- ADD_GROUP("Follow Camera", "follow_camera_");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "follow_camera_enabled"), "set_follow_camera_mode", "is_follow_camera_mode_enabled");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "follow_camera_push_ratio", PROPERTY_HINT_RANGE, "0.01,1,0.01"), "set_follow_camera_push_ratio", "get_follow_camera_push_ratio");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "resolution", PROPERTY_HINT_ENUM, "256 (Fastest),512 (Fast),1024 (Average),2048 (Slow),4096 (Slower),8192 (Slowest)"), "set_resolution", "get_resolution");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "update_mode", PROPERTY_HINT_ENUM, "When Moved (Fast),Always (Slow)"), "set_update_mode", "get_update_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "follow_camera_enabled"), "set_follow_camera_enabled", "is_follow_camera_enabled");
BIND_ENUM_CONSTANT(RESOLUTION_256);
BIND_ENUM_CONSTANT(RESOLUTION_512);
@@ -665,14 +660,6 @@ void GPUParticlesCollisionHeightField3D::_bind_methods() {
BIND_ENUM_CONSTANT(UPDATE_MODE_ALWAYS);
}
-void GPUParticlesCollisionHeightField3D::set_follow_camera_push_ratio(float p_follow_camera_push_ratio) {
- follow_camera_push_ratio = p_follow_camera_push_ratio;
-}
-
-float GPUParticlesCollisionHeightField3D::get_follow_camera_push_ratio() const {
- return follow_camera_push_ratio;
-}
-
void GPUParticlesCollisionHeightField3D::set_extents(const Vector3 &p_extents) {
extents = p_extents;
RS::get_singleton()->particles_collision_set_box_extents(_get_collision(), extents);
@@ -704,12 +691,12 @@ GPUParticlesCollisionHeightField3D::UpdateMode GPUParticlesCollisionHeightField3
return update_mode;
}
-void GPUParticlesCollisionHeightField3D::set_follow_camera_mode(bool p_enabled) {
+void GPUParticlesCollisionHeightField3D::set_follow_camera_enabled(bool p_enabled) {
follow_camera_mode = p_enabled;
set_process_internal(follow_camera_mode || update_mode == UPDATE_MODE_ALWAYS);
}
-bool GPUParticlesCollisionHeightField3D::is_follow_camera_mode_enabled() const {
+bool GPUParticlesCollisionHeightField3D::is_follow_camera_enabled() const {
return follow_camera_mode;
}
diff --git a/scene/3d/gpu_particles_collision_3d.h b/scene/3d/gpu_particles_collision_3d.h
index fdd2fa4b18..4b2cb930fa 100644
--- a/scene/3d/gpu_particles_collision_3d.h
+++ b/scene/3d/gpu_particles_collision_3d.h
@@ -211,7 +211,6 @@ private:
Vector3 extents = Vector3(1, 1, 1);
Resolution resolution = RESOLUTION_1024;
bool follow_camera_mode = false;
- float follow_camera_push_ratio = 0.1;
UpdateMode update_mode = UPDATE_MODE_WHEN_MOVED;
@@ -229,11 +228,8 @@ public:
void set_update_mode(UpdateMode p_update_mode);
UpdateMode get_update_mode() const;
- void set_follow_camera_mode(bool p_enabled);
- bool is_follow_camera_mode_enabled() const;
-
- void set_follow_camera_push_ratio(float p_ratio);
- float get_follow_camera_push_ratio() const;
+ void set_follow_camera_enabled(bool p_enabled);
+ bool is_follow_camera_enabled() const;
virtual AABB get_aabb() const override;
diff --git a/scene/3d/lightmap_gi.cpp b/scene/3d/lightmap_gi.cpp
index 88d2c1ad69..5c63bdcf1d 100644
--- a/scene/3d/lightmap_gi.cpp
+++ b/scene/3d/lightmap_gi.cpp
@@ -318,12 +318,11 @@ void LightmapGI::_find_meshes_and_lights(Node *p_at_node, Vector<MeshesFound> &m
int LightmapGI::_bsp_get_simplex_side(const Vector<Vector3> &p_points, const LocalVector<BSPSimplex> &p_simplices, const Plane &p_plane, uint32_t p_simplex) const {
int over = 0;
int under = 0;
- int coplanar = 0;
const BSPSimplex &s = p_simplices[p_simplex];
for (int i = 0; i < 4; i++) {
const Vector3 v = p_points[s.vertices[i]];
- if (p_plane.has_point(v)) { //coplanar
- coplanar++;
+ if (p_plane.has_point(v)) {
+ // Coplanar.
} else if (p_plane.is_point_over(v)) {
over++;
} else {
diff --git a/scene/3d/voxelizer.cpp b/scene/3d/voxelizer.cpp
index d6ac5ccf30..42a2a68e2d 100644
--- a/scene/3d/voxelizer.cpp
+++ b/scene/3d/voxelizer.cpp
@@ -592,7 +592,6 @@ void Voxelizer::_fixup_plot(int p_idx, int p_level) {
bake_cells.write[p_idx].albedo[2] = 0;
float alpha_average = 0;
- int children_found = 0;
for (int i = 0; i < 8; i++) {
uint32_t child = bake_cells[p_idx].children[i];
@@ -603,8 +602,6 @@ void Voxelizer::_fixup_plot(int p_idx, int p_level) {
_fixup_plot(child, p_level + 1);
alpha_average += bake_cells[child].alpha;
-
- children_found++;
}
bake_cells.write[p_idx].alpha = alpha_average / 8.0;
diff --git a/scene/3d/world_environment.cpp b/scene/3d/world_environment.cpp
index f638644628..fe9d9ae4dd 100644
--- a/scene/3d/world_environment.cpp
+++ b/scene/3d/world_environment.cpp
@@ -71,7 +71,7 @@ void WorldEnvironment::_update_current_environment() {
} else {
get_viewport()->find_world_3d()->set_environment(Ref<Environment>());
}
- get_tree()->call_group("_world_environment_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id()), "update_configuration_warnings");
+ get_tree()->call_group_flags(SceneTree::GROUP_CALL_DEFERRED, "_world_environment_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id()), "update_configuration_warnings");
}
void WorldEnvironment::_update_current_camera_effects() {
@@ -82,7 +82,7 @@ void WorldEnvironment::_update_current_camera_effects() {
get_viewport()->find_world_3d()->set_camera_effects(Ref<CameraEffects>());
}
- get_tree()->call_group("_world_camera_effects_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id()), "update_configuration_warnings");
+ get_tree()->call_group_flags(SceneTree::GROUP_CALL_DEFERRED, "_world_camera_effects_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id()), "update_configuration_warnings");
}
void WorldEnvironment::set_environment(const Ref<Environment> &p_environment) {
diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp
index b0590bc2bd..d957c7f2de 100644
--- a/scene/animation/animation_tree.cpp
+++ b/scene/animation/animation_tree.cpp
@@ -198,12 +198,11 @@ double AnimationNode::_blend_node(const StringName &p_subpath, const Vector<Stri
blendw[i] = 0.0; //all to zero by default
}
- const NodePath *K = nullptr;
- while ((K = filter.next(K))) {
- if (!state->track_map.has(*K)) {
+ for (const KeyValue<NodePath, bool> &E : filter) {
+ if (!state->track_map.has(E.key)) {
continue;
}
- int idx = state->track_map[*K];
+ int idx = state->track_map[E.key];
blendw[idx] = 1.0; //filtered goes to one
}
@@ -374,9 +373,8 @@ bool AnimationNode::has_filter() const {
Array AnimationNode::_get_filters() const {
Array paths;
- const NodePath *K = nullptr;
- while ((K = filter.next(K))) {
- paths.push_back(String(*K)); //use strings, so sorting is possible
+ for (const KeyValue<NodePath, bool> &E : filter) {
+ paths.push_back(String(E.key)); //use strings, so sorting is possible
}
paths.sort(); //done so every time the scene is saved, it does not change
@@ -803,11 +801,10 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) {
List<NodePath> to_delete;
- const NodePath *K = nullptr;
- while ((K = track_cache.next(K))) {
- TrackCache *tc = track_cache[*K];
+ for (const KeyValue<NodePath, TrackCache *> &K : track_cache) {
+ TrackCache *tc = track_cache[K.key];
if (tc->setup_pass != setup_pass) {
- to_delete.push_back(*K);
+ to_delete.push_back(K.key);
}
}
@@ -820,10 +817,9 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) {
state.track_map.clear();
- K = nullptr;
int idx = 0;
- while ((K = track_cache.next(K))) {
- state.track_map[*K] = idx;
+ for (const KeyValue<NodePath, TrackCache *> &K : track_cache) {
+ state.track_map[K.key] = idx;
idx++;
}
@@ -835,9 +831,8 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) {
}
void AnimationTree::_clear_caches() {
- const NodePath *K = nullptr;
- while ((K = track_cache.next(K))) {
- memdelete(track_cache[*K]);
+ for (KeyValue<NodePath, TrackCache *> &K : track_cache) {
+ memdelete(K.value);
}
playing_caches.clear();
@@ -1569,9 +1564,8 @@ void AnimationTree::_process_graph(double p_delta) {
{
// finally, set the tracks
- const NodePath *K = nullptr;
- while ((K = track_cache.next(K))) {
- TrackCache *track = track_cache[*K];
+ for (const KeyValue<NodePath, TrackCache *> &K : track_cache) {
+ TrackCache *track = K.value;
if (track->process_pass != process_pass) {
continue; //not processed, ignore
}
diff --git a/scene/gui/button.cpp b/scene/gui/button.cpp
index b7c1e674dd..ff194f979d 100644
--- a/scene/gui/button.cpp
+++ b/scene/gui/button.cpp
@@ -258,7 +258,8 @@ void Button::_notification(int p_what) {
if (expand_icon) {
Size2 _size = get_size() - style->get_offset() * 2;
- _size.width -= get_theme_constant(SNAME("h_separation")) + icon_ofs_region;
+ int icon_text_separation = text.is_empty() ? 0 : get_theme_constant(SNAME("h_separation"));
+ _size.width -= icon_text_separation + icon_ofs_region;
if (!clip_text && icon_align_rtl_checked != HORIZONTAL_ALIGNMENT_CENTER) {
_size.width -= text_buf->get_size().width;
}
diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp
index 05bb30f7e0..b2aa4030b7 100644
--- a/scene/gui/code_edit.cpp
+++ b/scene/gui/code_edit.cpp
@@ -357,6 +357,11 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
}
Ref<InputEventKey> k = p_gui_input;
+ if (TextEdit::alt_input(p_gui_input)) {
+ accept_event();
+ return;
+ }
+
bool update_code_completion = false;
if (!k.is_valid()) {
TextEdit::gui_input(p_gui_input);
@@ -3036,7 +3041,9 @@ void CodeEdit::_text_changed() {
lc = get_line_count();
List<int> breakpoints;
- breakpointed_lines.get_key_list(&breakpoints);
+ for (const KeyValue<int, bool> &E : breakpointed_lines) {
+ breakpoints.push_back(E.key);
+ }
for (const int &line : breakpoints) {
if (line < lines_edited_from || (line < lc && is_line_breakpointed(line))) {
continue;
diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp
index dddeec5610..54fa726260 100644
--- a/scene/gui/control.cpp
+++ b/scene/gui/control.cpp
@@ -1590,22 +1590,22 @@ void Control::set_anchor_and_offset(Side p_side, real_t p_anchor, real_t p_pos,
void Control::_set_anchors_layout_preset(int p_preset) {
bool list_changed = false;
- if (has_meta("_edit_layout_mode") && (int)get_meta("_edit_layout_mode") != (int)LayoutMode::LAYOUT_MODE_ANCHORS) {
+ if (get_meta("_edit_layout_mode", LayoutMode::LAYOUT_MODE_ANCHORS).operator int() != LayoutMode::LAYOUT_MODE_ANCHORS) {
list_changed = true;
- set_meta("_edit_layout_mode", (int)LayoutMode::LAYOUT_MODE_ANCHORS);
+ set_meta("_edit_layout_mode", LayoutMode::LAYOUT_MODE_ANCHORS);
}
if (p_preset == -1) {
- if (!has_meta("_edit_use_custom_anchors") || !(bool)get_meta("_edit_use_custom_anchors")) {
+ if (!get_meta("_edit_use_custom_anchors", false)) {
set_meta("_edit_use_custom_anchors", true);
notify_property_list_changed();
}
return; // Keep settings as is.
}
- if (!has_meta("_edit_use_custom_anchors") || (bool)get_meta("_edit_use_custom_anchors")) {
+ if (get_meta("_edit_use_custom_anchors", true)) {
list_changed = true;
- set_meta("_edit_use_custom_anchors", false);
+ remove_meta("_edit_use_custom_anchors");
}
LayoutPreset preset = (LayoutPreset)p_preset;
diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp
index f2b724fa39..4c8006e33d 100644
--- a/scene/gui/graph_edit.cpp
+++ b/scene/gui/graph_edit.cpp
@@ -523,7 +523,7 @@ void GraphEdit::_update_comment_enclosed_nodes_list(GraphNode *p_node, HashMap<S
}
}
- p_comment_enclosed_nodes.set(p_node->get_name(), enclosed_nodes);
+ p_comment_enclosed_nodes.insert(p_node->get_name(), enclosed_nodes);
}
void GraphEdit::_set_drag_comment_enclosed_nodes(GraphNode *p_node, HashMap<StringName, Vector<GraphNode *>> &p_comment_enclosed_nodes, bool p_drag) {
@@ -1742,11 +1742,11 @@ HashMap<int, Vector<StringName>> GraphEdit::_layering(const Set<StringName> &r_s
Vector<StringName> t;
t.push_back(E->get());
if (!l.has(current_layer)) {
- l.set(current_layer, Vector<StringName>{});
+ l.insert(current_layer, Vector<StringName>{});
}
selected = true;
t.append_array(l[current_layer]);
- l.set(current_layer, t);
+ l.insert(current_layer, t);
Set<StringName> V;
V.insert(E->get());
_set_operations(GraphEdit::UNION, u, V);
@@ -1860,10 +1860,10 @@ void GraphEdit::_crossing_minimisation(HashMap<int, Vector<StringName>> &r_layer
}
d[q] = crossings;
}
- c.set(p, d);
+ c.insert(p, d);
}
- r_layers.set(i, _split(lower_layer, c));
+ r_layers.insert(i, _split(lower_layer, c));
}
}
@@ -2026,7 +2026,7 @@ void GraphEdit::_place_block(StringName p_v, float p_delta, const HashMap<int, V
threshold = _calculate_threshold(p_v, w, r_node_name, r_layers, r_root, r_align, r_inner_shift, threshold, r_node_positions);
w = r_align[w];
} while (w != p_v);
- r_node_positions.set(p_v, pos);
+ r_node_positions.insert(p_v, pos);
}
#undef PRED
@@ -2082,10 +2082,10 @@ void GraphEdit::arrange_nodes() {
ports = p_ports;
}
}
- port_info.set(_connection, ports);
+ port_info.insert(_connection, ports);
}
}
- upper_neighbours.set(gn->get_name(), s);
+ upper_neighbours.insert(gn->get_name(), s);
}
}
@@ -2109,7 +2109,7 @@ void GraphEdit::arrange_nodes() {
inner_shift[E->get()] = 0.0f;
sink[E->get()] = E->get();
shift[E->get()] = FLT_MAX;
- new_positions.set(E->get(), default_position);
+ new_positions.insert(E->get(), default_position);
if ((StringName)root[E->get()] == E->get()) {
block_heads.insert(E->get());
}
@@ -2129,7 +2129,7 @@ void GraphEdit::arrange_nodes() {
do {
Vector2 cal_pos;
cal_pos.y = start_from + (real_t)inner_shift[u];
- new_positions.set(u, cal_pos);
+ new_positions.insert(u, cal_pos);
u = align[u];
} while (u != E->get());
}
@@ -2161,7 +2161,7 @@ void GraphEdit::arrange_nodes() {
}
cal_pos.x = current_node_start_pos;
}
- new_positions.set(layer[j], cal_pos);
+ new_positions.insert(layer[j], cal_pos);
}
start_from += largest_node_size + gap_h;
diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp
index e5b58a7cc8..73188d6602 100644
--- a/scene/gui/line_edit.cpp
+++ b/scene/gui/line_edit.cpp
@@ -406,9 +406,45 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) {
if (k.is_valid()) {
if (!k->is_pressed()) {
+ if (alt_start && k->get_keycode() == Key::ALT) {
+ alt_start = false;
+ if ((alt_code > 0x31 && alt_code < 0xd800) || (alt_code > 0xdfff && alt_code <= 0x10ffff)) {
+ char32_t ucodestr[2] = { (char32_t)alt_code, 0 };
+ insert_text_at_caret(ucodestr);
+ }
+ accept_event();
+ return;
+ }
return;
}
+ // Alt+ Unicode input:
+ if (k->is_alt_pressed()) {
+ if (!alt_start) {
+ if (k->get_keycode() == Key::KP_ADD) {
+ alt_start = true;
+ alt_code = 0;
+ accept_event();
+ return;
+ }
+ } else {
+ if (k->get_keycode() >= Key::KEY_0 && k->get_keycode() <= Key::KEY_9) {
+ alt_code = alt_code << 4;
+ alt_code += (uint32_t)(k->get_keycode() - Key::KEY_0);
+ }
+ if (k->get_keycode() >= Key::KP_0 && k->get_keycode() <= Key::KP_9) {
+ alt_code = alt_code << 4;
+ alt_code += (uint32_t)(k->get_keycode() - Key::KP_0);
+ }
+ if (k->get_keycode() >= Key::A && k->get_keycode() <= Key::F) {
+ alt_code = alt_code << 4;
+ alt_code += (uint32_t)(k->get_keycode() - Key::A) + 10;
+ }
+ accept_event();
+ return;
+ }
+ }
+
if (context_menu_enabled) {
if (k->is_action("ui_menu", true)) {
_ensure_menu();
diff --git a/scene/gui/line_edit.h b/scene/gui/line_edit.h
index 50aa2f4460..0fb178fca4 100644
--- a/scene/gui/line_edit.h
+++ b/scene/gui/line_edit.h
@@ -77,6 +77,9 @@ private:
bool pass = false;
bool text_changed_dirty = false;
+ bool alt_start = false;
+ uint32_t alt_code = 0;
+
String undo_text;
String text;
String placeholder;
diff --git a/scene/gui/progress_bar.cpp b/scene/gui/progress_bar.cpp
index 50ffb3ca67..f36682942f 100644
--- a/scene/gui/progress_bar.cpp
+++ b/scene/gui/progress_bar.cpp
@@ -62,15 +62,42 @@ void ProgressBar::_notification(int p_what) {
Color font_color = get_theme_color(SNAME("font_color"));
draw_style_box(bg, Rect2(Point2(), get_size()));
+
float r = get_as_ratio();
- int mp = fg->get_minimum_size().width;
- int p = r * (get_size().width - mp);
- if (p > 0) {
- if (is_layout_rtl()) {
- draw_style_box(fg, Rect2(Point2(p, 0), Size2(fg->get_minimum_size().width, get_size().height)));
- } else {
- draw_style_box(fg, Rect2(Point2(0, 0), Size2(p + fg->get_minimum_size().width, get_size().height)));
- }
+
+ switch (mode) {
+ case FILL_BEGIN_TO_END:
+ case FILL_END_TO_BEGIN: {
+ int mp = fg->get_minimum_size().width;
+ int p = round(r * (get_size().width - mp));
+ // We want FILL_BEGIN_TO_END to map to right to left when UI layout is RTL,
+ // and left to right otherwise. And likewise for FILL_END_TO_BEGIN.
+ bool right_to_left = is_layout_rtl() ? (mode == FILL_BEGIN_TO_END) : (mode == FILL_END_TO_BEGIN);
+ if (p > 0) {
+ if (right_to_left) {
+ int p_remaining = round((1.0 - r) * (get_size().width - mp));
+ draw_style_box(fg, Rect2(Point2(p_remaining, 0), Size2(p + fg->get_minimum_size().width, get_size().height)));
+ } else {
+ draw_style_box(fg, Rect2(Point2(0, 0), Size2(p + fg->get_minimum_size().width, get_size().height)));
+ }
+ }
+ } break;
+ case FILL_TOP_TO_BOTTOM:
+ case FILL_BOTTOM_TO_TOP: {
+ int mp = fg->get_minimum_size().height;
+ int p = round(r * (get_size().height - mp));
+
+ if (p > 0) {
+ if (mode == FILL_TOP_TO_BOTTOM) {
+ draw_style_box(fg, Rect2(Point2(0, 0), Size2(get_size().width, p + fg->get_minimum_size().height)));
+ } else {
+ int p_remaining = round((1.0 - r) * (get_size().height - mp));
+ draw_style_box(fg, Rect2(Point2(0, p_remaining), Size2(get_size().width, p + fg->get_minimum_size().height)));
+ }
+ }
+ } break;
+ case FILL_MODE_MAX:
+ break;
}
if (percent_visible) {
@@ -88,6 +115,16 @@ void ProgressBar::_notification(int p_what) {
}
}
+void ProgressBar::set_fill_mode(int p_fill) {
+ ERR_FAIL_INDEX(p_fill, FILL_MODE_MAX);
+ mode = (FillMode)p_fill;
+ update();
+}
+
+int ProgressBar::get_fill_mode() {
+ return mode;
+}
+
void ProgressBar::set_percent_visible(bool p_visible) {
percent_visible = p_visible;
update();
@@ -98,10 +135,18 @@ bool ProgressBar::is_percent_visible() const {
}
void ProgressBar::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_fill_mode", "mode"), &ProgressBar::set_fill_mode);
+ ClassDB::bind_method(D_METHOD("get_fill_mode"), &ProgressBar::get_fill_mode);
ClassDB::bind_method(D_METHOD("set_percent_visible", "visible"), &ProgressBar::set_percent_visible);
ClassDB::bind_method(D_METHOD("is_percent_visible"), &ProgressBar::is_percent_visible);
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "fill_mode", PROPERTY_HINT_ENUM, "Begin to End,End to Begin,Top to Bottom,Bottom to Top"), "set_fill_mode", "get_fill_mode");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "percent_visible"), "set_percent_visible", "is_percent_visible");
+
+ BIND_ENUM_CONSTANT(FILL_BEGIN_TO_END);
+ BIND_ENUM_CONSTANT(FILL_END_TO_BEGIN);
+ BIND_ENUM_CONSTANT(FILL_TOP_TO_BOTTOM);
+ BIND_ENUM_CONSTANT(FILL_BOTTOM_TO_TOP);
}
ProgressBar::ProgressBar() {
diff --git a/scene/gui/progress_bar.h b/scene/gui/progress_bar.h
index 2d89163f78..5ba21ad7d5 100644
--- a/scene/gui/progress_bar.h
+++ b/scene/gui/progress_bar.h
@@ -43,11 +43,27 @@ protected:
static void _bind_methods();
public:
+ enum FillMode {
+ FILL_BEGIN_TO_END,
+ FILL_END_TO_BEGIN,
+ FILL_TOP_TO_BOTTOM,
+ FILL_BOTTOM_TO_TOP,
+ FILL_MODE_MAX
+ };
+
+ void set_fill_mode(int p_fill);
+ int get_fill_mode();
+
void set_percent_visible(bool p_visible);
bool is_percent_visible() const;
Size2 get_minimum_size() const override;
ProgressBar();
+
+private:
+ FillMode mode = FILL_BEGIN_TO_END;
};
+VARIANT_ENUM_CAST(ProgressBar::FillMode);
+
#endif // PROGRESS_BAR_H
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index 1a439a5c1d..315ffbd419 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -1557,6 +1557,47 @@ void TextEdit::unhandled_key_input(const Ref<InputEvent> &p_event) {
}
}
+bool TextEdit::alt_input(const Ref<InputEvent> &p_gui_input) {
+ Ref<InputEventKey> k = p_gui_input;
+ if (k.is_valid()) {
+ if (!k->is_pressed()) {
+ if (alt_start && k->get_keycode() == Key::ALT) {
+ alt_start = false;
+ if ((alt_code > 0x31 && alt_code < 0xd800) || (alt_code > 0xdfff && alt_code <= 0x10ffff)) {
+ handle_unicode_input(alt_code);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ if (k->is_alt_pressed()) {
+ if (!alt_start) {
+ if (k->get_keycode() == Key::KP_ADD) {
+ alt_start = true;
+ alt_code = 0;
+ return true;
+ }
+ } else {
+ if (k->get_keycode() >= Key::KEY_0 && k->get_keycode() <= Key::KEY_9) {
+ alt_code = alt_code << 4;
+ alt_code += (uint32_t)(k->get_keycode() - Key::KEY_0);
+ }
+ if (k->get_keycode() >= Key::KP_0 && k->get_keycode() <= Key::KP_9) {
+ alt_code = alt_code << 4;
+ alt_code += (uint32_t)(k->get_keycode() - Key::KP_0);
+ }
+ if (k->get_keycode() >= Key::A && k->get_keycode() <= Key::F) {
+ alt_code = alt_code << 4;
+ alt_code += (uint32_t)(k->get_keycode() - Key::A) + 10;
+ }
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
ERR_FAIL_COND(p_gui_input.is_null());
@@ -1865,6 +1906,10 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
Ref<InputEventKey> k = p_gui_input;
if (k.is_valid()) {
+ if (alt_input(p_gui_input)) {
+ accept_event();
+ return;
+ }
if (!k->is_pressed()) {
return;
}
@@ -2388,7 +2433,7 @@ void TextEdit::_move_caret_page_down(bool p_select) {
}
void TextEdit::_do_backspace(bool p_word, bool p_all_to_left) {
- if (!editable || (caret.column == 0 && caret.line == 0)) {
+ if (!editable || (caret.column == 0 && caret.line == 0 && !has_selection())) {
return;
}
diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h
index a0ae9631f9..194cad72d1 100644
--- a/scene/gui/text_edit.h
+++ b/scene/gui/text_edit.h
@@ -247,6 +247,9 @@ private:
bool setting_text = false;
+ bool alt_start = false;
+ uint32_t alt_code = 0;
+
// Text properties.
String ime_text = "";
Point2 ime_selection;
@@ -625,6 +628,7 @@ public:
/* General overrides. */
virtual void unhandled_key_input(const Ref<InputEvent> &p_event) override;
virtual void gui_input(const Ref<InputEvent> &p_gui_input) override;
+ bool alt_input(const Ref<InputEvent> &p_gui_input);
virtual Size2 get_minimum_size() const override;
virtual bool is_text_field() const override;
virtual CursorShape get_cursor_shape(const Point2 &p_pos = Point2i()) const override;
diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp
index 89807dbe95..0ca9a66e08 100644
--- a/scene/gui/tree.cpp
+++ b/scene/gui/tree.cpp
@@ -1754,19 +1754,16 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
for (int j = p_item->cells[i].buttons.size() - 1; j >= 0; j--) {
Ref<Texture2D> b = p_item->cells[i].buttons[j].texture;
Size2 s = b->get_size() + cache.button_pressed->get_minimum_size();
- if (s.height < label_h) {
- s.height = label_h;
- }
Point2i o = Point2i(ofs + w - s.width, p_pos.y) - cache.offset + p_draw_ofs;
if (cache.click_type == Cache::CLICK_BUTTON && cache.click_item == p_item && cache.click_column == i && cache.click_index == j && !p_item->cells[i].buttons[j].disabled) {
- //being pressed
+ // Being pressed.
Point2 od = o;
if (rtl) {
od.x = get_size().width - od.x - s.x;
}
- cache.button_pressed->draw(get_canvas_item(), Rect2(od, s));
+ cache.button_pressed->draw(get_canvas_item(), Rect2(od.x, od.y, s.width, MAX(s.height, label_h)));
}
o.y += (label_h - s.height) / 2;
diff --git a/scene/main/missing_node.cpp b/scene/main/missing_node.cpp
index 6daa9dec6b..395fdad9e4 100644
--- a/scene/main/missing_node.cpp
+++ b/scene/main/missing_node.cpp
@@ -53,8 +53,8 @@ bool MissingNode::_get(const StringName &p_name, Variant &r_ret) const {
}
void MissingNode::_get_property_list(List<PropertyInfo> *p_list) const {
- for (OrderedHashMap<StringName, Variant>::ConstElement E = properties.front(); E; E = E.next()) {
- p_list->push_back(PropertyInfo(E.value().get_type(), E.key()));
+ for (const KeyValue<StringName, Variant> &E : properties) {
+ p_list->push_back(PropertyInfo(E.value.get_type(), E.key));
}
}
diff --git a/scene/main/missing_node.h b/scene/main/missing_node.h
index b0f9492456..d200fbb47f 100644
--- a/scene/main/missing_node.h
+++ b/scene/main/missing_node.h
@@ -36,7 +36,7 @@
class MissingNode : public Node {
GDCLASS(MissingNode, Node)
- OrderedHashMap<StringName, Variant> properties;
+ HashMap<StringName, Variant> properties;
String original_class;
bool recording_properties = false;
diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp
index a151d3cb33..baa0362f63 100644
--- a/scene/main/scene_tree.cpp
+++ b/scene/main/scene_tree.cpp
@@ -181,7 +181,7 @@ void SceneTree::_flush_ugc() {
argptrs[i] = &E->get()[i];
}
- call_group_flagsp(GROUP_CALL_REALTIME, E->key().group, E->key().call, argptrs, E->get().size());
+ call_group_flagsp(GROUP_CALL_DEFAULT, E->key().group, E->key().call, argptrs, E->get().size());
unique_group_calls.erase(E);
}
@@ -220,7 +220,7 @@ void SceneTree::call_group_flagsp(uint32_t p_call_flags, const StringName &p_gro
return;
}
- if (p_call_flags & GROUP_CALL_UNIQUE && !(p_call_flags & GROUP_CALL_REALTIME)) {
+ if (p_call_flags & GROUP_CALL_UNIQUE && p_call_flags & GROUP_CALL_DEFERRED) {
ERR_FAIL_COND(ugc_locked);
UGCall ug;
@@ -254,7 +254,7 @@ void SceneTree::call_group_flagsp(uint32_t p_call_flags, const StringName &p_gro
continue;
}
- if (p_call_flags & GROUP_CALL_REALTIME) {
+ if (!(p_call_flags & GROUP_CALL_DEFERRED)) {
Callable::CallError ce;
nodes[i]->callp(p_function, p_args, p_argcount, ce);
} else {
@@ -268,7 +268,7 @@ void SceneTree::call_group_flagsp(uint32_t p_call_flags, const StringName &p_gro
continue;
}
- if (p_call_flags & GROUP_CALL_REALTIME) {
+ if (!(p_call_flags & GROUP_CALL_DEFERRED)) {
Callable::CallError ce;
nodes[i]->callp(p_function, p_args, p_argcount, ce);
} else {
@@ -307,7 +307,7 @@ void SceneTree::notify_group_flags(uint32_t p_call_flags, const StringName &p_gr
continue;
}
- if (p_call_flags & GROUP_CALL_REALTIME) {
+ if (!(p_call_flags & GROUP_CALL_DEFERRED)) {
nodes[i]->notification(p_notification);
} else {
MessageQueue::get_singleton()->push_notification(nodes[i], p_notification);
@@ -320,7 +320,7 @@ void SceneTree::notify_group_flags(uint32_t p_call_flags, const StringName &p_gr
continue;
}
- if (p_call_flags & GROUP_CALL_REALTIME) {
+ if (!(p_call_flags & GROUP_CALL_DEFERRED)) {
nodes[i]->notification(p_notification);
} else {
MessageQueue::get_singleton()->push_notification(nodes[i], p_notification);
@@ -358,7 +358,7 @@ void SceneTree::set_group_flags(uint32_t p_call_flags, const StringName &p_group
continue;
}
- if (p_call_flags & GROUP_CALL_REALTIME) {
+ if (!(p_call_flags & GROUP_CALL_DEFERRED)) {
nodes[i]->set(p_name, p_value);
} else {
MessageQueue::get_singleton()->push_set(nodes[i], p_name, p_value);
@@ -371,7 +371,7 @@ void SceneTree::set_group_flags(uint32_t p_call_flags, const StringName &p_group
continue;
}
- if (p_call_flags & GROUP_CALL_REALTIME) {
+ if (!(p_call_flags & GROUP_CALL_DEFERRED)) {
nodes[i]->set(p_name, p_value);
} else {
MessageQueue::get_singleton()->push_set(nodes[i], p_name, p_value);
@@ -390,7 +390,7 @@ void SceneTree::notify_group(const StringName &p_group, int p_notification) {
}
void SceneTree::set_group(const StringName &p_group, const String &p_name, const Variant &p_value) {
- set_group_flags(0, p_group, p_name, p_value);
+ set_group_flags(GROUP_CALL_DEFAULT, p_group, p_name, p_value);
}
void SceneTree::initialize() {
@@ -413,7 +413,7 @@ bool SceneTree::physics_process(double p_time) {
emit_signal(SNAME("physics_frame"));
_notify_group_pause(SNAME("physics_process_internal"), Node::NOTIFICATION_INTERNAL_PHYSICS_PROCESS);
- call_group_flags(GROUP_CALL_REALTIME, SNAME("_picking_viewports"), SNAME("_process_picking"));
+ call_group(SNAME("_picking_viewports"), SNAME("_process_picking"));
_notify_group_pause(SNAME("physics_process"), Node::NOTIFICATION_PHYSICS_PROCESS);
_flush_ugc();
MessageQueue::get_singleton()->flush(); //small little hack
@@ -438,9 +438,8 @@ bool SceneTree::process(double p_time) {
if (multiplayer_poll) {
multiplayer->poll();
- const NodePath *rpath = nullptr;
- while ((rpath = custom_multiplayers.next(rpath))) {
- custom_multiplayers[*rpath]->poll();
+ for (KeyValue<NodePath, Ref<MultiplayerAPI>> &E : custom_multiplayers) {
+ E.value->poll();
}
}
@@ -944,7 +943,7 @@ void SceneTree::_call_group(const Variant **p_args, int p_argcount, Callable::Ca
StringName group = *p_args[0];
StringName method = *p_args[1];
- call_group_flagsp(0, group, method, p_args + 2, p_argcount - 2);
+ call_group_flagsp(GROUP_CALL_DEFAULT, group, method, p_args + 2, p_argcount - 2);
}
int64_t SceneTree::get_frame() const {
@@ -1137,9 +1136,8 @@ Array SceneTree::get_processed_tweens() {
Ref<MultiplayerAPI> SceneTree::get_multiplayer(const NodePath &p_for_path) const {
Ref<MultiplayerAPI> out = multiplayer;
- const NodePath *spath = nullptr;
- while ((spath = custom_multiplayers.next(spath))) {
- const Vector<StringName> snames = (*spath).get_names();
+ for (const KeyValue<NodePath, Ref<MultiplayerAPI>> &E : custom_multiplayers) {
+ const Vector<StringName> snames = E.key.get_names();
const Vector<StringName> tnames = p_for_path.get_names();
if (tnames.size() < snames.size()) {
continue;
@@ -1154,7 +1152,7 @@ Ref<MultiplayerAPI> SceneTree::get_multiplayer(const NodePath &p_for_path) const
}
}
if (valid) {
- out = custom_multiplayers[*spath];
+ out = E.value;
break;
}
}
@@ -1277,7 +1275,7 @@ void SceneTree::_bind_methods() {
BIND_ENUM_CONSTANT(GROUP_CALL_DEFAULT);
BIND_ENUM_CONSTANT(GROUP_CALL_REVERSE);
- BIND_ENUM_CONSTANT(GROUP_CALL_REALTIME);
+ BIND_ENUM_CONSTANT(GROUP_CALL_DEFERRED);
BIND_ENUM_CONSTANT(GROUP_CALL_UNIQUE);
}
diff --git a/scene/main/scene_tree.h b/scene/main/scene_tree.h
index 9d7757e0a3..d633fb38d0 100644
--- a/scene/main/scene_tree.h
+++ b/scene/main/scene_tree.h
@@ -151,7 +151,6 @@ private:
int collision_debug_contacts;
void _change_scene(Node *p_to);
- //void _call_group(uint32_t p_call_flags,const StringName& p_group,const StringName& p_function,const Variant& p_arg1,const Variant& p_arg2);
List<Ref<SceneTreeTimer>> timers;
List<Ref<Tween>> tweens;
@@ -225,7 +224,7 @@ public:
enum GroupCallFlags {
GROUP_CALL_DEFAULT = 0,
GROUP_CALL_REVERSE = 1,
- GROUP_CALL_REALTIME = 2,
+ GROUP_CALL_DEFERRED = 2,
GROUP_CALL_UNIQUE = 4,
};
@@ -235,17 +234,20 @@ public:
void notify_group_flags(uint32_t p_call_flags, const StringName &p_group, int p_notification);
void set_group_flags(uint32_t p_call_flags, const StringName &p_group, const String &p_name, const Variant &p_value);
+ // `notify_group()` is immediate by default since Godot 4.0.
void notify_group(const StringName &p_group, int p_notification);
+ // `set_group()` is immediate by default since Godot 4.0.
void set_group(const StringName &p_group, const String &p_name, const Variant &p_value);
template <typename... VarArgs>
+ // `call_group()` is immediate by default since Godot 4.0.
void call_group(const StringName &p_group, const StringName &p_function, VarArgs... p_args) {
Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported.
const Variant *argptrs[sizeof...(p_args) + 1];
for (uint32_t i = 0; i < sizeof...(p_args); i++) {
argptrs[i] = &args[i];
}
- call_group_flagsp(0, p_group, p_function, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args));
+ call_group_flagsp(GROUP_CALL_DEFAULT, p_group, p_function, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args));
}
template <typename... VarArgs>
diff --git a/scene/main/shader_globals_override.cpp b/scene/main/shader_globals_override.cpp
index 7c689bd436..0049359cad 100644
--- a/scene/main/shader_globals_override.cpp
+++ b/scene/main/shader_globals_override.cpp
@@ -229,20 +229,19 @@ void ShaderGlobalsOverride::_activate() {
active = true;
add_to_group(SceneStringNames::get_singleton()->shader_overrides_group_active);
- const StringName *K = nullptr;
- while ((K = overrides.next(K))) {
- Override *o = overrides.getptr(*K);
+ for (const KeyValue<StringName, Override> &E : overrides) {
+ const Override *o = &E.value;
if (o->in_use && o->override.get_type() != Variant::NIL) {
if (o->override.get_type() == Variant::OBJECT) {
RID tex_rid = o->override;
- RS::get_singleton()->global_variable_set_override(*K, tex_rid);
+ RS::get_singleton()->global_variable_set_override(E.key, tex_rid);
} else {
- RS::get_singleton()->global_variable_set_override(*K, o->override);
+ RS::get_singleton()->global_variable_set_override(E.key, o->override);
}
}
- }
- update_configuration_warnings(); //may have activated
+ update_configuration_warnings(); //may have activated
+ }
}
}
@@ -256,18 +255,17 @@ void ShaderGlobalsOverride::_notification(int p_what) {
case Node3D::NOTIFICATION_EXIT_TREE: {
if (active) {
//remove overrides
- const StringName *K = nullptr;
- while ((K = overrides.next(K))) {
- Override *o = overrides.getptr(*K);
+ for (const KeyValue<StringName, Override> &E : overrides) {
+ const Override *o = &E.value;
if (o->in_use) {
- RS::get_singleton()->global_variable_set_override(*K, Variant());
+ RS::get_singleton()->global_variable_set_override(E.key, Variant());
}
}
}
remove_from_group(SceneStringNames::get_singleton()->shader_overrides_group_active);
remove_from_group(SceneStringNames::get_singleton()->shader_overrides_group);
- get_tree()->call_group(SceneStringNames::get_singleton()->shader_overrides_group, "_activate"); //another may want to activate when this is removed
+ get_tree()->call_group_flags(SceneTree::GROUP_CALL_DEFERRED, SceneStringNames::get_singleton()->shader_overrides_group, "_activate"); //another may want to activate when this is removed
active = false;
} break;
}
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index d7e58ed707..e4037c2843 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -2186,7 +2186,7 @@ void Viewport::_gui_control_grab_focus(Control *p_control) {
// No need for change.
return;
}
- get_tree()->call_group_flags(SceneTree::GROUP_CALL_REALTIME, "_viewports", "_gui_remove_focus_for_window", (Node *)get_base_window());
+ get_tree()->call_group("_viewports", "_gui_remove_focus_for_window", (Node *)get_base_window());
gui.key_focus = p_control;
emit_signal(SNAME("gui_focus_changed"), p_control);
p_control->notification(Control::NOTIFICATION_FOCUS_ENTER);
diff --git a/scene/multiplayer/multiplayer_spawner.cpp b/scene/multiplayer/multiplayer_spawner.cpp
index 25ab27f3e7..a9b9ffa989 100644
--- a/scene/multiplayer/multiplayer_spawner.cpp
+++ b/scene/multiplayer/multiplayer_spawner.cpp
@@ -91,9 +91,9 @@ void MultiplayerSpawner::_notification(int p_what) {
case NOTIFICATION_EXIT_TREE: {
_update_spawn_node();
- const ObjectID *oid = nullptr;
- while ((oid = tracked_nodes.next(oid))) {
- Node *node = Object::cast_to<Node>(ObjectDB::get_instance(*oid));
+
+ for (const KeyValue<ObjectID, SpawnInfo> &E : tracked_nodes) {
+ Node *node = Object::cast_to<Node>(ObjectDB::get_instance(E.key));
ERR_CONTINUE(!node);
node->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &MultiplayerSpawner::_node_exit));
// This is unlikely, but might still crash the engine.
diff --git a/scene/multiplayer/scene_cache_interface.cpp b/scene/multiplayer/scene_cache_interface.cpp
index f05dc5a2da..a933758946 100644
--- a/scene/multiplayer/scene_cache_interface.cpp
+++ b/scene/multiplayer/scene_cache_interface.cpp
@@ -50,10 +50,8 @@ void SceneCacheInterface::on_peer_change(int p_id, bool p_connected) {
path_get_cache.erase(p_id);
// Cleanup sent cache.
// Some refactoring is needed to make this faster and do paths GC.
- List<NodePath> keys;
- path_send_cache.get_key_list(&keys);
- for (const NodePath &E : keys) {
- PathSentCache *psc = path_send_cache.getptr(E);
+ for (const KeyValue<NodePath, PathSentCache> &E : path_send_cache) {
+ PathSentCache *psc = path_send_cache.getptr(E.key);
psc->confirmed_peers.erase(p_id);
}
}
diff --git a/scene/multiplayer/scene_replication_interface.cpp b/scene/multiplayer/scene_replication_interface.cpp
index 2952512462..63259bcd39 100644
--- a/scene/multiplayer/scene_replication_interface.cpp
+++ b/scene/multiplayer/scene_replication_interface.cpp
@@ -49,9 +49,8 @@ void SceneReplicationInterface::make_default() {
void SceneReplicationInterface::_free_remotes(int p_id) {
const HashMap<uint32_t, ObjectID> remotes = rep_state->peer_get_remotes(p_id);
- const uint32_t *k = nullptr;
- while ((k = remotes.next(k))) {
- Node *node = rep_state->get_node(remotes.get(*k));
+ for (const KeyValue<uint32_t, ObjectID> &E : remotes) {
+ Node *node = rep_state->get_node(E.value);
ERR_CONTINUE(!node);
node->queue_delete();
}
diff --git a/scene/multiplayer/scene_replication_state.cpp b/scene/multiplayer/scene_replication_state.cpp
index b8dadeff24..9a9b08b67b 100644
--- a/scene/multiplayer/scene_replication_state.cpp
+++ b/scene/multiplayer/scene_replication_state.cpp
@@ -55,9 +55,8 @@ void SceneReplicationState::_untrack(const ObjectID &p_id) {
}
// If we spawned or synced it, we need to remove it from any peer it was sent to.
if (net_id || peer == 0) {
- const int *k = nullptr;
- while ((k = peers_info.next(k))) {
- peers_info.get(*k).known_nodes.erase(p_id);
+ for (KeyValue<int, PeerInfo> &E : peers_info) {
+ E.value.known_nodes.erase(p_id);
}
}
}
@@ -134,9 +133,8 @@ void SceneReplicationState::reset() {
peers_info.clear();
known_peers.clear();
// Tracked nodes are cleared on deletion, here we only reset the ids so they can be later re-assigned.
- const ObjectID *oid = nullptr;
- while ((oid = tracked_nodes.next(oid))) {
- TrackedNode &tobj = tracked_nodes[*oid];
+ for (KeyValue<ObjectID, TrackedNode> &E : tracked_nodes) {
+ TrackedNode &tobj = E.value;
tobj.net_id = 0;
tobj.remote_peer = 0;
tobj.last_sync = 0;
@@ -195,9 +193,8 @@ Error SceneReplicationState::peer_add_node(int p_peer, const ObjectID &p_id) {
ERR_FAIL_COND_V(!peers_info.has(p_peer), ERR_INVALID_PARAMETER);
peers_info[p_peer].known_nodes.insert(p_id);
} else {
- const int *pid = nullptr;
- while ((pid = peers_info.next(pid))) {
- peers_info.get(*pid).known_nodes.insert(p_id);
+ for (KeyValue<int, PeerInfo> &E : peers_info) {
+ E.value.known_nodes.insert(p_id);
}
}
return OK;
@@ -208,9 +205,8 @@ Error SceneReplicationState::peer_del_node(int p_peer, const ObjectID &p_id) {
ERR_FAIL_COND_V(!peers_info.has(p_peer), ERR_INVALID_PARAMETER);
peers_info[p_peer].known_nodes.erase(p_id);
} else {
- const int *pid = nullptr;
- while ((pid = peers_info.next(pid))) {
- peers_info.get(*pid).known_nodes.erase(p_id);
+ for (KeyValue<int, PeerInfo> &E : peers_info) {
+ E.value.known_nodes.erase(p_id);
}
}
return OK;
diff --git a/scene/multiplayer/scene_replication_state.h b/scene/multiplayer/scene_replication_state.h
index 18e4d9fa39..6ac9265b67 100644
--- a/scene/multiplayer/scene_replication_state.h
+++ b/scene/multiplayer/scene_replication_state.h
@@ -81,8 +81,8 @@ private:
public:
const Set<int> get_peers() const { return known_peers; }
- const Set<ObjectID> get_spawned_nodes() const { return spawned_nodes; }
- const Set<ObjectID> get_path_only_nodes() const { return path_only_nodes; }
+ const Set<ObjectID> &get_spawned_nodes() const { return spawned_nodes; }
+ const Set<ObjectID> &get_path_only_nodes() const { return path_only_nodes; }
MultiplayerSynchronizer *get_synchronizer(const ObjectID &p_id) { return tracked_nodes.has(p_id) ? tracked_nodes[p_id].get_synchronizer() : nullptr; }
MultiplayerSpawner *get_spawner(const ObjectID &p_id) { return tracked_nodes.has(p_id) ? tracked_nodes[p_id].get_spawner() : nullptr; }
diff --git a/scene/resources/curve.cpp b/scene/resources/curve.cpp
index 6485c1ac77..c719455a0a 100644
--- a/scene/resources/curve.cpp
+++ b/scene/resources/curve.cpp
@@ -681,7 +681,8 @@ void Curve2D::_bake() const {
List<Vector2> pointlist;
List<real_t> distlist;
- pointlist.push_back(position); //start always from origin
+ // Start always from origin.
+ pointlist.push_back(position);
distlist.push_back(0.0);
for (int i = 0; i < points.size() - 1; i++) {
@@ -728,15 +729,18 @@ void Curve2D::_bake() const {
p = np;
}
}
- }
- Vector2 lastpos = points[points.size() - 1].position;
+ Vector2 npp = points[i + 1].position;
+ real_t d = position.distance_to(npp);
+
+ position = npp;
+ dist += d;
+
+ pointlist.push_back(position);
+ distlist.push_back(dist);
+ }
- real_t rem = position.distance_to(lastpos);
- dist += rem;
baked_max_ofs = dist;
- pointlist.push_back(lastpos);
- distlist.push_back(dist);
baked_point_cache.resize(pointlist.size());
baked_dist_cache.resize(distlist.size());
@@ -763,7 +767,7 @@ Vector2 Curve2D::interpolate_baked(real_t p_offset, bool p_cubic) const {
_bake();
}
- //validate//
+ // Validate: Curve may not have baked points.
int pc = baked_point_cache.size();
ERR_FAIL_COND_V_MSG(pc == 0, Vector2(), "No points in Curve2D.");
@@ -771,18 +775,19 @@ Vector2 Curve2D::interpolate_baked(real_t p_offset, bool p_cubic) const {
return baked_point_cache.get(0);
}
- int bpc = baked_point_cache.size();
const Vector2 *r = baked_point_cache.ptr();
if (p_offset < 0) {
return r[0];
}
if (p_offset >= baked_max_ofs) {
- return r[bpc - 1];
+ return r[pc - 1];
}
- int start = 0, end = bpc, idx = (end + start) / 2;
- // binary search to find baked points
+ int start = 0;
+ int end = pc;
+ int idx = (end + start) / 2;
+ // Binary search to find baked points.
while (start < idx) {
real_t offset = baked_dist_cache[idx];
if (p_offset <= offset) {
@@ -803,7 +808,7 @@ Vector2 Curve2D::interpolate_baked(real_t p_offset, bool p_cubic) const {
if (p_cubic) {
Vector2 pre = idx > 0 ? r[idx - 1] : r[idx];
- Vector2 post = (idx < (bpc - 2)) ? r[idx + 2] : r[idx + 1];
+ Vector2 post = (idx < (pc - 2)) ? r[idx + 2] : r[idx + 1];
return r[idx].cubic_interpolate(r[idx + 1], pre, post, frac);
} else {
return r[idx].lerp(r[idx + 1], frac);
@@ -829,13 +834,13 @@ real_t Curve2D::get_bake_interval() const {
}
Vector2 Curve2D::get_closest_point(const Vector2 &p_to_point) const {
- // Brute force method
+ // Brute force method.
if (baked_cache_dirty) {
_bake();
}
- //validate//
+ // Validate: Curve may not have baked points.
int pc = baked_point_cache.size();
ERR_FAIL_COND_V_MSG(pc == 0, Vector2(), "No points in Curve2D.");
@@ -867,13 +872,13 @@ Vector2 Curve2D::get_closest_point(const Vector2 &p_to_point) const {
}
real_t Curve2D::get_closest_offset(const Vector2 &p_to_point) const {
- // Brute force method
+ // Brute force method.
if (baked_cache_dirty) {
_bake();
}
- //validate//
+ // Validate: Curve may not have baked points.
int pc = baked_point_cache.size();
ERR_FAIL_COND_V_MSG(pc == 0, 0.0f, "No points in Curve2D.");
@@ -1009,11 +1014,7 @@ void Curve2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data");
}
-Curve2D::Curve2D() {
- /* add_point(Vector2(-1,0,0));
- add_point(Vector2(0,2,0));
- add_point(Vector2(0,3,5));*/
-}
+Curve2D::Curve2D() {}
/***********************************************************************************/
/***********************************************************************************/
@@ -1194,6 +1195,7 @@ void Curve3D::_bake() const {
List<Plane> pointlist;
List<real_t> distlist;
+ // Start always from origin.
pointlist.push_back(Plane(position, points[0].tilt));
distlist.push_back(0.0);
@@ -1244,16 +1246,22 @@ void Curve3D::_bake() const {
p = np;
}
}
- }
- Vector3 lastpos = points[points.size() - 1].position;
- real_t lastilt = points[points.size() - 1].tilt;
+ Vector3 npp = points[i + 1].position;
+ real_t d = position.distance_to(npp);
+
+ position = npp;
+ Plane post;
+ post.normal = position;
+ post.d = points[i + 1].tilt;
+
+ dist += d;
+
+ pointlist.push_back(post);
+ distlist.push_back(dist);
+ }
- real_t rem = position.distance_to(lastpos);
- dist += rem;
baked_max_ofs = dist;
- pointlist.push_back(Plane(lastpos, lastilt));
- distlist.push_back(dist);
baked_point_cache.resize(pointlist.size());
Vector3 *w = baked_point_cache.ptrw();
@@ -1328,7 +1336,7 @@ Vector3 Curve3D::interpolate_baked(real_t p_offset, bool p_cubic) const {
_bake();
}
- //validate//
+ // Validate: Curve may not have baked points.
int pc = baked_point_cache.size();
ERR_FAIL_COND_V_MSG(pc == 0, Vector3(), "No points in Curve3D.");
@@ -1336,18 +1344,19 @@ Vector3 Curve3D::interpolate_baked(real_t p_offset, bool p_cubic) const {
return baked_point_cache.get(0);
}
- int bpc = baked_point_cache.size();
const Vector3 *r = baked_point_cache.ptr();
if (p_offset < 0) {
return r[0];
}
if (p_offset >= baked_max_ofs) {
- return r[bpc - 1];
+ return r[pc - 1];
}
- int start = 0, end = bpc, idx = (end + start) / 2;
- // binary search to find baked points
+ int start = 0;
+ int end = pc;
+ int idx = (end + start) / 2;
+ // Binary search to find baked points.
while (start < idx) {
real_t offset = baked_dist_cache[idx];
if (p_offset <= offset) {
@@ -1368,7 +1377,7 @@ Vector3 Curve3D::interpolate_baked(real_t p_offset, bool p_cubic) const {
if (p_cubic) {
Vector3 pre = idx > 0 ? r[idx - 1] : r[idx];
- Vector3 post = (idx < (bpc - 2)) ? r[idx + 2] : r[idx + 1];
+ Vector3 post = (idx < (pc - 2)) ? r[idx + 2] : r[idx + 1];
return r[idx].cubic_interpolate(r[idx + 1], pre, post, frac);
} else {
return r[idx].lerp(r[idx + 1], frac);
@@ -1380,7 +1389,7 @@ real_t Curve3D::interpolate_baked_tilt(real_t p_offset) const {
_bake();
}
- //validate//
+ // Validate: Curve may not have baked tilts.
int pc = baked_tilt_cache.size();
ERR_FAIL_COND_V_MSG(pc == 0, 0, "No tilts in Curve3D.");
@@ -1388,29 +1397,37 @@ real_t Curve3D::interpolate_baked_tilt(real_t p_offset) const {
return baked_tilt_cache.get(0);
}
- int bpc = baked_tilt_cache.size();
const real_t *r = baked_tilt_cache.ptr();
if (p_offset < 0) {
return r[0];
}
if (p_offset >= baked_max_ofs) {
- return r[bpc - 1];
+ return r[pc - 1];
}
- int idx = Math::floor((double)p_offset / (double)bake_interval);
- real_t frac = Math::fmod(p_offset, bake_interval);
-
- if (idx >= bpc - 1) {
- return r[bpc - 1];
- } else if (idx == bpc - 2) {
- if (frac > 0) {
- frac /= Math::fmod(baked_max_ofs, bake_interval);
+ int start = 0;
+ int end = pc;
+ int idx = (end + start) / 2;
+ // Binary search to find baked points.
+ while (start < idx) {
+ real_t offset = baked_dist_cache[idx];
+ if (p_offset <= offset) {
+ end = idx;
+ } else {
+ start = idx;
}
- } else {
- frac /= bake_interval;
+ idx = (end + start) / 2;
}
+ real_t offset_begin = baked_dist_cache[idx];
+ real_t offset_end = baked_dist_cache[idx + 1];
+
+ real_t idx_interval = offset_end - offset_begin;
+ ERR_FAIL_COND_V_MSG(p_offset < offset_begin || p_offset > offset_end, 0, "failed to find baked segment");
+
+ real_t frac = (p_offset - offset_begin) / idx_interval;
+
return Math::lerp(r[idx], r[idx + 1], (real_t)frac);
}
@@ -1419,8 +1436,7 @@ Vector3 Curve3D::interpolate_baked_up_vector(real_t p_offset, bool p_apply_tilt)
_bake();
}
- //validate//
- // curve may not have baked up vectors
+ // Validate: Curve may not have baked up vectors.
int count = baked_up_vector_cache.size();
ERR_FAIL_COND_V_MSG(count == 0, Vector3(0, 1, 0), "No up vectors in Curve3D.");
@@ -1432,10 +1448,27 @@ Vector3 Curve3D::interpolate_baked_up_vector(real_t p_offset, bool p_apply_tilt)
const Vector3 *rp = baked_point_cache.ptr();
const real_t *rt = baked_tilt_cache.ptr();
- real_t offset = CLAMP(p_offset, 0.0f, baked_max_ofs);
+ int start = 0;
+ int end = count;
+ int idx = (end + start) / 2;
+ // Binary search to find baked points.
+ while (start < idx) {
+ real_t offset = baked_dist_cache[idx];
+ if (p_offset <= offset) {
+ end = idx;
+ } else {
+ start = idx;
+ }
+ idx = (end + start) / 2;
+ }
+
+ real_t offset_begin = baked_dist_cache[idx];
+ real_t offset_end = baked_dist_cache[idx + 1];
- int idx = Math::floor((double)offset / (double)bake_interval);
- real_t frac = Math::fmod(offset, bake_interval) / bake_interval;
+ real_t idx_interval = offset_end - offset_begin;
+ ERR_FAIL_COND_V_MSG(p_offset < offset_begin || p_offset > offset_end, Vector3(0, 1, 0), "failed to find baked segment");
+
+ real_t frac = (p_offset - offset_begin) / idx_interval;
if (idx == count - 1) {
return p_apply_tilt ? r[idx].rotated((rp[idx] - rp[idx - 1]).normalized(), rt[idx]) : r[idx];
@@ -1486,13 +1519,13 @@ PackedVector3Array Curve3D::get_baked_up_vectors() const {
}
Vector3 Curve3D::get_closest_point(const Vector3 &p_to_point) const {
- // Brute force method
+ // Brute force method.
if (baked_cache_dirty) {
_bake();
}
- //validate//
+ // Validate: Curve may not have baked points.
int pc = baked_point_cache.size();
ERR_FAIL_COND_V_MSG(pc == 0, Vector3(), "No points in Curve3D.");
@@ -1524,13 +1557,13 @@ Vector3 Curve3D::get_closest_point(const Vector3 &p_to_point) const {
}
real_t Curve3D::get_closest_offset(const Vector3 &p_to_point) const {
- // Brute force method
+ // Brute force method.
if (baked_cache_dirty) {
_bake();
}
- //validate//
+ // Validate: Curve may not have baked points.
int pc = baked_point_cache.size();
ERR_FAIL_COND_V_MSG(pc == 0, 0.0f, "No points in Curve3D.");
@@ -1705,8 +1738,4 @@ void Curve3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "up_vector_enabled"), "set_up_vector_enabled", "is_up_vector_enabled");
}
-Curve3D::Curve3D() {
- /* add_point(Vector3(-1,0,0));
- add_point(Vector3(0,2,0));
- add_point(Vector3(0,3,5));*/
-}
+Curve3D::Curve3D() {}
diff --git a/scene/resources/environment.cpp b/scene/resources/environment.cpp
index d92d34437e..5d1e07f6cd 100644
--- a/scene/resources/environment.cpp
+++ b/scene/resources/environment.cpp
@@ -1096,7 +1096,6 @@ void Environment::_validate_property(PropertyInfo &property) const {
static const char *high_end_prefixes[] = {
"auto_exposure_",
- "tonemap_",
"ssr_",
"ssao_",
nullptr
diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp
index d6b2572628..d586abac14 100644
--- a/scene/resources/font.cpp
+++ b/scene/resources/font.cpp
@@ -33,6 +33,7 @@
#include "core/io/image_loader.h"
#include "core/io/resource_loader.h"
#include "core/string/translation.h"
+#include "core/templates/hash_map.h"
#include "core/templates/hashfuncs.h"
#include "scene/resources/text_line.h"
#include "scene/resources/text_paragraph.h"
@@ -963,7 +964,7 @@ Error FontData::load_bitmap_font(const String &p_path) {
int delimiter = line.find(" ");
String type = line.substr(0, delimiter);
int pos = delimiter + 1;
- Map<String, String> keys;
+ HashMap<String, String> keys;
while (pos < line.size() && line[pos] == ' ') {
pos++;
diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp
index 16fce5e08a..27e1590940 100644
--- a/scene/resources/material.cpp
+++ b/scene/resources/material.cpp
@@ -84,7 +84,7 @@ void Material::inspect_native_shader_code() {
SceneTree *st = Object::cast_to<SceneTree>(OS::get_singleton()->get_main_loop());
RID shader = get_shader_rid();
if (st && shader.is_valid()) {
- st->call_group("_native_shader_source_visualizer", "_inspect_shader", shader);
+ st->call_group_flags(SceneTree::GROUP_CALL_DEFERRED, "_native_shader_source_visualizer", "_inspect_shader", shader);
}
}
diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp
index d649942188..12d1dc9925 100644
--- a/scene/resources/packed_scene.cpp
+++ b/scene/resources/packed_scene.cpp
@@ -112,6 +112,7 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const {
const NodeData &n = nd[i];
Node *parent = nullptr;
+ String old_parent_path;
if (i > 0) {
ERR_FAIL_COND_V_MSG(n.parent == -1, nullptr, vformat("Invalid scene: node %s does not specify its parent node.", snames[n.name]));
@@ -119,6 +120,8 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const {
#ifdef DEBUG_ENABLED
if (!nparent && (n.parent & FLAG_ID_IS_PATH)) {
WARN_PRINT(String("Parent path '" + String(node_paths[n.parent & FLAG_MASK]) + "' for node '" + String(snames[n.name]) + "' has vanished when instancing: '" + get_path() + "'.").ascii().get_data());
+ old_parent_path = String(node_paths[n.parent & FLAG_MASK]).trim_prefix("./").replace("/", "@");
+ nparent = ret_nodes[0];
}
#endif
parent = nparent;
@@ -332,6 +335,10 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const {
}
}
+ if (!old_parent_path.is_empty()) {
+ node->_set_name_nocheck(old_parent_path + "@" + node->get_name());
+ }
+
if (n.owner >= 0) {
NODE_FROM_ID(owner, n.owner);
if (owner) {
@@ -906,10 +913,10 @@ Error SceneState::pack(Node *p_scene) {
}
variants.resize(variant_map.size());
- const Variant *K = nullptr;
- while ((K = variant_map.next(K))) {
- int idx = variant_map[*K];
- variants.write[idx] = *K;
+
+ for (const KeyValue<Variant, int> &E : variant_map) {
+ int idx = E.value;
+ variants.write[idx] = E.key;
}
node_paths.resize(nodepath_map.size());
diff --git a/scene/resources/polygon_path_finder.cpp b/scene/resources/polygon_path_finder.cpp
index 882afdb43d..94e7f46ea5 100644
--- a/scene/resources/polygon_path_finder.cpp
+++ b/scene/resources/polygon_path_finder.cpp
@@ -137,7 +137,7 @@ Vector<Vector2> PolygonPathFinder::find_path(const Vector2 &p_from, const Vector
Edge ignore_to_edge(-1, -1);
if (!_is_point_inside(from)) {
- float closest_dist = 1e20;
+ float closest_dist = 1e20f;
Vector2 closest_point;
for (Set<Edge>::Element *E = edges.front(); E; E = E->next()) {
@@ -161,7 +161,7 @@ Vector<Vector2> PolygonPathFinder::find_path(const Vector2 &p_from, const Vector
};
if (!_is_point_inside(to)) {
- float closest_dist = 1e20;
+ float closest_dist = 1e20f;
Vector2 closest_point;
for (Set<Edge>::Element *E = edges.front(); E; E = E->next()) {
@@ -489,7 +489,7 @@ bool PolygonPathFinder::is_point_inside(const Vector2 &p_point) const {
}
Vector2 PolygonPathFinder::get_closest_point(const Vector2 &p_point) const {
- float closest_dist = 1e20;
+ float closest_dist = 1e20f;
Vector2 closest_point;
for (Set<Edge>::Element *E = edges.front(); E; E = E->next()) {
@@ -508,7 +508,7 @@ Vector2 PolygonPathFinder::get_closest_point(const Vector2 &p_point) const {
}
}
- ERR_FAIL_COND_V(closest_dist == 1e20, Vector2());
+ ERR_FAIL_COND_V(Math::is_equal_approx(closest_dist, 1e20f), Vector2());
return closest_point;
}
diff --git a/scene/resources/skeleton_modification_stack_2d.cpp b/scene/resources/skeleton_modification_stack_2d.cpp
index b944c244b6..38ec19828f 100644
--- a/scene/resources/skeleton_modification_stack_2d.cpp
+++ b/scene/resources/skeleton_modification_stack_2d.cpp
@@ -263,7 +263,7 @@ void SkeletonModificationStack2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "get_enabled");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "strength", PROPERTY_HINT_RANGE, "0, 1, 0.001"), "set_strength", "get_strength");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "modification_count", PROPERTY_HINT_RANGE, "0, 100, 1"), "set_modification_count", "get_modification_count");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "modification_count", PROPERTY_HINT_RANGE, "0, 100, 1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_ARRAY, "Modifications,modifications/"), "set_modification_count", "get_modification_count");
}
SkeletonModificationStack2D::SkeletonModificationStack2D() {
diff --git a/scene/resources/skeleton_modification_stack_3d.cpp b/scene/resources/skeleton_modification_stack_3d.cpp
index 7ccba1228c..44fbfc934e 100644
--- a/scene/resources/skeleton_modification_stack_3d.cpp
+++ b/scene/resources/skeleton_modification_stack_3d.cpp
@@ -217,7 +217,7 @@ void SkeletonModificationStack3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "get_enabled");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "strength", PROPERTY_HINT_RANGE, "0, 1, 0.001"), "set_strength", "get_strength");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "modification_count", PROPERTY_HINT_RANGE, "0, 100, 1"), "set_modification_count", "get_modification_count");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "modification_count", PROPERTY_HINT_RANGE, "0, 100, 1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_ARRAY, "Modifications,modifications/"), "set_modification_count", "get_modification_count");
}
SkeletonModificationStack3D::SkeletonModificationStack3D() {
diff --git a/scene/resources/surface_tool.cpp b/scene/resources/surface_tool.cpp
index 8ff1fde2cf..0e41eed570 100644
--- a/scene/resources/surface_tool.cpp
+++ b/scene/resources/surface_tool.cpp
@@ -1165,7 +1165,7 @@ void SurfaceTool::generate_normals(bool p_flip) {
for (int i = 0; i < 3; i++) {
Vector3 *lv = vertex_hash.getptr(v[i]);
if (!lv) {
- vertex_hash.set(v[i], normal);
+ vertex_hash.insert(v[i], normal);
} else {
(*lv) += normal;
}
diff --git a/scene/resources/theme.cpp b/scene/resources/theme.cpp
index be54c309c8..10806aef45 100644
--- a/scene/resources/theme.cpp
+++ b/scene/resources/theme.cpp
@@ -123,76 +123,50 @@ bool Theme::_get(const StringName &p_name, Variant &r_ret) const {
void Theme::_get_property_list(List<PropertyInfo> *p_list) const {
List<PropertyInfo> list;
- const StringName *key = nullptr;
-
// Type variations.
- while ((key = variation_map.next(key))) {
- list.push_back(PropertyInfo(Variant::STRING_NAME, String() + *key + "/base_type"));
+ for (const KeyValue<StringName, StringName> &E : variation_map) {
+ list.push_back(PropertyInfo(Variant::STRING_NAME, String() + E.key + "/base_type"));
}
- key = nullptr;
-
// Icons.
- while ((key = icon_map.next(key))) {
- const StringName *key2 = nullptr;
-
- while ((key2 = icon_map[*key].next(key2))) {
- list.push_back(PropertyInfo(Variant::OBJECT, String() + *key + "/icons/" + *key2, PROPERTY_HINT_RESOURCE_TYPE, "Texture2D", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_STORE_IF_NULL));
+ for (const KeyValue<StringName, HashMap<StringName, Ref<Texture2D>>> &E : icon_map) {
+ for (const KeyValue<StringName, Ref<Texture2D>> &F : E.value) {
+ list.push_back(PropertyInfo(Variant::OBJECT, String() + E.key + "/icons/" + F.key, PROPERTY_HINT_RESOURCE_TYPE, "Texture2D", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_STORE_IF_NULL));
}
}
- key = nullptr;
-
// Styles.
- while ((key = style_map.next(key))) {
- const StringName *key2 = nullptr;
-
- while ((key2 = style_map[*key].next(key2))) {
- list.push_back(PropertyInfo(Variant::OBJECT, String() + *key + "/styles/" + *key2, PROPERTY_HINT_RESOURCE_TYPE, "StyleBox", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_STORE_IF_NULL));
+ for (const KeyValue<StringName, HashMap<StringName, Ref<StyleBox>>> &E : style_map) {
+ for (const KeyValue<StringName, Ref<StyleBox>> &F : E.value) {
+ list.push_back(PropertyInfo(Variant::OBJECT, String() + E.key + "/styles/" + F.key, PROPERTY_HINT_RESOURCE_TYPE, "StyleBox", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_STORE_IF_NULL));
}
}
- key = nullptr;
-
// Fonts.
- while ((key = font_map.next(key))) {
- const StringName *key2 = nullptr;
-
- while ((key2 = font_map[*key].next(key2))) {
- list.push_back(PropertyInfo(Variant::OBJECT, String() + *key + "/fonts/" + *key2, PROPERTY_HINT_RESOURCE_TYPE, "Font", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_STORE_IF_NULL));
+ for (const KeyValue<StringName, HashMap<StringName, Ref<Font>>> &E : font_map) {
+ for (const KeyValue<StringName, Ref<Font>> &F : E.value) {
+ list.push_back(PropertyInfo(Variant::OBJECT, String() + E.key + "/fonts/" + F.key, PROPERTY_HINT_RESOURCE_TYPE, "Font", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_STORE_IF_NULL));
}
}
- key = nullptr;
-
// Font sizes.
- while ((key = font_size_map.next(key))) {
- const StringName *key2 = nullptr;
-
- while ((key2 = font_size_map[*key].next(key2))) {
- list.push_back(PropertyInfo(Variant::INT, String() + *key + "/font_sizes/" + *key2, PROPERTY_HINT_RANGE, "0,256,1,or_greater"));
+ for (const KeyValue<StringName, HashMap<StringName, int>> &E : font_size_map) {
+ for (const KeyValue<StringName, int> &F : E.value) {
+ list.push_back(PropertyInfo(Variant::INT, String() + E.key + "/font_sizes/" + F.key, PROPERTY_HINT_RANGE, "0,256,1,or_greater"));
}
}
- key = nullptr;
-
// Colors.
- while ((key = color_map.next(key))) {
- const StringName *key2 = nullptr;
-
- while ((key2 = color_map[*key].next(key2))) {
- list.push_back(PropertyInfo(Variant::COLOR, String() + *key + "/colors/" + *key2));
+ for (const KeyValue<StringName, HashMap<StringName, Color>> &E : color_map) {
+ for (const KeyValue<StringName, Color> &F : E.value) {
+ list.push_back(PropertyInfo(Variant::INT, String() + E.key + "/colors/" + F.key));
}
}
- key = nullptr;
-
// Constants.
- while ((key = constant_map.next(key))) {
- const StringName *key2 = nullptr;
-
- while ((key2 = constant_map[*key].next(key2))) {
- list.push_back(PropertyInfo(Variant::INT, String() + *key + "/constants/" + *key2));
+ for (const KeyValue<StringName, HashMap<StringName, int>> &E : constant_map) {
+ for (const KeyValue<StringName, int> &F : E.value) {
+ list.push_back(PropertyInfo(Variant::INT, String() + E.key + "/constants/" + F.key));
}
}
@@ -414,10 +388,8 @@ void Theme::get_icon_list(StringName p_theme_type, List<StringName> *p_list) con
return;
}
- const StringName *key = nullptr;
-
- while ((key = icon_map[p_theme_type].next(key))) {
- p_list->push_back(*key);
+ for (const KeyValue<StringName, Ref<Texture2D>> &E : icon_map[p_theme_type]) {
+ p_list->push_back(E.key);
}
}
@@ -437,9 +409,8 @@ void Theme::remove_icon_type(const StringName &p_theme_type) {
_freeze_change_propagation();
- const StringName *L = nullptr;
- while ((L = icon_map[p_theme_type].next(L))) {
- Ref<Texture2D> icon = icon_map[p_theme_type][*L];
+ for (const KeyValue<StringName, Ref<Texture2D>> &E : icon_map[p_theme_type]) {
+ Ref<Texture2D> icon = E.value;
if (icon.is_valid()) {
icon->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
}
@@ -453,9 +424,8 @@ void Theme::remove_icon_type(const StringName &p_theme_type) {
void Theme::get_icon_type_list(List<StringName> *p_list) const {
ERR_FAIL_NULL(p_list);
- const StringName *key = nullptr;
- while ((key = icon_map.next(key))) {
- p_list->push_back(*key);
+ for (const KeyValue<StringName, HashMap<StringName, Ref<Texture2D>>> &E : icon_map) {
+ p_list->push_back(E.key);
}
}
@@ -528,10 +498,8 @@ void Theme::get_stylebox_list(StringName p_theme_type, List<StringName> *p_list)
return;
}
- const StringName *key = nullptr;
-
- while ((key = style_map[p_theme_type].next(key))) {
- p_list->push_back(*key);
+ for (const KeyValue<StringName, Ref<StyleBox>> &E : style_map[p_theme_type]) {
+ p_list->push_back(E.key);
}
}
@@ -551,9 +519,8 @@ void Theme::remove_stylebox_type(const StringName &p_theme_type) {
_freeze_change_propagation();
- const StringName *L = nullptr;
- while ((L = style_map[p_theme_type].next(L))) {
- Ref<StyleBox> style = style_map[p_theme_type][*L];
+ for (const KeyValue<StringName, Ref<StyleBox>> &E : style_map[p_theme_type]) {
+ Ref<StyleBox> style = E.value;
if (style.is_valid()) {
style->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
}
@@ -567,9 +534,8 @@ void Theme::remove_stylebox_type(const StringName &p_theme_type) {
void Theme::get_stylebox_type_list(List<StringName> *p_list) const {
ERR_FAIL_NULL(p_list);
- const StringName *key = nullptr;
- while ((key = style_map.next(key))) {
- p_list->push_back(*key);
+ for (const KeyValue<StringName, HashMap<StringName, Ref<StyleBox>>> &E : style_map) {
+ p_list->push_back(E.key);
}
}
@@ -644,10 +610,8 @@ void Theme::get_font_list(StringName p_theme_type, List<StringName> *p_list) con
return;
}
- const StringName *key = nullptr;
-
- while ((key = font_map[p_theme_type].next(key))) {
- p_list->push_back(*key);
+ for (const KeyValue<StringName, Ref<Font>> &E : font_map[p_theme_type]) {
+ p_list->push_back(E.key);
}
}
@@ -667,9 +631,8 @@ void Theme::remove_font_type(const StringName &p_theme_type) {
_freeze_change_propagation();
- const StringName *L = nullptr;
- while ((L = font_map[p_theme_type].next(L))) {
- Ref<Font> font = font_map[p_theme_type][*L];
+ for (const KeyValue<StringName, Ref<Font>> &E : font_map[p_theme_type]) {
+ Ref<Font> font = E.value;
if (font.is_valid()) {
font->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
}
@@ -683,9 +646,8 @@ void Theme::remove_font_type(const StringName &p_theme_type) {
void Theme::get_font_type_list(List<StringName> *p_list) const {
ERR_FAIL_NULL(p_list);
- const StringName *key = nullptr;
- while ((key = font_map.next(key))) {
- p_list->push_back(*key);
+ for (const KeyValue<StringName, HashMap<StringName, Ref<Font>>> &E : font_map) {
+ p_list->push_back(E.key);
}
}
@@ -747,10 +709,8 @@ void Theme::get_font_size_list(StringName p_theme_type, List<StringName> *p_list
return;
}
- const StringName *key = nullptr;
-
- while ((key = font_size_map[p_theme_type].next(key))) {
- p_list->push_back(*key);
+ for (const KeyValue<StringName, int> &E : font_size_map[p_theme_type]) {
+ p_list->push_back(E.key);
}
}
@@ -774,9 +734,8 @@ void Theme::remove_font_size_type(const StringName &p_theme_type) {
void Theme::get_font_size_type_list(List<StringName> *p_list) const {
ERR_FAIL_NULL(p_list);
- const StringName *key = nullptr;
- while ((key = font_size_map.next(key))) {
- p_list->push_back(*key);
+ for (const KeyValue<StringName, HashMap<StringName, int>> &E : font_size_map) {
+ p_list->push_back(E.key);
}
}
@@ -836,10 +795,8 @@ void Theme::get_color_list(StringName p_theme_type, List<StringName> *p_list) co
return;
}
- const StringName *key = nullptr;
-
- while ((key = color_map[p_theme_type].next(key))) {
- p_list->push_back(*key);
+ for (const KeyValue<StringName, Color> &E : color_map[p_theme_type]) {
+ p_list->push_back(E.key);
}
}
@@ -863,9 +820,8 @@ void Theme::remove_color_type(const StringName &p_theme_type) {
void Theme::get_color_type_list(List<StringName> *p_list) const {
ERR_FAIL_NULL(p_list);
- const StringName *key = nullptr;
- while ((key = color_map.next(key))) {
- p_list->push_back(*key);
+ for (const KeyValue<StringName, HashMap<StringName, Color>> &E : color_map) {
+ p_list->push_back(E.key);
}
}
@@ -925,10 +881,8 @@ void Theme::get_constant_list(StringName p_theme_type, List<StringName> *p_list)
return;
}
- const StringName *key = nullptr;
-
- while ((key = constant_map[p_theme_type].next(key))) {
- p_list->push_back(*key);
+ for (const KeyValue<StringName, int> &E : constant_map[p_theme_type]) {
+ p_list->push_back(E.key);
}
}
@@ -952,9 +906,8 @@ void Theme::remove_constant_type(const StringName &p_theme_type) {
void Theme::get_constant_type_list(List<StringName> *p_list) const {
ERR_FAIL_NULL(p_list);
- const StringName *key = nullptr;
- while ((key = constant_map.next(key))) {
- p_list->push_back(*key);
+ for (const KeyValue<StringName, HashMap<StringName, int>> &E : constant_map) {
+ p_list->push_back(E.key);
}
}
@@ -1311,52 +1264,12 @@ void Theme::remove_type(const StringName &p_theme_type) {
void Theme::get_type_list(List<StringName> *p_list) const {
ERR_FAIL_NULL(p_list);
- Set<StringName> types;
- const StringName *key = nullptr;
-
- // Icons.
- while ((key = icon_map.next(key))) {
- types.insert(*key);
- }
-
- key = nullptr;
-
- // StyleBoxes.
- while ((key = style_map.next(key))) {
- types.insert(*key);
- }
-
- key = nullptr;
-
- // Fonts.
- while ((key = font_map.next(key))) {
- types.insert(*key);
- }
-
- key = nullptr;
-
- // Font sizes.
- while ((key = font_size_map.next(key))) {
- types.insert(*key);
- }
-
- key = nullptr;
-
- // Colors.
- while ((key = color_map.next(key))) {
- types.insert(*key);
- }
-
- key = nullptr;
-
- // Constants.
- while ((key = constant_map.next(key))) {
- types.insert(*key);
- }
-
- for (Set<StringName>::Element *E = types.front(); E; E = E->next()) {
- p_list->push_back(E->get());
- }
+ get_icon_type_list(p_list);
+ get_stylebox_type_list(p_list);
+ get_font_type_list(p_list);
+ get_font_size_type_list(p_list);
+ get_color_type_list(p_list);
+ get_constant_type_list(p_list);
}
void Theme::get_type_dependencies(const StringName &p_base_type, const StringName &p_type_variation, List<StringName> *p_list) {
@@ -1667,75 +1580,62 @@ void Theme::merge_with(const Ref<Theme> &p_other) {
// Colors.
{
- const StringName *K = nullptr;
- while ((K = p_other->color_map.next(K))) {
- const StringName *L = nullptr;
- while ((L = p_other->color_map[*K].next(L))) {
- set_color(*L, *K, p_other->color_map[*K][*L]);
+ for (const KeyValue<StringName, HashMap<StringName, Color>> &E : p_other->color_map) {
+ for (const KeyValue<StringName, Color> &F : E.value) {
+ set_color(F.key, E.key, F.value);
}
}
}
// Constants.
{
- const StringName *K = nullptr;
- while ((K = p_other->constant_map.next(K))) {
- const StringName *L = nullptr;
- while ((L = p_other->constant_map[*K].next(L))) {
- set_constant(*L, *K, p_other->constant_map[*K][*L]);
+ for (const KeyValue<StringName, HashMap<StringName, int>> &E : p_other->constant_map) {
+ for (const KeyValue<StringName, int> &F : E.value) {
+ set_constant(F.key, E.key, F.value);
}
}
}
// Fonts.
{
- const StringName *K = nullptr;
- while ((K = p_other->font_map.next(K))) {
- const StringName *L = nullptr;
- while ((L = p_other->font_map[*K].next(L))) {
- set_font(*L, *K, p_other->font_map[*K][*L]);
+ for (const KeyValue<StringName, HashMap<StringName, Ref<Font>>> &E : p_other->font_map) {
+ for (const KeyValue<StringName, Ref<Font>> &F : E.value) {
+ set_font(F.key, E.key, F.value);
}
}
}
// Font sizes.
{
- const StringName *K = nullptr;
- while ((K = p_other->font_size_map.next(K))) {
- const StringName *L = nullptr;
- while ((L = p_other->font_size_map[*K].next(L))) {
- set_font_size(*L, *K, p_other->font_size_map[*K][*L]);
+ for (const KeyValue<StringName, HashMap<StringName, int>> &E : p_other->font_size_map) {
+ for (const KeyValue<StringName, int> &F : E.value) {
+ set_font_size(F.key, E.key, F.value);
}
}
}
// Icons.
{
- const StringName *K = nullptr;
- while ((K = p_other->icon_map.next(K))) {
- const StringName *L = nullptr;
- while ((L = p_other->icon_map[*K].next(L))) {
- set_icon(*L, *K, p_other->icon_map[*K][*L]);
+ for (const KeyValue<StringName, HashMap<StringName, Ref<Texture2D>>> &E : p_other->icon_map) {
+ for (const KeyValue<StringName, Ref<Texture2D>> &F : E.value) {
+ set_icon(F.key, E.key, F.value);
}
}
}
// Styleboxes.
{
- const StringName *K = nullptr;
- while ((K = p_other->style_map.next(K))) {
- const StringName *L = nullptr;
- while ((L = p_other->style_map[*K].next(L))) {
- set_stylebox(*L, *K, p_other->style_map[*K][*L]);
+ for (const KeyValue<StringName, HashMap<StringName, Ref<StyleBox>>> &E : p_other->style_map) {
+ for (const KeyValue<StringName, Ref<StyleBox>> &F : E.value) {
+ set_stylebox(F.key, E.key, F.value);
}
}
}
// Type variations.
{
- const StringName *K = nullptr;
- while ((K = p_other->variation_map.next(K))) {
- set_type_variation(*K, p_other->variation_map[*K]);
+ for (const KeyValue<StringName, StringName> &E : p_other->variation_map) {
+ set_type_variation(E.key, E.value);
}
}
@@ -1745,12 +1645,10 @@ void Theme::merge_with(const Ref<Theme> &p_other) {
void Theme::clear() {
// These items need disconnecting.
{
- const StringName *K = nullptr;
- while ((K = icon_map.next(K))) {
- const StringName *L = nullptr;
- while ((L = icon_map[*K].next(L))) {
- Ref<Texture2D> icon = icon_map[*K][*L];
- if (icon.is_valid()) {
+ for (const KeyValue<StringName, HashMap<StringName, Ref<Texture2D>>> &E : icon_map) {
+ for (const KeyValue<StringName, Ref<Texture2D>> &F : E.value) {
+ if (F.value.is_valid()) {
+ Ref<Texture2D> icon = F.value;
icon->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
}
}
@@ -1758,12 +1656,10 @@ void Theme::clear() {
}
{
- const StringName *K = nullptr;
- while ((K = style_map.next(K))) {
- const StringName *L = nullptr;
- while ((L = style_map[*K].next(L))) {
- Ref<StyleBox> style = style_map[*K][*L];
- if (style.is_valid()) {
+ for (const KeyValue<StringName, HashMap<StringName, Ref<StyleBox>>> &E : style_map) {
+ for (const KeyValue<StringName, Ref<StyleBox>> &F : E.value) {
+ if (F.value.is_valid()) {
+ Ref<StyleBox> style = F.value;
style->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
}
}
@@ -1771,12 +1667,10 @@ void Theme::clear() {
}
{
- const StringName *K = nullptr;
- while ((K = font_map.next(K))) {
- const StringName *L = nullptr;
- while ((L = font_map[*K].next(L))) {
- Ref<Font> font = font_map[*K][*L];
- if (font.is_valid()) {
+ for (const KeyValue<StringName, HashMap<StringName, Ref<Font>>> &E : font_map) {
+ for (const KeyValue<StringName, Ref<Font>> &F : E.value) {
+ if (F.value.is_valid()) {
+ Ref<Font> font = F.value;
font->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
}
}
diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp
index da9e1ef2f6..67b82af617 100644
--- a/scene/resources/tile_set.cpp
+++ b/scene/resources/tile_set.cpp
@@ -3264,16 +3264,10 @@ void TileSet::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "uv_clipping"), "set_uv_clipping", "is_uv_clipping");
ADD_ARRAY("occlusion_layers", "occlusion_layer_");
- ADD_GROUP("Physics", "");
+ ADD_GROUP("", "");
ADD_ARRAY("physics_layers", "physics_layer_");
-
- ADD_GROUP("Terrains", "");
ADD_ARRAY("terrain_sets", "terrain_set_");
-
- ADD_GROUP("Navigation", "");
ADD_ARRAY("navigation_layers", "navigation_layer_");
-
- ADD_GROUP("Custom Data", "");
ADD_ARRAY("custom_data_layers", "custom_data_layer_");
// -- Enum binding --
diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp
index 755962b96c..6848011228 100644
--- a/scene/resources/visual_shader.cpp
+++ b/scene/resources/visual_shader.cpp
@@ -3479,89 +3479,89 @@ const VisualShaderNodeOutput::Port VisualShaderNodeOutput::ports[] = {
////////////////////////////////////////////////////////////////////////
// Node3D, Vertex.
////////////////////////////////////////////////////////////////////////
- { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "vertex", "VERTEX" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "normal", "NORMAL" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "tangent", "TANGENT" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "binormal", "BINORMAL" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv", "UV" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv2", "UV2" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_4D, "color", "COLOR" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "roughness", "ROUGHNESS" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "point_size", "POINT_SIZE" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "model_view_matrix", "MODELVIEW_MATRIX" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Vertex", "VERTEX" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Normal", "NORMAL" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Tangent", "TANGENT" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Binormal", "BINORMAL" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "UV", "UV" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "UV2", "UV2" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_4D, "Color", "COLOR" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "Roughness", "ROUGHNESS" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "Point Size", "POINT_SIZE" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "Model View Matrix", "MODELVIEW_MATRIX" },
////////////////////////////////////////////////////////////////////////
// Node3D, Fragment.
////////////////////////////////////////////////////////////////////////
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "albedo", "ALBEDO" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "ALPHA" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "metallic", "METALLIC" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "roughness", "ROUGHNESS" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "specular", "SPECULAR" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "emission", "EMISSION" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "ao", "AO" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "ao_light_affect", "AO_LIGHT_AFFECT" },
-
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "normal", "NORMAL" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "normal_map", "NORMAL_MAP" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "normal_map_depth", "NORMAL_MAP_DEPTH" },
-
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "rim", "RIM" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "rim_tint", "RIM_TINT" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "clearcoat", "CLEARCOAT" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "clearcoat_roughness", "CLEARCOAT_ROUGHNESS" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "anisotropy", "ANISOTROPY" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "anisotropy_flow", "ANISOTROPY_FLOW" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "subsurf_scatter", "SSS_STRENGTH" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "backlight", "BACKLIGHT" },
-
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha_scissor_threshold", "ALPHA_SCISSOR_THRESHOLD" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha_hash_scale", "ALPHA_HASH_SCALE" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha_aa_edge", "ALPHA_ANTIALIASING_EDGE" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "alpha_uv", "ALPHA_TEXTURE_COORDINATE" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Albedo", "ALBEDO" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "Alpha", "ALPHA" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "Metallic", "METALLIC" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "Roughness", "ROUGHNESS" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "Specular", "SPECULAR" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Emission", "EMISSION" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "AO", "AO" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "AO Light Affect", "AO_LIGHT_AFFECT" },
+
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Normal", "NORMAL" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Normal Map", "NORMAL_MAP" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "Normal Map Depth", "NORMAL_MAP_DEPTH" },
+
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "Rim", "RIM" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "Rim Tint", "RIM_TINT" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "Clearcoat", "CLEARCOAT" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "Clearcoat Roughness", "CLEARCOAT_ROUGHNESS" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "Anisotropy", "ANISOTROPY" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "Anisotropy Flow", "ANISOTROPY_FLOW" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "Subsurf Scatter", "SSS_STRENGTH" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Backlight", "BACKLIGHT" },
+
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "Alpha Scissor Threshold", "ALPHA_SCISSOR_THRESHOLD" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "Alpha Hash Scale", "ALPHA_HASH_SCALE" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "Alpha AA Edge", "ALPHA_ANTIALIASING_EDGE" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "Alpha UV", "ALPHA_TEXTURE_COORDINATE" },
////////////////////////////////////////////////////////////////////////
// Node3D, Light.
////////////////////////////////////////////////////////////////////////
- { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "diffuse", "DIFFUSE_LIGHT" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "specular", "SPECULAR_LIGHT" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "ALPHA" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Diffuse", "DIFFUSE_LIGHT" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Specular", "SPECULAR_LIGHT" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "Alpha", "ALPHA" },
////////////////////////////////////////////////////////////////////////
// Canvas Item.
////////////////////////////////////////////////////////////////////////
// Canvas Item, Vertex.
////////////////////////////////////////////////////////////////////////
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "vertex", "VERTEX" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv", "UV" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_4D, "color", "COLOR" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "point_size", "POINT_SIZE" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "Vertex", "VERTEX" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "UV", "UV" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_4D, "Color", "COLOR" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "Point Size", "POINT_SIZE" },
////////////////////////////////////////////////////////////////////////
// Canvas Item, Fragment.
////////////////////////////////////////////////////////////////////////
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "color", "COLOR" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "normal", "NORMAL" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "normal_map", "NORMAL_MAP" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "normal_map_depth", "NORMAL_MAP_DEPTH" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "light_vertex", "LIGHT_VERTEX" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "shadow_vertex", "SHADOW_VERTEX" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "Color", "COLOR" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Normal", "NORMAL" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Normal Map", "NORMAL_MAP" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "Normal Map Depth", "NORMAL_MAP_DEPTH" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Light Vertex", "LIGHT_VERTEX" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "Shadow Vertex", "SHADOW_VERTEX" },
////////////////////////////////////////////////////////////////////////
// Canvas Item, Light.
////////////////////////////////////////////////////////////////////////
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "light", "LIGHT" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "Light", "LIGHT" },
////////////////////////////////////////////////////////////////////////
// Sky, Sky.
////////////////////////////////////////////////////////////////////////
- { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR_3D, "color", "COLOR" },
- { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "ALPHA" },
- { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR_4D, "fog", "FOG" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Color", "COLOR" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "Alpha", "ALPHA" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR_4D, "Fog", "FOG" },
////////////////////////////////////////////////////////////////////////
// Fog, Fog.
////////////////////////////////////////////////////////////////////////
- { Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_SCALAR, "density", "DENSITY" },
- { Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_VECTOR_3D, "albedo", "ALBEDO" },
- { Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_VECTOR_3D, "emission", "EMISSION" },
+ { Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_SCALAR, "Density", "DENSITY" },
+ { Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Albedo", "ALBEDO" },
+ { Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Emission", "EMISSION" },
////////////////////////////////////////////////////////////////////////
{ Shader::MODE_MAX, VisualShader::TYPE_MAX, VisualShaderNode::PORT_TYPE_TRANSFORM, nullptr, nullptr },
@@ -3605,7 +3605,7 @@ String VisualShaderNodeOutput::get_input_port_name(int p_port) const {
while (ports[idx].mode != Shader::MODE_MAX) {
if (ports[idx].mode == shader_mode && ports[idx].shader_type == shader_type) {
if (count == p_port) {
- return String(ports[idx].name).capitalize();
+ return String(ports[idx].name);
}
count++;
}
@@ -3638,7 +3638,7 @@ bool VisualShaderNodeOutput::is_port_separator(int p_index) const {
}
if (shader_mode == Shader::MODE_SPATIAL && shader_type == VisualShader::TYPE_FRAGMENT) {
String name = get_input_port_name(p_index);
- return bool(name == "Ao" || name == "Normal" || name == "Rim" || name == "Clearcoat" || name == "Anisotropy" || name == "Subsurf Scatter" || name == "Alpha Scissor Threshold");
+ return bool(name == "AO" || name == "Normal" || name == "Rim" || name == "Clearcoat" || name == "Anisotropy" || name == "Subsurf Scatter" || name == "Alpha Scissor Threshold");
}
return false;
}
diff --git a/scene/resources/visual_shader_nodes.cpp b/scene/resources/visual_shader_nodes.cpp
index 99d5d5c5d5..6af34a8a82 100644
--- a/scene/resources/visual_shader_nodes.cpp
+++ b/scene/resources/visual_shader_nodes.cpp
@@ -2608,8 +2608,6 @@ String VisualShaderNodeVectorFunc::generate_code(Shader::Mode p_mode, VisualShad
"", // FUNC_SATURATE
"-($)",
"1.0 / ($)",
- "", // FUNC_RGB2HSV
- "", // FUNC_HSV2RGB
"abs($)",
"acos($)",
"acosh($)",
@@ -2667,43 +2665,7 @@ String VisualShaderNodeVectorFunc::generate_code(Shader::Mode p_mode, VisualShad
return " " + p_output_vars[0] + " = " + code.replace("$", p_input_vars[0]) + ";\n";
}
- String code;
-
- if (func == FUNC_RGB2HSV) {
- if (op_type == OP_TYPE_VECTOR_2D) { // Not supported.
- return " " + p_output_vars[0] + " = vec2(0.0);\n";
- }
- if (op_type == OP_TYPE_VECTOR_4D) { // Not supported.
- return " " + p_output_vars[0] + " = vec4(0.0);\n";
- }
- code += " {\n";
- code += " vec3 c = " + p_input_vars[0] + ";\n";
- code += " vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);\n";
- code += " vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));\n";
- code += " vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));\n";
- code += " float d = q.x - min(q.w, q.y);\n";
- code += " float e = 1.0e-10;\n";
- code += " " + p_output_vars[0] + " = vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);\n";
- code += " }\n";
- } else if (func == FUNC_HSV2RGB) {
- if (op_type == OP_TYPE_VECTOR_2D) { // Not supported.
- return " " + p_output_vars[0] + " = vec2(0.0);\n";
- }
- if (op_type == OP_TYPE_VECTOR_4D) { // Not supported.
- return " " + p_output_vars[0] + " = vec4(0.0);\n";
- }
- code += " {\n";
- code += " vec3 c = " + p_input_vars[0] + ";\n";
- code += " vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);\n";
- code += " vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);\n";
- code += " " + p_output_vars[0] + " = c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);\n";
- code += " }\n";
-
- } else {
- code += " " + p_output_vars[0] + " = " + String(funcs[func]).replace("$", p_input_vars[0]) + ";\n";
- }
-
- return code;
+ return " " + p_output_vars[0] + " = " + String(funcs[func]).replace("$", p_input_vars[0]) + ";\n";
}
void VisualShaderNodeVectorFunc::set_op_type(OpType p_op_type) {
@@ -2733,13 +2695,6 @@ void VisualShaderNodeVectorFunc::set_function(Function p_func) {
if (func == p_func) {
return;
}
- if (p_func == FUNC_RGB2HSV) {
- simple_decl = false;
- } else if (p_func == FUNC_HSV2RGB) {
- simple_decl = false;
- } else {
- simple_decl = true;
- }
func = p_func;
emit_changed();
}
@@ -2754,34 +2709,16 @@ Vector<StringName> VisualShaderNodeVectorFunc::get_editable_properties() const {
return props;
}
-String VisualShaderNodeVectorFunc::get_warning(Shader::Mode p_mode, VisualShader::Type p_type) const {
- bool invalid_type = false;
-
- if (op_type == OP_TYPE_VECTOR_2D || op_type == OP_TYPE_VECTOR_4D) {
- if (func == FUNC_RGB2HSV || func == FUNC_HSV2RGB) {
- invalid_type = true;
- }
- }
-
- if (invalid_type) {
- return RTR("Invalid function for that type.");
- }
-
- return String();
-}
-
void VisualShaderNodeVectorFunc::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_function", "func"), &VisualShaderNodeVectorFunc::set_function);
ClassDB::bind_method(D_METHOD("get_function"), &VisualShaderNodeVectorFunc::get_function);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "Normalize,Saturate,Negate,Reciprocal,RGB2HSV,HSV2RGB,Abs,ACos,ACosH,ASin,ASinH,ATan,ATanH,Ceil,Cos,CosH,Degrees,Exp,Exp2,Floor,Frac,InverseSqrt,Log,Log2,Radians,Round,RoundEven,Sign,Sin,SinH,Sqrt,Tan,TanH,Trunc,OneMinus"), "set_function", "get_function");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "Normalize,Saturate,Negate,Reciprocal,Abs,ACos,ACosH,ASin,ASinH,ATan,ATanH,Ceil,Cos,CosH,Degrees,Exp,Exp2,Floor,Frac,InverseSqrt,Log,Log2,Radians,Round,RoundEven,Sign,Sin,SinH,Sqrt,Tan,TanH,Trunc,OneMinus"), "set_function", "get_function");
BIND_ENUM_CONSTANT(FUNC_NORMALIZE);
BIND_ENUM_CONSTANT(FUNC_SATURATE);
BIND_ENUM_CONSTANT(FUNC_NEGATE);
BIND_ENUM_CONSTANT(FUNC_RECIPROCAL);
- BIND_ENUM_CONSTANT(FUNC_RGB2HSV);
- BIND_ENUM_CONSTANT(FUNC_HSV2RGB);
BIND_ENUM_CONSTANT(FUNC_ABS);
BIND_ENUM_CONSTANT(FUNC_ACOS);
BIND_ENUM_CONSTANT(FUNC_ACOSH);
@@ -2872,6 +2809,25 @@ String VisualShaderNodeColorFunc::generate_code(Shader::Mode p_mode, VisualShade
code += " " + p_output_vars[0] + " = vec3(max2, max2, max2);\n";
code += " }\n";
break;
+ case FUNC_HSV2RGB:
+ code += " {\n";
+ code += " vec3 c = " + p_input_vars[0] + ";\n";
+ code += " vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);\n";
+ code += " vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);\n";
+ code += " " + p_output_vars[0] + " = c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);\n";
+ code += " }\n";
+ break;
+ case FUNC_RGB2HSV:
+ code += " {\n";
+ code += " vec3 c = " + p_input_vars[0] + ";\n";
+ code += " vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);\n";
+ code += " vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));\n";
+ code += " vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));\n";
+ code += " float d = q.x - min(q.w, q.y);\n";
+ code += " float e = 1.0e-10;\n";
+ code += " " + p_output_vars[0] + " = vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);\n";
+ code += " }\n";
+ break;
case FUNC_SEPIA:
code += " {\n";
code += " vec3 c = " + p_input_vars[0] + ";\n";
@@ -2911,9 +2867,11 @@ void VisualShaderNodeColorFunc::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_function", "func"), &VisualShaderNodeColorFunc::set_function);
ClassDB::bind_method(D_METHOD("get_function"), &VisualShaderNodeColorFunc::get_function);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "Grayscale,Sepia"), "set_function", "get_function");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "Grayscale,HSV2RGB,RGB2HSV,Sepia"), "set_function", "get_function");
BIND_ENUM_CONSTANT(FUNC_GRAYSCALE);
+ BIND_ENUM_CONSTANT(FUNC_HSV2RGB);
+ BIND_ENUM_CONSTANT(FUNC_RGB2HSV);
BIND_ENUM_CONSTANT(FUNC_SEPIA);
BIND_ENUM_CONSTANT(FUNC_MAX);
}
@@ -5104,7 +5062,7 @@ int VisualShaderNodeColorUniform::get_input_port_count() const {
}
VisualShaderNodeColorUniform::PortType VisualShaderNodeColorUniform::get_input_port_type(int p_port) const {
- return PORT_TYPE_VECTOR_3D;
+ return PORT_TYPE_SCALAR;
}
String VisualShaderNodeColorUniform::get_input_port_name(int p_port) const {
@@ -5112,15 +5070,22 @@ String VisualShaderNodeColorUniform::get_input_port_name(int p_port) const {
}
int VisualShaderNodeColorUniform::get_output_port_count() const {
- return 2;
+ return 1;
}
VisualShaderNodeColorUniform::PortType VisualShaderNodeColorUniform::get_output_port_type(int p_port) const {
- return p_port == 0 ? PORT_TYPE_VECTOR_3D : PORT_TYPE_SCALAR;
+ return p_port == 0 ? PORT_TYPE_VECTOR_4D : PORT_TYPE_SCALAR;
}
String VisualShaderNodeColorUniform::get_output_port_name(int p_port) const {
- return p_port == 0 ? "color" : "alpha"; //no output port means the editor will be used as port
+ return "color";
+}
+
+bool VisualShaderNodeColorUniform::is_output_port_expandable(int p_port) const {
+ if (p_port == 0) {
+ return true;
+ }
+ return false;
}
void VisualShaderNodeColorUniform::set_default_value_enabled(bool p_enabled) {
@@ -5157,9 +5122,7 @@ String VisualShaderNodeColorUniform::generate_global(Shader::Mode p_mode, Visual
}
String VisualShaderNodeColorUniform::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
- String code = " " + p_output_vars[0] + " = " + get_uniform_name() + ".rgb;\n";
- code += " " + p_output_vars[1] + " = " + get_uniform_name() + ".a;\n";
- return code;
+ return " " + p_output_vars[0] + " = " + get_uniform_name() + ";\n";
}
bool VisualShaderNodeColorUniform::is_show_prop_names() const {
diff --git a/scene/resources/visual_shader_nodes.h b/scene/resources/visual_shader_nodes.h
index 26c98bd2ea..338f1157d3 100644
--- a/scene/resources/visual_shader_nodes.h
+++ b/scene/resources/visual_shader_nodes.h
@@ -1039,8 +1039,6 @@ public:
FUNC_SATURATE,
FUNC_NEGATE,
FUNC_RECIPROCAL,
- FUNC_RGB2HSV,
- FUNC_HSV2RGB,
FUNC_ABS,
FUNC_ACOS,
FUNC_ACOSH,
@@ -1095,7 +1093,6 @@ public:
Function get_function() const;
virtual Vector<StringName> get_editable_properties() const override;
- String get_warning(Shader::Mode p_mode, VisualShader::Type p_type) const override;
VisualShaderNodeVectorFunc();
};
@@ -1112,6 +1109,8 @@ class VisualShaderNodeColorFunc : public VisualShaderNode {
public:
enum Function {
FUNC_GRAYSCALE,
+ FUNC_HSV2RGB,
+ FUNC_RGB2HSV,
FUNC_SEPIA,
FUNC_MAX,
};
@@ -1922,6 +1921,8 @@ public:
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) const override;
+ bool is_output_port_expandable(int p_port) const override;
+
virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const override;
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
diff --git a/servers/rendering/dummy/rasterizer_dummy.h b/servers/rendering/dummy/rasterizer_dummy.h
index 6cad45ea6d..5c6fcc8386 100644
--- a/servers/rendering/dummy/rasterizer_dummy.h
+++ b/servers/rendering/dummy/rasterizer_dummy.h
@@ -95,9 +95,9 @@ public:
static void make_current() {
_create_func = _create_current;
+ low_end = true;
}
- bool is_low_end() const override { return true; }
uint64_t get_frame_number() const override { return frame; }
double get_frame_delta_time() const override { return delta; }
diff --git a/servers/rendering/renderer_compositor.cpp b/servers/rendering/renderer_compositor.cpp
index fa4d9c8b31..b331ec2c1d 100644
--- a/servers/rendering/renderer_compositor.cpp
+++ b/servers/rendering/renderer_compositor.cpp
@@ -35,6 +35,7 @@
#include "core/string/print_string.h"
RendererCompositor *(*RendererCompositor::_create_func)() = nullptr;
+bool RendererCompositor::low_end = false;
RendererCompositor *RendererCompositor::create() {
return _create_func();
diff --git a/servers/rendering/renderer_compositor.h b/servers/rendering/renderer_compositor.h
index 9466148a31..df3df1077a 100644
--- a/servers/rendering/renderer_compositor.h
+++ b/servers/rendering/renderer_compositor.h
@@ -71,6 +71,7 @@ private:
protected:
static RendererCompositor *(*_create_func)();
bool back_end = false;
+ static bool low_end;
public:
static RendererCompositor *create();
@@ -97,7 +98,7 @@ public:
virtual uint64_t get_frame_number() const = 0;
virtual double get_frame_delta_time() const = 0;
- _FORCE_INLINE_ virtual bool is_low_end() const { return back_end; };
+ static bool is_low_end() { return low_end; };
virtual bool is_xr_enabled() const;
RendererCompositor();
diff --git a/servers/rendering/renderer_rd/effects/bokeh_dof.cpp b/servers/rendering/renderer_rd/effects/bokeh_dof.cpp
new file mode 100644
index 0000000000..cc7441776d
--- /dev/null
+++ b/servers/rendering/renderer_rd/effects/bokeh_dof.cpp
@@ -0,0 +1,475 @@
+/*************************************************************************/
+/* bokeh_dof.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. */
+/*************************************************************************/
+
+#include "bokeh_dof.h"
+#include "copy_effects.h"
+#include "servers/rendering/renderer_rd/renderer_compositor_rd.h"
+#include "servers/rendering/renderer_rd/storage_rd/material_storage.h"
+#include "servers/rendering/renderer_rd/uniform_set_cache_rd.h"
+
+using namespace RendererRD;
+
+BokehDOF::BokehDOF(bool p_prefer_raster_effects) {
+ prefer_raster_effects = p_prefer_raster_effects;
+
+ // Initialize bokeh
+ Vector<String> bokeh_modes;
+ bokeh_modes.push_back("\n#define MODE_GEN_BLUR_SIZE\n");
+ bokeh_modes.push_back("\n#define MODE_BOKEH_BOX\n#define OUTPUT_WEIGHT\n");
+ bokeh_modes.push_back("\n#define MODE_BOKEH_BOX\n");
+ bokeh_modes.push_back("\n#define MODE_BOKEH_HEXAGONAL\n#define OUTPUT_WEIGHT\n");
+ bokeh_modes.push_back("\n#define MODE_BOKEH_HEXAGONAL\n");
+ bokeh_modes.push_back("\n#define MODE_BOKEH_CIRCULAR\n#define OUTPUT_WEIGHT\n");
+ bokeh_modes.push_back("\n#define MODE_COMPOSITE_BOKEH\n");
+ if (prefer_raster_effects) {
+ bokeh.raster_shader.initialize(bokeh_modes);
+
+ bokeh.shader_version = bokeh.raster_shader.version_create();
+
+ const int att_count[BOKEH_MAX] = { 1, 2, 1, 2, 1, 2, 1 };
+ for (int i = 0; i < BOKEH_MAX; i++) {
+ RD::PipelineColorBlendState blend_state = (i == BOKEH_COMPOSITE) ? RD::PipelineColorBlendState::create_blend(att_count[i]) : RD::PipelineColorBlendState::create_disabled(att_count[i]);
+ bokeh.raster_pipelines[i].setup(bokeh.raster_shader.version_get_shader(bokeh.shader_version, i), RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), blend_state, 0);
+ }
+ } else {
+ bokeh.compute_shader.initialize(bokeh_modes);
+ bokeh.compute_shader.set_variant_enabled(BOKEH_GEN_BOKEH_BOX_NOWEIGHT, false);
+ bokeh.compute_shader.set_variant_enabled(BOKEH_GEN_BOKEH_HEXAGONAL_NOWEIGHT, false);
+ bokeh.shader_version = bokeh.compute_shader.version_create();
+
+ for (int i = 0; i < BOKEH_MAX; i++) {
+ if (bokeh.compute_shader.is_variant_enabled(i)) {
+ bokeh.compute_pipelines[i] = RD::get_singleton()->compute_pipeline_create(bokeh.compute_shader.version_get_shader(bokeh.shader_version, i));
+ }
+ }
+
+ for (int i = 0; i < BOKEH_MAX; i++) {
+ bokeh.raster_pipelines[i].clear();
+ }
+ }
+}
+
+BokehDOF::~BokehDOF() {
+ if (prefer_raster_effects) {
+ bokeh.raster_shader.version_free(bokeh.shader_version);
+ } else {
+ bokeh.compute_shader.version_free(bokeh.shader_version);
+ }
+}
+
+void BokehDOF::bokeh_dof_compute(const BokehBuffers &p_buffers, bool p_dof_far, float p_dof_far_begin, float p_dof_far_size, bool p_dof_near, float p_dof_near_begin, float p_dof_near_size, float p_bokeh_size, RenderingServer::DOFBokehShape p_bokeh_shape, RS::DOFBlurQuality p_quality, bool p_use_jitter, float p_cam_znear, float p_cam_zfar, bool p_cam_orthogonal) {
+ ERR_FAIL_COND_MSG(prefer_raster_effects, "Can't use compute version of bokeh depth of field with the mobile renderer.");
+
+ UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
+ ERR_FAIL_NULL(uniform_set_cache);
+ MaterialStorage *material_storage = MaterialStorage::get_singleton();
+ ERR_FAIL_NULL(material_storage);
+
+ // setup our push constant
+ memset(&bokeh.push_constant, 0, sizeof(BokehPushConstant));
+ bokeh.push_constant.blur_far_active = p_dof_far;
+ bokeh.push_constant.blur_far_begin = p_dof_far_begin;
+ bokeh.push_constant.blur_far_end = p_dof_far_begin + p_dof_far_size;
+
+ bokeh.push_constant.blur_near_active = p_dof_near;
+ bokeh.push_constant.blur_near_begin = p_dof_near_begin;
+ bokeh.push_constant.blur_near_end = MAX(0, p_dof_near_begin - p_dof_near_size);
+ bokeh.push_constant.use_jitter = p_use_jitter;
+ bokeh.push_constant.jitter_seed = Math::randf() * 1000.0;
+
+ bokeh.push_constant.z_near = p_cam_znear;
+ bokeh.push_constant.z_far = p_cam_zfar;
+ bokeh.push_constant.orthogonal = p_cam_orthogonal;
+ bokeh.push_constant.blur_size = p_bokeh_size;
+
+ bokeh.push_constant.second_pass = false;
+ bokeh.push_constant.half_size = false;
+
+ bokeh.push_constant.blur_scale = 0.5;
+
+ // setup our uniforms
+ RID default_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
+
+ RD::Uniform u_base_texture(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_buffers.base_texture }));
+ RD::Uniform u_depth_texture(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_buffers.depth_texture }));
+ RD::Uniform u_secondary_texture(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_buffers.secondary_texture }));
+ RD::Uniform u_half_texture0(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_buffers.half_texture[0] }));
+ RD::Uniform u_half_texture1(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_buffers.half_texture[1] }));
+
+ RD::Uniform u_base_image(RD::UNIFORM_TYPE_IMAGE, 0, p_buffers.base_texture);
+ RD::Uniform u_secondary_image(RD::UNIFORM_TYPE_IMAGE, 0, p_buffers.secondary_texture);
+ RD::Uniform u_half_image0(RD::UNIFORM_TYPE_IMAGE, 0, p_buffers.half_texture[0]);
+ RD::Uniform u_half_image1(RD::UNIFORM_TYPE_IMAGE, 0, p_buffers.half_texture[1]);
+
+ RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
+
+ /* FIRST PASS */
+ // The alpha channel of the source color texture is filled with the expected circle size
+ // If used for DOF far, the size is positive, if used for near, its negative.
+
+ RID shader = bokeh.compute_shader.version_get_shader(bokeh.shader_version, BOKEH_GEN_BLUR_SIZE);
+ ERR_FAIL_COND(shader.is_null());
+
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, bokeh.compute_pipelines[BOKEH_GEN_BLUR_SIZE]);
+
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_base_image), 0);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_depth_texture), 1);
+
+ bokeh.push_constant.size[0] = p_buffers.base_texture_size.x;
+ bokeh.push_constant.size[1] = p_buffers.base_texture_size.y;
+
+ RD::get_singleton()->compute_list_set_push_constant(compute_list, &bokeh.push_constant, sizeof(BokehPushConstant));
+
+ RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_buffers.base_texture_size.x, p_buffers.base_texture_size.y, 1);
+ RD::get_singleton()->compute_list_add_barrier(compute_list);
+
+ if (p_bokeh_shape == RS::DOF_BOKEH_BOX || p_bokeh_shape == RS::DOF_BOKEH_HEXAGON) {
+ //second pass
+ BokehMode mode = p_bokeh_shape == RS::DOF_BOKEH_BOX ? BOKEH_GEN_BOKEH_BOX : BOKEH_GEN_BOKEH_HEXAGONAL;
+ shader = bokeh.compute_shader.version_get_shader(bokeh.shader_version, mode);
+ ERR_FAIL_COND(shader.is_null());
+
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, bokeh.compute_pipelines[mode]);
+
+ static const int quality_samples[4] = { 6, 12, 12, 24 };
+
+ bokeh.push_constant.steps = quality_samples[p_quality];
+
+ if (p_quality == RS::DOF_BLUR_QUALITY_VERY_LOW || p_quality == RS::DOF_BLUR_QUALITY_LOW) {
+ //box and hexagon are more or less the same, and they can work in either half (very low and low quality) or full (medium and high quality_ sizes)
+
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_half_image0), 0);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_base_texture), 1);
+
+ bokeh.push_constant.size[0] = p_buffers.base_texture_size.x >> 1;
+ bokeh.push_constant.size[1] = p_buffers.base_texture_size.y >> 1;
+ bokeh.push_constant.half_size = true;
+ bokeh.push_constant.blur_size *= 0.5;
+
+ } else {
+ //medium and high quality use full size
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_secondary_image), 0);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_base_texture), 1);
+ }
+
+ RD::get_singleton()->compute_list_set_push_constant(compute_list, &bokeh.push_constant, sizeof(BokehPushConstant));
+
+ RD::get_singleton()->compute_list_dispatch_threads(compute_list, bokeh.push_constant.size[0], bokeh.push_constant.size[1], 1);
+ RD::get_singleton()->compute_list_add_barrier(compute_list);
+
+ //third pass
+ bokeh.push_constant.second_pass = true;
+
+ if (p_quality == RS::DOF_BLUR_QUALITY_VERY_LOW || p_quality == RS::DOF_BLUR_QUALITY_LOW) {
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_half_image1), 0);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_half_texture0), 1);
+ } else {
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_base_image), 0);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_secondary_texture), 1);
+ }
+
+ RD::get_singleton()->compute_list_set_push_constant(compute_list, &bokeh.push_constant, sizeof(BokehPushConstant));
+
+ RD::get_singleton()->compute_list_dispatch_threads(compute_list, bokeh.push_constant.size[0], bokeh.push_constant.size[1], 1);
+ RD::get_singleton()->compute_list_add_barrier(compute_list);
+
+ if (p_quality == RS::DOF_BLUR_QUALITY_VERY_LOW || p_quality == RS::DOF_BLUR_QUALITY_LOW) {
+ //forth pass, upscale for low quality
+
+ shader = bokeh.compute_shader.version_get_shader(bokeh.shader_version, BOKEH_COMPOSITE);
+ ERR_FAIL_COND(shader.is_null());
+
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, bokeh.compute_pipelines[BOKEH_COMPOSITE]);
+
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_base_image), 0);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_half_texture1), 1);
+
+ bokeh.push_constant.size[0] = p_buffers.base_texture_size.x;
+ bokeh.push_constant.size[1] = p_buffers.base_texture_size.y;
+ bokeh.push_constant.half_size = false;
+ bokeh.push_constant.second_pass = false;
+
+ RD::get_singleton()->compute_list_set_push_constant(compute_list, &bokeh.push_constant, sizeof(BokehPushConstant));
+
+ RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_buffers.base_texture_size.x, p_buffers.base_texture_size.y, 1);
+ }
+ } else {
+ //circle
+
+ shader = bokeh.compute_shader.version_get_shader(bokeh.shader_version, BOKEH_GEN_BOKEH_CIRCULAR);
+ ERR_FAIL_COND(shader.is_null());
+
+ //second pass
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, bokeh.compute_pipelines[BOKEH_GEN_BOKEH_CIRCULAR]);
+
+ static const float quality_scale[4] = { 8.0, 4.0, 1.0, 0.5 };
+
+ bokeh.push_constant.steps = 0;
+ bokeh.push_constant.blur_scale = quality_scale[p_quality];
+
+ //circle always runs in half size, otherwise too expensive
+
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_half_image0), 0);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_base_texture), 1);
+
+ bokeh.push_constant.size[0] = p_buffers.base_texture_size.x >> 1;
+ bokeh.push_constant.size[1] = p_buffers.base_texture_size.y >> 1;
+ bokeh.push_constant.half_size = true;
+
+ RD::get_singleton()->compute_list_set_push_constant(compute_list, &bokeh.push_constant, sizeof(BokehPushConstant));
+
+ RD::get_singleton()->compute_list_dispatch_threads(compute_list, bokeh.push_constant.size[0], bokeh.push_constant.size[1], 1);
+ RD::get_singleton()->compute_list_add_barrier(compute_list);
+
+ //circle is just one pass, then upscale
+
+ // upscale
+
+ shader = bokeh.compute_shader.version_get_shader(bokeh.shader_version, BOKEH_COMPOSITE);
+ ERR_FAIL_COND(shader.is_null());
+
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, bokeh.compute_pipelines[BOKEH_COMPOSITE]);
+
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_base_image), 0);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_half_texture0), 1);
+
+ bokeh.push_constant.size[0] = p_buffers.base_texture_size.x;
+ bokeh.push_constant.size[1] = p_buffers.base_texture_size.y;
+ bokeh.push_constant.half_size = false;
+ bokeh.push_constant.second_pass = false;
+
+ RD::get_singleton()->compute_list_set_push_constant(compute_list, &bokeh.push_constant, sizeof(BokehPushConstant));
+
+ RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_buffers.base_texture_size.x, p_buffers.base_texture_size.y, 1);
+ }
+
+ RD::get_singleton()->compute_list_end();
+}
+
+void BokehDOF::bokeh_dof_raster(const BokehBuffers &p_buffers, bool p_dof_far, float p_dof_far_begin, float p_dof_far_size, bool p_dof_near, float p_dof_near_begin, float p_dof_near_size, float p_dof_blur_amount, RenderingServer::DOFBokehShape p_bokeh_shape, RS::DOFBlurQuality p_quality, float p_cam_znear, float p_cam_zfar, bool p_cam_orthogonal) {
+ ERR_FAIL_COND_MSG(!prefer_raster_effects, "Can't blur-based depth of field with the clustered renderer.");
+
+ UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
+ ERR_FAIL_NULL(uniform_set_cache);
+ MaterialStorage *material_storage = MaterialStorage::get_singleton();
+ ERR_FAIL_NULL(material_storage);
+
+ // setup our base push constant
+ memset(&bokeh.push_constant, 0, sizeof(BokehPushConstant));
+
+ bokeh.push_constant.orthogonal = p_cam_orthogonal;
+ bokeh.push_constant.size[0] = p_buffers.base_texture_size.width;
+ bokeh.push_constant.size[1] = p_buffers.base_texture_size.height;
+ bokeh.push_constant.z_far = p_cam_zfar;
+ bokeh.push_constant.z_near = p_cam_znear;
+
+ bokeh.push_constant.second_pass = false;
+ bokeh.push_constant.half_size = false;
+ bokeh.push_constant.blur_size = p_dof_blur_amount;
+
+ // setup our uniforms
+ RID default_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
+
+ RD::Uniform u_base_texture(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_buffers.base_texture }));
+ RD::Uniform u_depth_texture(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_buffers.depth_texture }));
+ RD::Uniform u_secondary_texture(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_buffers.secondary_texture }));
+ RD::Uniform u_half_texture0(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_buffers.half_texture[0] }));
+ RD::Uniform u_half_texture1(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_buffers.half_texture[1] }));
+ RD::Uniform u_weight_texture0(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_buffers.weight_texture[0] }));
+ RD::Uniform u_weight_texture1(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_buffers.weight_texture[1] }));
+ RD::Uniform u_weight_texture2(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_buffers.weight_texture[2] }));
+ RD::Uniform u_weight_texture3(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_buffers.weight_texture[3] }));
+
+ if (p_dof_far || p_dof_near) {
+ if (p_dof_far) {
+ bokeh.push_constant.blur_far_active = true;
+ bokeh.push_constant.blur_far_begin = p_dof_far_begin;
+ bokeh.push_constant.blur_far_end = p_dof_far_begin + p_dof_far_size;
+ }
+
+ if (p_dof_near) {
+ bokeh.push_constant.blur_near_active = true;
+ bokeh.push_constant.blur_near_begin = p_dof_near_begin;
+ bokeh.push_constant.blur_near_end = p_dof_near_begin - p_dof_near_size;
+ }
+
+ {
+ // generate our depth data
+ RID shader = bokeh.raster_shader.version_get_shader(bokeh.shader_version, BOKEH_GEN_BLUR_SIZE);
+ ERR_FAIL_COND(shader.is_null());
+
+ RID framebuffer = p_buffers.base_weight_fb;
+ RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD);
+ RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, bokeh.raster_pipelines[BOKEH_GEN_BLUR_SIZE].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(framebuffer)));
+ RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_depth_texture), 0);
+ RD::get_singleton()->draw_list_bind_index_array(draw_list, material_storage->get_quad_index_array());
+
+ RD::get_singleton()->draw_list_set_push_constant(draw_list, &bokeh.push_constant, sizeof(BokehPushConstant));
+
+ RD::get_singleton()->draw_list_draw(draw_list, true);
+ RD::get_singleton()->draw_list_end();
+ }
+
+ if (p_bokeh_shape == RS::DOF_BOKEH_BOX || p_bokeh_shape == RS::DOF_BOKEH_HEXAGON) {
+ // double pass approach
+ BokehMode mode = p_bokeh_shape == RS::DOF_BOKEH_BOX ? BOKEH_GEN_BOKEH_BOX : BOKEH_GEN_BOKEH_HEXAGONAL;
+
+ RID shader = bokeh.raster_shader.version_get_shader(bokeh.shader_version, mode);
+ ERR_FAIL_COND(shader.is_null());
+
+ if (p_quality == RS::DOF_BLUR_QUALITY_VERY_LOW || p_quality == RS::DOF_BLUR_QUALITY_LOW) {
+ //box and hexagon are more or less the same, and they can work in either half (very low and low quality) or full (medium and high quality_ sizes)
+ bokeh.push_constant.size[0] = p_buffers.base_texture_size.x >> 1;
+ bokeh.push_constant.size[1] = p_buffers.base_texture_size.y >> 1;
+ bokeh.push_constant.half_size = true;
+ bokeh.push_constant.blur_size *= 0.5;
+ }
+
+ static const int quality_samples[4] = { 6, 12, 12, 24 };
+ bokeh.push_constant.blur_scale = 0.5;
+ bokeh.push_constant.steps = quality_samples[p_quality];
+
+ RID framebuffer = bokeh.push_constant.half_size ? p_buffers.half_fb[0] : p_buffers.secondary_fb;
+
+ // Pass 1
+ RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD);
+ RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, bokeh.raster_pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(framebuffer)));
+ RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_base_texture), 0);
+ RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 1, u_weight_texture0), 1);
+ RD::get_singleton()->draw_list_bind_index_array(draw_list, material_storage->get_quad_index_array());
+
+ RD::get_singleton()->draw_list_set_push_constant(draw_list, &bokeh.push_constant, sizeof(BokehPushConstant));
+
+ RD::get_singleton()->draw_list_draw(draw_list, true);
+ RD::get_singleton()->draw_list_end();
+
+ // Pass 2
+ if (!bokeh.push_constant.half_size) {
+ // do not output weight, we're writing back into our base buffer
+ mode = p_bokeh_shape == RS::DOF_BOKEH_BOX ? BOKEH_GEN_BOKEH_BOX_NOWEIGHT : BOKEH_GEN_BOKEH_HEXAGONAL_NOWEIGHT;
+
+ shader = bokeh.raster_shader.version_get_shader(bokeh.shader_version, mode);
+ ERR_FAIL_COND(shader.is_null());
+ }
+ bokeh.push_constant.second_pass = true;
+
+ framebuffer = bokeh.push_constant.half_size ? p_buffers.half_fb[1] : p_buffers.base_fb;
+ RD::Uniform texture = bokeh.push_constant.half_size ? u_half_texture0 : u_secondary_texture;
+ RD::Uniform weight = bokeh.push_constant.half_size ? u_weight_texture2 : u_weight_texture1;
+
+ draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD);
+ RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, bokeh.raster_pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(framebuffer)));
+ RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, texture), 0);
+ RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 1, weight), 1);
+ RD::get_singleton()->draw_list_bind_index_array(draw_list, material_storage->get_quad_index_array());
+
+ RD::get_singleton()->draw_list_set_push_constant(draw_list, &bokeh.push_constant, sizeof(BokehPushConstant));
+
+ RD::get_singleton()->draw_list_draw(draw_list, true);
+ RD::get_singleton()->draw_list_end();
+
+ if (bokeh.push_constant.half_size) {
+ // Compose pass
+ mode = BOKEH_COMPOSITE;
+ shader = bokeh.raster_shader.version_get_shader(bokeh.shader_version, mode);
+ ERR_FAIL_COND(shader.is_null());
+
+ framebuffer = p_buffers.base_fb;
+
+ draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD);
+ RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, bokeh.raster_pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(framebuffer)));
+ RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_half_texture1), 0);
+ RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 1, u_weight_texture3), 1);
+ RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 2, u_weight_texture0), 2);
+ RD::get_singleton()->draw_list_bind_index_array(draw_list, material_storage->get_quad_index_array());
+
+ RD::get_singleton()->draw_list_set_push_constant(draw_list, &bokeh.push_constant, sizeof(BokehPushConstant));
+
+ RD::get_singleton()->draw_list_draw(draw_list, true);
+ RD::get_singleton()->draw_list_end();
+ }
+
+ } else {
+ // circular is a single pass approach
+ BokehMode mode = BOKEH_GEN_BOKEH_CIRCULAR;
+
+ RID shader = bokeh.raster_shader.version_get_shader(bokeh.shader_version, mode);
+ ERR_FAIL_COND(shader.is_null());
+
+ {
+ // circle always runs in half size, otherwise too expensive (though the code below does support making this optional)
+ bokeh.push_constant.size[0] = p_buffers.base_texture_size.x >> 1;
+ bokeh.push_constant.size[1] = p_buffers.base_texture_size.y >> 1;
+ bokeh.push_constant.half_size = true;
+ // bokeh.push_constant.blur_size *= 0.5;
+ }
+
+ static const float quality_scale[4] = { 8.0, 4.0, 1.0, 0.5 };
+ bokeh.push_constant.blur_scale = quality_scale[p_quality];
+ bokeh.push_constant.steps = 0.0;
+
+ RID framebuffer = bokeh.push_constant.half_size ? p_buffers.half_fb[0] : p_buffers.secondary_fb;
+
+ RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD);
+ RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, bokeh.raster_pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(framebuffer)));
+ RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_base_texture), 0);
+ RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 1, u_weight_texture0), 1);
+ RD::get_singleton()->draw_list_bind_index_array(draw_list, material_storage->get_quad_index_array());
+
+ RD::get_singleton()->draw_list_set_push_constant(draw_list, &bokeh.push_constant, sizeof(BokehPushConstant));
+
+ RD::get_singleton()->draw_list_draw(draw_list, true);
+ RD::get_singleton()->draw_list_end();
+
+ if (bokeh.push_constant.half_size) {
+ // Compose
+ mode = BOKEH_COMPOSITE;
+ shader = bokeh.raster_shader.version_get_shader(bokeh.shader_version, mode);
+ ERR_FAIL_COND(shader.is_null());
+
+ framebuffer = p_buffers.base_fb;
+
+ draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD);
+ RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, bokeh.raster_pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(framebuffer)));
+ RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_half_texture0), 0);
+ RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 1, u_weight_texture2), 1);
+ RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 2, u_weight_texture0), 2);
+ RD::get_singleton()->draw_list_bind_index_array(draw_list, material_storage->get_quad_index_array());
+
+ RD::get_singleton()->draw_list_set_push_constant(draw_list, &bokeh.push_constant, sizeof(BokehPushConstant));
+
+ RD::get_singleton()->draw_list_draw(draw_list, true);
+ RD::get_singleton()->draw_list_end();
+ } else {
+ CopyEffects::get_singleton()->copy_raster(p_buffers.secondary_texture, p_buffers.base_fb);
+ }
+ }
+ }
+}
diff --git a/servers/rendering/renderer_rd/effects/bokeh_dof.h b/servers/rendering/renderer_rd/effects/bokeh_dof.h
new file mode 100644
index 0000000000..d7b736119c
--- /dev/null
+++ b/servers/rendering/renderer_rd/effects/bokeh_dof.h
@@ -0,0 +1,120 @@
+/*************************************************************************/
+/* bokeh_dof.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. */
+/*************************************************************************/
+
+#ifndef BOKEH_DOF_RD_H
+#define BOKEH_DOF_RD_H
+
+#include "servers/rendering/renderer_rd/pipeline_cache_rd.h"
+#include "servers/rendering/renderer_rd/shaders/effects/bokeh_dof.glsl.gen.h"
+#include "servers/rendering/renderer_rd/shaders/effects/bokeh_dof_raster.glsl.gen.h"
+#include "servers/rendering/renderer_scene_render.h"
+
+#include "servers/rendering_server.h"
+
+namespace RendererRD {
+
+class BokehDOF {
+private:
+ bool prefer_raster_effects;
+
+ struct BokehPushConstant {
+ uint32_t size[2];
+ float z_far;
+ float z_near;
+
+ uint32_t orthogonal;
+ float blur_size;
+ float blur_scale;
+ uint32_t steps;
+
+ uint32_t blur_near_active;
+ float blur_near_begin;
+ float blur_near_end;
+ uint32_t blur_far_active;
+
+ float blur_far_begin;
+ float blur_far_end;
+ uint32_t second_pass;
+ uint32_t half_size;
+
+ uint32_t use_jitter;
+ float jitter_seed;
+ uint32_t pad[2];
+ };
+
+ enum BokehMode {
+ BOKEH_GEN_BLUR_SIZE,
+ BOKEH_GEN_BOKEH_BOX,
+ BOKEH_GEN_BOKEH_BOX_NOWEIGHT,
+ BOKEH_GEN_BOKEH_HEXAGONAL,
+ BOKEH_GEN_BOKEH_HEXAGONAL_NOWEIGHT,
+ BOKEH_GEN_BOKEH_CIRCULAR,
+ BOKEH_COMPOSITE,
+ BOKEH_MAX
+ };
+
+ struct Bokeh {
+ BokehPushConstant push_constant;
+ BokehDofShaderRD compute_shader;
+ BokehDofRasterShaderRD raster_shader;
+ RID shader_version;
+ RID compute_pipelines[BOKEH_MAX];
+ PipelineCacheRD raster_pipelines[BOKEH_MAX];
+ } bokeh;
+
+public:
+ struct BokehBuffers {
+ // bokeh buffers
+
+ // textures
+ Size2i base_texture_size;
+ RID base_texture;
+ RID depth_texture;
+ RID secondary_texture;
+ RID half_texture[2];
+
+ // raster only
+ RID base_fb;
+ RID secondary_fb; // with weights
+ RID half_fb[2]; // with weights
+ RID base_weight_fb;
+ RID weight_texture[4];
+ };
+
+ BokehDOF(bool p_prefer_raster_effects);
+ ~BokehDOF();
+
+ void bokeh_dof_compute(const BokehBuffers &p_buffers, bool p_dof_far, float p_dof_far_begin, float p_dof_far_size, bool p_dof_near, float p_dof_near_begin, float p_dof_near_size, float p_bokeh_size, RS::DOFBokehShape p_bokeh_shape, RS::DOFBlurQuality p_quality, bool p_use_jitter, float p_cam_znear, float p_cam_zfar, bool p_cam_orthogonal);
+ void bokeh_dof_raster(const BokehBuffers &p_buffers, bool p_dof_far, float p_dof_far_begin, float p_dof_far_size, bool p_dof_near, float p_dof_near_begin, float p_dof_near_size, float p_dof_blur_amount, RenderingServer::DOFBokehShape p_bokeh_shape, RS::DOFBlurQuality p_quality, float p_cam_znear, float p_cam_zfar, bool p_cam_orthogonal);
+};
+
+} // namespace RendererRD
+
+#endif // !BOKEH_DOF_RD_H
diff --git a/servers/rendering/renderer_rd/effects/copy_effects.cpp b/servers/rendering/renderer_rd/effects/copy_effects.cpp
new file mode 100644
index 0000000000..6b786fdf16
--- /dev/null
+++ b/servers/rendering/renderer_rd/effects/copy_effects.cpp
@@ -0,0 +1,683 @@
+/*************************************************************************/
+/* copy_effects.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. */
+/*************************************************************************/
+
+#include "copy_effects.h"
+#include "servers/rendering/renderer_rd/renderer_compositor_rd.h"
+#include "servers/rendering/renderer_rd/storage_rd/material_storage.h"
+#include "servers/rendering/renderer_rd/uniform_set_cache_rd.h"
+
+using namespace RendererRD;
+
+CopyEffects *CopyEffects::singleton = nullptr;
+
+CopyEffects *CopyEffects::get_singleton() {
+ return singleton;
+}
+
+CopyEffects::CopyEffects(bool p_prefer_raster_effects) {
+ singleton = this;
+ prefer_raster_effects = p_prefer_raster_effects;
+
+ if (prefer_raster_effects) {
+ // init blur shader (on compute use copy shader)
+
+ Vector<String> blur_modes;
+ blur_modes.push_back("\n#define MODE_MIPMAP\n"); // BLUR_MIPMAP
+ blur_modes.push_back("\n#define MODE_GAUSSIAN_BLUR\n"); // BLUR_MODE_GAUSSIAN_BLUR
+ blur_modes.push_back("\n#define MODE_GAUSSIAN_GLOW\n"); // BLUR_MODE_GAUSSIAN_GLOW
+ blur_modes.push_back("\n#define MODE_GAUSSIAN_GLOW\n#define GLOW_USE_AUTO_EXPOSURE\n"); // BLUR_MODE_GAUSSIAN_GLOW_AUTO_EXPOSURE
+ blur_modes.push_back("\n#define MODE_COPY\n"); // BLUR_MODE_COPY
+
+ blur_raster.shader.initialize(blur_modes);
+ memset(&blur_raster.push_constant, 0, sizeof(BlurRasterPushConstant));
+ blur_raster.shader_version = blur_raster.shader.version_create();
+
+ for (int i = 0; i < BLUR_MODE_MAX; i++) {
+ blur_raster.pipelines[i].setup(blur_raster.shader.version_get_shader(blur_raster.shader_version, i), RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), RD::PipelineColorBlendState::create_disabled(), 0);
+ }
+
+ } else {
+ // not used in clustered
+ for (int i = 0; i < BLUR_MODE_MAX; i++) {
+ blur_raster.pipelines[i].clear();
+ }
+
+ Vector<String> copy_modes;
+ copy_modes.push_back("\n#define MODE_GAUSSIAN_BLUR\n");
+ copy_modes.push_back("\n#define MODE_GAUSSIAN_BLUR\n#define DST_IMAGE_8BIT\n");
+ copy_modes.push_back("\n#define MODE_GAUSSIAN_BLUR\n#define MODE_GLOW\n");
+ copy_modes.push_back("\n#define MODE_GAUSSIAN_BLUR\n#define MODE_GLOW\n#define GLOW_USE_AUTO_EXPOSURE\n");
+ copy_modes.push_back("\n#define MODE_SIMPLE_COPY\n");
+ copy_modes.push_back("\n#define MODE_SIMPLE_COPY\n#define DST_IMAGE_8BIT\n");
+ copy_modes.push_back("\n#define MODE_SIMPLE_COPY_DEPTH\n");
+ copy_modes.push_back("\n#define MODE_SET_COLOR\n");
+ copy_modes.push_back("\n#define MODE_SET_COLOR\n#define DST_IMAGE_8BIT\n");
+ copy_modes.push_back("\n#define MODE_MIPMAP\n");
+ copy_modes.push_back("\n#define MODE_LINEARIZE_DEPTH_COPY\n");
+ copy_modes.push_back("\n#define MODE_CUBEMAP_TO_PANORAMA\n");
+ copy_modes.push_back("\n#define MODE_CUBEMAP_ARRAY_TO_PANORAMA\n");
+
+ copy.shader.initialize(copy_modes);
+ memset(&copy.push_constant, 0, sizeof(CopyPushConstant));
+
+ copy.shader_version = copy.shader.version_create();
+
+ for (int i = 0; i < COPY_MODE_MAX; i++) {
+ if (copy.shader.is_variant_enabled(i)) {
+ copy.pipelines[i] = RD::get_singleton()->compute_pipeline_create(copy.shader.version_get_shader(copy.shader_version, i));
+ }
+ }
+ }
+
+ {
+ Vector<String> copy_modes;
+ copy_modes.push_back("\n");
+ copy_modes.push_back("\n#define MODE_PANORAMA_TO_DP\n");
+ copy_modes.push_back("\n#define MODE_TWO_SOURCES\n");
+ copy_modes.push_back("\n#define MULTIVIEW\n");
+ copy_modes.push_back("\n#define MULTIVIEW\n#define MODE_TWO_SOURCES\n");
+
+ copy_to_fb.shader.initialize(copy_modes);
+
+ if (!RendererCompositorRD::singleton->is_xr_enabled()) {
+ copy_to_fb.shader.set_variant_enabled(COPY_TO_FB_MULTIVIEW, false);
+ copy_to_fb.shader.set_variant_enabled(COPY_TO_FB_MULTIVIEW_WITH_DEPTH, false);
+ }
+
+ copy_to_fb.shader_version = copy_to_fb.shader.version_create();
+
+ //use additive
+
+ for (int i = 0; i < COPY_TO_FB_MAX; i++) {
+ if (copy_to_fb.shader.is_variant_enabled(i)) {
+ copy_to_fb.pipelines[i].setup(copy_to_fb.shader.version_get_shader(copy_to_fb.shader_version, i), RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), RD::PipelineColorBlendState::create_disabled(), 0);
+ } else {
+ copy_to_fb.pipelines[i].clear();
+ }
+ }
+ }
+}
+
+CopyEffects::~CopyEffects() {
+ if (prefer_raster_effects) {
+ blur_raster.shader.version_free(blur_raster.shader_version);
+ } else {
+ copy.shader.version_free(copy.shader_version);
+ }
+
+ copy_to_fb.shader.version_free(copy_to_fb.shader_version);
+
+ singleton = nullptr;
+}
+
+void CopyEffects::copy_to_rect(RID p_source_rd_texture, RID p_dest_texture, const Rect2i &p_rect, bool p_flip_y, bool p_force_luminance, bool p_all_source, bool p_8_bit_dst, bool p_alpha_to_one) {
+ ERR_FAIL_COND_MSG(prefer_raster_effects, "Can't use the compute version of the copy_to_rect shader with the mobile renderer.");
+
+ UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
+ ERR_FAIL_NULL(uniform_set_cache);
+ MaterialStorage *material_storage = MaterialStorage::get_singleton();
+ ERR_FAIL_NULL(material_storage);
+
+ memset(&copy.push_constant, 0, sizeof(CopyPushConstant));
+ if (p_flip_y) {
+ copy.push_constant.flags |= COPY_FLAG_FLIP_Y;
+ }
+
+ if (p_force_luminance) {
+ copy.push_constant.flags |= COPY_FLAG_FORCE_LUMINANCE;
+ }
+
+ if (p_all_source) {
+ copy.push_constant.flags |= COPY_FLAG_ALL_SOURCE;
+ }
+
+ if (p_alpha_to_one) {
+ copy.push_constant.flags |= COPY_FLAG_ALPHA_TO_ONE;
+ }
+
+ copy.push_constant.section[0] = 0;
+ copy.push_constant.section[1] = 0;
+ copy.push_constant.section[2] = p_rect.size.width;
+ copy.push_constant.section[3] = p_rect.size.height;
+ copy.push_constant.target[0] = p_rect.position.x;
+ copy.push_constant.target[1] = p_rect.position.y;
+
+ // setup our uniforms
+ RID default_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
+
+ RD::Uniform u_source_rd_texture(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_source_rd_texture }));
+ RD::Uniform u_dest_texture(RD::UNIFORM_TYPE_IMAGE, 0, p_dest_texture);
+
+ CopyMode mode = p_8_bit_dst ? COPY_MODE_SIMPLY_COPY_8BIT : COPY_MODE_SIMPLY_COPY;
+ RID shader = copy.shader.version_get_shader(copy.shader_version, mode);
+ ERR_FAIL_COND(shader.is_null());
+
+ RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, copy.pipelines[mode]);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_source_rd_texture), 0);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 3, u_dest_texture), 3);
+ RD::get_singleton()->compute_list_set_push_constant(compute_list, &copy.push_constant, sizeof(CopyPushConstant));
+ RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_rect.size.width, p_rect.size.height, 1);
+ RD::get_singleton()->compute_list_end();
+}
+
+void CopyEffects::copy_cubemap_to_panorama(RID p_source_cube, RID p_dest_panorama, const Size2i &p_panorama_size, float p_lod, bool p_is_array) {
+ ERR_FAIL_COND_MSG(prefer_raster_effects, "Can't use the compute version of the copy_cubemap_to_panorama shader with the mobile renderer.");
+
+ UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
+ ERR_FAIL_NULL(uniform_set_cache);
+ MaterialStorage *material_storage = MaterialStorage::get_singleton();
+ ERR_FAIL_NULL(material_storage);
+
+ memset(&copy.push_constant, 0, sizeof(CopyPushConstant));
+
+ copy.push_constant.section[0] = 0;
+ copy.push_constant.section[1] = 0;
+ copy.push_constant.section[2] = p_panorama_size.width;
+ copy.push_constant.section[3] = p_panorama_size.height;
+ copy.push_constant.target[0] = 0;
+ copy.push_constant.target[1] = 0;
+ copy.push_constant.camera_z_far = p_lod;
+
+ // setup our uniforms
+ RID default_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
+
+ RD::Uniform u_source_cube(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_source_cube }));
+ RD::Uniform u_dest_panorama(RD::UNIFORM_TYPE_IMAGE, 0, p_dest_panorama);
+
+ CopyMode mode = p_is_array ? COPY_MODE_CUBE_ARRAY_TO_PANORAMA : COPY_MODE_CUBE_TO_PANORAMA;
+ RID shader = copy.shader.version_get_shader(copy.shader_version, mode);
+ ERR_FAIL_COND(shader.is_null());
+
+ RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, copy.pipelines[mode]);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_source_cube), 0);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 3, u_dest_panorama), 3);
+ RD::get_singleton()->compute_list_set_push_constant(compute_list, &copy.push_constant, sizeof(CopyPushConstant));
+ RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_panorama_size.width, p_panorama_size.height, 1);
+ RD::get_singleton()->compute_list_end();
+}
+
+void CopyEffects::copy_depth_to_rect(RID p_source_rd_texture, RID p_dest_texture, const Rect2i &p_rect, bool p_flip_y) {
+ ERR_FAIL_COND_MSG(prefer_raster_effects, "Can't use the compute version of the copy_depth_to_rect shader with the mobile renderer.");
+
+ UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
+ ERR_FAIL_NULL(uniform_set_cache);
+ MaterialStorage *material_storage = MaterialStorage::get_singleton();
+ ERR_FAIL_NULL(material_storage);
+
+ memset(&copy.push_constant, 0, sizeof(CopyPushConstant));
+ if (p_flip_y) {
+ copy.push_constant.flags |= COPY_FLAG_FLIP_Y;
+ }
+
+ copy.push_constant.section[0] = 0;
+ copy.push_constant.section[1] = 0;
+ copy.push_constant.section[2] = p_rect.size.width;
+ copy.push_constant.section[3] = p_rect.size.height;
+ copy.push_constant.target[0] = p_rect.position.x;
+ copy.push_constant.target[1] = p_rect.position.y;
+
+ // setup our uniforms
+ RID default_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
+
+ RD::Uniform u_source_rd_texture(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_source_rd_texture }));
+ RD::Uniform u_dest_texture(RD::UNIFORM_TYPE_IMAGE, 0, p_dest_texture);
+
+ CopyMode mode = COPY_MODE_SIMPLY_COPY_DEPTH;
+ RID shader = copy.shader.version_get_shader(copy.shader_version, mode);
+ ERR_FAIL_COND(shader.is_null());
+
+ RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, copy.pipelines[mode]);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_source_rd_texture), 0);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 3, u_dest_texture), 3);
+ RD::get_singleton()->compute_list_set_push_constant(compute_list, &copy.push_constant, sizeof(CopyPushConstant));
+ RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_rect.size.width, p_rect.size.height, 1);
+ RD::get_singleton()->compute_list_end();
+}
+
+void CopyEffects::copy_depth_to_rect_and_linearize(RID p_source_rd_texture, RID p_dest_texture, const Rect2i &p_rect, bool p_flip_y, float p_z_near, float p_z_far) {
+ ERR_FAIL_COND_MSG(prefer_raster_effects, "Can't use the compute version of the copy_depth_to_rect_and_linearize shader with the mobile renderer.");
+
+ UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
+ ERR_FAIL_NULL(uniform_set_cache);
+ MaterialStorage *material_storage = MaterialStorage::get_singleton();
+ ERR_FAIL_NULL(material_storage);
+
+ memset(&copy.push_constant, 0, sizeof(CopyPushConstant));
+ if (p_flip_y) {
+ copy.push_constant.flags |= COPY_FLAG_FLIP_Y;
+ }
+
+ copy.push_constant.section[0] = 0;
+ copy.push_constant.section[1] = 0;
+ copy.push_constant.section[2] = p_rect.size.width;
+ copy.push_constant.section[3] = p_rect.size.height;
+ copy.push_constant.target[0] = p_rect.position.x;
+ copy.push_constant.target[1] = p_rect.position.y;
+ copy.push_constant.camera_z_far = p_z_far;
+ copy.push_constant.camera_z_near = p_z_near;
+
+ // setup our uniforms
+ RID default_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
+
+ RD::Uniform u_source_rd_texture(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_source_rd_texture }));
+ RD::Uniform u_dest_texture(RD::UNIFORM_TYPE_IMAGE, 0, p_dest_texture);
+
+ CopyMode mode = COPY_MODE_LINEARIZE_DEPTH;
+ RID shader = copy.shader.version_get_shader(copy.shader_version, mode);
+ ERR_FAIL_COND(shader.is_null());
+
+ RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, copy.pipelines[mode]);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_source_rd_texture), 0);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 3, u_dest_texture), 3);
+ RD::get_singleton()->compute_list_set_push_constant(compute_list, &copy.push_constant, sizeof(CopyPushConstant));
+ RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_rect.size.width, p_rect.size.height, 1);
+ RD::get_singleton()->compute_list_end();
+}
+
+void CopyEffects::copy_to_atlas_fb(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2 &p_uv_rect, RD::DrawListID p_draw_list, bool p_flip_y, bool p_panorama) {
+ UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
+ ERR_FAIL_NULL(uniform_set_cache);
+ MaterialStorage *material_storage = MaterialStorage::get_singleton();
+ ERR_FAIL_NULL(material_storage);
+
+ memset(&copy_to_fb.push_constant, 0, sizeof(CopyToFbPushConstant));
+
+ copy_to_fb.push_constant.use_section = true;
+ copy_to_fb.push_constant.section[0] = p_uv_rect.position.x;
+ copy_to_fb.push_constant.section[1] = p_uv_rect.position.y;
+ copy_to_fb.push_constant.section[2] = p_uv_rect.size.x;
+ copy_to_fb.push_constant.section[3] = p_uv_rect.size.y;
+
+ if (p_flip_y) {
+ copy_to_fb.push_constant.flip_y = true;
+ }
+
+ // setup our uniforms
+ RID default_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
+
+ RD::Uniform u_source_rd_texture(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_source_rd_texture }));
+
+ CopyToFBMode mode = p_panorama ? COPY_TO_FB_COPY_PANORAMA_TO_DP : COPY_TO_FB_COPY;
+ RID shader = copy_to_fb.shader.version_get_shader(copy_to_fb.shader_version, mode);
+ ERR_FAIL_COND(shader.is_null());
+
+ RD::DrawListID draw_list = p_draw_list;
+ RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, copy_to_fb.pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_framebuffer)));
+ RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_rd_texture), 0);
+ RD::get_singleton()->draw_list_bind_index_array(draw_list, material_storage->get_quad_index_array());
+ RD::get_singleton()->draw_list_set_push_constant(draw_list, &copy_to_fb.push_constant, sizeof(CopyToFbPushConstant));
+ RD::get_singleton()->draw_list_draw(draw_list, true);
+}
+
+void CopyEffects::copy_to_fb_rect(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2i &p_rect, bool p_flip_y, bool p_force_luminance, bool p_alpha_to_zero, bool p_srgb, RID p_secondary, bool p_multiview) {
+ UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
+ ERR_FAIL_NULL(uniform_set_cache);
+ MaterialStorage *material_storage = MaterialStorage::get_singleton();
+ ERR_FAIL_NULL(material_storage);
+
+ memset(&copy_to_fb.push_constant, 0, sizeof(CopyToFbPushConstant));
+
+ if (p_flip_y) {
+ copy_to_fb.push_constant.flip_y = true;
+ }
+ if (p_force_luminance) {
+ copy_to_fb.push_constant.force_luminance = true;
+ }
+ if (p_alpha_to_zero) {
+ copy_to_fb.push_constant.alpha_to_zero = true;
+ }
+ if (p_srgb) {
+ copy_to_fb.push_constant.srgb = true;
+ }
+
+ // setup our uniforms
+ RID default_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
+
+ RD::Uniform u_source_rd_texture(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_source_rd_texture }));
+
+ CopyToFBMode mode;
+ if (p_multiview) {
+ mode = p_secondary.is_valid() ? COPY_TO_FB_MULTIVIEW_WITH_DEPTH : COPY_TO_FB_MULTIVIEW;
+ } else {
+ mode = p_secondary.is_valid() ? COPY_TO_FB_COPY2 : COPY_TO_FB_COPY;
+ }
+
+ RID shader = copy_to_fb.shader.version_get_shader(copy_to_fb.shader_version, mode);
+ ERR_FAIL_COND(shader.is_null());
+
+ RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD, Vector<Color>(), 1.0, 0, p_rect);
+ RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, copy_to_fb.pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_framebuffer)));
+ RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_rd_texture), 0);
+ if (p_secondary.is_valid()) {
+ // TODO may need to do this differently when reading from depth buffer for multiview
+ RD::Uniform u_secondary(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_secondary }));
+ RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 1, u_secondary), 1);
+ }
+ RD::get_singleton()->draw_list_bind_index_array(draw_list, material_storage->get_quad_index_array());
+ RD::get_singleton()->draw_list_set_push_constant(draw_list, &copy_to_fb.push_constant, sizeof(CopyToFbPushConstant));
+ RD::get_singleton()->draw_list_draw(draw_list, true);
+ RD::get_singleton()->draw_list_end();
+}
+
+void CopyEffects::copy_raster(RID p_source_texture, RID p_dest_framebuffer) {
+ ERR_FAIL_COND_MSG(!prefer_raster_effects, "Can't use the raster version of the copy with the clustered renderer.");
+
+ UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
+ ERR_FAIL_NULL(uniform_set_cache);
+ MaterialStorage *material_storage = MaterialStorage::get_singleton();
+ ERR_FAIL_NULL(material_storage);
+
+ memset(&blur_raster.push_constant, 0, sizeof(BlurRasterPushConstant));
+
+ // setup our uniforms
+ RID default_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
+
+ RD::Uniform u_source_texture(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_source_texture }));
+
+ RID shader = blur_raster.shader.version_get_shader(blur_raster.shader_version, BLUR_MODE_COPY);
+ ERR_FAIL_COND(shader.is_null());
+
+ // Just copy it back (we use our blur raster shader here)..
+ RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD);
+ RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, blur_raster.pipelines[BLUR_MODE_COPY].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_framebuffer)));
+ RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_texture), 0);
+ RD::get_singleton()->draw_list_bind_index_array(draw_list, material_storage->get_quad_index_array());
+
+ memset(&blur_raster.push_constant, 0, sizeof(BlurRasterPushConstant));
+ RD::get_singleton()->draw_list_set_push_constant(draw_list, &blur_raster.push_constant, sizeof(BlurRasterPushConstant));
+
+ RD::get_singleton()->draw_list_draw(draw_list, true);
+ RD::get_singleton()->draw_list_end();
+}
+
+void CopyEffects::gaussian_blur(RID p_source_rd_texture, RID p_texture, const Rect2i &p_region, bool p_8bit_dst) {
+ ERR_FAIL_COND_MSG(prefer_raster_effects, "Can't use the compute version of the gaussian blur with the mobile renderer.");
+
+ UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
+ ERR_FAIL_NULL(uniform_set_cache);
+ MaterialStorage *material_storage = MaterialStorage::get_singleton();
+ ERR_FAIL_NULL(material_storage);
+
+ memset(&copy.push_constant, 0, sizeof(CopyPushConstant));
+
+ copy.push_constant.section[0] = p_region.position.x;
+ copy.push_constant.section[1] = p_region.position.y;
+ copy.push_constant.section[2] = p_region.size.width;
+ copy.push_constant.section[3] = p_region.size.height;
+
+ // setup our uniforms
+ RID default_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
+
+ RD::Uniform u_source_rd_texture(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_source_rd_texture }));
+ RD::Uniform u_texture(RD::UNIFORM_TYPE_IMAGE, 0, p_texture);
+
+ CopyMode mode = p_8bit_dst ? COPY_MODE_GAUSSIAN_COPY_8BIT : COPY_MODE_GAUSSIAN_COPY;
+ RID shader = copy.shader.version_get_shader(copy.shader_version, mode);
+ ERR_FAIL_COND(shader.is_null());
+
+ //HORIZONTAL
+ RD::DrawListID compute_list = RD::get_singleton()->compute_list_begin();
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, copy.pipelines[mode]);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_source_rd_texture), 0);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 3, u_texture), 3);
+
+ RD::get_singleton()->compute_list_set_push_constant(compute_list, &copy.push_constant, sizeof(CopyPushConstant));
+
+ RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_region.size.width, p_region.size.height, 1);
+
+ RD::get_singleton()->compute_list_end();
+}
+
+void CopyEffects::gaussian_glow(RID p_source_rd_texture, RID p_back_texture, const Size2i &p_size, float p_strength, bool p_high_quality, bool p_first_pass, float p_luminance_cap, float p_exposure, float p_bloom, float p_hdr_bleed_threshold, float p_hdr_bleed_scale, RID p_auto_exposure, float p_auto_exposure_grey) {
+ ERR_FAIL_COND_MSG(prefer_raster_effects, "Can't use the compute version of the gaussian glow with the mobile renderer.");
+
+ UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
+ ERR_FAIL_NULL(uniform_set_cache);
+ MaterialStorage *material_storage = MaterialStorage::get_singleton();
+ ERR_FAIL_NULL(material_storage);
+
+ memset(&copy.push_constant, 0, sizeof(CopyPushConstant));
+
+ CopyMode copy_mode = p_first_pass && p_auto_exposure.is_valid() ? COPY_MODE_GAUSSIAN_GLOW_AUTO_EXPOSURE : COPY_MODE_GAUSSIAN_GLOW;
+ uint32_t base_flags = 0;
+
+ copy.push_constant.section[2] = p_size.x;
+ copy.push_constant.section[3] = p_size.y;
+
+ copy.push_constant.glow_strength = p_strength;
+ copy.push_constant.glow_bloom = p_bloom;
+ copy.push_constant.glow_hdr_threshold = p_hdr_bleed_threshold;
+ copy.push_constant.glow_hdr_scale = p_hdr_bleed_scale;
+ copy.push_constant.glow_exposure = p_exposure;
+ copy.push_constant.glow_white = 0; //actually unused
+ copy.push_constant.glow_luminance_cap = p_luminance_cap;
+
+ copy.push_constant.glow_auto_exposure_grey = p_auto_exposure_grey; //unused also
+
+ // setup our uniforms
+ RID default_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
+
+ RD::Uniform u_source_rd_texture(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_source_rd_texture }));
+ RD::Uniform u_back_texture(RD::UNIFORM_TYPE_IMAGE, 0, p_back_texture);
+
+ RID shader = copy.shader.version_get_shader(copy.shader_version, copy_mode);
+ ERR_FAIL_COND(shader.is_null());
+
+ RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, copy.pipelines[copy_mode]);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_source_rd_texture), 0);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 3, u_back_texture), 3);
+ if (p_auto_exposure.is_valid() && p_first_pass) {
+ RD::Uniform u_auto_exposure(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_auto_exposure }));
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_auto_exposure), 1);
+ }
+
+ copy.push_constant.flags = base_flags | (p_first_pass ? COPY_FLAG_GLOW_FIRST_PASS : 0) | (p_high_quality ? COPY_FLAG_HIGH_QUALITY_GLOW : 0);
+ RD::get_singleton()->compute_list_set_push_constant(compute_list, &copy.push_constant, sizeof(CopyPushConstant));
+
+ RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_size.width, p_size.height, 1);
+ RD::get_singleton()->compute_list_end();
+}
+
+void CopyEffects::gaussian_glow_raster(RID p_source_rd_texture, float p_luminance_multiplier, RID p_framebuffer_half, RID p_rd_texture_half, RID p_dest_framebuffer, const Size2i &p_size, float p_strength, bool p_high_quality, bool p_first_pass, float p_luminance_cap, float p_exposure, float p_bloom, float p_hdr_bleed_threshold, float p_hdr_bleed_scale, RID p_auto_exposure, float p_auto_exposure_grey) {
+ ERR_FAIL_COND_MSG(!prefer_raster_effects, "Can't use the raster version of the gaussian glow with the clustered renderer.");
+
+ UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
+ ERR_FAIL_NULL(uniform_set_cache);
+ MaterialStorage *material_storage = MaterialStorage::get_singleton();
+ ERR_FAIL_NULL(material_storage);
+
+ memset(&blur_raster.push_constant, 0, sizeof(BlurRasterPushConstant));
+
+ BlurRasterMode blur_mode = p_first_pass && p_auto_exposure.is_valid() ? BLUR_MODE_GAUSSIAN_GLOW_AUTO_EXPOSURE : BLUR_MODE_GAUSSIAN_GLOW;
+ uint32_t base_flags = 0;
+
+ blur_raster.push_constant.pixel_size[0] = 1.0 / float(p_size.x);
+ blur_raster.push_constant.pixel_size[1] = 1.0 / float(p_size.y);
+
+ blur_raster.push_constant.glow_strength = p_strength;
+ blur_raster.push_constant.glow_bloom = p_bloom;
+ blur_raster.push_constant.glow_hdr_threshold = p_hdr_bleed_threshold;
+ blur_raster.push_constant.glow_hdr_scale = p_hdr_bleed_scale;
+ blur_raster.push_constant.glow_exposure = p_exposure;
+ blur_raster.push_constant.glow_white = 0; //actually unused
+ blur_raster.push_constant.glow_luminance_cap = p_luminance_cap;
+
+ blur_raster.push_constant.glow_auto_exposure_grey = p_auto_exposure_grey; //unused also
+
+ blur_raster.push_constant.luminance_multiplier = p_luminance_multiplier;
+
+ // setup our uniforms
+ RID default_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
+
+ RD::Uniform u_source_rd_texture(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_source_rd_texture }));
+ RD::Uniform u_rd_texture_half(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_rd_texture_half }));
+
+ RID shader = blur_raster.shader.version_get_shader(blur_raster.shader_version, blur_mode);
+ ERR_FAIL_COND(shader.is_null());
+
+ //HORIZONTAL
+ RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_framebuffer_half, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD);
+ RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, blur_raster.pipelines[blur_mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_framebuffer_half)));
+ RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_rd_texture), 0);
+ if (p_auto_exposure.is_valid() && p_first_pass) {
+ RD::Uniform u_auto_exposure(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_auto_exposure }));
+ RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 1, u_auto_exposure), 1);
+ }
+ RD::get_singleton()->draw_list_bind_index_array(draw_list, material_storage->get_quad_index_array());
+
+ blur_raster.push_constant.flags = base_flags | BLUR_FLAG_HORIZONTAL | (p_first_pass ? BLUR_FLAG_GLOW_FIRST_PASS : 0);
+ RD::get_singleton()->draw_list_set_push_constant(draw_list, &blur_raster.push_constant, sizeof(BlurRasterPushConstant));
+
+ RD::get_singleton()->draw_list_draw(draw_list, true);
+ RD::get_singleton()->draw_list_end();
+
+ blur_mode = BLUR_MODE_GAUSSIAN_GLOW;
+
+ shader = blur_raster.shader.version_get_shader(blur_raster.shader_version, blur_mode);
+ ERR_FAIL_COND(shader.is_null());
+
+ //VERTICAL
+ draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD);
+ RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, blur_raster.pipelines[blur_mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_framebuffer)));
+ RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_rd_texture_half), 0);
+ RD::get_singleton()->draw_list_bind_index_array(draw_list, material_storage->get_quad_index_array());
+
+ blur_raster.push_constant.flags = base_flags;
+ RD::get_singleton()->draw_list_set_push_constant(draw_list, &blur_raster.push_constant, sizeof(BlurRasterPushConstant));
+
+ RD::get_singleton()->draw_list_draw(draw_list, true);
+ RD::get_singleton()->draw_list_end();
+}
+
+void CopyEffects::make_mipmap(RID p_source_rd_texture, RID p_dest_texture, const Size2i &p_size) {
+ ERR_FAIL_COND_MSG(prefer_raster_effects, "Can't use the compute version of the make_mipmap shader with the mobile renderer.");
+
+ UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
+ ERR_FAIL_NULL(uniform_set_cache);
+ MaterialStorage *material_storage = MaterialStorage::get_singleton();
+ ERR_FAIL_NULL(material_storage);
+
+ memset(&copy.push_constant, 0, sizeof(CopyPushConstant));
+
+ copy.push_constant.section[0] = 0;
+ copy.push_constant.section[1] = 0;
+ copy.push_constant.section[2] = p_size.width;
+ copy.push_constant.section[3] = p_size.height;
+
+ // setup our uniforms
+ RID default_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
+
+ RD::Uniform u_source_rd_texture(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_source_rd_texture }));
+ RD::Uniform u_dest_texture(RD::UNIFORM_TYPE_IMAGE, 0, p_dest_texture);
+
+ CopyMode mode = COPY_MODE_MIPMAP;
+ RID shader = copy.shader.version_get_shader(copy.shader_version, mode);
+ ERR_FAIL_COND(shader.is_null());
+
+ RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, copy.pipelines[mode]);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_source_rd_texture), 0);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 3, u_dest_texture), 3);
+ RD::get_singleton()->compute_list_set_push_constant(compute_list, &copy.push_constant, sizeof(CopyPushConstant));
+ RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_size.width, p_size.height, 1);
+ RD::get_singleton()->compute_list_end();
+}
+
+void CopyEffects::make_mipmap_raster(RID p_source_rd_texture, RID p_dest_framebuffer, const Size2i &p_size) {
+ ERR_FAIL_COND_MSG(!prefer_raster_effects, "Can't use the raster version of mipmap with the clustered renderer.");
+
+ UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
+ ERR_FAIL_NULL(uniform_set_cache);
+ MaterialStorage *material_storage = MaterialStorage::get_singleton();
+ ERR_FAIL_NULL(material_storage);
+
+ memset(&blur_raster.push_constant, 0, sizeof(BlurRasterPushConstant));
+
+ BlurRasterMode mode = BLUR_MIPMAP;
+
+ blur_raster.push_constant.pixel_size[0] = 1.0 / float(p_size.x);
+ blur_raster.push_constant.pixel_size[1] = 1.0 / float(p_size.y);
+
+ // setup our uniforms
+ RID default_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
+
+ RD::Uniform u_source_rd_texture(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_source_rd_texture }));
+
+ RID shader = blur_raster.shader.version_get_shader(blur_raster.shader_version, mode);
+ ERR_FAIL_COND(shader.is_null());
+
+ RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD);
+ RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, blur_raster.pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_framebuffer)));
+ RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_rd_texture), 0);
+ RD::get_singleton()->draw_list_bind_index_array(draw_list, material_storage->get_quad_index_array());
+ RD::get_singleton()->draw_list_set_push_constant(draw_list, &blur_raster.push_constant, sizeof(BlurRasterPushConstant));
+
+ RD::get_singleton()->draw_list_draw(draw_list, true);
+ RD::get_singleton()->draw_list_end();
+}
+
+void CopyEffects::set_color(RID p_dest_texture, const Color &p_color, const Rect2i &p_region, bool p_8bit_dst) {
+ ERR_FAIL_COND_MSG(prefer_raster_effects, "Can't use the compute version of the set_color shader with the mobile renderer.");
+
+ UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
+ ERR_FAIL_NULL(uniform_set_cache);
+
+ memset(&copy.push_constant, 0, sizeof(CopyPushConstant));
+
+ copy.push_constant.section[0] = 0;
+ copy.push_constant.section[1] = 0;
+ copy.push_constant.section[2] = p_region.size.width;
+ copy.push_constant.section[3] = p_region.size.height;
+ copy.push_constant.target[0] = p_region.position.x;
+ copy.push_constant.target[1] = p_region.position.y;
+ copy.push_constant.set_color[0] = p_color.r;
+ copy.push_constant.set_color[1] = p_color.g;
+ copy.push_constant.set_color[2] = p_color.b;
+ copy.push_constant.set_color[3] = p_color.a;
+
+ // setup our uniforms
+ RD::Uniform u_dest_texture(RD::UNIFORM_TYPE_IMAGE, 0, p_dest_texture);
+
+ CopyMode mode = p_8bit_dst ? COPY_MODE_SET_COLOR_8BIT : COPY_MODE_SET_COLOR;
+ RID shader = copy.shader.version_get_shader(copy.shader_version, mode);
+ ERR_FAIL_COND(shader.is_null());
+
+ RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, copy.pipelines[mode]);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 3, u_dest_texture), 3);
+ RD::get_singleton()->compute_list_set_push_constant(compute_list, &copy.push_constant, sizeof(CopyPushConstant));
+ RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_region.size.width, p_region.size.height, 1);
+ RD::get_singleton()->compute_list_end();
+}
diff --git a/servers/rendering/renderer_rd/effects/copy_effects.h b/servers/rendering/renderer_rd/effects/copy_effects.h
new file mode 100644
index 0000000000..e522408d20
--- /dev/null
+++ b/servers/rendering/renderer_rd/effects/copy_effects.h
@@ -0,0 +1,220 @@
+/*************************************************************************/
+/* copy_effects.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. */
+/*************************************************************************/
+
+#ifndef COPY_RD_H
+#define COPY_RD_H
+
+#include "servers/rendering/renderer_rd/pipeline_cache_rd.h"
+#include "servers/rendering/renderer_rd/shaders/effects/blur_raster.glsl.gen.h"
+#include "servers/rendering/renderer_rd/shaders/effects/copy.glsl.gen.h"
+#include "servers/rendering/renderer_rd/shaders/effects/copy_to_fb.glsl.gen.h"
+#include "servers/rendering/renderer_scene_render.h"
+
+#include "servers/rendering_server.h"
+
+namespace RendererRD {
+
+class CopyEffects {
+private:
+ bool prefer_raster_effects;
+
+ // Blur raster shader
+
+ enum BlurRasterMode {
+ BLUR_MIPMAP,
+
+ BLUR_MODE_GAUSSIAN_BLUR,
+ BLUR_MODE_GAUSSIAN_GLOW,
+ BLUR_MODE_GAUSSIAN_GLOW_AUTO_EXPOSURE,
+ BLUR_MODE_COPY,
+
+ BLUR_MODE_MAX
+ };
+
+ enum {
+ BLUR_FLAG_HORIZONTAL = (1 << 0),
+ BLUR_FLAG_USE_ORTHOGONAL_PROJECTION = (1 << 1),
+ BLUR_FLAG_GLOW_FIRST_PASS = (1 << 2),
+ };
+
+ struct BlurRasterPushConstant {
+ float pixel_size[2];
+ uint32_t flags;
+ uint32_t pad;
+
+ //glow
+ float glow_strength;
+ float glow_bloom;
+ float glow_hdr_threshold;
+ float glow_hdr_scale;
+
+ float glow_exposure;
+ float glow_white;
+ float glow_luminance_cap;
+ float glow_auto_exposure_grey;
+
+ float luminance_multiplier;
+ float res1;
+ float res2;
+ float res3;
+ };
+
+ struct BlurRaster {
+ BlurRasterPushConstant push_constant;
+ BlurRasterShaderRD shader;
+ RID shader_version;
+ PipelineCacheRD pipelines[BLUR_MODE_MAX];
+ } blur_raster;
+
+ // Copy shader
+
+ enum CopyMode {
+ COPY_MODE_GAUSSIAN_COPY,
+ COPY_MODE_GAUSSIAN_COPY_8BIT,
+ COPY_MODE_GAUSSIAN_GLOW,
+ COPY_MODE_GAUSSIAN_GLOW_AUTO_EXPOSURE,
+ COPY_MODE_SIMPLY_COPY,
+ COPY_MODE_SIMPLY_COPY_8BIT,
+ COPY_MODE_SIMPLY_COPY_DEPTH,
+ COPY_MODE_SET_COLOR,
+ COPY_MODE_SET_COLOR_8BIT,
+ COPY_MODE_MIPMAP,
+ COPY_MODE_LINEARIZE_DEPTH,
+ COPY_MODE_CUBE_TO_PANORAMA,
+ COPY_MODE_CUBE_ARRAY_TO_PANORAMA,
+ COPY_MODE_MAX,
+
+ };
+
+ enum {
+ COPY_FLAG_HORIZONTAL = (1 << 0),
+ COPY_FLAG_USE_COPY_SECTION = (1 << 1),
+ COPY_FLAG_USE_ORTHOGONAL_PROJECTION = (1 << 2),
+ COPY_FLAG_DOF_NEAR_FIRST_TAP = (1 << 3),
+ COPY_FLAG_GLOW_FIRST_PASS = (1 << 4),
+ COPY_FLAG_FLIP_Y = (1 << 5),
+ COPY_FLAG_FORCE_LUMINANCE = (1 << 6),
+ COPY_FLAG_ALL_SOURCE = (1 << 7),
+ COPY_FLAG_HIGH_QUALITY_GLOW = (1 << 8),
+ COPY_FLAG_ALPHA_TO_ONE = (1 << 9),
+ };
+
+ struct CopyPushConstant {
+ int32_t section[4];
+ int32_t target[2];
+ uint32_t flags;
+ uint32_t pad;
+ // Glow.
+ float glow_strength;
+ float glow_bloom;
+ float glow_hdr_threshold;
+ float glow_hdr_scale;
+
+ float glow_exposure;
+ float glow_white;
+ float glow_luminance_cap;
+ float glow_auto_exposure_grey;
+ // DOF.
+ float camera_z_far;
+ float camera_z_near;
+ uint32_t pad2[2];
+ //SET color
+ float set_color[4];
+ };
+
+ struct Copy {
+ CopyPushConstant push_constant;
+ CopyShaderRD shader;
+ RID shader_version;
+ RID pipelines[COPY_MODE_MAX];
+
+ } copy;
+
+ // Copy to FB shader
+
+ enum CopyToFBMode {
+ COPY_TO_FB_COPY,
+ COPY_TO_FB_COPY_PANORAMA_TO_DP,
+ COPY_TO_FB_COPY2,
+
+ COPY_TO_FB_MULTIVIEW,
+ COPY_TO_FB_MULTIVIEW_WITH_DEPTH,
+ COPY_TO_FB_MAX,
+ };
+
+ struct CopyToFbPushConstant {
+ float section[4];
+ float pixel_size[2];
+ uint32_t flip_y;
+ uint32_t use_section;
+
+ uint32_t force_luminance;
+ uint32_t alpha_to_zero;
+ uint32_t srgb;
+ uint32_t pad;
+ };
+
+ struct CopyToFb {
+ CopyToFbPushConstant push_constant;
+ CopyToFbShaderRD shader;
+ RID shader_version;
+ PipelineCacheRD pipelines[COPY_TO_FB_MAX];
+
+ } copy_to_fb;
+
+ static CopyEffects *singleton;
+
+public:
+ static CopyEffects *get_singleton();
+
+ CopyEffects(bool p_prefer_raster_effects);
+ ~CopyEffects();
+
+ void copy_to_rect(RID p_source_rd_texture, RID p_dest_texture, const Rect2i &p_rect, bool p_flip_y = false, bool p_force_luminance = false, bool p_all_source = false, bool p_8_bit_dst = false, bool p_alpha_to_one = false);
+ void copy_cubemap_to_panorama(RID p_source_cube, RID p_dest_panorama, const Size2i &p_panorama_size, float p_lod, bool p_is_array);
+ void copy_depth_to_rect(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2i &p_rect, bool p_flip_y = false);
+ void copy_depth_to_rect_and_linearize(RID p_source_rd_texture, RID p_dest_texture, const Rect2i &p_rect, bool p_flip_y, float p_z_near, float p_z_far);
+ void copy_to_fb_rect(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2i &p_rect, bool p_flip_y = false, bool p_force_luminance = false, bool p_alpha_to_zero = false, bool p_srgb = false, RID p_secondary = RID(), bool p_multiview = false);
+ void copy_to_atlas_fb(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2 &p_uv_rect, RD::DrawListID p_draw_list, bool p_flip_y = false, bool p_panorama = false);
+ void copy_raster(RID p_source_texture, RID p_dest_framebuffer);
+
+ void gaussian_blur(RID p_source_rd_texture, RID p_texture, const Rect2i &p_region, bool p_8bit_dst = false);
+ void gaussian_glow(RID p_source_rd_texture, RID p_back_texture, const Size2i &p_size, float p_strength = 1.0, bool p_high_quality = false, bool p_first_pass = false, float p_luminance_cap = 16.0, float p_exposure = 1.0, float p_bloom = 0.0, float p_hdr_bleed_threshold = 1.0, float p_hdr_bleed_scale = 1.0, RID p_auto_exposure = RID(), float p_auto_exposure_grey = 1.0);
+ void gaussian_glow_raster(RID p_source_rd_texture, float p_luminance_multiplier, RID p_framebuffer_half, RID p_rd_texture_half, RID p_dest_framebuffer, const Size2i &p_size, float p_strength = 1.0, bool p_high_quality = false, bool p_first_pass = false, float p_luminance_cap = 16.0, float p_exposure = 1.0, float p_bloom = 0.0, float p_hdr_bleed_threshold = 1.0, float p_hdr_bleed_scale = 1.0, RID p_auto_exposure = RID(), float p_auto_exposure_grey = 1.0);
+
+ void make_mipmap(RID p_source_rd_texture, RID p_dest_texture, const Size2i &p_size);
+ void make_mipmap_raster(RID p_source_rd_texture, RID p_dest_framebuffer, const Size2i &p_size);
+
+ void set_color(RID p_dest_texture, const Color &p_color, const Rect2i &p_region, bool p_8bit_dst = false);
+};
+
+} // namespace RendererRD
+
+#endif // !COPY_RD_H
diff --git a/servers/rendering/renderer_rd/effects/tone_mapper.cpp b/servers/rendering/renderer_rd/effects/tone_mapper.cpp
index e5642116bb..38a4a37b8a 100644
--- a/servers/rendering/renderer_rd/effects/tone_mapper.cpp
+++ b/servers/rendering/renderer_rd/effects/tone_mapper.cpp
@@ -75,28 +75,9 @@ ToneMapper::ToneMapper() {
}
}
}
-
- // TODO maybe centralise this in mesh_storage?
- { //create index array for copy shaders
- Vector<uint8_t> pv;
- pv.resize(6 * 4);
- {
- uint8_t *w = pv.ptrw();
- int *p32 = (int *)w;
- p32[0] = 0;
- p32[1] = 1;
- p32[2] = 2;
- p32[3] = 0;
- p32[4] = 2;
- p32[5] = 3;
- }
- index_buffer = RD::get_singleton()->index_buffer_create(6, RenderingDevice::INDEX_BUFFER_FORMAT_UINT32, pv);
- index_array = RD::get_singleton()->index_array_create(index_buffer, 0, 6);
- }
}
ToneMapper::~ToneMapper() {
- RD::get_singleton()->free(index_buffer); //array gets freed as dependency
tonemap.shader.version_free(tonemap.shader_version);
}
@@ -154,11 +135,7 @@ void ToneMapper::tonemapper(RID p_source_color, RID p_dst_framebuffer, const Ton
RID default_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
RID default_mipmap_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
- RD::Uniform u_source_color;
- u_source_color.uniform_type = RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE;
- u_source_color.binding = 0;
- u_source_color.append_id(default_sampler);
- u_source_color.append_id(p_source_color);
+ RD::Uniform u_source_color(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_source_color }));
RD::Uniform u_exposure_texture;
u_exposure_texture.uniform_type = RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE;
@@ -193,7 +170,7 @@ void ToneMapper::tonemapper(RID p_source_color, RID p_dst_framebuffer, const Ton
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 1, u_exposure_texture), 1);
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 2, u_glow_texture, u_glow_map), 2);
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 3, u_color_correction_texture), 3);
- RD::get_singleton()->draw_list_bind_index_array(draw_list, index_array);
+ RD::get_singleton()->draw_list_bind_index_array(draw_list, material_storage->get_quad_index_array());
RD::get_singleton()->draw_list_set_push_constant(draw_list, &tonemap.push_constant, sizeof(TonemapPushConstant));
RD::get_singleton()->draw_list_draw(draw_list, true);
@@ -273,7 +250,7 @@ void ToneMapper::tonemapper(RD::DrawListID p_subpass_draw_list, RID p_source_col
RD::get_singleton()->draw_list_bind_uniform_set(p_subpass_draw_list, uniform_set_cache->get_cache(shader, 1, u_exposure_texture), 1); // should be set to a default texture, it's ignored
RD::get_singleton()->draw_list_bind_uniform_set(p_subpass_draw_list, uniform_set_cache->get_cache(shader, 2, u_glow_texture, u_glow_map), 2); // should be set to a default texture, it's ignored
RD::get_singleton()->draw_list_bind_uniform_set(p_subpass_draw_list, uniform_set_cache->get_cache(shader, 3, u_color_correction_texture), 3);
- RD::get_singleton()->draw_list_bind_index_array(p_subpass_draw_list, index_array);
+ RD::get_singleton()->draw_list_bind_index_array(p_subpass_draw_list, material_storage->get_quad_index_array());
RD::get_singleton()->draw_list_set_push_constant(p_subpass_draw_list, &tonemap.push_constant, sizeof(TonemapPushConstant));
RD::get_singleton()->draw_list_draw(p_subpass_draw_list, true);
diff --git a/servers/rendering/renderer_rd/effects/tone_mapper.h b/servers/rendering/renderer_rd/effects/tone_mapper.h
index 357e814845..a90849dbeb 100644
--- a/servers/rendering/renderer_rd/effects/tone_mapper.h
+++ b/servers/rendering/renderer_rd/effects/tone_mapper.h
@@ -96,9 +96,6 @@ private:
PipelineCacheRD pipelines[TONEMAP_MODE_MAX];
} tonemap;
- RID index_buffer;
- RID index_array;
-
public:
ToneMapper();
~ToneMapper();
diff --git a/servers/rendering/renderer_rd/effects_rd.cpp b/servers/rendering/renderer_rd/effects_rd.cpp
index f05027d569..774745abdc 100644
--- a/servers/rendering/renderer_rd/effects_rd.cpp
+++ b/servers/rendering/renderer_rd/effects_rd.cpp
@@ -86,7 +86,7 @@ RID EffectsRD::_get_uniform_set_from_texture(RID p_texture, bool p_use_mipmaps)
u.append_id(p_texture);
uniforms.push_back(u);
// anything with the same configuration (one texture in binding 0 for set 0), is good
- RID uniform_set = RD::get_singleton()->uniform_set_create(uniforms, copy_to_fb.shader.version_get_shader(copy_to_fb.shader_version, 0), 0);
+ RID uniform_set = RD::get_singleton()->uniform_set_create(uniforms, cube_to_dp.shader.version_get_shader(cube_to_dp.shader_version, 0), 0);
texture_to_uniform_set_cache[p_texture] = uniform_set;
@@ -252,295 +252,6 @@ void EffectsRD::fsr_upscale(RID p_source_rd_texture, RID p_secondary_texture, RI
RD::get_singleton()->compute_list_end(compute_list);
}
-void EffectsRD::copy_to_atlas_fb(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2 &p_uv_rect, RD::DrawListID p_draw_list, bool p_flip_y, bool p_panorama) {
- memset(&copy_to_fb.push_constant, 0, sizeof(CopyToFbPushConstant));
-
- copy_to_fb.push_constant.use_section = true;
- copy_to_fb.push_constant.section[0] = p_uv_rect.position.x;
- copy_to_fb.push_constant.section[1] = p_uv_rect.position.y;
- copy_to_fb.push_constant.section[2] = p_uv_rect.size.x;
- copy_to_fb.push_constant.section[3] = p_uv_rect.size.y;
-
- if (p_flip_y) {
- copy_to_fb.push_constant.flip_y = true;
- }
-
- RD::DrawListID draw_list = p_draw_list;
- RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, copy_to_fb.pipelines[p_panorama ? COPY_TO_FB_COPY_PANORAMA_TO_DP : COPY_TO_FB_COPY].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_framebuffer)));
- RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_source_rd_texture), 0);
- RD::get_singleton()->draw_list_bind_index_array(draw_list, index_array);
- RD::get_singleton()->draw_list_set_push_constant(draw_list, &copy_to_fb.push_constant, sizeof(CopyToFbPushConstant));
- RD::get_singleton()->draw_list_draw(draw_list, true);
-}
-
-void EffectsRD::copy_to_fb_rect(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2i &p_rect, bool p_flip_y, bool p_force_luminance, bool p_alpha_to_zero, bool p_srgb, RID p_secondary, bool p_multiview) {
- memset(&copy_to_fb.push_constant, 0, sizeof(CopyToFbPushConstant));
-
- if (p_flip_y) {
- copy_to_fb.push_constant.flip_y = true;
- }
- if (p_force_luminance) {
- copy_to_fb.push_constant.force_luminance = true;
- }
- if (p_alpha_to_zero) {
- copy_to_fb.push_constant.alpha_to_zero = true;
- }
- if (p_srgb) {
- copy_to_fb.push_constant.srgb = true;
- }
-
- CopyToFBMode mode;
- if (p_multiview) {
- mode = p_secondary.is_valid() ? COPY_TO_FB_MULTIVIEW_WITH_DEPTH : COPY_TO_FB_MULTIVIEW;
- } else {
- mode = p_secondary.is_valid() ? COPY_TO_FB_COPY2 : COPY_TO_FB_COPY;
- }
-
- RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD, Vector<Color>(), 1.0, 0, p_rect);
- RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, copy_to_fb.pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_framebuffer)));
- RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_source_rd_texture), 0);
- if (p_secondary.is_valid()) {
- // TODO may need to do this differently when reading from depth buffer for multiview
- RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_secondary), 1);
- }
- RD::get_singleton()->draw_list_bind_index_array(draw_list, index_array);
- RD::get_singleton()->draw_list_set_push_constant(draw_list, &copy_to_fb.push_constant, sizeof(CopyToFbPushConstant));
- RD::get_singleton()->draw_list_draw(draw_list, true);
- RD::get_singleton()->draw_list_end();
-}
-
-void EffectsRD::copy_to_rect(RID p_source_rd_texture, RID p_dest_texture, const Rect2i &p_rect, bool p_flip_y, bool p_force_luminance, bool p_all_source, bool p_8_bit_dst, bool p_alpha_to_one) {
- memset(&copy.push_constant, 0, sizeof(CopyPushConstant));
- if (p_flip_y) {
- copy.push_constant.flags |= COPY_FLAG_FLIP_Y;
- }
-
- if (p_force_luminance) {
- copy.push_constant.flags |= COPY_FLAG_FORCE_LUMINANCE;
- }
-
- if (p_all_source) {
- copy.push_constant.flags |= COPY_FLAG_ALL_SOURCE;
- }
-
- if (p_alpha_to_one) {
- copy.push_constant.flags |= COPY_FLAG_ALPHA_TO_ONE;
- }
-
- copy.push_constant.section[0] = 0;
- copy.push_constant.section[1] = 0;
- copy.push_constant.section[2] = p_rect.size.width;
- copy.push_constant.section[3] = p_rect.size.height;
- copy.push_constant.target[0] = p_rect.position.x;
- copy.push_constant.target[1] = p_rect.position.y;
-
- RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
- RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, copy.pipelines[p_8_bit_dst ? COPY_MODE_SIMPLY_COPY_8BIT : COPY_MODE_SIMPLY_COPY]);
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_source_rd_texture), 0);
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_dest_texture), 3);
- RD::get_singleton()->compute_list_set_push_constant(compute_list, &copy.push_constant, sizeof(CopyPushConstant));
- RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_rect.size.width, p_rect.size.height, 1);
- RD::get_singleton()->compute_list_end();
-}
-
-void EffectsRD::copy_cubemap_to_panorama(RID p_source_cube, RID p_dest_panorama, const Size2i &p_panorama_size, float p_lod, bool p_is_array) {
- memset(&copy.push_constant, 0, sizeof(CopyPushConstant));
-
- copy.push_constant.section[0] = 0;
- copy.push_constant.section[1] = 0;
- copy.push_constant.section[2] = p_panorama_size.width;
- copy.push_constant.section[3] = p_panorama_size.height;
- copy.push_constant.target[0] = 0;
- copy.push_constant.target[1] = 0;
- copy.push_constant.camera_z_far = p_lod;
-
- RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
- RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, copy.pipelines[p_is_array ? COPY_MODE_CUBE_ARRAY_TO_PANORAMA : COPY_MODE_CUBE_TO_PANORAMA]);
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_source_cube), 0);
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_dest_panorama), 3);
- RD::get_singleton()->compute_list_set_push_constant(compute_list, &copy.push_constant, sizeof(CopyPushConstant));
- RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_panorama_size.width, p_panorama_size.height, 1);
- RD::get_singleton()->compute_list_end();
-}
-
-void EffectsRD::copy_depth_to_rect_and_linearize(RID p_source_rd_texture, RID p_dest_texture, const Rect2i &p_rect, bool p_flip_y, float p_z_near, float p_z_far) {
- memset(&copy.push_constant, 0, sizeof(CopyPushConstant));
- if (p_flip_y) {
- copy.push_constant.flags |= COPY_FLAG_FLIP_Y;
- }
-
- copy.push_constant.section[0] = 0;
- copy.push_constant.section[1] = 0;
- copy.push_constant.section[2] = p_rect.size.width;
- copy.push_constant.section[3] = p_rect.size.height;
- copy.push_constant.target[0] = p_rect.position.x;
- copy.push_constant.target[1] = p_rect.position.y;
- copy.push_constant.camera_z_far = p_z_far;
- copy.push_constant.camera_z_near = p_z_near;
-
- RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
- RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, copy.pipelines[COPY_MODE_LINEARIZE_DEPTH]);
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_source_rd_texture), 0);
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_dest_texture), 3);
- RD::get_singleton()->compute_list_set_push_constant(compute_list, &copy.push_constant, sizeof(CopyPushConstant));
- RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_rect.size.width, p_rect.size.height, 1);
- RD::get_singleton()->compute_list_end();
-}
-
-void EffectsRD::copy_depth_to_rect(RID p_source_rd_texture, RID p_dest_texture, const Rect2i &p_rect, bool p_flip_y) {
- memset(&copy.push_constant, 0, sizeof(CopyPushConstant));
- if (p_flip_y) {
- copy.push_constant.flags |= COPY_FLAG_FLIP_Y;
- }
-
- copy.push_constant.section[0] = 0;
- copy.push_constant.section[1] = 0;
- copy.push_constant.section[2] = p_rect.size.width;
- copy.push_constant.section[3] = p_rect.size.height;
- copy.push_constant.target[0] = p_rect.position.x;
- copy.push_constant.target[1] = p_rect.position.y;
-
- RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
- RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, copy.pipelines[COPY_MODE_SIMPLY_COPY_DEPTH]);
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_source_rd_texture), 0);
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_dest_texture), 3);
- RD::get_singleton()->compute_list_set_push_constant(compute_list, &copy.push_constant, sizeof(CopyPushConstant));
- RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_rect.size.width, p_rect.size.height, 1);
- RD::get_singleton()->compute_list_end();
-}
-
-void EffectsRD::set_color(RID p_dest_texture, const Color &p_color, const Rect2i &p_region, bool p_8bit_dst) {
- memset(&copy.push_constant, 0, sizeof(CopyPushConstant));
-
- copy.push_constant.section[0] = 0;
- copy.push_constant.section[1] = 0;
- copy.push_constant.section[2] = p_region.size.width;
- copy.push_constant.section[3] = p_region.size.height;
- copy.push_constant.target[0] = p_region.position.x;
- copy.push_constant.target[1] = p_region.position.y;
- copy.push_constant.set_color[0] = p_color.r;
- copy.push_constant.set_color[1] = p_color.g;
- copy.push_constant.set_color[2] = p_color.b;
- copy.push_constant.set_color[3] = p_color.a;
-
- RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
- RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, copy.pipelines[p_8bit_dst ? COPY_MODE_SET_COLOR_8BIT : COPY_MODE_SET_COLOR]);
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_dest_texture), 3);
- RD::get_singleton()->compute_list_set_push_constant(compute_list, &copy.push_constant, sizeof(CopyPushConstant));
- RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_region.size.width, p_region.size.height, 1);
- RD::get_singleton()->compute_list_end();
-}
-
-void EffectsRD::gaussian_blur(RID p_source_rd_texture, RID p_texture, const Rect2i &p_region, bool p_8bit_dst) {
- ERR_FAIL_COND_MSG(prefer_raster_effects, "Can't use the compute version of the gaussian blur with the mobile renderer.");
-
- memset(&copy.push_constant, 0, sizeof(CopyPushConstant));
-
- copy.push_constant.section[0] = p_region.position.x;
- copy.push_constant.section[1] = p_region.position.y;
- copy.push_constant.section[2] = p_region.size.width;
- copy.push_constant.section[3] = p_region.size.height;
-
- //HORIZONTAL
- RD::DrawListID compute_list = RD::get_singleton()->compute_list_begin();
- RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, copy.pipelines[p_8bit_dst ? COPY_MODE_GAUSSIAN_COPY_8BIT : COPY_MODE_GAUSSIAN_COPY]);
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_source_rd_texture), 0);
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_texture), 3);
-
- RD::get_singleton()->compute_list_set_push_constant(compute_list, &copy.push_constant, sizeof(CopyPushConstant));
-
- RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_region.size.width, p_region.size.height, 1);
-
- RD::get_singleton()->compute_list_end();
-}
-
-void EffectsRD::gaussian_glow(RID p_source_rd_texture, RID p_back_texture, const Size2i &p_size, float p_strength, bool p_high_quality, bool p_first_pass, float p_luminance_cap, float p_exposure, float p_bloom, float p_hdr_bleed_threshold, float p_hdr_bleed_scale, RID p_auto_exposure, float p_auto_exposure_grey) {
- ERR_FAIL_COND_MSG(prefer_raster_effects, "Can't use the compute version of the gaussian glow with the mobile renderer.");
-
- memset(&copy.push_constant, 0, sizeof(CopyPushConstant));
-
- CopyMode copy_mode = p_first_pass && p_auto_exposure.is_valid() ? COPY_MODE_GAUSSIAN_GLOW_AUTO_EXPOSURE : COPY_MODE_GAUSSIAN_GLOW;
- uint32_t base_flags = 0;
-
- copy.push_constant.section[2] = p_size.x;
- copy.push_constant.section[3] = p_size.y;
-
- copy.push_constant.glow_strength = p_strength;
- copy.push_constant.glow_bloom = p_bloom;
- copy.push_constant.glow_hdr_threshold = p_hdr_bleed_threshold;
- copy.push_constant.glow_hdr_scale = p_hdr_bleed_scale;
- copy.push_constant.glow_exposure = p_exposure;
- copy.push_constant.glow_white = 0; //actually unused
- copy.push_constant.glow_luminance_cap = p_luminance_cap;
-
- copy.push_constant.glow_auto_exposure_grey = p_auto_exposure_grey; //unused also
-
- RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
- RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, copy.pipelines[copy_mode]);
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_source_rd_texture), 0);
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_back_texture), 3);
- if (p_auto_exposure.is_valid() && p_first_pass) {
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_auto_exposure), 1);
- }
-
- copy.push_constant.flags = base_flags | (p_first_pass ? COPY_FLAG_GLOW_FIRST_PASS : 0) | (p_high_quality ? COPY_FLAG_HIGH_QUALITY_GLOW : 0);
- RD::get_singleton()->compute_list_set_push_constant(compute_list, &copy.push_constant, sizeof(CopyPushConstant));
-
- RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_size.width, p_size.height, 1);
- RD::get_singleton()->compute_list_end();
-}
-
-void EffectsRD::gaussian_glow_raster(RID p_source_rd_texture, RID p_framebuffer_half, RID p_rd_texture_half, RID p_dest_framebuffer, const Vector2 &p_pixel_size, float p_strength, bool p_high_quality, bool p_first_pass, float p_luminance_cap, float p_exposure, float p_bloom, float p_hdr_bleed_threshold, float p_hdr_bleed_scale, RID p_auto_exposure, float p_auto_exposure_grey) {
- ERR_FAIL_COND_MSG(!prefer_raster_effects, "Can't use the raster version of the gaussian glow with the clustered renderer.");
-
- memset(&blur_raster.push_constant, 0, sizeof(BlurRasterPushConstant));
-
- BlurRasterMode blur_mode = p_first_pass && p_auto_exposure.is_valid() ? BLUR_MODE_GAUSSIAN_GLOW_AUTO_EXPOSURE : BLUR_MODE_GAUSSIAN_GLOW;
- uint32_t base_flags = 0;
-
- blur_raster.push_constant.pixel_size[0] = p_pixel_size.x;
- blur_raster.push_constant.pixel_size[1] = p_pixel_size.y;
-
- blur_raster.push_constant.glow_strength = p_strength;
- blur_raster.push_constant.glow_bloom = p_bloom;
- blur_raster.push_constant.glow_hdr_threshold = p_hdr_bleed_threshold;
- blur_raster.push_constant.glow_hdr_scale = p_hdr_bleed_scale;
- blur_raster.push_constant.glow_exposure = p_exposure;
- blur_raster.push_constant.glow_white = 0; //actually unused
- blur_raster.push_constant.glow_luminance_cap = p_luminance_cap;
-
- blur_raster.push_constant.glow_auto_exposure_grey = p_auto_exposure_grey; //unused also
-
- //HORIZONTAL
- RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_framebuffer_half, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD);
- RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, blur_raster.pipelines[blur_mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_framebuffer_half)));
- RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_source_rd_texture), 0);
- if (p_auto_exposure.is_valid() && p_first_pass) {
- RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_auto_exposure), 1);
- }
- RD::get_singleton()->draw_list_bind_index_array(draw_list, index_array);
-
- blur_raster.push_constant.flags = base_flags | BLUR_FLAG_HORIZONTAL | (p_first_pass ? BLUR_FLAG_GLOW_FIRST_PASS : 0);
- RD::get_singleton()->draw_list_set_push_constant(draw_list, &blur_raster.push_constant, sizeof(BlurRasterPushConstant));
-
- RD::get_singleton()->draw_list_draw(draw_list, true);
- RD::get_singleton()->draw_list_end();
-
- blur_mode = BLUR_MODE_GAUSSIAN_GLOW;
-
- //VERTICAL
- draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD);
- RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, blur_raster.pipelines[blur_mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_framebuffer)));
- RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_rd_texture_half), 0);
- RD::get_singleton()->draw_list_bind_index_array(draw_list, index_array);
-
- blur_raster.push_constant.flags = base_flags;
- RD::get_singleton()->draw_list_set_push_constant(draw_list, &blur_raster.push_constant, sizeof(BlurRasterPushConstant));
-
- RD::get_singleton()->draw_list_draw(draw_list, true);
- RD::get_singleton()->draw_list_end();
-}
-
void EffectsRD::screen_space_reflection(RID p_diffuse, RID p_normal_roughness, RenderingServer::EnvironmentSSRRoughnessQuality p_roughness_quality, RID p_blur_radius, RID p_blur_radius2, RID p_metallic, const Color &p_metallic_mask, RID p_depth, RID p_scale_depth, RID p_scale_normal, RID p_output, RID p_output_blur, const Size2i &p_screen_size, int p_max_steps, float p_fade_in, float p_fade_out, float p_tolerance, const CameraMatrix &p_camera) {
RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
@@ -733,43 +444,6 @@ void EffectsRD::merge_specular(RID p_dest_framebuffer, RID p_specular, RID p_bas
RD::get_singleton()->draw_list_end();
}
-void EffectsRD::make_mipmap(RID p_source_rd_texture, RID p_dest_texture, const Size2i &p_size) {
- memset(&copy.push_constant, 0, sizeof(CopyPushConstant));
-
- copy.push_constant.section[0] = 0;
- copy.push_constant.section[1] = 0;
- copy.push_constant.section[2] = p_size.width;
- copy.push_constant.section[3] = p_size.height;
-
- RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
- RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, copy.pipelines[COPY_MODE_MIPMAP]);
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_source_rd_texture), 0);
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_dest_texture), 3);
- RD::get_singleton()->compute_list_set_push_constant(compute_list, &copy.push_constant, sizeof(CopyPushConstant));
- RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_size.width, p_size.height, 1);
- RD::get_singleton()->compute_list_end();
-}
-
-void EffectsRD::make_mipmap_raster(RID p_source_rd_texture, RID p_dest_framebuffer, const Size2i &p_size) {
- ERR_FAIL_COND_MSG(!prefer_raster_effects, "Can't use the raster version of mipmap with the clustered renderer.");
-
- memset(&blur_raster.push_constant, 0, sizeof(BlurRasterPushConstant));
-
- BlurRasterMode mode = BLUR_MIPMAP;
-
- blur_raster.push_constant.pixel_size[0] = 1.0 / float(p_size.x);
- blur_raster.push_constant.pixel_size[1] = 1.0 / float(p_size.y);
-
- RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD);
- RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, blur_raster.pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_framebuffer)));
- RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_source_rd_texture), 0);
- RD::get_singleton()->draw_list_bind_index_array(draw_list, index_array);
- RD::get_singleton()->draw_list_set_push_constant(draw_list, &blur_raster.push_constant, sizeof(BlurRasterPushConstant));
-
- RD::get_singleton()->draw_list_draw(draw_list, true);
- RD::get_singleton()->draw_list_end();
-}
-
void EffectsRD::copy_cubemap_to_dp(RID p_source_rd_texture, RID p_dst_framebuffer, const Rect2 &p_rect, const Vector2 &p_dst_size, float p_z_near, float p_z_far, bool p_dp_flip) {
CopyToDPPushConstant push_constant;
push_constant.screen_rect[0] = p_rect.position.x;
@@ -865,332 +539,6 @@ void EffectsRD::luminance_reduction_raster(RID p_source_texture, const Size2i p_
}
}
-void EffectsRD::bokeh_dof(const BokehBuffers &p_buffers, bool p_dof_far, float p_dof_far_begin, float p_dof_far_size, bool p_dof_near, float p_dof_near_begin, float p_dof_near_size, float p_bokeh_size, RenderingServer::DOFBokehShape p_bokeh_shape, RS::DOFBlurQuality p_quality, bool p_use_jitter, float p_cam_znear, float p_cam_zfar, bool p_cam_orthogonal) {
- ERR_FAIL_COND_MSG(prefer_raster_effects, "Can't use compute version of BOKEH DOF with the mobile renderer.");
-
- bokeh.push_constant.blur_far_active = p_dof_far;
- bokeh.push_constant.blur_far_begin = p_dof_far_begin;
- bokeh.push_constant.blur_far_end = p_dof_far_begin + p_dof_far_size;
-
- bokeh.push_constant.blur_near_active = p_dof_near;
- bokeh.push_constant.blur_near_begin = p_dof_near_begin;
- bokeh.push_constant.blur_near_end = MAX(0, p_dof_near_begin - p_dof_near_size);
- bokeh.push_constant.use_jitter = p_use_jitter;
- bokeh.push_constant.jitter_seed = Math::randf() * 1000.0;
-
- bokeh.push_constant.z_near = p_cam_znear;
- bokeh.push_constant.z_far = p_cam_zfar;
- bokeh.push_constant.orthogonal = p_cam_orthogonal;
- bokeh.push_constant.blur_size = p_bokeh_size;
-
- bokeh.push_constant.second_pass = false;
- bokeh.push_constant.half_size = false;
-
- bokeh.push_constant.blur_scale = 0.5;
-
- RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
-
- /* FIRST PASS */
- // The alpha channel of the source color texture is filled with the expected circle size
- // If used for DOF far, the size is positive, if used for near, its negative.
-
- RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, bokeh.compute_pipelines[BOKEH_GEN_BLUR_SIZE]);
-
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_buffers.base_texture), 0);
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_buffers.depth_texture), 1);
-
- bokeh.push_constant.size[0] = p_buffers.base_texture_size.x;
- bokeh.push_constant.size[1] = p_buffers.base_texture_size.y;
-
- RD::get_singleton()->compute_list_set_push_constant(compute_list, &bokeh.push_constant, sizeof(BokehPushConstant));
-
- RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_buffers.base_texture_size.x, p_buffers.base_texture_size.y, 1);
- RD::get_singleton()->compute_list_add_barrier(compute_list);
-
- if (p_bokeh_shape == RS::DOF_BOKEH_BOX || p_bokeh_shape == RS::DOF_BOKEH_HEXAGON) {
- //second pass
- RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, bokeh.compute_pipelines[p_bokeh_shape == RS::DOF_BOKEH_BOX ? BOKEH_GEN_BOKEH_BOX : BOKEH_GEN_BOKEH_HEXAGONAL]);
-
- static const int quality_samples[4] = { 6, 12, 12, 24 };
-
- bokeh.push_constant.steps = quality_samples[p_quality];
-
- if (p_quality == RS::DOF_BLUR_QUALITY_VERY_LOW || p_quality == RS::DOF_BLUR_QUALITY_LOW) {
- //box and hexagon are more or less the same, and they can work in either half (very low and low quality) or full (medium and high quality_ sizes)
-
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_buffers.half_texture[0]), 0);
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_buffers.base_texture), 1);
-
- bokeh.push_constant.size[0] = p_buffers.base_texture_size.x >> 1;
- bokeh.push_constant.size[1] = p_buffers.base_texture_size.y >> 1;
- bokeh.push_constant.half_size = true;
- bokeh.push_constant.blur_size *= 0.5;
-
- } else {
- //medium and high quality use full size
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_buffers.secondary_texture), 0);
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_buffers.base_texture), 1);
- }
-
- RD::get_singleton()->compute_list_set_push_constant(compute_list, &bokeh.push_constant, sizeof(BokehPushConstant));
-
- RD::get_singleton()->compute_list_dispatch_threads(compute_list, bokeh.push_constant.size[0], bokeh.push_constant.size[1], 1);
- RD::get_singleton()->compute_list_add_barrier(compute_list);
-
- //third pass
- bokeh.push_constant.second_pass = true;
-
- if (p_quality == RS::DOF_BLUR_QUALITY_VERY_LOW || p_quality == RS::DOF_BLUR_QUALITY_LOW) {
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_buffers.half_texture[1]), 0);
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_buffers.half_texture[0]), 1);
- } else {
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_buffers.base_texture), 0);
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_buffers.secondary_texture), 1);
- }
-
- RD::get_singleton()->compute_list_set_push_constant(compute_list, &bokeh.push_constant, sizeof(BokehPushConstant));
-
- RD::get_singleton()->compute_list_dispatch_threads(compute_list, bokeh.push_constant.size[0], bokeh.push_constant.size[1], 1);
- RD::get_singleton()->compute_list_add_barrier(compute_list);
-
- if (p_quality == RS::DOF_BLUR_QUALITY_VERY_LOW || p_quality == RS::DOF_BLUR_QUALITY_LOW) {
- //forth pass, upscale for low quality
-
- RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, bokeh.compute_pipelines[BOKEH_COMPOSITE]);
-
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_buffers.base_texture), 0);
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_buffers.half_texture[1]), 1);
-
- bokeh.push_constant.size[0] = p_buffers.base_texture_size.x;
- bokeh.push_constant.size[1] = p_buffers.base_texture_size.y;
- bokeh.push_constant.half_size = false;
- bokeh.push_constant.second_pass = false;
-
- RD::get_singleton()->compute_list_set_push_constant(compute_list, &bokeh.push_constant, sizeof(BokehPushConstant));
-
- RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_buffers.base_texture_size.x, p_buffers.base_texture_size.y, 1);
- }
- } else {
- //circle
-
- //second pass
- RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, bokeh.compute_pipelines[BOKEH_GEN_BOKEH_CIRCULAR]);
-
- static const float quality_scale[4] = { 8.0, 4.0, 1.0, 0.5 };
-
- bokeh.push_constant.steps = 0;
- bokeh.push_constant.blur_scale = quality_scale[p_quality];
-
- //circle always runs in half size, otherwise too expensive
-
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_buffers.half_texture[0]), 0);
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_buffers.base_texture), 1);
-
- bokeh.push_constant.size[0] = p_buffers.base_texture_size.x >> 1;
- bokeh.push_constant.size[1] = p_buffers.base_texture_size.y >> 1;
- bokeh.push_constant.half_size = true;
-
- RD::get_singleton()->compute_list_set_push_constant(compute_list, &bokeh.push_constant, sizeof(BokehPushConstant));
-
- RD::get_singleton()->compute_list_dispatch_threads(compute_list, bokeh.push_constant.size[0], bokeh.push_constant.size[1], 1);
- RD::get_singleton()->compute_list_add_barrier(compute_list);
-
- //circle is just one pass, then upscale
-
- // upscale
-
- RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, bokeh.compute_pipelines[BOKEH_COMPOSITE]);
-
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_buffers.base_texture), 0);
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_buffers.half_texture[0]), 1);
-
- bokeh.push_constant.size[0] = p_buffers.base_texture_size.x;
- bokeh.push_constant.size[1] = p_buffers.base_texture_size.y;
- bokeh.push_constant.half_size = false;
- bokeh.push_constant.second_pass = false;
-
- RD::get_singleton()->compute_list_set_push_constant(compute_list, &bokeh.push_constant, sizeof(BokehPushConstant));
-
- RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_buffers.base_texture_size.x, p_buffers.base_texture_size.y, 1);
- }
-
- RD::get_singleton()->compute_list_end();
-}
-
-void EffectsRD::bokeh_dof_raster(const BokehBuffers &p_buffers, bool p_dof_far, float p_dof_far_begin, float p_dof_far_size, bool p_dof_near, float p_dof_near_begin, float p_dof_near_size, float p_dof_blur_amount, RenderingServer::DOFBokehShape p_bokeh_shape, RS::DOFBlurQuality p_quality, float p_cam_znear, float p_cam_zfar, bool p_cam_orthogonal) {
- ERR_FAIL_COND_MSG(!prefer_raster_effects, "Can't use blur DOF with the clustered renderer.");
-
- memset(&bokeh.push_constant, 0, sizeof(BokehPushConstant));
-
- bokeh.push_constant.orthogonal = p_cam_orthogonal;
- bokeh.push_constant.size[0] = p_buffers.base_texture_size.width;
- bokeh.push_constant.size[1] = p_buffers.base_texture_size.height;
- bokeh.push_constant.z_far = p_cam_zfar;
- bokeh.push_constant.z_near = p_cam_znear;
-
- bokeh.push_constant.second_pass = false;
- bokeh.push_constant.half_size = false;
- bokeh.push_constant.blur_size = p_dof_blur_amount;
-
- if (p_dof_far || p_dof_near) {
- if (p_dof_far) {
- bokeh.push_constant.blur_far_active = true;
- bokeh.push_constant.blur_far_begin = p_dof_far_begin;
- bokeh.push_constant.blur_far_end = p_dof_far_begin + p_dof_far_size;
- }
-
- if (p_dof_near) {
- bokeh.push_constant.blur_near_active = true;
- bokeh.push_constant.blur_near_begin = p_dof_near_begin;
- bokeh.push_constant.blur_near_end = p_dof_near_begin - p_dof_near_size;
- }
-
- {
- // generate our depth data
- RID framebuffer = p_buffers.base_weight_fb;
- RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD);
- RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, bokeh.raster_pipelines[BOKEH_GEN_BLUR_SIZE].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(framebuffer)));
- RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_buffers.depth_texture), 0);
- RD::get_singleton()->draw_list_bind_index_array(draw_list, index_array);
-
- RD::get_singleton()->draw_list_set_push_constant(draw_list, &bokeh.push_constant, sizeof(BokehPushConstant));
-
- RD::get_singleton()->draw_list_draw(draw_list, true);
- RD::get_singleton()->draw_list_end();
- }
-
- if (p_bokeh_shape == RS::DOF_BOKEH_BOX || p_bokeh_shape == RS::DOF_BOKEH_HEXAGON) {
- // double pass approach
- BokehMode mode = p_bokeh_shape == RS::DOF_BOKEH_BOX ? BOKEH_GEN_BOKEH_BOX : BOKEH_GEN_BOKEH_HEXAGONAL;
-
- if (p_quality == RS::DOF_BLUR_QUALITY_VERY_LOW || p_quality == RS::DOF_BLUR_QUALITY_LOW) {
- //box and hexagon are more or less the same, and they can work in either half (very low and low quality) or full (medium and high quality_ sizes)
- bokeh.push_constant.size[0] = p_buffers.base_texture_size.x >> 1;
- bokeh.push_constant.size[1] = p_buffers.base_texture_size.y >> 1;
- bokeh.push_constant.half_size = true;
- bokeh.push_constant.blur_size *= 0.5;
- }
-
- static const int quality_samples[4] = { 6, 12, 12, 24 };
- bokeh.push_constant.blur_scale = 0.5;
- bokeh.push_constant.steps = quality_samples[p_quality];
-
- RID framebuffer = bokeh.push_constant.half_size ? p_buffers.half_fb[0] : p_buffers.secondary_fb;
-
- // Pass 1
- RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD);
- RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, bokeh.raster_pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(framebuffer)));
- RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_buffers.base_texture), 0);
- RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_buffers.weight_texture[0]), 1);
- RD::get_singleton()->draw_list_bind_index_array(draw_list, index_array);
-
- RD::get_singleton()->draw_list_set_push_constant(draw_list, &bokeh.push_constant, sizeof(BokehPushConstant));
-
- RD::get_singleton()->draw_list_draw(draw_list, true);
- RD::get_singleton()->draw_list_end();
-
- // Pass 2
- if (!bokeh.push_constant.half_size) {
- // do not output weight, we're writing back into our base buffer
- mode = p_bokeh_shape == RS::DOF_BOKEH_BOX ? BOKEH_GEN_BOKEH_BOX_NOWEIGHT : BOKEH_GEN_BOKEH_HEXAGONAL_NOWEIGHT;
- }
- bokeh.push_constant.second_pass = true;
-
- framebuffer = bokeh.push_constant.half_size ? p_buffers.half_fb[1] : p_buffers.base_fb;
- RID texture = bokeh.push_constant.half_size ? p_buffers.half_texture[0] : p_buffers.secondary_texture;
- RID weight = bokeh.push_constant.half_size ? p_buffers.weight_texture[2] : p_buffers.weight_texture[1];
-
- draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD);
- RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, bokeh.raster_pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(framebuffer)));
- RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(texture), 0);
- RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(weight), 1);
- RD::get_singleton()->draw_list_bind_index_array(draw_list, index_array);
-
- RD::get_singleton()->draw_list_set_push_constant(draw_list, &bokeh.push_constant, sizeof(BokehPushConstant));
-
- RD::get_singleton()->draw_list_draw(draw_list, true);
- RD::get_singleton()->draw_list_end();
-
- if (bokeh.push_constant.half_size) {
- // Compose pass
- mode = BOKEH_COMPOSITE;
- framebuffer = p_buffers.base_fb;
-
- draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD);
- RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, bokeh.raster_pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(framebuffer)));
- RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_buffers.half_texture[1]), 0);
- RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_buffers.weight_texture[3]), 1);
- RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_buffers.weight_texture[0]), 2);
- RD::get_singleton()->draw_list_bind_index_array(draw_list, index_array);
-
- RD::get_singleton()->draw_list_set_push_constant(draw_list, &bokeh.push_constant, sizeof(BokehPushConstant));
-
- RD::get_singleton()->draw_list_draw(draw_list, true);
- RD::get_singleton()->draw_list_end();
- }
-
- } else {
- // circular is a single pass approach
- BokehMode mode = BOKEH_GEN_BOKEH_CIRCULAR;
-
- {
- // circle always runs in half size, otherwise too expensive (though the code below does support making this optional)
- bokeh.push_constant.size[0] = p_buffers.base_texture_size.x >> 1;
- bokeh.push_constant.size[1] = p_buffers.base_texture_size.y >> 1;
- bokeh.push_constant.half_size = true;
- // bokeh.push_constant.blur_size *= 0.5;
- }
-
- static const float quality_scale[4] = { 8.0, 4.0, 1.0, 0.5 };
- bokeh.push_constant.blur_scale = quality_scale[p_quality];
- bokeh.push_constant.steps = 0.0;
-
- RID framebuffer = bokeh.push_constant.half_size ? p_buffers.half_fb[0] : p_buffers.secondary_fb;
-
- RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD);
- RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, bokeh.raster_pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(framebuffer)));
- RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_buffers.base_texture), 0);
- RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_buffers.weight_texture[0]), 1);
- RD::get_singleton()->draw_list_bind_index_array(draw_list, index_array);
-
- RD::get_singleton()->draw_list_set_push_constant(draw_list, &bokeh.push_constant, sizeof(BokehPushConstant));
-
- RD::get_singleton()->draw_list_draw(draw_list, true);
- RD::get_singleton()->draw_list_end();
-
- if (bokeh.push_constant.half_size) {
- // Compose
- mode = BOKEH_COMPOSITE;
- framebuffer = p_buffers.base_fb;
-
- draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD);
- RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, bokeh.raster_pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(framebuffer)));
- RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_buffers.half_texture[0]), 0);
- RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_buffers.weight_texture[2]), 1);
- RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_buffers.weight_texture[0]), 2);
- RD::get_singleton()->draw_list_bind_index_array(draw_list, index_array);
-
- RD::get_singleton()->draw_list_set_push_constant(draw_list, &bokeh.push_constant, sizeof(BokehPushConstant));
-
- RD::get_singleton()->draw_list_draw(draw_list, true);
- RD::get_singleton()->draw_list_end();
- } else {
- // Just copy it back (we use our blur raster shader here)..
- draw_list = RD::get_singleton()->draw_list_begin(p_buffers.base_fb, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD);
- RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, blur_raster.pipelines[BLUR_MODE_COPY].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_buffers.base_fb)));
- RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_buffers.secondary_texture), 0);
- RD::get_singleton()->draw_list_bind_index_array(draw_list, index_array);
-
- memset(&blur_raster.push_constant, 0, sizeof(BlurRasterPushConstant));
- RD::get_singleton()->draw_list_set_push_constant(draw_list, &blur_raster.push_constant, sizeof(BlurRasterPushConstant));
-
- RD::get_singleton()->draw_list_draw(draw_list, true);
- RD::get_singleton()->draw_list_end();
- }
- }
- }
-}
-
void EffectsRD::downsample_depth(RID p_depth_buffer, const Vector<RID> &p_depth_mipmaps, RS::EnvironmentSSAOQuality p_ssao_quality, RS::EnvironmentSSILQuality p_ssil_quality, bool p_invalidate_uniform_set, bool p_ssao_half_size, bool p_ssil_half_size, Size2i p_full_screen_size, const CameraMatrix &p_projection) {
// Downsample and deinterleave the depth buffer for SSAO and SSIL
RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
@@ -2152,94 +1500,6 @@ EffectsRD::EffectsRD(bool p_prefer_raster_effects) {
prefer_raster_effects = p_prefer_raster_effects;
- if (prefer_raster_effects) {
- // init blur shader (on compute use copy shader)
-
- Vector<String> blur_modes;
- blur_modes.push_back("\n#define MODE_MIPMAP\n"); // BLUR_MIPMAP
- blur_modes.push_back("\n#define MODE_GAUSSIAN_BLUR\n"); // BLUR_MODE_GAUSSIAN_BLUR
- blur_modes.push_back("\n#define MODE_GAUSSIAN_GLOW\n"); // BLUR_MODE_GAUSSIAN_GLOW
- blur_modes.push_back("\n#define MODE_GAUSSIAN_GLOW\n#define GLOW_USE_AUTO_EXPOSURE\n"); // BLUR_MODE_GAUSSIAN_GLOW_AUTO_EXPOSURE
- blur_modes.push_back("\n#define MODE_COPY\n"); // BLUR_MODE_COPY
-
- blur_raster.shader.initialize(blur_modes);
- memset(&blur_raster.push_constant, 0, sizeof(BlurRasterPushConstant));
- blur_raster.shader_version = blur_raster.shader.version_create();
-
- for (int i = 0; i < BLUR_MODE_MAX; i++) {
- blur_raster.pipelines[i].setup(blur_raster.shader.version_get_shader(blur_raster.shader_version, i), RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), RD::PipelineColorBlendState::create_disabled(), 0);
- }
-
- } else {
- // not used in clustered
- for (int i = 0; i < BLUR_MODE_MAX; i++) {
- blur_raster.pipelines[i].clear();
- }
- }
-
- if (!prefer_raster_effects) { // Initialize copy
- Vector<String> copy_modes;
- copy_modes.push_back("\n#define MODE_GAUSSIAN_BLUR\n");
- copy_modes.push_back("\n#define MODE_GAUSSIAN_BLUR\n#define DST_IMAGE_8BIT\n");
- copy_modes.push_back("\n#define MODE_GAUSSIAN_BLUR\n#define MODE_GLOW\n");
- copy_modes.push_back("\n#define MODE_GAUSSIAN_BLUR\n#define MODE_GLOW\n#define GLOW_USE_AUTO_EXPOSURE\n");
- copy_modes.push_back("\n#define MODE_SIMPLE_COPY\n");
- copy_modes.push_back("\n#define MODE_SIMPLE_COPY\n#define DST_IMAGE_8BIT\n");
- copy_modes.push_back("\n#define MODE_SIMPLE_COPY_DEPTH\n");
- copy_modes.push_back("\n#define MODE_SET_COLOR\n");
- copy_modes.push_back("\n#define MODE_SET_COLOR\n#define DST_IMAGE_8BIT\n");
- copy_modes.push_back("\n#define MODE_MIPMAP\n");
- copy_modes.push_back("\n#define MODE_LINEARIZE_DEPTH_COPY\n");
- copy_modes.push_back("\n#define MODE_CUBEMAP_TO_PANORAMA\n");
- copy_modes.push_back("\n#define MODE_CUBEMAP_ARRAY_TO_PANORAMA\n");
-
- copy.shader.initialize(copy_modes);
- memset(&copy.push_constant, 0, sizeof(CopyPushConstant));
-
- if (prefer_raster_effects) {
- // disable shaders we can't use
- copy.shader.set_variant_enabled(COPY_MODE_GAUSSIAN_COPY, false);
- copy.shader.set_variant_enabled(COPY_MODE_GAUSSIAN_COPY_8BIT, false);
- copy.shader.set_variant_enabled(COPY_MODE_GAUSSIAN_GLOW, false);
- copy.shader.set_variant_enabled(COPY_MODE_GAUSSIAN_GLOW_AUTO_EXPOSURE, false);
- }
-
- copy.shader_version = copy.shader.version_create();
-
- for (int i = 0; i < COPY_MODE_MAX; i++) {
- if (copy.shader.is_variant_enabled(i)) {
- copy.pipelines[i] = RD::get_singleton()->compute_pipeline_create(copy.shader.version_get_shader(copy.shader_version, i));
- }
- }
- }
- {
- Vector<String> copy_modes;
- copy_modes.push_back("\n");
- copy_modes.push_back("\n#define MODE_PANORAMA_TO_DP\n");
- copy_modes.push_back("\n#define MODE_TWO_SOURCES\n");
- copy_modes.push_back("\n#define MULTIVIEW\n");
- copy_modes.push_back("\n#define MULTIVIEW\n#define MODE_TWO_SOURCES\n");
-
- copy_to_fb.shader.initialize(copy_modes);
-
- if (!RendererCompositorRD::singleton->is_xr_enabled()) {
- copy_to_fb.shader.set_variant_enabled(COPY_TO_FB_MULTIVIEW, false);
- copy_to_fb.shader.set_variant_enabled(COPY_TO_FB_MULTIVIEW_WITH_DEPTH, false);
- }
-
- copy_to_fb.shader_version = copy_to_fb.shader.version_create();
-
- //use additive
-
- for (int i = 0; i < COPY_TO_FB_MAX; i++) {
- if (copy_to_fb.shader.is_variant_enabled(i)) {
- copy_to_fb.pipelines[i].setup(copy_to_fb.shader.version_get_shader(copy_to_fb.shader_version, i), RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), RD::PipelineColorBlendState::create_disabled(), 0);
- } else {
- copy_to_fb.pipelines[i].clear();
- }
- }
- }
-
{
// Initialize roughness
Vector<String> cubemap_roughness_modes;
@@ -2311,42 +1571,6 @@ EffectsRD::EffectsRD(bool p_prefer_raster_effects) {
cube_to_dp.pipeline.setup(shader, RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), dss, RD::PipelineColorBlendState(), 0);
}
- // Initialize bokeh
- Vector<String> bokeh_modes;
- bokeh_modes.push_back("\n#define MODE_GEN_BLUR_SIZE\n");
- bokeh_modes.push_back("\n#define MODE_BOKEH_BOX\n#define OUTPUT_WEIGHT\n");
- bokeh_modes.push_back("\n#define MODE_BOKEH_BOX\n");
- bokeh_modes.push_back("\n#define MODE_BOKEH_HEXAGONAL\n#define OUTPUT_WEIGHT\n");
- bokeh_modes.push_back("\n#define MODE_BOKEH_HEXAGONAL\n");
- bokeh_modes.push_back("\n#define MODE_BOKEH_CIRCULAR\n#define OUTPUT_WEIGHT\n");
- bokeh_modes.push_back("\n#define MODE_COMPOSITE_BOKEH\n");
- if (prefer_raster_effects) {
- bokeh.raster_shader.initialize(bokeh_modes);
-
- bokeh.shader_version = bokeh.raster_shader.version_create();
-
- const int att_count[BOKEH_MAX] = { 1, 2, 1, 2, 1, 2, 1 };
- for (int i = 0; i < BOKEH_MAX; i++) {
- RD::PipelineColorBlendState blend_state = (i == BOKEH_COMPOSITE) ? RD::PipelineColorBlendState::create_blend(att_count[i]) : RD::PipelineColorBlendState::create_disabled(att_count[i]);
- bokeh.raster_pipelines[i].setup(bokeh.raster_shader.version_get_shader(bokeh.shader_version, i), RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), blend_state, 0);
- }
- } else {
- bokeh.compute_shader.initialize(bokeh_modes);
- bokeh.compute_shader.set_variant_enabled(BOKEH_GEN_BOKEH_BOX_NOWEIGHT, false);
- bokeh.compute_shader.set_variant_enabled(BOKEH_GEN_BOKEH_HEXAGONAL_NOWEIGHT, false);
- bokeh.shader_version = bokeh.compute_shader.version_create();
-
- for (int i = 0; i < BOKEH_MAX; i++) {
- if (bokeh.compute_shader.is_variant_enabled(i)) {
- bokeh.compute_pipelines[i] = RD::get_singleton()->compute_pipeline_create(bokeh.compute_shader.version_get_shader(bokeh.shader_version, i));
- }
- }
-
- for (int i = 0; i < BOKEH_MAX; i++) {
- bokeh.raster_pipelines[i].clear();
- }
- }
-
if (!prefer_raster_effects) {
{
// Initialize depth buffer for screen space effects
@@ -2837,21 +2061,17 @@ EffectsRD::~EffectsRD() {
FSR_upscale.shader.version_free(FSR_upscale.shader_version);
if (prefer_raster_effects) {
- blur_raster.shader.version_free(blur_raster.shader_version);
- bokeh.raster_shader.version_free(blur_raster.shader_version);
luminance_reduce_raster.shader.version_free(luminance_reduce_raster.shader_version);
roughness.raster_shader.version_free(roughness.shader_version);
cubemap_downsampler.raster_shader.version_free(cubemap_downsampler.shader_version);
filter.raster_shader.version_free(filter.shader_version);
} else {
- bokeh.compute_shader.version_free(bokeh.shader_version);
luminance_reduce.shader.version_free(luminance_reduce.shader_version);
roughness.compute_shader.version_free(roughness.shader_version);
cubemap_downsampler.compute_shader.version_free(cubemap_downsampler.shader_version);
filter.compute_shader.version_free(filter.shader_version);
}
if (!prefer_raster_effects) {
- copy.shader.version_free(copy.shader_version);
resolve.shader.version_free(resolve.shader_version);
specular_merge.shader.version_free(specular_merge.shader_version);
ss_effects.downsample_shader.version_free(ss_effects.downsample_shader_version);
@@ -2875,7 +2095,6 @@ EffectsRD::~EffectsRD() {
RD::get_singleton()->free(ssil.importance_map_load_counter);
RD::get_singleton()->free(ssil.projection_uniform_buffer);
}
- copy_to_fb.shader.version_free(copy_to_fb.shader_version);
cube_to_dp.shader.version_free(cube_to_dp.shader_version);
sort.shader.version_free(sort.shader_version);
}
diff --git a/servers/rendering/renderer_rd/effects_rd.h b/servers/rendering/renderer_rd/effects_rd.h
index 8174e30238..4774864824 100644
--- a/servers/rendering/renderer_rd/effects_rd.h
+++ b/servers/rendering/renderer_rd/effects_rd.h
@@ -33,11 +33,6 @@
#include "core/math/camera_matrix.h"
#include "servers/rendering/renderer_rd/pipeline_cache_rd.h"
-#include "servers/rendering/renderer_rd/shaders/blur_raster.glsl.gen.h"
-#include "servers/rendering/renderer_rd/shaders/bokeh_dof.glsl.gen.h"
-#include "servers/rendering/renderer_rd/shaders/bokeh_dof_raster.glsl.gen.h"
-#include "servers/rendering/renderer_rd/shaders/copy.glsl.gen.h"
-#include "servers/rendering/renderer_rd/shaders/copy_to_fb.glsl.gen.h"
#include "servers/rendering/renderer_rd/shaders/cube_to_dp.glsl.gen.h"
#include "servers/rendering/renderer_rd/shaders/cubemap_downsampler.glsl.gen.h"
#include "servers/rendering/renderer_rd/shaders/cubemap_downsampler_raster.glsl.gen.h"
@@ -95,140 +90,6 @@ private:
RID pipeline;
} FSR_upscale;
- enum BlurRasterMode {
- BLUR_MIPMAP,
-
- BLUR_MODE_GAUSSIAN_BLUR,
- BLUR_MODE_GAUSSIAN_GLOW,
- BLUR_MODE_GAUSSIAN_GLOW_AUTO_EXPOSURE,
- BLUR_MODE_COPY,
-
- BLUR_MODE_MAX
- };
-
- enum {
- BLUR_FLAG_HORIZONTAL = (1 << 0),
- BLUR_FLAG_USE_ORTHOGONAL_PROJECTION = (1 << 1),
- BLUR_FLAG_GLOW_FIRST_PASS = (1 << 2),
- };
-
- struct BlurRasterPushConstant {
- float pixel_size[2];
- uint32_t flags;
- uint32_t pad;
-
- //glow
- float glow_strength;
- float glow_bloom;
- float glow_hdr_threshold;
- float glow_hdr_scale;
-
- float glow_exposure;
- float glow_white;
- float glow_luminance_cap;
- float glow_auto_exposure_grey;
- };
-
- struct BlurRaster {
- BlurRasterPushConstant push_constant;
- BlurRasterShaderRD shader;
- RID shader_version;
- PipelineCacheRD pipelines[BLUR_MODE_MAX];
- } blur_raster;
-
- enum CopyMode {
- COPY_MODE_GAUSSIAN_COPY,
- COPY_MODE_GAUSSIAN_COPY_8BIT,
- COPY_MODE_GAUSSIAN_GLOW,
- COPY_MODE_GAUSSIAN_GLOW_AUTO_EXPOSURE,
- COPY_MODE_SIMPLY_COPY,
- COPY_MODE_SIMPLY_COPY_8BIT,
- COPY_MODE_SIMPLY_COPY_DEPTH,
- COPY_MODE_SET_COLOR,
- COPY_MODE_SET_COLOR_8BIT,
- COPY_MODE_MIPMAP,
- COPY_MODE_LINEARIZE_DEPTH,
- COPY_MODE_CUBE_TO_PANORAMA,
- COPY_MODE_CUBE_ARRAY_TO_PANORAMA,
- COPY_MODE_MAX,
-
- };
-
- enum {
- COPY_FLAG_HORIZONTAL = (1 << 0),
- COPY_FLAG_USE_COPY_SECTION = (1 << 1),
- COPY_FLAG_USE_ORTHOGONAL_PROJECTION = (1 << 2),
- COPY_FLAG_DOF_NEAR_FIRST_TAP = (1 << 3),
- COPY_FLAG_GLOW_FIRST_PASS = (1 << 4),
- COPY_FLAG_FLIP_Y = (1 << 5),
- COPY_FLAG_FORCE_LUMINANCE = (1 << 6),
- COPY_FLAG_ALL_SOURCE = (1 << 7),
- COPY_FLAG_HIGH_QUALITY_GLOW = (1 << 8),
- COPY_FLAG_ALPHA_TO_ONE = (1 << 9),
- };
-
- struct CopyPushConstant {
- int32_t section[4];
- int32_t target[2];
- uint32_t flags;
- uint32_t pad;
- // Glow.
- float glow_strength;
- float glow_bloom;
- float glow_hdr_threshold;
- float glow_hdr_scale;
-
- float glow_exposure;
- float glow_white;
- float glow_luminance_cap;
- float glow_auto_exposure_grey;
- // DOF.
- float camera_z_far;
- float camera_z_near;
- uint32_t pad2[2];
- //SET color
- float set_color[4];
- };
-
- struct Copy {
- CopyPushConstant push_constant;
- CopyShaderRD shader;
- RID shader_version;
- RID pipelines[COPY_MODE_MAX];
-
- } copy;
-
- enum CopyToFBMode {
- COPY_TO_FB_COPY,
- COPY_TO_FB_COPY_PANORAMA_TO_DP,
- COPY_TO_FB_COPY2,
-
- COPY_TO_FB_MULTIVIEW,
- COPY_TO_FB_MULTIVIEW_WITH_DEPTH,
- COPY_TO_FB_MAX,
-
- };
-
- struct CopyToFbPushConstant {
- float section[4];
- float pixel_size[2];
- uint32_t flip_y;
- uint32_t use_section;
-
- uint32_t force_luminance;
- uint32_t alpha_to_zero;
- uint32_t srgb;
- uint32_t pad;
- };
-
- struct CopyToFb {
- CopyToFbPushConstant push_constant;
- CopyToFbShaderRD shader;
- RID shader_version;
- PipelineCacheRD pipelines[COPY_TO_FB_MAX];
-
- } copy_to_fb;
-
struct CubemapRoughnessPushConstant {
uint32_t face_id;
uint32_t sample_count;
@@ -305,51 +166,6 @@ private:
PipelineCacheRD pipeline;
} cube_to_dp;
- struct BokehPushConstant {
- uint32_t size[2];
- float z_far;
- float z_near;
-
- uint32_t orthogonal;
- float blur_size;
- float blur_scale;
- uint32_t steps;
-
- uint32_t blur_near_active;
- float blur_near_begin;
- float blur_near_end;
- uint32_t blur_far_active;
-
- float blur_far_begin;
- float blur_far_end;
- uint32_t second_pass;
- uint32_t half_size;
-
- uint32_t use_jitter;
- float jitter_seed;
- uint32_t pad[2];
- };
-
- enum BokehMode {
- BOKEH_GEN_BLUR_SIZE,
- BOKEH_GEN_BOKEH_BOX,
- BOKEH_GEN_BOKEH_BOX_NOWEIGHT,
- BOKEH_GEN_BOKEH_HEXAGONAL,
- BOKEH_GEN_BOKEH_HEXAGONAL_NOWEIGHT,
- BOKEH_GEN_BOKEH_CIRCULAR,
- BOKEH_COMPOSITE,
- BOKEH_MAX
- };
-
- struct Bokeh {
- BokehPushConstant push_constant;
- BokehDofShaderRD compute_shader;
- BokehDofRasterShaderRD raster_shader;
- RID shader_version;
- RID compute_pipelines[BOKEH_MAX];
- PipelineCacheRD raster_pipelines[BOKEH_MAX];
- } bokeh;
-
struct SSEffectsDownsamplePushConstant {
float pixel_size[2];
float z_far;
@@ -838,46 +654,13 @@ public:
bool get_prefer_raster_effects();
void fsr_upscale(RID p_source_rd_texture, RID p_secondary_texture, RID p_destination_texture, const Size2i &p_internal_size, const Size2i &p_size, float p_fsr_upscale_sharpness);
- void copy_to_fb_rect(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2i &p_rect, bool p_flip_y = false, bool p_force_luminance = false, bool p_alpha_to_zero = false, bool p_srgb = false, RID p_secondary = RID(), bool p_multiview = false);
- void copy_to_rect(RID p_source_rd_texture, RID p_dest_texture, const Rect2i &p_rect, bool p_flip_y = false, bool p_force_luminance = false, bool p_all_source = false, bool p_8_bit_dst = false, bool p_alpha_to_one = false);
- void copy_cubemap_to_panorama(RID p_source_cube, RID p_dest_panorama, const Size2i &p_panorama_size, float p_lod, bool p_is_array);
- void copy_depth_to_rect(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2i &p_rect, bool p_flip_y = false);
- void copy_depth_to_rect_and_linearize(RID p_source_rd_texture, RID p_dest_texture, const Rect2i &p_rect, bool p_flip_y, float p_z_near, float p_z_far);
- void copy_to_atlas_fb(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2 &p_uv_rect, RD::DrawListID p_draw_list, bool p_flip_y = false, bool p_panorama = false);
- void gaussian_blur(RID p_source_rd_texture, RID p_texture, const Rect2i &p_region, bool p_8bit_dst = false);
- void set_color(RID p_dest_texture, const Color &p_color, const Rect2i &p_region, bool p_8bit_dst = false);
- void gaussian_glow(RID p_source_rd_texture, RID p_back_texture, const Size2i &p_size, float p_strength = 1.0, bool p_high_quality = false, bool p_first_pass = false, float p_luminance_cap = 16.0, float p_exposure = 1.0, float p_bloom = 0.0, float p_hdr_bleed_threshold = 1.0, float p_hdr_bleed_scale = 1.0, RID p_auto_exposure = RID(), float p_auto_exposure_grey = 1.0);
- void gaussian_glow_raster(RID p_source_rd_texture, RID p_framebuffer_half, RID p_rd_texture_half, RID p_dest_framebuffer, const Vector2 &p_pixel_size, float p_strength = 1.0, bool p_high_quality = false, bool p_first_pass = false, float p_luminance_cap = 16.0, float p_exposure = 1.0, float p_bloom = 0.0, float p_hdr_bleed_threshold = 1.0, float p_hdr_bleed_scale = 1.0, RID p_auto_exposure = RID(), float p_auto_exposure_grey = 1.0);
void cubemap_roughness(RID p_source_rd_texture, RID p_dest_texture, uint32_t p_face_id, uint32_t p_sample_count, float p_roughness, float p_size);
void cubemap_roughness_raster(RID p_source_rd_texture, RID p_dest_framebuffer, uint32_t p_face_id, uint32_t p_sample_count, float p_roughness, float p_size);
- void make_mipmap(RID p_source_rd_texture, RID p_dest_texture, const Size2i &p_size);
- void make_mipmap_raster(RID p_source_rd_texture, RID p_dest_framebuffer, const Size2i &p_size);
void copy_cubemap_to_dp(RID p_source_rd_texture, RID p_dst_framebuffer, const Rect2 &p_rect, const Vector2 &p_dst_size, float p_z_near, float p_z_far, bool p_dp_flip);
void luminance_reduction(RID p_source_texture, const Size2i p_source_size, const Vector<RID> p_reduce, RID p_prev_luminance, float p_min_luminance, float p_max_luminance, float p_adjust, bool p_set = false);
void luminance_reduction_raster(RID p_source_texture, const Size2i p_source_size, const Vector<RID> p_reduce, Vector<RID> p_fb, RID p_prev_luminance, float p_min_luminance, float p_max_luminance, float p_adjust, bool p_set = false);
- struct BokehBuffers {
- // bokeh buffers
-
- // textures
- Size2i base_texture_size;
- RID base_texture;
- RID depth_texture;
- RID secondary_texture;
- RID half_texture[2];
-
- // raster only
- RID base_fb;
- RID secondary_fb; // with weights
- RID half_fb[2]; // with weights
- RID base_weight_fb;
- RID weight_texture[4];
- };
-
- void bokeh_dof(const BokehBuffers &p_buffers, bool p_dof_far, float p_dof_far_begin, float p_dof_far_size, bool p_dof_near, float p_dof_near_begin, float p_dof_near_size, float p_bokeh_size, RS::DOFBokehShape p_bokeh_shape, RS::DOFBlurQuality p_quality, bool p_use_jitter, float p_cam_znear, float p_cam_zfar, bool p_cam_orthogonal);
- void bokeh_dof_raster(const BokehBuffers &p_buffers, bool p_dof_far, float p_dof_far_begin, float p_dof_far_size, bool p_dof_near, float p_dof_near_begin, float p_dof_near_size, float p_dof_blur_amount, RenderingServer::DOFBokehShape p_bokeh_shape, RS::DOFBlurQuality p_quality, float p_cam_znear, float p_cam_zfar, bool p_cam_orthogonal);
-
struct SSAOSettings {
float radius = 1.0;
float intensity = 2.0;
diff --git a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp
index e60651f092..036ad0f18a 100644
--- a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp
+++ b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp
@@ -748,7 +748,7 @@ void SceneShaderForwardClustered::init(RendererStorageRD *p_storage, const Strin
actions.default_filter = ShaderLanguage::FILTER_LINEAR_MIPMAP;
actions.default_repeat = ShaderLanguage::REPEAT_ENABLE;
actions.global_buffer_array_variable = "global_variables.data";
- actions.instance_uniform_index_variable = "instances.data[instance_index].instance_uniforms_ofs";
+ actions.instance_uniform_index_variable = "instances.data[instance_index_interp].instance_uniforms_ofs";
compiler.initialize(actions);
}
diff --git a/servers/rendering/renderer_rd/renderer_compositor_rd.h b/servers/rendering/renderer_rd/renderer_compositor_rd.h
index 26bd28286b..1fd6550fb8 100644
--- a/servers/rendering/renderer_rd/renderer_compositor_rd.h
+++ b/servers/rendering/renderer_rd/renderer_compositor_rd.h
@@ -131,6 +131,7 @@ public:
static void make_current() {
_create_func = _create_current;
+ low_end = false;
}
static RendererCompositorRD *singleton;
diff --git a/servers/rendering/renderer_rd/renderer_scene_gi_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_gi_rd.cpp
index 5cbacb0ef3..7aede6bb48 100644
--- a/servers/rendering/renderer_rd/renderer_scene_gi_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_scene_gi_rd.cpp
@@ -1115,6 +1115,7 @@ void RendererSceneGIRD::SDFGI::update_cascades() {
void RendererSceneGIRD::SDFGI::debug_draw(const CameraMatrix &p_projection, const Transform3D &p_transform, int p_width, int p_height, RID p_render_target, RID p_texture) {
RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton();
RendererRD::MaterialStorage *material_storage = RendererRD::MaterialStorage::get_singleton();
+ RendererRD::CopyEffects *copy_effects = RendererRD::CopyEffects::get_singleton();
if (!debug_uniform_set.is_valid() || !RD::get_singleton()->uniform_set_is_valid(debug_uniform_set)) {
Vector<RD::Uniform> uniforms;
@@ -1251,7 +1252,7 @@ void RendererSceneGIRD::SDFGI::debug_draw(const CameraMatrix &p_projection, cons
RD::get_singleton()->compute_list_end();
Size2 rtsize = texture_storage->render_target_get_size(p_render_target);
- storage->get_effects()->copy_to_fb_rect(p_texture, texture_storage->render_target_get_rd_framebuffer(p_render_target), Rect2(Vector2(), rtsize), true);
+ copy_effects->copy_to_fb_rect(p_texture, texture_storage->render_target_get_rd_framebuffer(p_render_target), Rect2(Vector2(), rtsize), true);
}
void RendererSceneGIRD::SDFGI::debug_probes(RD::DrawListID p_draw_list, RID p_framebuffer, const CameraMatrix &p_camera_with_transform) {
diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
index bf65dca56a..21b1b45802 100644
--- a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
@@ -1556,8 +1556,6 @@ void RendererSceneRenderRD::_allocate_blur_textures(RenderBuffers *rb) {
uint32_t mipmaps_required = Image::get_image_required_mipmaps(rb->width, rb->height, Image::FORMAT_RGBAH);
- // TODO make sure texture_create_shared_from_slice works for multiview
-
RD::TextureFormat tf;
tf.format = _render_buffers_get_color_format(); // RD::DATA_FORMAT_R16G16B16A16_SFLOAT;
tf.width = rb->internal_width;
@@ -1583,55 +1581,63 @@ void RendererSceneRenderRD::_allocate_blur_textures(RenderBuffers *rb) {
tf.mipmaps--;
rb->blur[1].texture = RD::get_singleton()->texture_create(tf, RD::TextureView());
- int base_width = rb->internal_width;
- int base_height = rb->internal_height;
+ for (uint32_t l = 0; l < rb->view_count; l++) {
+ RenderBuffers::Blur::Layer ll[2];
+ int base_width = rb->internal_width;
+ int base_height = rb->internal_height;
- for (uint32_t i = 0; i < mipmaps_required; i++) {
- RenderBuffers::Blur::Mipmap mm;
- mm.texture = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), rb->blur[0].texture, 0, i);
+ for (uint32_t i = 0; i < mipmaps_required; i++) {
+ RenderBuffers::Blur::Mipmap mm;
+ mm.texture = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), rb->blur[0].texture, l, i);
- mm.width = base_width;
- mm.height = base_height;
+ mm.width = base_width;
+ mm.height = base_height;
- if (!_render_buffers_can_be_storage()) {
- Vector<RID> fb;
- fb.push_back(mm.texture);
-
- mm.fb = RD::get_singleton()->framebuffer_create(fb);
- }
+ if (!_render_buffers_can_be_storage()) {
+ Vector<RID> fb;
+ fb.push_back(mm.texture);
- if (!_render_buffers_can_be_storage()) {
- // and half texture, this is an intermediate result so just allocate a texture, is this good enough?
- tf.width = MAX(1, base_width >> 1);
- tf.height = base_height;
- tf.mipmaps = 1; // 1 or 0?
+ mm.fb = RD::get_singleton()->framebuffer_create(fb);
+ }
- mm.half_texture = RD::get_singleton()->texture_create(tf, RD::TextureView());
+ if (!_render_buffers_can_be_storage()) {
+ // and half texture, this is an intermediate result so just allocate a texture, is this good enough?
+ tf.width = MAX(1, base_width >> 1);
+ tf.height = base_height;
+ tf.texture_type = RD::TEXTURE_TYPE_2D;
+ tf.array_layers = 1;
+ tf.mipmaps = 1;
+
+ mm.half_texture = RD::get_singleton()->texture_create(tf, RD::TextureView());
+
+ Vector<RID> half_fb;
+ half_fb.push_back(mm.half_texture);
+ mm.half_fb = RD::get_singleton()->framebuffer_create(half_fb);
+ }
- Vector<RID> half_fb;
- half_fb.push_back(mm.half_texture);
- mm.half_fb = RD::get_singleton()->framebuffer_create(half_fb);
- }
+ ll[0].mipmaps.push_back(mm);
- rb->blur[0].mipmaps.push_back(mm);
+ if (i > 0) {
+ mm.texture = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), rb->blur[1].texture, l, i - 1);
- if (i > 0) {
- mm.texture = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), rb->blur[1].texture, 0, i - 1);
+ if (!_render_buffers_can_be_storage()) {
+ Vector<RID> fb;
+ fb.push_back(mm.texture);
- if (!_render_buffers_can_be_storage()) {
- Vector<RID> fb;
- fb.push_back(mm.texture);
+ mm.fb = RD::get_singleton()->framebuffer_create(fb);
- mm.fb = RD::get_singleton()->framebuffer_create(fb);
+ // We can re-use the half texture here as it is an intermediate result
+ }
- // We can re-use the half texture here as it is an intermediate result
+ ll[1].mipmaps.push_back(mm);
}
- rb->blur[1].mipmaps.push_back(mm);
+ base_width = MAX(1, base_width >> 1);
+ base_height = MAX(1, base_height >> 1);
}
- base_width = MAX(1, base_width >> 1);
- base_height = MAX(1, base_height >> 1);
+ rb->blur[0].layers.push_back(ll[0]);
+ rb->blur[1].layers.push_back(ll[1]);
}
if (!_render_buffers_can_be_storage()) {
@@ -1640,21 +1646,19 @@ void RendererSceneRenderRD::_allocate_blur_textures(RenderBuffers *rb) {
tf.format = RD::DATA_FORMAT_R16_SFLOAT; // We could probably use DATA_FORMAT_R8_SNORM if we don't pre-multiply by blur_size but that depends on whether we can remove DEPTH_GAP
tf.width = rb->internal_width;
tf.height = rb->internal_height;
- tf.texture_type = rb->view_count > 1 ? RD::TEXTURE_TYPE_2D_ARRAY : RD::TEXTURE_TYPE_2D;
- tf.array_layers = rb->view_count;
+ tf.texture_type = RD::TEXTURE_TYPE_2D;
+ tf.array_layers = 1; // Our DOF effect handles one eye per turn
tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_COPY_TO_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
tf.mipmaps = 1;
for (uint32_t i = 0; i < 4; i++) {
// associated blur texture
RID texture;
- if (i == 0) {
- texture = rb->texture;
- } else if (i == 1) {
- texture = rb->blur[0].mipmaps[0].texture;
+ if (i == 1) {
+ texture = rb->blur[0].layers[0].mipmaps[0].texture;
} else if (i == 2) {
- texture = rb->blur[1].mipmaps[0].texture;
+ texture = rb->blur[1].layers[0].mipmaps[0].texture;
} else if (i == 3) {
- texture = rb->blur[0].mipmaps[1].texture;
+ texture = rb->blur[0].layers[0].mipmaps[1].texture;
}
// create weight texture
@@ -1662,7 +1666,9 @@ void RendererSceneRenderRD::_allocate_blur_textures(RenderBuffers *rb) {
// create frame buffer
Vector<RID> fb;
- fb.push_back(texture);
+ if (i != 0) {
+ fb.push_back(texture);
+ }
fb.push_back(rb->weight_buffers[i].weight);
rb->weight_buffers[i].fb = RD::get_singleton()->framebuffer_create(fb);
@@ -1672,13 +1678,6 @@ void RendererSceneRenderRD::_allocate_blur_textures(RenderBuffers *rb) {
tf.height = MAX(1u, tf.height >> 1);
}
}
-
- {
- // and finally an FB for just our base weights
- Vector<RID> fb;
- fb.push_back(rb->weight_buffers[0].weight);
- rb->base_weight_fb = RD::get_singleton()->framebuffer_create(fb);
- }
}
}
@@ -1765,6 +1764,21 @@ void RendererSceneRenderRD::_allocate_luminance_textures(RenderBuffers *rb) {
}
void RendererSceneRenderRD::_free_render_buffer_data(RenderBuffers *rb) {
+ if (rb->views.size() > 1) { // if 1 these are copies ofs rb->internal_texture, rb->depth_texture and rb->texture_fb
+ for (int i = 0; i < rb->views.size(); i++) {
+ if (rb->views[i].view_fb.is_valid()) {
+ RD::get_singleton()->free(rb->views[i].view_fb);
+ }
+ if (rb->views[i].view_texture.is_valid()) {
+ RD::get_singleton()->free(rb->views[i].view_texture);
+ }
+ if (rb->views[i].view_depth.is_valid()) {
+ RD::get_singleton()->free(rb->views[i].view_depth);
+ }
+ }
+ }
+ rb->views.clear();
+
if (rb->texture_fb.is_valid()) {
RD::get_singleton()->free(rb->texture_fb);
rb->texture_fb = RID();
@@ -1813,24 +1827,26 @@ void RendererSceneRenderRD::_free_render_buffer_data(RenderBuffers *rb) {
}
for (int i = 0; i < 2; i++) {
- for (int m = 0; m < rb->blur[i].mipmaps.size(); m++) {
- // do we free the texture slice here? or is it enough to free the main texture?
+ for (int l = 0; l < rb->blur[i].layers.size(); l++) {
+ for (int m = 0; m < rb->blur[i].layers[l].mipmaps.size(); m++) {
+ // do we free the texture slice here? or is it enough to free the main texture?
- // do free the mobile extra stuff
- if (rb->blur[i].mipmaps[m].fb.is_valid()) {
- RD::get_singleton()->free(rb->blur[i].mipmaps[m].fb);
- }
- // texture and framebuffer in both blur mipmaps are shared, so only free from the first one
- if (i == 0) {
- if (rb->blur[i].mipmaps[m].half_fb.is_valid()) {
- RD::get_singleton()->free(rb->blur[i].mipmaps[m].half_fb);
+ // do free the mobile extra stuff
+ if (rb->blur[i].layers[l].mipmaps[m].fb.is_valid()) {
+ RD::get_singleton()->free(rb->blur[i].layers[l].mipmaps[m].fb);
}
- if (rb->blur[i].mipmaps[m].half_texture.is_valid()) {
- RD::get_singleton()->free(rb->blur[i].mipmaps[m].half_texture);
+ // texture and framebuffer in both blur mipmaps are shared, so only free from the first one
+ if (i == 0) {
+ if (rb->blur[i].layers[l].mipmaps[m].half_fb.is_valid()) {
+ RD::get_singleton()->free(rb->blur[i].layers[l].mipmaps[m].half_fb);
+ }
+ if (rb->blur[i].layers[l].mipmaps[m].half_texture.is_valid()) {
+ RD::get_singleton()->free(rb->blur[i].layers[l].mipmaps[m].half_texture);
+ }
}
}
}
- rb->blur[i].mipmaps.clear();
+ rb->blur[i].layers.clear();
if (rb->blur[i].texture.is_valid()) {
RD::get_singleton()->free(rb->blur[i].texture);
@@ -1998,8 +2014,8 @@ void RendererSceneRenderRD::_process_ssr(RID p_render_buffers, RID p_dest_frameb
_allocate_blur_textures(rb);
}
- storage->get_effects()->screen_space_reflection(rb->internal_texture, p_normal_buffer, ssr_roughness_quality, rb->ssr.blur_radius[0], rb->ssr.blur_radius[1], p_metallic, p_metallic_mask, rb->depth_texture, rb->ssr.depth_scaled, rb->ssr.normal_scaled, rb->blur[0].mipmaps[1].texture, rb->blur[1].mipmaps[0].texture, Size2i(rb->internal_width / 2, rb->internal_height / 2), env->ssr_max_steps, env->ssr_fade_in, env->ssr_fade_out, env->ssr_depth_tolerance, p_projection);
- storage->get_effects()->merge_specular(p_dest_framebuffer, p_specular_buffer, p_use_additive ? RID() : rb->internal_texture, rb->blur[0].mipmaps[1].texture);
+ storage->get_effects()->screen_space_reflection(rb->internal_texture, p_normal_buffer, ssr_roughness_quality, rb->ssr.blur_radius[0], rb->ssr.blur_radius[1], p_metallic, p_metallic_mask, rb->depth_texture, rb->ssr.depth_scaled, rb->ssr.normal_scaled, rb->blur[0].layers[0].mipmaps[1].texture, rb->blur[1].layers[0].mipmaps[0].texture, Size2i(rb->internal_width / 2, rb->internal_height / 2), env->ssr_max_steps, env->ssr_fade_in, env->ssr_fade_out, env->ssr_depth_tolerance, p_projection);
+ storage->get_effects()->merge_specular(p_dest_framebuffer, p_specular_buffer, p_use_additive ? RID() : rb->internal_texture, rb->blur[0].layers[0].mipmaps[1].texture);
}
void RendererSceneRenderRD::_process_ssao(RID p_render_buffers, RID p_environment, RID p_normal_buffer, const CameraMatrix &p_projection) {
@@ -2295,14 +2311,14 @@ void RendererSceneRenderRD::_copy_framebuffer_to_ssil(RID p_render_buffers) {
ERR_FAIL_COND(!rb);
if (rb->ss_effects.last_frame.is_valid()) {
- storage->get_effects()->copy_to_rect(rb->texture, rb->ss_effects.last_frame, Rect2i(0, 0, rb->width, rb->height));
+ copy_effects->copy_to_rect(rb->texture, rb->ss_effects.last_frame, Rect2i(0, 0, rb->width, rb->height));
int width = rb->width;
int height = rb->height;
for (int i = 0; i < rb->ss_effects.last_frame_slices.size() - 1; i++) {
width = MAX(1, width >> 1);
height = MAX(1, height >> 1);
- storage->get_effects()->make_mipmap(rb->ss_effects.last_frame_slices[i], rb->ss_effects.last_frame_slices[i + 1], Size2i(width, height));
+ copy_effects->make_mipmap(rb->ss_effects.last_frame_slices[i], rb->ss_effects.last_frame_slices[i + 1], Size2i(width, height));
}
}
}
@@ -2317,19 +2333,19 @@ void RendererSceneRenderRD::_render_buffers_copy_screen_texture(const RenderData
_allocate_blur_textures(rb);
}
- // @TODO IMPLEMENT MULTIVIEW, all effects need to support stereo buffers or effects are only applied to the left eye
-
bool can_use_storage = _render_buffers_can_be_storage();
- if (can_use_storage) {
- storage->get_effects()->copy_to_rect(rb->texture, rb->blur[0].mipmaps[0].texture, Rect2i(0, 0, rb->width, rb->height));
- for (int i = 1; i < rb->blur[0].mipmaps.size(); i++) {
- storage->get_effects()->make_mipmap(rb->blur[0].mipmaps[i - 1].texture, rb->blur[0].mipmaps[i].texture, Size2i(rb->blur[0].mipmaps[i].width, rb->blur[0].mipmaps[i].height));
- }
- } else {
- storage->get_effects()->copy_to_fb_rect(rb->texture, rb->blur[0].mipmaps[0].fb, Rect2i(0, 0, rb->width, rb->height));
- for (int i = 1; i < rb->blur[0].mipmaps.size(); i++) {
- storage->get_effects()->make_mipmap_raster(rb->blur[0].mipmaps[i - 1].texture, rb->blur[0].mipmaps[i].fb, Size2i(rb->blur[0].mipmaps[i].width, rb->blur[0].mipmaps[i].height));
+ for (uint32_t v = 0; v < rb->view_count; v++) {
+ if (can_use_storage) {
+ copy_effects->copy_to_rect(rb->views[v].view_texture, rb->blur[0].layers[v].mipmaps[0].texture, Rect2i(0, 0, rb->width, rb->height));
+ for (int i = 1; i < rb->blur[0].layers[v].mipmaps.size(); i++) {
+ copy_effects->make_mipmap(rb->blur[0].layers[v].mipmaps[i - 1].texture, rb->blur[0].layers[v].mipmaps[i].texture, Size2i(rb->blur[0].layers[v].mipmaps[i].width, rb->blur[0].layers[v].mipmaps[i].height));
+ }
+ } else {
+ copy_effects->copy_to_fb_rect(rb->views[v].view_texture, rb->blur[0].layers[v].mipmaps[0].fb, Rect2i(0, 0, rb->width, rb->height));
+ for (int i = 1; i < rb->blur[0].layers[v].mipmaps.size(); i++) {
+ copy_effects->make_mipmap_raster(rb->blur[0].layers[v].mipmaps[i - 1].texture, rb->blur[0].layers[v].mipmaps[i].fb, Size2i(rb->blur[0].layers[v].mipmaps[i].width, rb->blur[0].layers[v].mipmaps[i].height));
+ }
}
}
@@ -2351,9 +2367,9 @@ void RendererSceneRenderRD::_render_buffers_copy_depth_texture(const RenderDataR
bool can_use_storage = _render_buffers_can_be_storage();
if (can_use_storage) {
- storage->get_effects()->copy_to_rect(rb->depth_texture, rb->depth_back_texture, Rect2i(0, 0, rb->width, rb->height));
+ copy_effects->copy_to_rect(rb->depth_texture, rb->depth_back_texture, Rect2i(0, 0, rb->width, rb->height));
} else {
- storage->get_effects()->copy_to_fb_rect(rb->depth_texture, rb->depth_back_fb, Rect2i(0, 0, rb->width, rb->height));
+ copy_effects->copy_to_fb_rect(rb->depth_texture, rb->depth_back_fb, Rect2i(0, 0, rb->width, rb->height));
}
RD::get_singleton()->draw_command_end_label();
@@ -2371,30 +2387,33 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende
bool can_use_effects = rb->width >= 8 && rb->height >= 8;
bool can_use_storage = _render_buffers_can_be_storage();
- // @TODO IMPLEMENT MULTIVIEW, all effects need to support stereo buffers or effects are only applied to the left eye
-
if (can_use_effects && camfx && (camfx->dof_blur_near_enabled || camfx->dof_blur_far_enabled) && camfx->dof_blur_amount > 0.0) {
RD::get_singleton()->draw_command_begin_label("DOF");
if (rb->blur[0].texture.is_null()) {
_allocate_blur_textures(rb);
}
- EffectsRD::BokehBuffers buffers;
+ RendererRD::BokehDOF::BokehBuffers buffers;
// Textures we use
buffers.base_texture_size = Size2i(rb->internal_width, rb->internal_height);
- buffers.base_texture = rb->internal_texture;
- buffers.depth_texture = rb->depth_texture;
- buffers.secondary_texture = rb->blur[0].mipmaps[0].texture;
- buffers.half_texture[0] = rb->blur[1].mipmaps[0].texture;
- buffers.half_texture[1] = rb->blur[0].mipmaps[1].texture;
+ buffers.secondary_texture = rb->blur[0].layers[0].mipmaps[0].texture;
+ buffers.half_texture[0] = rb->blur[1].layers[0].mipmaps[0].texture;
+ buffers.half_texture[1] = rb->blur[0].layers[0].mipmaps[1].texture;
float bokeh_size = camfx->dof_blur_amount * 64.0;
if (can_use_storage) {
- storage->get_effects()->bokeh_dof(buffers, camfx->dof_blur_far_enabled, camfx->dof_blur_far_distance, camfx->dof_blur_far_transition, camfx->dof_blur_near_enabled, camfx->dof_blur_near_distance, camfx->dof_blur_near_transition, bokeh_size, dof_blur_bokeh_shape, dof_blur_quality, dof_blur_use_jitter, p_render_data->z_near, p_render_data->z_far, p_render_data->cam_orthogonal);
+ for (uint32_t i = 0; i < rb->view_count; i++) {
+ buffers.base_texture = rb->views[i].view_texture;
+ buffers.depth_texture = rb->views[i].view_depth;
+
+ // In stereo p_render_data->z_near and p_render_data->z_far can be offset for our combined frustrum
+ float z_near = p_render_data->view_projection[i].get_z_near();
+ float z_far = p_render_data->view_projection[i].get_z_far();
+ bokeh_dof->bokeh_dof_compute(buffers, camfx->dof_blur_far_enabled, camfx->dof_blur_far_distance, camfx->dof_blur_far_transition, camfx->dof_blur_near_enabled, camfx->dof_blur_near_distance, camfx->dof_blur_near_transition, bokeh_size, dof_blur_bokeh_shape, dof_blur_quality, dof_blur_use_jitter, z_near, z_far, p_render_data->cam_orthogonal);
+ };
} else {
// Set framebuffers.
- buffers.base_fb = rb->texture_fb;
buffers.secondary_fb = rb->weight_buffers[1].fb;
buffers.half_fb[0] = rb->weight_buffers[2].fb;
buffers.half_fb[1] = rb->weight_buffers[3].fb;
@@ -2404,9 +2423,18 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende
buffers.weight_texture[3] = rb->weight_buffers[3].weight;
// Set weight buffers.
- buffers.base_weight_fb = rb->base_weight_fb;
+ buffers.base_weight_fb = rb->weight_buffers[0].fb;
+
+ for (uint32_t i = 0; i < rb->view_count; i++) {
+ buffers.base_texture = rb->views[i].view_texture;
+ buffers.depth_texture = rb->views[i].view_depth;
+ buffers.base_fb = rb->views[i].view_fb;
- storage->get_effects()->bokeh_dof_raster(buffers, camfx->dof_blur_far_enabled, camfx->dof_blur_far_distance, camfx->dof_blur_far_transition, camfx->dof_blur_near_enabled, camfx->dof_blur_near_distance, camfx->dof_blur_near_transition, bokeh_size, dof_blur_bokeh_shape, dof_blur_quality, p_render_data->z_near, p_render_data->z_far, p_render_data->cam_orthogonal);
+ // In stereo p_render_data->z_near and p_render_data->z_far can be offset for our combined frustrum
+ float z_near = p_render_data->view_projection[i].get_z_near();
+ float z_far = p_render_data->view_projection[i].get_z_far();
+ bokeh_dof->bokeh_dof_raster(buffers, camfx->dof_blur_far_enabled, camfx->dof_blur_far_distance, camfx->dof_blur_far_transition, camfx->dof_blur_near_enabled, camfx->dof_blur_near_distance, camfx->dof_blur_near_transition, bokeh_size, dof_blur_bokeh_shape, dof_blur_quality, z_near, z_far, p_render_data->cam_orthogonal);
+ }
}
RD::get_singleton()->draw_command_end_label();
}
@@ -2449,33 +2477,36 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende
for (int i = 0; i < RS::MAX_GLOW_LEVELS; i++) {
if (env->glow_levels[i] > 0.0) {
- if (i >= rb->blur[1].mipmaps.size()) {
- max_glow_level = rb->blur[1].mipmaps.size() - 1;
+ if (i >= rb->blur[1].layers[0].mipmaps.size()) {
+ max_glow_level = rb->blur[1].layers[0].mipmaps.size() - 1;
} else {
max_glow_level = i;
}
}
}
- for (int i = 0; i < (max_glow_level + 1); i++) {
- int vp_w = rb->blur[1].mipmaps[i].width;
- int vp_h = rb->blur[1].mipmaps[i].height;
+ float luminance_multiplier = _render_buffers_get_luminance_multiplier();
+ for (uint32_t l = 0; l < rb->view_count; l++) {
+ for (int i = 0; i < (max_glow_level + 1); i++) {
+ int vp_w = rb->blur[1].layers[l].mipmaps[i].width;
+ int vp_h = rb->blur[1].layers[l].mipmaps[i].height;
- if (i == 0) {
- RID luminance_texture;
- if (env->auto_exposure && rb->luminance.current.is_valid()) {
- luminance_texture = rb->luminance.current;
- }
- if (can_use_storage) {
- storage->get_effects()->gaussian_glow(rb->internal_texture, rb->blur[1].mipmaps[i].texture, Size2i(vp_w, vp_h), env->glow_strength, glow_high_quality, true, env->glow_hdr_luminance_cap, env->exposure, env->glow_bloom, env->glow_hdr_bleed_threshold, env->glow_hdr_bleed_scale, luminance_texture, env->auto_exp_scale);
- } else {
- storage->get_effects()->gaussian_glow_raster(rb->internal_texture, rb->blur[1].mipmaps[i].half_fb, rb->blur[1].mipmaps[i].half_texture, rb->blur[1].mipmaps[i].fb, Size2i(vp_w, vp_h), env->glow_strength, glow_high_quality, true, env->glow_hdr_luminance_cap, env->exposure, env->glow_bloom, env->glow_hdr_bleed_threshold, env->glow_hdr_bleed_scale, luminance_texture, env->auto_exp_scale);
- }
- } else {
- if (can_use_storage) {
- storage->get_effects()->gaussian_glow(rb->blur[1].mipmaps[i - 1].texture, rb->blur[1].mipmaps[i].texture, Size2i(vp_w, vp_h), env->glow_strength, glow_high_quality);
+ if (i == 0) {
+ RID luminance_texture;
+ if (env->auto_exposure && rb->luminance.current.is_valid()) {
+ luminance_texture = rb->luminance.current;
+ }
+ if (can_use_storage) {
+ copy_effects->gaussian_glow(rb->views[l].view_texture, rb->blur[1].layers[l].mipmaps[i].texture, Size2i(vp_w, vp_h), env->glow_strength, glow_high_quality, true, env->glow_hdr_luminance_cap, env->exposure, env->glow_bloom, env->glow_hdr_bleed_threshold, env->glow_hdr_bleed_scale, luminance_texture, env->auto_exp_scale);
+ } else {
+ copy_effects->gaussian_glow_raster(rb->views[l].view_texture, luminance_multiplier, rb->blur[1].layers[l].mipmaps[i].half_fb, rb->blur[1].layers[l].mipmaps[i].half_texture, rb->blur[1].layers[l].mipmaps[i].fb, Size2i(vp_w, vp_h), env->glow_strength, glow_high_quality, true, env->glow_hdr_luminance_cap, env->exposure, env->glow_bloom, env->glow_hdr_bleed_threshold, env->glow_hdr_bleed_scale, luminance_texture, env->auto_exp_scale);
+ }
} else {
- storage->get_effects()->gaussian_glow_raster(rb->blur[1].mipmaps[i - 1].texture, rb->blur[1].mipmaps[i].half_fb, rb->blur[1].mipmaps[i].half_texture, rb->blur[1].mipmaps[i].fb, Vector2(1.0 / vp_w, 1.0 / vp_h), env->glow_strength, glow_high_quality);
+ if (can_use_storage) {
+ copy_effects->gaussian_glow(rb->blur[1].layers[l].mipmaps[i - 1].texture, rb->blur[1].layers[l].mipmaps[i].texture, Size2i(vp_w, vp_h), env->glow_strength, glow_high_quality);
+ } else {
+ copy_effects->gaussian_glow_raster(rb->blur[1].layers[l].mipmaps[i - 1].texture, luminance_multiplier, rb->blur[1].layers[l].mipmaps[i].half_fb, rb->blur[1].layers[l].mipmaps[i].half_texture, rb->blur[1].layers[l].mipmaps[i].fb, Size2i(vp_w, vp_h), env->glow_strength, glow_high_quality);
+ }
}
}
}
@@ -2503,8 +2534,8 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende
for (int i = 0; i < RS::MAX_GLOW_LEVELS; i++) {
tonemap.glow_levels[i] = env->glow_levels[i];
}
- tonemap.glow_texture_size.x = rb->blur[1].mipmaps[0].width;
- tonemap.glow_texture_size.y = rb->blur[1].mipmaps[0].height;
+ tonemap.glow_texture_size.x = rb->blur[1].layers[0].mipmaps[0].width;
+ tonemap.glow_texture_size.y = rb->blur[1].layers[0].mipmaps[0].height;
tonemap.glow_use_bicubic_upscale = glow_bicubic_upscale;
tonemap.glow_texture = rb->blur[1].texture;
if (env->glow_map.is_valid()) {
@@ -2562,7 +2593,7 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende
}
if (can_use_effects && can_use_storage && (rb->internal_width != rb->width || rb->internal_height != rb->height)) {
- RD::get_singleton()->draw_command_begin_label("FSR Upscale");
+ RD::get_singleton()->draw_command_begin_label("FSR 1.0 Upscale");
storage->get_effects()->fsr_upscale(rb->internal_texture, rb->upscale_texture, rb->texture, Size2i(rb->internal_width, rb->internal_height), Size2i(rb->width, rb->height), rb->fsr_sharpness);
@@ -2652,7 +2683,6 @@ void RendererSceneRenderRD::_disable_clear_request(const RenderDataRD *p_render_
void RendererSceneRenderRD::_render_buffers_debug_draw(RID p_render_buffers, RID p_shadow_atlas, RID p_occlusion_buffer) {
RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton();
- EffectsRD *effects = storage->get_effects();
RenderBuffers *rb = render_buffers_owner.get_or_null(p_render_buffers);
ERR_FAIL_COND(!rb);
@@ -2666,7 +2696,7 @@ void RendererSceneRenderRD::_render_buffers_debug_draw(RID p_render_buffers, RID
}
Size2 rtsize = texture_storage->render_target_get_size(rb->render_target);
- effects->copy_to_fb_rect(shadow_atlas_texture, texture_storage->render_target_get_rd_framebuffer(rb->render_target), Rect2i(Vector2(), rtsize / 2), false, true);
+ copy_effects->copy_to_fb_rect(shadow_atlas_texture, texture_storage->render_target_get_rd_framebuffer(rb->render_target), Rect2i(Vector2(), rtsize / 2), false, true);
}
}
@@ -2675,7 +2705,7 @@ void RendererSceneRenderRD::_render_buffers_debug_draw(RID p_render_buffers, RID
RID shadow_atlas_texture = directional_shadow_get_texture();
Size2 rtsize = texture_storage->render_target_get_size(rb->render_target);
- effects->copy_to_fb_rect(shadow_atlas_texture, texture_storage->render_target_get_rd_framebuffer(rb->render_target), Rect2i(Vector2(), rtsize / 2), false, true);
+ copy_effects->copy_to_fb_rect(shadow_atlas_texture, texture_storage->render_target_get_rd_framebuffer(rb->render_target), Rect2i(Vector2(), rtsize / 2), false, true);
}
}
@@ -2685,7 +2715,7 @@ void RendererSceneRenderRD::_render_buffers_debug_draw(RID p_render_buffers, RID
if (decal_atlas.is_valid()) {
Size2 rtsize = texture_storage->render_target_get_size(rb->render_target);
- effects->copy_to_fb_rect(decal_atlas, texture_storage->render_target_get_rd_framebuffer(rb->render_target), Rect2i(Vector2(), rtsize / 2), false, false, true);
+ copy_effects->copy_to_fb_rect(decal_atlas, texture_storage->render_target_get_rd_framebuffer(rb->render_target), Rect2i(Vector2(), rtsize / 2), false, false, true);
}
}
@@ -2693,36 +2723,36 @@ void RendererSceneRenderRD::_render_buffers_debug_draw(RID p_render_buffers, RID
if (rb->luminance.current.is_valid()) {
Size2 rtsize = texture_storage->render_target_get_size(rb->render_target);
- effects->copy_to_fb_rect(rb->luminance.current, texture_storage->render_target_get_rd_framebuffer(rb->render_target), Rect2(Vector2(), rtsize / 8), false, true);
+ copy_effects->copy_to_fb_rect(rb->luminance.current, texture_storage->render_target_get_rd_framebuffer(rb->render_target), Rect2(Vector2(), rtsize / 8), false, true);
}
}
if (debug_draw == RS::VIEWPORT_DEBUG_DRAW_SSAO && rb->ss_effects.ssao.ao_final.is_valid()) {
Size2 rtsize = texture_storage->render_target_get_size(rb->render_target);
- effects->copy_to_fb_rect(rb->ss_effects.ssao.ao_final, texture_storage->render_target_get_rd_framebuffer(rb->render_target), Rect2(Vector2(), rtsize), false, true);
+ copy_effects->copy_to_fb_rect(rb->ss_effects.ssao.ao_final, texture_storage->render_target_get_rd_framebuffer(rb->render_target), Rect2(Vector2(), rtsize), false, true);
}
if (debug_draw == RS::VIEWPORT_DEBUG_DRAW_SSIL && rb->ss_effects.ssil.ssil_final.is_valid()) {
Size2 rtsize = texture_storage->render_target_get_size(rb->render_target);
- effects->copy_to_fb_rect(rb->ss_effects.ssil.ssil_final, texture_storage->render_target_get_rd_framebuffer(rb->render_target), Rect2(Vector2(), rtsize), false, false);
+ copy_effects->copy_to_fb_rect(rb->ss_effects.ssil.ssil_final, texture_storage->render_target_get_rd_framebuffer(rb->render_target), Rect2(Vector2(), rtsize), false, false);
}
if (debug_draw == RS::VIEWPORT_DEBUG_DRAW_NORMAL_BUFFER && _render_buffers_get_normal_texture(p_render_buffers).is_valid()) {
Size2 rtsize = texture_storage->render_target_get_size(rb->render_target);
- effects->copy_to_fb_rect(_render_buffers_get_normal_texture(p_render_buffers), texture_storage->render_target_get_rd_framebuffer(rb->render_target), Rect2(Vector2(), rtsize), false, false);
+ copy_effects->copy_to_fb_rect(_render_buffers_get_normal_texture(p_render_buffers), texture_storage->render_target_get_rd_framebuffer(rb->render_target), Rect2(Vector2(), rtsize), false, false);
}
if (debug_draw == RS::VIEWPORT_DEBUG_DRAW_GI_BUFFER && rb->ambient_buffer.is_valid()) {
Size2 rtsize = texture_storage->render_target_get_size(rb->render_target);
RID ambient_texture = rb->ambient_buffer;
RID reflection_texture = rb->reflection_buffer;
- effects->copy_to_fb_rect(ambient_texture, texture_storage->render_target_get_rd_framebuffer(rb->render_target), Rect2(Vector2(), rtsize), false, false, false, true, reflection_texture);
+ copy_effects->copy_to_fb_rect(ambient_texture, texture_storage->render_target_get_rd_framebuffer(rb->render_target), Rect2(Vector2(), rtsize), false, false, false, true, reflection_texture);
}
if (debug_draw == RS::VIEWPORT_DEBUG_DRAW_OCCLUDERS) {
if (p_occlusion_buffer.is_valid()) {
Size2 rtsize = texture_storage->render_target_get_size(rb->render_target);
- effects->copy_to_fb_rect(texture_storage->texture_get_rd_texture(p_occlusion_buffer), texture_storage->render_target_get_rd_framebuffer(rb->render_target), Rect2i(Vector2(), rtsize), true, false);
+ copy_effects->copy_to_fb_rect(texture_storage->texture_get_rd_texture(p_occlusion_buffer), texture_storage->render_target_get_rd_framebuffer(rb->render_target), Rect2i(Vector2(), rtsize), true, false);
}
}
}
@@ -3035,12 +3065,38 @@ void RendererSceneRenderRD::render_buffers_configure(RID p_render_buffers, RID p
rb->depth_texture = RD::get_singleton()->texture_create(tf, RD::TextureView());
}
- if (!_render_buffers_can_be_storage()) {
- // ONLY USED ON MOBILE RENDERER, ONLY USED FOR POST EFFECTS!
- Vector<RID> fb;
- fb.push_back(rb->internal_texture);
+ {
+ if (!_render_buffers_can_be_storage()) {
+ // ONLY USED ON MOBILE RENDERER, ONLY USED FOR POST EFFECTS!
+ Vector<RID> fb;
+ fb.push_back(rb->internal_texture);
+
+ rb->texture_fb = RD::get_singleton()->framebuffer_create(fb, RenderingDevice::INVALID_ID, rb->view_count);
+ }
- rb->texture_fb = RD::get_singleton()->framebuffer_create(fb, RenderingDevice::INVALID_ID, rb->view_count);
+ rb->views.clear(); // JIC
+ if (rb->view_count == 1) {
+ // copy as a convenience
+ RenderBuffers::View view;
+ view.view_texture = rb->internal_texture;
+ view.view_depth = rb->depth_texture;
+ view.view_fb = rb->texture_fb;
+ rb->views.push_back(view);
+ } else {
+ for (uint32_t i = 0; i < rb->view_count; i++) {
+ RenderBuffers::View view;
+ view.view_texture = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), rb->internal_texture, i, 0);
+ view.view_depth = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), rb->depth_texture, i, 0);
+
+ if (!_render_buffers_can_be_storage()) {
+ Vector<RID> fb;
+ fb.push_back(view.view_texture);
+ view.view_fb = RD::get_singleton()->framebuffer_create(fb, RenderingDevice::INVALID_ID, 1);
+ }
+
+ rb->views.push_back(view);
+ }
+ }
}
RID target_texture = texture_storage->render_target_get_rd_texture(rb->render_target);
@@ -5836,12 +5892,21 @@ void fog() {
cull_argument.set_page_pool(&cull_argument_pool);
+ bool can_use_storage = _render_buffers_can_be_storage();
+ bokeh_dof = memnew(RendererRD::BokehDOF(!can_use_storage));
+ copy_effects = memnew(RendererRD::CopyEffects(!can_use_storage));
tone_mapper = memnew(RendererRD::ToneMapper);
}
RendererSceneRenderRD::~RendererSceneRenderRD() {
RendererRD::MaterialStorage *material_storage = RendererRD::MaterialStorage::get_singleton();
+ if (bokeh_dof) {
+ memdelete(bokeh_dof);
+ }
+ if (copy_effects) {
+ memdelete(copy_effects);
+ }
if (tone_mapper) {
memdelete(tone_mapper);
}
diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.h b/servers/rendering/renderer_rd/renderer_scene_render_rd.h
index 6bf3a95dd0..f60b7c7232 100644
--- a/servers/rendering/renderer_rd/renderer_scene_render_rd.h
+++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.h
@@ -35,6 +35,8 @@
#include "core/templates/rid_owner.h"
#include "servers/rendering/renderer_compositor.h"
#include "servers/rendering/renderer_rd/cluster_builder_rd.h"
+#include "servers/rendering/renderer_rd/effects/bokeh_dof.h"
+#include "servers/rendering/renderer_rd/effects/copy_effects.h"
#include "servers/rendering/renderer_rd/effects/tone_mapper.h"
#include "servers/rendering/renderer_rd/renderer_scene_environment_rd.h"
#include "servers/rendering/renderer_rd/renderer_scene_gi_rd.h"
@@ -94,6 +96,8 @@ class RendererSceneRenderRD : public RendererSceneRender {
protected:
RendererStorageRD *storage = nullptr;
+ RendererRD::BokehDOF *bokeh_dof = nullptr;
+ RendererRD::CopyEffects *copy_effects = nullptr;
RendererRD::ToneMapper *tone_mapper = nullptr;
double time = 0.0;
double time_step = 0.0;
@@ -482,6 +486,14 @@ private:
RID texture_fb; // framebuffer for the main texture, ONLY USED FOR MOBILE RENDERER POST EFFECTS, DO NOT USE FOR RENDERING 3D!!!
RID upscale_texture; //used when upscaling internal_texture (This uses the same resource as internal_texture if there is no upscaling)
+ // Access to the layers for each of our views (specifically needed for applying post effects on stereoscopic images)
+ struct View {
+ RID view_texture; // texture slice for this view/layer
+ RID view_depth; // depth slice for this view/layer
+ RID view_fb; // framebuffer for this view/layer, ONLY USED FOR MOBILE RENDERER POST EFFECTS, DO NOT USE FOR RENDERING 3D!!!
+ };
+ Vector<View> views;
+
RendererSceneGIRD::SDFGI *sdfgi = nullptr;
VolumetricFog *volumetric_fog = nullptr;
RendererSceneGIRD::RenderBuffersGI gi;
@@ -503,19 +515,22 @@ private:
RID half_fb;
};
- Vector<Mipmap> mipmaps;
+ struct Layer {
+ Vector<Mipmap> mipmaps;
+ };
+
+ Vector<Layer> layers;
};
Blur blur[2]; //the second one starts from the first mipmap
struct WeightBuffers {
RID weight;
- RID fb; // FB with both texture and weight
+ RID fb; // FB with both texture and weight writing into one level lower
};
// 2 full size, 2 half size
WeightBuffers weight_buffers[4]; // Only used in raster
- RID base_weight_fb; // base buffer for weight
RID depth_back_texture;
RID depth_back_fb; // only used on mobile
diff --git a/servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp
index 51cf0e952b..4e62dee6af 100644
--- a/servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp
@@ -32,6 +32,7 @@
#include "core/config/project_settings.h"
#include "core/math/math_defs.h"
#include "renderer_scene_render_rd.h"
+#include "servers/rendering/renderer_rd/effects/copy_effects.h"
#include "servers/rendering/renderer_rd/renderer_compositor_rd.h"
#include "servers/rendering/renderer_rd/storage_rd/material_storage.h"
#include "servers/rendering/renderer_rd/storage_rd/texture_storage.h"
@@ -730,8 +731,10 @@ bool RendererSceneSkyRD::Sky::set_material(RID p_material) {
return true;
}
-Ref<Image> RendererSceneSkyRD::Sky::bake_panorama(RendererStorageRD *p_storage, float p_energy, int p_roughness_layers, const Size2i &p_size) {
+Ref<Image> RendererSceneSkyRD::Sky::bake_panorama(float p_energy, int p_roughness_layers, const Size2i &p_size) {
if (radiance.is_valid()) {
+ RendererRD::CopyEffects *copy_effects = RendererRD::CopyEffects::get_singleton();
+
RD::TextureFormat tf;
tf.format = RD::DATA_FORMAT_R32G32B32A32_SFLOAT;
tf.width = p_size.width;
@@ -739,7 +742,7 @@ Ref<Image> RendererSceneSkyRD::Sky::bake_panorama(RendererStorageRD *p_storage,
tf.usage_bits = RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT;
RID rad_tex = RD::get_singleton()->texture_create(tf, RD::TextureView());
- p_storage->get_effects()->copy_cubemap_to_panorama(radiance, rad_tex, p_size, p_roughness_layers, reflection.layers.size() > 1);
+ copy_effects->copy_cubemap_to_panorama(radiance, rad_tex, p_size, p_roughness_layers, reflection.layers.size() > 1);
Vector<uint8_t> data = RD::get_singleton()->texture_get_data(rad_tex, 0);
RD::get_singleton()->free(rad_tex);
@@ -1905,7 +1908,7 @@ Ref<Image> RendererSceneSkyRD::sky_bake_panorama(RID p_sky, float p_energy, bool
update_dirty_skys();
- return sky->bake_panorama(storage, p_energy, p_bake_irradiance ? roughness_layers : 0, p_size);
+ return sky->bake_panorama(p_energy, p_bake_irradiance ? roughness_layers : 0, p_size);
}
RID RendererSceneSkyRD::sky_get_radiance_texture_rd(RID p_sky) const {
diff --git a/servers/rendering/renderer_rd/renderer_scene_sky_rd.h b/servers/rendering/renderer_rd/renderer_scene_sky_rd.h
index 497d27ec26..ec225019c8 100644
--- a/servers/rendering/renderer_rd/renderer_scene_sky_rd.h
+++ b/servers/rendering/renderer_rd/renderer_scene_sky_rd.h
@@ -273,7 +273,7 @@ public:
bool set_radiance_size(int p_radiance_size);
bool set_mode(RS::SkyMode p_mode);
bool set_material(RID p_material);
- Ref<Image> bake_panorama(RendererStorageRD *p_storage, float p_energy, int p_roughness_layers, const Size2i &p_size);
+ Ref<Image> bake_panorama(float p_energy, int p_roughness_layers, const Size2i &p_size);
};
uint32_t sky_ggx_samples_quality;
diff --git a/servers/rendering/renderer_rd/shaders/blur_raster_inc.glsl b/servers/rendering/renderer_rd/shaders/blur_raster_inc.glsl
deleted file mode 100644
index e7a2e18323..0000000000
--- a/servers/rendering/renderer_rd/shaders/blur_raster_inc.glsl
+++ /dev/null
@@ -1,21 +0,0 @@
-#define FLAG_HORIZONTAL (1 << 0)
-#define FLAG_USE_ORTHOGONAL_PROJECTION (1 << 1)
-#define FLAG_GLOW_FIRST_PASS (1 << 2)
-
-layout(push_constant, std430) uniform Blur {
- vec2 pixel_size;
- uint flags;
- uint pad;
-
- // Glow.
- float glow_strength;
- float glow_bloom;
- float glow_hdr_threshold;
- float glow_hdr_scale;
-
- float glow_exposure;
- float glow_white;
- float glow_luminance_cap;
- float glow_auto_exposure_grey;
-}
-blur;
diff --git a/servers/rendering/renderer_rd/shaders/blur_raster.glsl b/servers/rendering/renderer_rd/shaders/effects/blur_raster.glsl
index f8b4e3f610..96f5c3e9f2 100644
--- a/servers/rendering/renderer_rd/shaders/blur_raster.glsl
+++ b/servers/rendering/renderer_rd/shaders/effects/blur_raster.glsl
@@ -53,7 +53,9 @@ void main() {
#ifdef MODE_GAUSSIAN_BLUR
- //Simpler blur uses SIGMA2 for the gaussian kernel for a stronger effect
+ // Simpler blur uses SIGMA2 for the gaussian kernel for a stronger effect
+
+ // note, for blur blur.luminance_multiplier is irrelavant, we would be multiplying and then dividing by this amount.
if (bool(blur.flags & FLAG_HORIZONTAL)) {
vec2 pix_size = blur.pixel_size;
@@ -94,6 +96,7 @@ void main() {
if (bool(blur.flags & FLAG_HORIZONTAL)) {
vec2 pix_size = blur.pixel_size;
pix_size *= 0.5; //reading from larger buffer, so use more samples
+
vec4 color = texture(source_color, uv_interp + vec2(0.0, 0.0) * pix_size) * 0.174938;
GLOW_ADD(vec2(1.0, 0.0), 0.165569);
GLOW_ADD(vec2(2.0, 0.0), 0.140367);
@@ -101,7 +104,10 @@ void main() {
GLOW_ADD(vec2(-1.0, 0.0), 0.165569);
GLOW_ADD(vec2(-2.0, 0.0), 0.140367);
GLOW_ADD(vec2(-3.0, 0.0), 0.106595);
+
+ // only do this in the horizontal pass, if we also do this in the vertical pass we're doubling up.
color *= blur.glow_strength;
+
frag_color = color;
} else {
vec2 pix_size = blur.pixel_size;
@@ -110,13 +116,17 @@ void main() {
GLOW_ADD(vec2(0.0, 2.0), 0.122581);
GLOW_ADD(vec2(0.0, -1.0), 0.233062);
GLOW_ADD(vec2(0.0, -2.0), 0.122581);
- color *= blur.glow_strength;
+
frag_color = color;
}
#undef GLOW_ADD
if (bool(blur.flags & FLAG_GLOW_FIRST_PASS)) {
+ // In the first pass bring back to correct color range else we're applying the wrong threshold
+ // in subsequent passes we can use it as is as we'd just be undoing it right after.
+ frag_color *= blur.luminance_multiplier;
+
#ifdef GLOW_USE_AUTO_EXPOSURE
frag_color /= texelFetch(source_auto_exposure, ivec2(0, 0), 0).r / blur.glow_auto_exposure_grey;
@@ -126,10 +136,10 @@ void main() {
float luminance = max(frag_color.r, max(frag_color.g, frag_color.b));
float feedback = max(smoothstep(blur.glow_hdr_threshold, blur.glow_hdr_threshold + blur.glow_hdr_scale, luminance), blur.glow_bloom);
- frag_color = min(frag_color * feedback, vec4(blur.glow_luminance_cap));
+ frag_color = min(frag_color * feedback, vec4(blur.glow_luminance_cap)) / blur.luminance_multiplier;
}
-#endif
+#endif // MODE_GAUSSIAN_GLOW
#ifdef MODE_COPY
vec4 color = textureLod(source_color, uv_interp, 0.0);
diff --git a/servers/rendering/renderer_rd/shaders/effects/blur_raster_inc.glsl b/servers/rendering/renderer_rd/shaders/effects/blur_raster_inc.glsl
new file mode 100644
index 0000000000..730504571a
--- /dev/null
+++ b/servers/rendering/renderer_rd/shaders/effects/blur_raster_inc.glsl
@@ -0,0 +1,26 @@
+#define FLAG_HORIZONTAL (1 << 0)
+#define FLAG_USE_ORTHOGONAL_PROJECTION (1 << 1)
+#define FLAG_GLOW_FIRST_PASS (1 << 2)
+
+layout(push_constant, std430) uniform Blur {
+ vec2 pixel_size; // 08 - 08
+ uint flags; // 04 - 12
+ uint pad; // 04 - 16
+
+ // Glow.
+ float glow_strength; // 04 - 20
+ float glow_bloom; // 04 - 24
+ float glow_hdr_threshold; // 04 - 28
+ float glow_hdr_scale; // 04 - 32
+
+ float glow_exposure; // 04 - 36
+ float glow_white; // 04 - 40
+ float glow_luminance_cap; // 04 - 44
+ float glow_auto_exposure_grey; // 04 - 48
+
+ float luminance_multiplier; // 04 - 52
+ float res1; // 04 - 56
+ float res2; // 04 - 60
+ float res3; // 04 - 64
+}
+blur;
diff --git a/servers/rendering/renderer_rd/shaders/bokeh_dof.glsl b/servers/rendering/renderer_rd/shaders/effects/bokeh_dof.glsl
index 0438671dd2..0438671dd2 100644
--- a/servers/rendering/renderer_rd/shaders/bokeh_dof.glsl
+++ b/servers/rendering/renderer_rd/shaders/effects/bokeh_dof.glsl
diff --git a/servers/rendering/renderer_rd/shaders/bokeh_dof_inc.glsl b/servers/rendering/renderer_rd/shaders/effects/bokeh_dof_inc.glsl
index b90a527554..b90a527554 100644
--- a/servers/rendering/renderer_rd/shaders/bokeh_dof_inc.glsl
+++ b/servers/rendering/renderer_rd/shaders/effects/bokeh_dof_inc.glsl
diff --git a/servers/rendering/renderer_rd/shaders/bokeh_dof_raster.glsl b/servers/rendering/renderer_rd/shaders/effects/bokeh_dof_raster.glsl
index a3b3938ee9..a3b3938ee9 100644
--- a/servers/rendering/renderer_rd/shaders/bokeh_dof_raster.glsl
+++ b/servers/rendering/renderer_rd/shaders/effects/bokeh_dof_raster.glsl
diff --git a/servers/rendering/renderer_rd/shaders/copy.glsl b/servers/rendering/renderer_rd/shaders/effects/copy.glsl
index 4563ac7af9..3a4ef86ef0 100644
--- a/servers/rendering/renderer_rd/shaders/copy.glsl
+++ b/servers/rendering/renderer_rd/shaders/effects/copy.glsl
@@ -189,7 +189,7 @@ void main() {
#endif
color *= params.glow_exposure;
- float luminance = dot(color.rgb, vec3(0.299, 0.587, 0.114));
+ float luminance = max(color.r, max(color.g, color.b));
float feedback = max(smoothstep(params.glow_hdr_threshold, params.glow_hdr_threshold + params.glow_hdr_scale, luminance), params.glow_bloom);
color = min(color * feedback, vec4(params.glow_luminance_cap));
diff --git a/servers/rendering/renderer_rd/shaders/copy_to_fb.glsl b/servers/rendering/renderer_rd/shaders/effects/copy_to_fb.glsl
index 9787c9879d..9787c9879d 100644
--- a/servers/rendering/renderer_rd/shaders/copy_to_fb.glsl
+++ b/servers/rendering/renderer_rd/shaders/effects/copy_to_fb.glsl
diff --git a/servers/rendering/renderer_rd/shaders/effects/tonemap.glsl b/servers/rendering/renderer_rd/shaders/effects/tonemap.glsl
index 19a9350137..5a238452c0 100644
--- a/servers/rendering/renderer_rd/shaders/effects/tonemap.glsl
+++ b/servers/rendering/renderer_rd/shaders/effects/tonemap.glsl
@@ -44,7 +44,11 @@ layout(set = 0, binding = 0) uniform sampler2D source_color;
#endif
layout(set = 1, binding = 0) uniform sampler2D source_auto_exposure;
+#ifdef MULTIVIEW
+layout(set = 2, binding = 0) uniform sampler2DArray source_glow;
+#else
layout(set = 2, binding = 0) uniform sampler2D source_glow;
+#endif
layout(set = 2, binding = 1) uniform sampler2D glow_map;
#ifdef USE_1D_LUT
@@ -118,6 +122,36 @@ float h1(float a) {
return 1.0f + w3(a) / (w2(a) + w3(a));
}
+#ifdef MULTIVIEW
+vec4 texture2D_bicubic(sampler2DArray tex, vec2 uv, int p_lod) {
+ float lod = float(p_lod);
+ vec2 tex_size = vec2(params.glow_texture_size >> p_lod);
+ vec2 pixel_size = vec2(1.0f) / tex_size;
+
+ uv = uv * tex_size + vec2(0.5f);
+
+ vec2 iuv = floor(uv);
+ vec2 fuv = fract(uv);
+
+ float g0x = g0(fuv.x);
+ float g1x = g1(fuv.x);
+ float h0x = h0(fuv.x);
+ float h1x = h1(fuv.x);
+ float h0y = h0(fuv.y);
+ float h1y = h1(fuv.y);
+
+ vec3 p0 = vec3((vec2(iuv.x + h0x, iuv.y + h0y) - vec2(0.5f)) * pixel_size, ViewIndex);
+ vec3 p1 = vec3((vec2(iuv.x + h1x, iuv.y + h0y) - vec2(0.5f)) * pixel_size, ViewIndex);
+ vec3 p2 = vec3((vec2(iuv.x + h0x, iuv.y + h1y) - vec2(0.5f)) * pixel_size, ViewIndex);
+ vec3 p3 = vec3((vec2(iuv.x + h1x, iuv.y + h1y) - vec2(0.5f)) * pixel_size, ViewIndex);
+
+ return (g0(fuv.y) * (g0x * textureLod(tex, p0, lod) + g1x * textureLod(tex, p1, lod))) +
+ (g1(fuv.y) * (g0x * textureLod(tex, p2, lod) + g1x * textureLod(tex, p3, lod)));
+}
+
+#define GLOW_TEXTURE_SAMPLE(m_tex, m_uv, m_lod) texture2D_bicubic(m_tex, m_uv, m_lod)
+#else // MULTIVIEW
+
vec4 texture2D_bicubic(sampler2D tex, vec2 uv, int p_lod) {
float lod = float(p_lod);
vec2 tex_size = vec2(params.glow_texture_size >> p_lod);
@@ -145,12 +179,17 @@ vec4 texture2D_bicubic(sampler2D tex, vec2 uv, int p_lod) {
}
#define GLOW_TEXTURE_SAMPLE(m_tex, m_uv, m_lod) texture2D_bicubic(m_tex, m_uv, m_lod)
+#endif // !MULTIVIEW
-#else
+#else // USE_GLOW_FILTER_BICUBIC
+#ifdef MULTIVIEW
+#define GLOW_TEXTURE_SAMPLE(m_tex, m_uv, m_lod) textureLod(m_tex, vec3(m_uv, ViewIndex), float(m_lod))
+#else // MULTIVIEW
#define GLOW_TEXTURE_SAMPLE(m_tex, m_uv, m_lod) textureLod(m_tex, m_uv, float(m_lod))
+#endif // !MULTIVIEW
-#endif
+#endif // !USE_GLOW_FILTER_BICUBIC
vec3 tonemap_filmic(vec3 color, float white) {
// exposure bias: input scale (color *= bias, white *= bias) to make the brightness consistent with other tonemappers
@@ -231,7 +270,11 @@ vec3 apply_tonemapping(vec3 color, float white) { // inputs are LINEAR, always o
}
}
+#ifdef MULTIVIEW
+vec3 gather_glow(sampler2DArray tex, vec2 uv) { // sample all selected glow levels, view is added to uv later
+#else
vec3 gather_glow(sampler2D tex, vec2 uv) { // sample all selected glow levels
+#endif // defined(MULTIVIEW)
vec3 glow = vec3(0.0f);
if (params.glow_levels[0] > 0.0001) {
diff --git a/servers/rendering/renderer_rd/storage_rd/material_storage.cpp b/servers/rendering/renderer_rd/storage_rd/material_storage.cpp
index 9ca2973fd4..550fc7d788 100644
--- a/servers/rendering/renderer_rd/storage_rd/material_storage.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/material_storage.cpp
@@ -1420,6 +1420,25 @@ MaterialStorage::MaterialStorage() {
//custom sampler
sampler_rd_configure_custom(0.0f);
+ // buffers
+ { //create index array for copy shaders
+ Vector<uint8_t> pv;
+ pv.resize(6 * 4);
+ {
+ uint8_t *w = pv.ptrw();
+ int *p32 = (int *)w;
+ p32[0] = 0;
+ p32[1] = 1;
+ p32[2] = 2;
+ p32[3] = 0;
+ p32[4] = 2;
+ p32[5] = 3;
+ }
+ quad_index_buffer = RD::get_singleton()->index_buffer_create(6, RenderingDevice::INDEX_BUFFER_FORMAT_UINT32, pv);
+ quad_index_array = RD::get_singleton()->index_array_create(quad_index_buffer, 0, 6);
+ }
+
+ // Shaders
for (int i = 0; i < SHADER_TYPE_MAX; i++) {
shader_data_request_func[i] = nullptr;
}
@@ -1441,6 +1460,10 @@ MaterialStorage::~MaterialStorage() {
memdelete_arr(global_variables.buffer_dirty_regions);
RD::get_singleton()->free(global_variables.buffer);
+ // buffers
+
+ RD::get_singleton()->free(quad_index_buffer); //array gets freed as dependency
+
//def samplers
for (int i = 1; i < RS::CANVAS_ITEM_TEXTURE_FILTER_MAX; i++) {
for (int j = 1; j < RS::CANVAS_ITEM_TEXTURE_REPEAT_MAX; j++) {
@@ -1936,10 +1959,9 @@ Vector<StringName> MaterialStorage::global_variable_get_list() const {
ERR_FAIL_V_MSG(Vector<StringName>(), "This function should never be used outside the editor, it can severely damage performance.");
}
- const StringName *K = nullptr;
Vector<StringName> names;
- while ((K = global_variables.variables.next(K))) {
- names.push_back(*K);
+ for (const KeyValue<StringName, GlobalVariables::Variable> &E : global_variables.variables) {
+ names.push_back(E.key);
}
names.sort_custom<StringName::AlphCompare>();
return names;
diff --git a/servers/rendering/renderer_rd/storage_rd/material_storage.h b/servers/rendering/renderer_rd/storage_rd/material_storage.h
index c8cc418c3a..0e899e37c8 100644
--- a/servers/rendering/renderer_rd/storage_rd/material_storage.h
+++ b/servers/rendering/renderer_rd/storage_rd/material_storage.h
@@ -200,6 +200,11 @@ private:
RID default_rd_samplers[RS::CANVAS_ITEM_TEXTURE_FILTER_MAX][RS::CANVAS_ITEM_TEXTURE_REPEAT_MAX];
RID custom_rd_samplers[RS::CANVAS_ITEM_TEXTURE_FILTER_MAX][RS::CANVAS_ITEM_TEXTURE_REPEAT_MAX];
+ /* Buffers */
+
+ RID quad_index_buffer;
+ RID quad_index_array;
+
/* GLOBAL VARIABLE API */
GlobalVariables global_variables;
@@ -240,6 +245,10 @@ public:
// void sampler_rd_set_default(float p_mipmap_bias);
+ /* Buffers */
+
+ RID get_quad_index_array() { return quad_index_array; }
+
/* GLOBAL VARIABLE API */
void _update_global_variables();
diff --git a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp
index 3b52a187f6..7d4808f936 100644
--- a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp
@@ -29,8 +29,8 @@
/*************************************************************************/
#include "texture_storage.h"
-
-#include "../renderer_storage_rd.h"
+#include "../effects/copy_effects.h"
+#include "material_storage.h"
using namespace RendererRD;
@@ -1808,8 +1808,8 @@ AABB TextureStorage::decal_get_aabb(RID p_decal) const {
}
void TextureStorage::update_decal_atlas() {
- EffectsRD *effects = RendererStorageRD::base_singleton->get_effects();
- ERR_FAIL_NULL(effects);
+ RendererRD::CopyEffects *copy_effects = RendererRD::CopyEffects::get_singleton();
+ ERR_FAIL_NULL(copy_effects);
if (!decal_atlas.dirty) {
return; //nothing to do
@@ -1831,13 +1831,13 @@ void TextureStorage::update_decal_atlas() {
Vector<DecalAtlas::SortItem> itemsv;
itemsv.resize(decal_atlas.textures.size());
int base_size = 8;
- const RID *K = nullptr;
int idx = 0;
- while ((K = decal_atlas.textures.next(K))) {
+
+ for (const KeyValue<RID, DecalAtlas::Texture> &E : decal_atlas.textures) {
DecalAtlas::SortItem &si = itemsv.write[idx];
- Texture *src_tex = get_texture(*K);
+ Texture *src_tex = get_texture(E.key);
si.size.width = (src_tex->width / border) + 1;
si.size.height = (src_tex->height / border) + 1;
@@ -1847,7 +1847,7 @@ void TextureStorage::update_decal_atlas() {
base_size = nearest_power_of_2_templated(si.size.width);
}
- si.texture = *K;
+ si.texture = E.key;
idx++;
}
@@ -1983,18 +1983,17 @@ void TextureStorage::update_decal_atlas() {
RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(mm.fb, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_DROP, RD::FINAL_ACTION_DISCARD, cc);
- const RID *K = nullptr;
- while ((K = decal_atlas.textures.next(K))) {
- DecalAtlas::Texture *t = decal_atlas.textures.getptr(*K);
- Texture *src_tex = get_texture(*K);
- effects->copy_to_atlas_fb(src_tex->rd_texture, mm.fb, t->uv_rect, draw_list, false, t->panorama_to_dp_users > 0);
+ for (const KeyValue<RID, DecalAtlas::Texture> &E : decal_atlas.textures) {
+ DecalAtlas::Texture *t = decal_atlas.textures.getptr(E.key);
+ Texture *src_tex = get_texture(E.key);
+ copy_effects->copy_to_atlas_fb(src_tex->rd_texture, mm.fb, t->uv_rect, draw_list, false, t->panorama_to_dp_users > 0);
}
RD::get_singleton()->draw_list_end();
prev_texture = mm.texture;
} else {
- effects->copy_to_fb_rect(prev_texture, mm.fb, Rect2i(Point2i(), mm.size));
+ copy_effects->copy_to_fb_rect(prev_texture, mm.fb, Rect2i(Point2i(), mm.size));
prev_texture = mm.texture;
}
} else {
@@ -2623,8 +2622,8 @@ void TextureStorage::render_target_sdf_process(RID p_render_target) {
}
void TextureStorage::render_target_copy_to_back_buffer(RID p_render_target, const Rect2i &p_region, bool p_gen_mipmaps) {
- EffectsRD *effects = RendererStorageRD::base_singleton->get_effects();
- ERR_FAIL_NULL(effects);
+ CopyEffects *copy_effects = CopyEffects::get_singleton();
+ ERR_FAIL_NULL(copy_effects);
RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
ERR_FAIL_COND(!rt);
@@ -2642,9 +2641,11 @@ void TextureStorage::render_target_copy_to_back_buffer(RID p_render_target, cons
}
}
+ // TODO figure out stereo support here
+
//single texture copy for backbuffer
//RD::get_singleton()->texture_copy(rt->color, rt->backbuffer_mipmap0, Vector3(region.position.x, region.position.y, 0), Vector3(region.position.x, region.position.y, 0), Vector3(region.size.x, region.size.y, 1), 0, 0, 0, 0, true);
- effects->copy_to_rect(rt->color, rt->backbuffer_mipmap0, region, false, false, false, true, true);
+ copy_effects->copy_to_rect(rt->color, rt->backbuffer_mipmap0, region, false, false, false, true, true);
if (!p_gen_mipmaps) {
return;
@@ -2660,7 +2661,7 @@ void TextureStorage::render_target_copy_to_back_buffer(RID p_render_target, cons
region.size.y = MAX(1, region.size.y >> 1);
RID mipmap = rt->backbuffer_mipmaps[i];
- effects->gaussian_blur(prev_texture, mipmap, region, true);
+ copy_effects->gaussian_blur(prev_texture, mipmap, region, true);
prev_texture = mipmap;
}
RD::get_singleton()->draw_command_end_label();
@@ -2670,8 +2671,8 @@ void TextureStorage::render_target_clear_back_buffer(RID p_render_target, const
RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
ERR_FAIL_COND(!rt);
- EffectsRD *effects = RendererStorageRD::base_singleton->get_effects();
- ERR_FAIL_NULL(effects);
+ CopyEffects *copy_effects = CopyEffects::get_singleton();
+ ERR_FAIL_NULL(copy_effects);
if (!rt->backbuffer.is_valid()) {
_create_render_target_backbuffer(rt);
@@ -2688,15 +2689,15 @@ void TextureStorage::render_target_clear_back_buffer(RID p_render_target, const
}
//single texture copy for backbuffer
- effects->set_color(rt->backbuffer_mipmap0, p_color, region, true);
+ copy_effects->set_color(rt->backbuffer_mipmap0, p_color, region, true);
}
void TextureStorage::render_target_gen_back_buffer_mipmaps(RID p_render_target, const Rect2i &p_region) {
RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
ERR_FAIL_COND(!rt);
- EffectsRD *effects = RendererStorageRD::base_singleton->get_effects();
- ERR_FAIL_NULL(effects);
+ CopyEffects *copy_effects = CopyEffects::get_singleton();
+ ERR_FAIL_NULL(copy_effects);
if (!rt->backbuffer.is_valid()) {
_create_render_target_backbuffer(rt);
@@ -2722,7 +2723,7 @@ void TextureStorage::render_target_gen_back_buffer_mipmaps(RID p_render_target,
region.size.y = MAX(1, region.size.y >> 1);
RID mipmap = rt->backbuffer_mipmaps[i];
- effects->gaussian_blur(prev_texture, mipmap, region, true);
+ copy_effects->gaussian_blur(prev_texture, mipmap, region, true);
prev_texture = mipmap;
}
RD::get_singleton()->draw_command_end_label();
diff --git a/servers/rendering/renderer_viewport.cpp b/servers/rendering/renderer_viewport.cpp
index 7e18eac6ae..3ea67ae115 100644
--- a/servers/rendering/renderer_viewport.cpp
+++ b/servers/rendering/renderer_viewport.cpp
@@ -226,8 +226,6 @@ void RendererViewport::_draw_viewport(Viewport *p_viewport) {
}
if (!p_viewport->disable_2d) {
- int i = 0;
-
Map<Viewport::CanvasKey, Viewport::CanvasData *> canvas_map;
Rect2 clip_rect(0, 0, p_viewport->size.x, p_viewport->size.y);
@@ -238,13 +236,13 @@ void RendererViewport::_draw_viewport(Viewport *p_viewport) {
RendererCanvasRender::Light *directional_lights_with_shadow = nullptr;
if (p_viewport->sdf_active) {
- //process SDF
+ // Process SDF.
Rect2 sdf_rect = RSG::texture_storage->render_target_get_sdf_rect(p_viewport->render_target);
RendererCanvasRender::LightOccluderInstance *occluders = nullptr;
- //make list of occluders
+ // Make list of occluders.
for (KeyValue<RID, Viewport::CanvasData> &E : p_viewport->canvas_map) {
RendererCanvasCull::Canvas *canvas = static_cast<RendererCanvasCull::Canvas *>(E.value.canvas);
Transform2D xf = _canvas_get_transform(p_viewport, canvas, &E.value, clip_rect.size);
@@ -265,14 +263,13 @@ void RendererViewport::_draw_viewport(Viewport *p_viewport) {
RSG::canvas_render->render_sdf(p_viewport->render_target, occluders);
RSG::texture_storage->render_target_mark_sdf_enabled(p_viewport->render_target, true);
- p_viewport->sdf_active = false; // if used, gets set active again
+ p_viewport->sdf_active = false; // If used, gets set active again.
} else {
RSG::texture_storage->render_target_mark_sdf_enabled(p_viewport->render_target, false);
}
Rect2 shadow_rect;
- int light_count = 0;
int shadow_count = 0;
int directional_light_count = 0;
@@ -282,7 +279,7 @@ void RendererViewport::_draw_viewport(Viewport *p_viewport) {
Transform2D xf = _canvas_get_transform(p_viewport, canvas, &E.value, clip_rect.size);
- //find lights in canvas
+ // Find lights in canvas.
for (Set<RendererCanvasRender::Light *>::Element *F = canvas->lights.front(); F; F = F->next()) {
RendererCanvasRender::Light *cl = F->get();
@@ -298,12 +295,10 @@ void RendererViewport::_draw_viewport(Viewport *p_viewport) {
if (clip_rect.intersects_transformed(cl->xform_cache, cl->rect_cache)) {
cl->filter_next_ptr = lights;
lights = cl;
- // cl->texture_cache = nullptr;
Transform2D scale;
scale.scale(cl->rect_cache.size);
scale.columns[2] = cl->rect_cache.position;
cl->light_shader_xform = cl->xform * scale;
- //cl->light_shader_pos = cl->xform_cache[2];
if (cl->use_shadow) {
cl->shadows_next_ptr = lights_with_shadow;
if (lights_with_shadow == nullptr) {
@@ -314,11 +309,7 @@ void RendererViewport::_draw_viewport(Viewport *p_viewport) {
lights_with_shadow = cl;
cl->radius_cache = cl->rect_cache.size.length();
}
-
- light_count++;
}
-
- //guess this is not needed, but keeping because it may be
}
}
@@ -433,8 +424,7 @@ void RendererViewport::_draw_viewport(Viewport *p_viewport) {
RENDER_TIMESTAMP("> Render DirectionalLight2D Shadows");
- //make list of occluders
- int occ_cullded = 0;
+ // Make list of occluders.
for (KeyValue<RID, Viewport::CanvasData> &E : p_viewport->canvas_map) {
RendererCanvasCull::Canvas *canvas = static_cast<RendererCanvasCull::Canvas *>(E.value.canvas);
Transform2D xf = _canvas_get_transform(p_viewport, canvas, &E.value, clip_rect.size);
@@ -452,7 +442,6 @@ void RendererViewport::_draw_viewport(Viewport *p_viewport) {
if (F->get()->aabb_cache.intersects_filled_polygon(xf_points, point_count)) {
F->get()->next = occluders;
occluders = F->get();
- occ_cullded++;
}
}
}
@@ -504,7 +493,6 @@ void RendererViewport::_draw_viewport(Viewport *p_viewport) {
if (RSG::canvas->was_sdf_used()) {
p_viewport->sdf_active = true;
}
- i++;
if (scenario_draw_canvas_bg && E.key.get_layer() >= scenario_canvas_max_layer) {
if (!can_draw_3d) {
diff --git a/servers/rendering/rendering_server_default.cpp b/servers/rendering/rendering_server_default.cpp
index 000b253e8a..c4538e0776 100644
--- a/servers/rendering/rendering_server_default.cpp
+++ b/servers/rendering/rendering_server_default.cpp
@@ -179,10 +179,10 @@ void RenderingServerDefault::_draw(bool p_swap_buffers, double frame_step) {
print_line("GPU PROFILE (total " + rtos(total_time) + "ms): ");
float print_threshold = 0.01;
- for (OrderedHashMap<String, float>::Element E = print_gpu_profile_task_time.front(); E; E = E.next()) {
- double time = E.value() / double(print_frame_profile_frame_count);
+ for (const KeyValue<String, float> &E : print_gpu_profile_task_time) {
+ double time = E.value / double(print_frame_profile_frame_count);
if (time > print_threshold) {
- print_line("\t-" + E.key() + ": " + rtos(time) + "ms");
+ print_line("\t-" + E.key + ": " + rtos(time) + "ms");
}
}
print_gpu_profile_task_time.clear();
@@ -321,11 +321,7 @@ void RenderingServerDefault::set_debug_generate_wireframes(bool p_generate) {
}
bool RenderingServerDefault::is_low_end() const {
- // FIXME: Commented out when rebasing vulkan branch on master,
- // causes a crash, it seems rasterizer is not initialized yet the
- // first time it's called.
- //return RSG::rasterizer->is_low_end();
- return false;
+ return RendererCompositor::is_low_end();
}
void RenderingServerDefault::_thread_exit() {
diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h
index de4f8d9194..9d4059b9df 100644
--- a/servers/rendering/rendering_server_default.h
+++ b/servers/rendering/rendering_server_default.h
@@ -33,7 +33,7 @@
#include "core/math/octree.h"
#include "core/templates/command_queue_mt.h"
-#include "core/templates/ordered_hash_map.h"
+#include "core/templates/hash_map.h"
#include "renderer_canvas_cull.h"
#include "renderer_scene_cull.h"
#include "renderer_viewport.h"
@@ -69,7 +69,7 @@ class RenderingServerDefault : public RenderingServer {
//for printing
bool print_gpu_profile = false;
- OrderedHashMap<String, float> print_gpu_profile_task_time;
+ HashMap<String, float> print_gpu_profile_task_time;
uint64_t print_frame_profile_ticks_from = 0;
uint32_t print_frame_profile_frame_count = 0;
@@ -113,7 +113,9 @@ public:
_changes_changed();
#else
- _FORCE_INLINE_ static void redraw_request() { changes++; }
+ _FORCE_INLINE_ static void redraw_request() {
+ changes++;
+ }
#endif
#define WRITE_ACTION redraw_request();
diff --git a/servers/rendering/shader_compiler.cpp b/servers/rendering/shader_compiler.cpp
index 812d636a0b..5669cb2054 100644
--- a/servers/rendering/shader_compiler.cpp
+++ b/servers/rendering/shader_compiler.cpp
@@ -48,8 +48,8 @@ static String _mktab(int p_level) {
static String _typestr(SL::DataType p_type) {
String type = ShaderLanguage::get_datatype_name(p_type);
- if (ShaderLanguage::is_sampler_type(p_type)) {
- type = type.replace("sampler", "texture"); //we use textures instead of samplers
+ if (!RS::get_singleton()->is_low_end() && ShaderLanguage::is_sampler_type(p_type)) {
+ type = type.replace("sampler", "texture"); //we use textures instead of samplers in Vulkan GLSL
}
return type;
}
@@ -538,7 +538,11 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene
continue; // Instances are indexed directly, don't need index uniforms.
}
if (SL::is_sampler_type(uniform.type)) {
- ucode = "layout(set = " + itos(actions.texture_layout_set) + ", binding = " + itos(actions.base_texture_binding_index + uniform.texture_binding) + ") uniform ";
+ // Texture layouts are different for OpenGL GLSL and Vulkan GLSL
+ if (!RS::get_singleton()->is_low_end()) {
+ ucode = "layout(set = " + itos(actions.texture_layout_set) + ", binding = " + itos(actions.base_texture_binding_index + uniform.texture_binding) + ") ";
+ }
+ ucode += "uniform ";
}
bool is_buffer_global = !SL::is_sampler_type(uniform.type) && uniform.scope == SL::ShaderNode::Uniform::SCOPE_GLOBAL;
@@ -681,9 +685,13 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene
}
vcode += ";\n";
-
- r_gen_code.stage_globals[STAGE_VERTEX] += "layout(location=" + itos(index) + ") " + interp_mode + "out " + vcode;
- r_gen_code.stage_globals[STAGE_FRAGMENT] += "layout(location=" + itos(index) + ") " + interp_mode + "in " + vcode;
+ // GLSL ES 3.0 does not allow layout qualifiers for varyings
+ if (!RS::get_singleton()->is_low_end()) {
+ r_gen_code.stage_globals[STAGE_VERTEX] += "layout(location=" + itos(index) + ") ";
+ r_gen_code.stage_globals[STAGE_FRAGMENT] += "layout(location=" + itos(index) + ") ";
+ }
+ r_gen_code.stage_globals[STAGE_VERTEX] += interp_mode + "out " + vcode;
+ r_gen_code.stage_globals[STAGE_FRAGMENT] += interp_mode + "in " + vcode;
index += inc;
}
@@ -1125,8 +1133,8 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene
code += ", ";
}
String node_code = _dump_node_code(onode->arguments[i], p_level, r_gen_code, p_actions, p_default_actions, p_assigning);
- if (is_texture_func && i == 1) {
- //need to map from texture to sampler in order to sample
+ if (!RS::get_singleton()->is_low_end() && is_texture_func && i == 1) {
+ //need to map from texture to sampler in order to sample when using Vulkan GLSL
StringName texture_uniform;
bool correct_texture_uniform = false;
diff --git a/servers/rendering/shader_types.h b/servers/rendering/shader_types.h
index 385083b670..6ba05a3b43 100644
--- a/servers/rendering/shader_types.h
+++ b/servers/rendering/shader_types.h
@@ -31,7 +31,7 @@
#ifndef SHADERTYPES_H
#define SHADERTYPES_H
-#include "core/templates/ordered_hash_map.h"
+#include "core/templates/map.h"
#include "servers/rendering_server.h"
#include "shader_language.h"
diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp
index 136664e333..fc40f058aa 100644
--- a/servers/rendering_server.cpp
+++ b/servers/rendering_server.cpp
@@ -2885,6 +2885,7 @@ RenderingServer::RenderingServer() {
GLOBAL_DEF("rendering/shading/overrides/force_lambert_over_burley.mobile", true);
GLOBAL_DEF("rendering/driver/depth_prepass/enable", true);
+ GLOBAL_DEF("rendering/driver/depth_prepass/disable_for_vendors", "PowerVR,Mali,Adreno,Apple");
GLOBAL_DEF_RST("rendering/textures/default_filters/use_nearest_mipmap_filter", false);
GLOBAL_DEF_RST("rendering/textures/default_filters/anisotropic_filtering_level", 2);
@@ -2933,7 +2934,7 @@ RenderingServer::RenderingServer() {
ProjectSettings::get_singleton()->set_custom_property_info("rendering/scaling_3d/mode",
PropertyInfo(Variant::INT,
"rendering/scaling_3d/mode",
- PROPERTY_HINT_ENUM, "Bilinear (Fastest),FSR (Fast)"));
+ PROPERTY_HINT_ENUM, "Bilinear (Fastest),FSR 1.0 (Fast)"));
ProjectSettings::get_singleton()->set_custom_property_info("rendering/scaling_3d/scale",
PropertyInfo(Variant::FLOAT,
@@ -3002,44 +3003,15 @@ RenderingServer::RenderingServer() {
GLOBAL_DEF("rendering/limits/cluster_builder/max_clustered_elements", 512);
ProjectSettings::get_singleton()->set_custom_property_info("rendering/limits/cluster_builder/max_clustered_elements", PropertyInfo(Variant::FLOAT, "rendering/limits/cluster_builder/max_clustered_elements", PROPERTY_HINT_RANGE, "32,8192,1"));
- GLOBAL_DEF_RST_BASIC("xr/shaders/enabled", false);
+ // OpenGL limits
+ GLOBAL_DEF_RST("rendering/limits/opengl/max_renderable_elements", 65536);
+ ProjectSettings::get_singleton()->set_custom_property_info("rendering/limits/opengl/max_renderable_elements", PropertyInfo(Variant::INT, "rendering/limits/opengl/max_renderable_elements", PROPERTY_HINT_RANGE, "1024,65536,1"));
+ GLOBAL_DEF_RST("rendering/limits/opengl/max_renderable_lights", 256);
+ ProjectSettings::get_singleton()->set_custom_property_info("rendering/limits/opengl/max_renderable_lights", PropertyInfo(Variant::INT, "rendering/limits/opengl/max_renderable_lights", PROPERTY_HINT_RANGE, "16,4096,1"));
+ GLOBAL_DEF_RST("rendering/limits/opengl/max_lights_per_object", 8);
+ ProjectSettings::get_singleton()->set_custom_property_info("rendering/limits/opengl/max_lights_per_object", PropertyInfo(Variant::INT, "rendering/limits/opengl/max_lights_per_object", PROPERTY_HINT_RANGE, "2,1024,1"));
- GLOBAL_DEF_RST("rendering/2d/options/use_software_skinning", true);
- GLOBAL_DEF_RST("rendering/2d/options/ninepatch_mode", 1);
- ProjectSettings::get_singleton()->set_custom_property_info("rendering/2d/options/ninepatch_mode", PropertyInfo(Variant::INT, "rendering/2d/options/ninepatch_mode", PROPERTY_HINT_ENUM, "Fixed,Scaling"));
-
- GLOBAL_DEF_RST("rendering/2d/opengl/batching_send_null", 0);
- ProjectSettings::get_singleton()->set_custom_property_info("rendering/2d/opengl/batching_send_null", PropertyInfo(Variant::INT, "rendering/2d/opengl/batching_send_null", PROPERTY_HINT_ENUM, "Default (On),Off,On"));
- GLOBAL_DEF_RST("rendering/2d/opengl/batching_stream", 0);
- ProjectSettings::get_singleton()->set_custom_property_info("rendering/2d/opengl/batching_stream", PropertyInfo(Variant::INT, "rendering/2d/opengl/batching_stream", PROPERTY_HINT_ENUM, "Default (Off),Off,On"));
- GLOBAL_DEF_RST("rendering/2d/opengl/legacy_orphan_buffers", 0);
- ProjectSettings::get_singleton()->set_custom_property_info("rendering/2d/opengl/legacy_orphan_buffers", PropertyInfo(Variant::INT, "rendering/2d/opengl/legacy_orphan_buffers", PROPERTY_HINT_ENUM, "Default (On),Off,On"));
- GLOBAL_DEF_RST("rendering/2d/opengl/legacy_stream", 0);
- ProjectSettings::get_singleton()->set_custom_property_info("rendering/2d/opengl/legacy_stream", PropertyInfo(Variant::INT, "rendering/2d/opengl/legacy_stream", PROPERTY_HINT_ENUM, "Default (On),Off,On"));
-
- GLOBAL_DEF("rendering/batching/options/use_batching", false);
- GLOBAL_DEF_RST("rendering/batching/options/use_batching_in_editor", false);
- GLOBAL_DEF("rendering/batching/options/single_rect_fallback", false);
- GLOBAL_DEF("rendering/batching/parameters/max_join_item_commands", 16);
- GLOBAL_DEF("rendering/batching/parameters/colored_vertex_format_threshold", 0.25f);
- GLOBAL_DEF("rendering/batching/lights/scissor_area_threshold", 1.0f);
- GLOBAL_DEF("rendering/batching/lights/max_join_items", 32);
- GLOBAL_DEF("rendering/batching/parameters/batch_buffer_size", 16384);
- GLOBAL_DEF("rendering/batching/parameters/item_reordering_lookahead", 4);
- GLOBAL_DEF("rendering/batching/debug/flash_batching", false);
- GLOBAL_DEF("rendering/batching/debug/diagnose_frame", false);
- GLOBAL_DEF("rendering/gles2/compatibility/disable_half_float", false);
- GLOBAL_DEF("rendering/gles2/compatibility/enable_high_float.Android", false);
- GLOBAL_DEF("rendering/batching/precision/uv_contract", false);
- GLOBAL_DEF("rendering/batching/precision/uv_contract_amount", 100);
-
- ProjectSettings::get_singleton()->set_custom_property_info("rendering/batching/parameters/max_join_item_commands", PropertyInfo(Variant::INT, "rendering/batching/parameters/max_join_item_commands", PROPERTY_HINT_RANGE, "0,65535"));
- ProjectSettings::get_singleton()->set_custom_property_info("rendering/batching/parameters/colored_vertex_format_threshold", PropertyInfo(Variant::FLOAT, "rendering/batching/parameters/colored_vertex_format_threshold", PROPERTY_HINT_RANGE, "0.0,1.0,0.01"));
- ProjectSettings::get_singleton()->set_custom_property_info("rendering/batching/parameters/batch_buffer_size", PropertyInfo(Variant::INT, "rendering/batching/parameters/batch_buffer_size", PROPERTY_HINT_RANGE, "1024,65535,1024"));
- ProjectSettings::get_singleton()->set_custom_property_info("rendering/batching/lights/scissor_area_threshold", PropertyInfo(Variant::FLOAT, "rendering/batching/lights/scissor_area_threshold", PROPERTY_HINT_RANGE, "0.0,1.0"));
- ProjectSettings::get_singleton()->set_custom_property_info("rendering/batching/lights/max_join_items", PropertyInfo(Variant::INT, "rendering/batching/lights/max_join_items", PROPERTY_HINT_RANGE, "0,512"));
- ProjectSettings::get_singleton()->set_custom_property_info("rendering/batching/parameters/item_reordering_lookahead", PropertyInfo(Variant::INT, "rendering/batching/parameters/item_reordering_lookahead", PROPERTY_HINT_RANGE, "0,256"));
- ProjectSettings::get_singleton()->set_custom_property_info("rendering/batching/precision/uv_contract_amount", PropertyInfo(Variant::INT, "rendering/batching/precision/uv_contract_amount", PROPERTY_HINT_RANGE, "0,10000"));
+ GLOBAL_DEF_RST_BASIC("xr/shaders/enabled", false);
}
RenderingServer::~RenderingServer() {
diff --git a/tests/core/math/test_geometry_2d.h b/tests/core/math/test_geometry_2d.h
index 3487e4d7e8..db4e6e2177 100644
--- a/tests/core/math/test_geometry_2d.h
+++ b/tests/core/math/test_geometry_2d.h
@@ -135,7 +135,7 @@ TEST_CASE("[Geometry2D] Line intersection") {
"Parallel lines should not intersect.");
}
-TEST_CASE("[Geometry2D] Segment intersection.") {
+TEST_CASE("[Geometry2D] Segment intersection") {
Vector2 r;
CHECK(Geometry2D::segment_intersects_segment(Vector2(-1, 1), Vector2(1, -1), Vector2(1, 1), Vector2(-1, -1), &r));
@@ -148,6 +148,10 @@ TEST_CASE("[Geometry2D] Segment intersection.") {
Geometry2D::segment_intersects_segment(Vector2(-1, 1), Vector2(1, -1), Vector2(0, 1), Vector2(2, -1), &r),
"Parallel segments should not intersect.");
+ CHECK_FALSE_MESSAGE(
+ Geometry2D::segment_intersects_segment(Vector2(1, 2), Vector2(3, 2), Vector2(0, 2), Vector2(-2, 2), &r),
+ "Non-overlapping collinear segments should not intersect.");
+
CHECK_MESSAGE(
Geometry2D::segment_intersects_segment(Vector2(0, 0), Vector2(0, 1), Vector2(0, 0), Vector2(1, 0), &r),
"Touching segments should intersect.");
@@ -159,11 +163,114 @@ TEST_CASE("[Geometry2D] Segment intersection.") {
CHECK(r.is_equal_approx(Vector2(0, 0)));
}
+TEST_CASE("[Geometry2D] Segment intersection with circle") {
+ real_t minus_one = -1.0;
+ real_t zero = 0.0;
+ real_t one_quarter = 0.25;
+ real_t three_quarters = 0.75;
+ real_t one = 1.0;
+
+ CHECK_MESSAGE(
+ Math::is_equal_approx(Geometry2D::segment_intersects_circle(Vector2(0, 0), Vector2(4, 0), Vector2(0, 0), 1.0), one_quarter),
+ "Segment from inside to outside of circle should intersect it.");
+ CHECK_MESSAGE(
+ Math::is_equal_approx(Geometry2D::segment_intersects_circle(Vector2(4, 0), Vector2(0, 0), Vector2(0, 0), 1.0), three_quarters),
+ "Segment from outside to inside of circle should intersect it.");
+
+ CHECK_MESSAGE(
+ Math::is_equal_approx(Geometry2D::segment_intersects_circle(Vector2(-2, 0), Vector2(2, 0), Vector2(0, 0), 1.0), one_quarter),
+ "Segment running through circle should intersect it.");
+ CHECK_MESSAGE(
+ Math::is_equal_approx(Geometry2D::segment_intersects_circle(Vector2(2, 0), Vector2(-2, 0), Vector2(0, 0), 1.0), one_quarter),
+ "Segment running through circle should intersect it.");
+
+ CHECK_MESSAGE(
+ Math::is_equal_approx(Geometry2D::segment_intersects_circle(Vector2(0, 0), Vector2(1, 0), Vector2(0, 0), 1.0), one),
+ "Segment starting inside the circle and ending on the circle should intersect it");
+ CHECK_MESSAGE(
+ Math::is_equal_approx(Geometry2D::segment_intersects_circle(Vector2(1, 0), Vector2(0, 0), Vector2(0, 0), 1.0), zero),
+ "Segment starting on the circle and going inwards should intersect it");
+ CHECK_MESSAGE(
+ Math::is_equal_approx(Geometry2D::segment_intersects_circle(Vector2(1, 0), Vector2(2, 0), Vector2(0, 0), 1.0), zero),
+ "Segment starting on the circle and going outwards should intersect it");
+ CHECK_MESSAGE(
+ Math::is_equal_approx(Geometry2D::segment_intersects_circle(Vector2(2, 0), Vector2(1, 0), Vector2(0, 0), 1.0), one),
+ "Segment starting outside the circle and ending on the circle intersect it");
+
+ CHECK_MESSAGE(
+ Math::is_equal_approx(Geometry2D::segment_intersects_circle(Vector2(-1, 0), Vector2(1, 0), Vector2(0, 0), 2.0), minus_one),
+ "Segment completely within the circle should not intersect it");
+ CHECK_MESSAGE(
+ Math::is_equal_approx(Geometry2D::segment_intersects_circle(Vector2(1, 0), Vector2(-1, 0), Vector2(0, 0), 2.0), minus_one),
+ "Segment completely within the circle should not intersect it");
+ CHECK_MESSAGE(
+ Math::is_equal_approx(Geometry2D::segment_intersects_circle(Vector2(2, 0), Vector2(3, 0), Vector2(0, 0), 1.0), minus_one),
+ "Segment completely outside the circle should not intersect it");
+ CHECK_MESSAGE(
+ Math::is_equal_approx(Geometry2D::segment_intersects_circle(Vector2(3, 0), Vector2(2, 0), Vector2(0, 0), 1.0), minus_one),
+ "Segment completely outside the circle should not intersect it");
+}
+
+TEST_CASE("[Geometry2D] Segment intersection with polygon") {
+ Vector<Point2> a;
+
+ a.push_back(Point2(-2, 2));
+ a.push_back(Point2(3, 4));
+ a.push_back(Point2(1, 1));
+ a.push_back(Point2(2, -2));
+ a.push_back(Point2(-1, -1));
+
+ CHECK_MESSAGE(
+ Geometry2D::is_segment_intersecting_polygon(Vector2(0, 2), Vector2(2, 2), a),
+ "Segment from inside to outside of polygon should intersect it.");
+ CHECK_MESSAGE(
+ Geometry2D::is_segment_intersecting_polygon(Vector2(2, 2), Vector2(0, 2), a),
+ "Segment from outside to inside of polygon should intersect it.");
+
+ CHECK_MESSAGE(
+ Geometry2D::is_segment_intersecting_polygon(Vector2(2, 4), Vector2(3, 3), a),
+ "Segment running through polygon should intersect it.");
+ CHECK_MESSAGE(
+ Geometry2D::is_segment_intersecting_polygon(Vector2(3, 3), Vector2(2, 4), a),
+ "Segment running through polygon should intersect it.");
+
+ CHECK_MESSAGE(
+ Geometry2D::is_segment_intersecting_polygon(Vector2(0, 0), Vector2(1, 1), a),
+ "Segment starting inside the polygon and ending on the polygon should intersect it");
+ CHECK_MESSAGE(
+ Geometry2D::is_segment_intersecting_polygon(Vector2(1, 1), Vector2(0, 0), a),
+ "Segment starting on the polygon and going inwards should intersect it");
+ CHECK_MESSAGE(
+ Geometry2D::is_segment_intersecting_polygon(Vector2(-2, 2), Vector2(-2, -1), a),
+ "Segment starting on the polygon and going outwards should intersect it");
+ CHECK_MESSAGE(
+ Geometry2D::is_segment_intersecting_polygon(Vector2(-2, 1), Vector2(-2, 2), a),
+ "Segment starting outside the polygon and ending on the polygon intersect it");
+
+ CHECK_FALSE_MESSAGE(
+ Geometry2D::is_segment_intersecting_polygon(Vector2(-1, 2), Vector2(1, -1), a),
+ "Segment completely within the polygon should not intersect it");
+ CHECK_FALSE_MESSAGE(
+ Geometry2D::is_segment_intersecting_polygon(Vector2(1, -1), Vector2(-1, 2), a),
+ "Segment completely within the polygon should not intersect it");
+ CHECK_FALSE_MESSAGE(
+ Geometry2D::is_segment_intersecting_polygon(Vector2(2, 2), Vector2(2, -1), a),
+ "Segment completely outside the polygon should not intersect it");
+ CHECK_FALSE_MESSAGE(
+ Geometry2D::is_segment_intersecting_polygon(Vector2(2, -1), Vector2(2, 2), a),
+ "Segment completely outside the polygon should not intersect it");
+}
+
TEST_CASE("[Geometry2D] Closest point to segment") {
Vector2 s[] = { Vector2(-4, -4), Vector2(4, 4) };
CHECK(Geometry2D::get_closest_point_to_segment(Vector2(4.1, 4.1), s).is_equal_approx(Vector2(4, 4)));
CHECK(Geometry2D::get_closest_point_to_segment(Vector2(-4.1, -4.1), s).is_equal_approx(Vector2(-4, -4)));
CHECK(Geometry2D::get_closest_point_to_segment(Vector2(-1, 1), s).is_equal_approx(Vector2(0, 0)));
+
+ Vector2 t[] = { Vector2(1, -2), Vector2(1, -2) };
+ CHECK_MESSAGE(
+ Geometry2D::get_closest_point_to_segment(Vector2(-3, 4), t).is_equal_approx(Vector2(1, -2)),
+ "Line segment is only a single point. This point should be the closest.");
}
TEST_CASE("[Geometry2D] Closest point to uncapped segment") {
@@ -186,6 +293,30 @@ TEST_CASE("[Geometry2D] Closest points between segments") {
Geometry2D::get_closest_points_between_segments(Vector2(-1, 1), Vector2(1, -1), Vector2(1, 1), Vector2(-1, -1), c1, c2);
CHECK(c1.is_equal_approx(Vector2(0, 0)));
CHECK(c2.is_equal_approx(Vector2(0, 0)));
+
+ Geometry2D::get_closest_points_between_segments(Vector2(-3, 4), Vector2(-3, 4), Vector2(-4, 3), Vector2(-2, 3), c1, c2);
+ CHECK_MESSAGE(
+ c1.is_equal_approx(Vector2(-3, 4)),
+ "1st line segment is only a point, this point should be the closest point to the 2nd line segment.");
+ CHECK_MESSAGE(
+ c2.is_equal_approx(Vector2(-3, 3)),
+ "1st line segment is only a point, this should not matter when determining the closest point on the 2nd line segment.");
+
+ Geometry2D::get_closest_points_between_segments(Vector2(-4, 3), Vector2(-2, 3), Vector2(-3, 4), Vector2(-3, 4), c1, c2);
+ CHECK_MESSAGE(
+ c1.is_equal_approx(Vector2(-3, 3)),
+ "2nd line segment is only a point, this should not matter when determining the closest point on the 1st line segment.");
+ CHECK_MESSAGE(
+ c2.is_equal_approx(Vector2(-3, 4)),
+ "2nd line segment is only a point, this point should be the closest point to the 1st line segment.");
+
+ Geometry2D::get_closest_points_between_segments(Vector2(5, -4), Vector2(5, -4), Vector2(-2, 1), Vector2(-2, 1), c1, c2);
+ CHECK_MESSAGE(
+ c1.is_equal_approx(Vector2(5, -4)),
+ "Both line segments are only a point. On the 1st line segment, that point should be the closest point to the 2nd line segment.");
+ CHECK_MESSAGE(
+ c2.is_equal_approx(Vector2(-2, 1)),
+ "Both line segments are only a point. On the 2nd line segment, that point should be the closest point to the 1st line segment.");
}
TEST_CASE("[Geometry2D] Make atlas") {
@@ -562,6 +693,174 @@ TEST_CASE("[Geometry2D] Clip polyline with polygon") {
CHECK(r[1][1].is_equal_approx(Vector2(55, 70)));
}
}
+
+TEST_CASE("[Geometry2D] Convex hull") {
+ Vector<Point2> a;
+ Vector<Point2> r;
+
+ a.push_back(Point2(-4, -8));
+ a.push_back(Point2(-10, -4));
+ a.push_back(Point2(8, 2));
+ a.push_back(Point2(-6, 10));
+ a.push_back(Point2(-12, 4));
+ a.push_back(Point2(10, -8));
+ a.push_back(Point2(4, 8));
+
+ SUBCASE("[Geometry2D] No points") {
+ r = Geometry2D::convex_hull(Vector<Vector2>());
+
+ CHECK_MESSAGE(r.is_empty(), "The convex hull should be empty if there are no input points.");
+ }
+
+ SUBCASE("[Geometry2D] Single point") {
+ Vector<Point2> b;
+ b.push_back(Point2(4, -3));
+
+ r = Geometry2D::convex_hull(b);
+ REQUIRE_MESSAGE(r.size() == 1, "Convex hull should contain 1 point.");
+ CHECK(r[0].is_equal_approx(b[0]));
+ }
+
+ SUBCASE("[Geometry2D] All points form the convex hull") {
+ r = Geometry2D::convex_hull(a);
+ REQUIRE_MESSAGE(r.size() == 8, "Convex hull should contain 8 points.");
+ CHECK(r[0].is_equal_approx(Point2(-12, 4)));
+ CHECK(r[1].is_equal_approx(Point2(-10, -4)));
+ CHECK(r[2].is_equal_approx(Point2(-4, -8)));
+ CHECK(r[3].is_equal_approx(Point2(10, -8)));
+ CHECK(r[4].is_equal_approx(Point2(8, 2)));
+ CHECK(r[5].is_equal_approx(Point2(4, 8)));
+ CHECK(r[6].is_equal_approx(Point2(-6, 10)));
+ CHECK(r[7].is_equal_approx(Point2(-12, 4)));
+ }
+
+ SUBCASE("[Geometry2D] Add extra points inside original convex hull") {
+ a.push_back(Point2(-4, -8));
+ a.push_back(Point2(0, 0));
+ a.push_back(Point2(0, 8));
+ a.push_back(Point2(-10, -3));
+ a.push_back(Point2(9, -4));
+ a.push_back(Point2(6, 4));
+
+ r = Geometry2D::convex_hull(a);
+ REQUIRE_MESSAGE(r.size() == 8, "Convex hull should contain 8 points.");
+ CHECK(r[0].is_equal_approx(Point2(-12, 4)));
+ CHECK(r[1].is_equal_approx(Point2(-10, -4)));
+ CHECK(r[2].is_equal_approx(Point2(-4, -8)));
+ CHECK(r[3].is_equal_approx(Point2(10, -8)));
+ CHECK(r[4].is_equal_approx(Point2(8, 2)));
+ CHECK(r[5].is_equal_approx(Point2(4, 8)));
+ CHECK(r[6].is_equal_approx(Point2(-6, 10)));
+ CHECK(r[7].is_equal_approx(Point2(-12, 4)));
+ }
+
+ SUBCASE("[Geometry2D] Add extra points on border of original convex hull") {
+ a.push_back(Point2(9, -3));
+ a.push_back(Point2(-2, -8));
+
+ r = Geometry2D::convex_hull(a);
+ REQUIRE_MESSAGE(r.size() == 8, "Convex hull should contain 8 points.");
+ CHECK(r[0].is_equal_approx(Point2(-12, 4)));
+ CHECK(r[1].is_equal_approx(Point2(-10, -4)));
+ CHECK(r[2].is_equal_approx(Point2(-4, -8)));
+ CHECK(r[3].is_equal_approx(Point2(10, -8)));
+ CHECK(r[4].is_equal_approx(Point2(8, 2)));
+ CHECK(r[5].is_equal_approx(Point2(4, 8)));
+ CHECK(r[6].is_equal_approx(Point2(-6, 10)));
+ CHECK(r[7].is_equal_approx(Point2(-12, 4)));
+ }
+
+ SUBCASE("[Geometry2D] Add extra points outside border of original convex hull") {
+ a.push_back(Point2(-11, -1));
+ a.push_back(Point2(7, 6));
+
+ r = Geometry2D::convex_hull(a);
+ REQUIRE_MESSAGE(r.size() == 10, "Convex hull should contain 10 points.");
+ CHECK(r[0].is_equal_approx(Point2(-12, 4)));
+ CHECK(r[1].is_equal_approx(Point2(-11, -1)));
+ CHECK(r[2].is_equal_approx(Point2(-10, -4)));
+ CHECK(r[3].is_equal_approx(Point2(-4, -8)));
+ CHECK(r[4].is_equal_approx(Point2(10, -8)));
+ CHECK(r[5].is_equal_approx(Point2(8, 2)));
+ CHECK(r[6].is_equal_approx(Point2(7, 6)));
+ CHECK(r[7].is_equal_approx(Point2(4, 8)));
+ CHECK(r[8].is_equal_approx(Point2(-6, 10)));
+ CHECK(r[9].is_equal_approx(Point2(-12, 4)));
+ }
+}
+
+TEST_CASE("[Geometry2D] Bresenham line") {
+ Vector<Vector2i> r;
+
+ SUBCASE("[Geometry2D] Single point") {
+ r = Geometry2D::bresenham_line(Point2i(0, 0), Point2i(0, 0));
+
+ REQUIRE_MESSAGE(r.size() == 1, "The Bresenham line should contain exactly one point.");
+ CHECK(r[0] == Vector2i(0, 0));
+ }
+
+ SUBCASE("[Geometry2D] Line parallel to x-axis") {
+ r = Geometry2D::bresenham_line(Point2i(1, 2), Point2i(5, 2));
+
+ REQUIRE_MESSAGE(r.size() == 5, "The Bresenham line should contain exactly five points.");
+ CHECK(r[0] == Vector2i(1, 2));
+ CHECK(r[1] == Vector2i(2, 2));
+ CHECK(r[2] == Vector2i(3, 2));
+ CHECK(r[3] == Vector2i(4, 2));
+ CHECK(r[4] == Vector2i(5, 2));
+ }
+
+ SUBCASE("[Geometry2D] 45 degree line from the origin") {
+ r = Geometry2D::bresenham_line(Point2i(0, 0), Point2i(4, 4));
+
+ REQUIRE_MESSAGE(r.size() == 5, "The Bresenham line should contain exactly five points.");
+ CHECK(r[0] == Vector2i(0, 0));
+ CHECK(r[1] == Vector2i(1, 1));
+ CHECK(r[2] == Vector2i(2, 2));
+ CHECK(r[3] == Vector2i(3, 3));
+ CHECK(r[4] == Vector2i(4, 4));
+ }
+
+ SUBCASE("[Geometry2D] Sloped line going up one unit") {
+ r = Geometry2D::bresenham_line(Point2i(0, 0), Point2i(4, 1));
+
+ REQUIRE_MESSAGE(r.size() == 5, "The Bresenham line should contain exactly five points.");
+ CHECK(r[0] == Vector2i(0, 0));
+ CHECK(r[1] == Vector2i(1, 0));
+ CHECK(r[2] == Vector2i(2, 0));
+ CHECK(r[3] == Vector2i(3, 1));
+ CHECK(r[4] == Vector2i(4, 1));
+ }
+
+ SUBCASE("[Geometry2D] Sloped line going up two units") {
+ r = Geometry2D::bresenham_line(Point2i(0, 0), Point2i(4, 2));
+
+ REQUIRE_MESSAGE(r.size() == 5, "The Bresenham line should contain exactly five points.");
+ CHECK(r[0] == Vector2i(0, 0));
+ CHECK(r[1] == Vector2i(1, 0));
+ CHECK(r[2] == Vector2i(2, 1));
+ CHECK(r[3] == Vector2i(3, 1));
+ CHECK(r[4] == Vector2i(4, 2));
+ }
+
+ SUBCASE("[Geometry2D] Long sloped line") {
+ r = Geometry2D::bresenham_line(Point2i(0, 0), Point2i(11, 5));
+
+ REQUIRE_MESSAGE(r.size() == 12, "The Bresenham line should contain exactly twelve points.");
+ CHECK(r[0] == Vector2i(0, 0));
+ CHECK(r[1] == Vector2i(1, 0));
+ CHECK(r[2] == Vector2i(2, 1));
+ CHECK(r[3] == Vector2i(3, 1));
+ CHECK(r[4] == Vector2i(4, 2));
+ CHECK(r[5] == Vector2i(5, 2));
+ CHECK(r[6] == Vector2i(6, 3));
+ CHECK(r[7] == Vector2i(7, 3));
+ CHECK(r[8] == Vector2i(8, 4));
+ CHECK(r[9] == Vector2i(9, 4));
+ CHECK(r[10] == Vector2i(10, 5));
+ CHECK(r[11] == Vector2i(11, 5));
+ }
+}
} // namespace TestGeometry2D
#endif // TEST_GEOMETRY_2D_H
diff --git a/tests/core/object/test_class_db.h b/tests/core/object/test_class_db.h
index 5cf5403a50..2e316a7d95 100644
--- a/tests/core/object/test_class_db.h
+++ b/tests/core/object/test_class_db.h
@@ -173,7 +173,7 @@ struct NamesCache {
}
};
-typedef OrderedHashMap<StringName, ExposedClass> ExposedClasses;
+typedef HashMap<StringName, ExposedClass> ExposedClasses;
struct Context {
Vector<StringName> enum_types;
@@ -183,13 +183,13 @@ struct Context {
NamesCache names_cache;
const ExposedClass *find_exposed_class(const StringName &p_name) const {
- ExposedClasses::ConstElement elem = exposed_classes.find(p_name);
- return elem ? &elem.value() : nullptr;
+ ExposedClasses::ConstIterator elem = exposed_classes.find(p_name);
+ return elem ? &elem->value : nullptr;
}
const ExposedClass *find_exposed_class(const TypeReference &p_type_ref) const {
- ExposedClasses::ConstElement elem = exposed_classes.find(p_type_ref.name);
- return elem ? &elem.value() : nullptr;
+ ExposedClasses::ConstIterator elem = exposed_classes.find(p_type_ref.name);
+ return elem ? &elem->value : nullptr;
}
bool has_type(const TypeReference &p_type_ref) const {
@@ -676,12 +676,11 @@ void add_exposed_classes(Context &r_context) {
// Add signals
const HashMap<StringName, MethodInfo> &signal_map = class_info->signal_map;
- const StringName *k = nullptr;
- while ((k = signal_map.next(k))) {
+ for (const KeyValue<StringName, MethodInfo> &K : signal_map) {
SignalData signal;
- const MethodInfo &method_info = signal_map.get(*k);
+ const MethodInfo &method_info = signal_map.get(K.key);
signal.name = method_info.name;
@@ -734,14 +733,12 @@ void add_exposed_classes(Context &r_context) {
ClassDB::get_integer_constant_list(class_name, &constants, true);
const HashMap<StringName, List<StringName>> &enum_map = class_info->enum_map;
- k = nullptr;
- while ((k = enum_map.next(k))) {
+ for (const KeyValue<StringName, List<StringName>> &K : enum_map) {
EnumData enum_;
- enum_.name = *k;
+ enum_.name = K.key;
- const List<StringName> &enum_constants = enum_map.get(*k);
- for (const StringName &E : enum_constants) {
+ for (const StringName &E : K.value) {
const StringName &constant_name = E;
TEST_FAIL_COND(String(constant_name).find("::") != -1,
"Enum constant contains '::', check bindings to remove the scope: '",
@@ -760,7 +757,7 @@ void add_exposed_classes(Context &r_context) {
exposed_class.enums.push_back(enum_);
- r_context.enum_types.push_back(String(class_name) + "." + String(*k));
+ r_context.enum_types.push_back(String(class_name) + "." + String(K.key));
}
for (const String &E : constants) {
@@ -850,8 +847,8 @@ TEST_SUITE("[ClassDB]") {
TEST_FAIL_COND(object_class->base != StringName(),
"Object class derives from another class: '", object_class->base, "'.");
- for (ExposedClasses::Element E = context.exposed_classes.front(); E; E = E.next()) {
- validate_class(context, E.value());
+ for (const KeyValue<StringName, ExposedClass> &E : context.exposed_classes) {
+ validate_class(context, E.value);
}
}
}
diff --git a/tests/core/templates/test_ordered_hash_map.h b/tests/core/templates/test_hash_map.h
index 08c5c9b72a..7a3d5f5d47 100644
--- a/tests/core/templates/test_ordered_hash_map.h
+++ b/tests/core/templates/test_hash_map.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* test_ordered_hash_map.h */
+/* test_hash_map.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,56 +28,53 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef TEST_ORDERED_HASH_MAP_H
-#define TEST_ORDERED_HASH_MAP_H
+#ifndef TEST_HASH_MAP_H
+#define TEST_HASH_MAP_H
-#include "core/templates/ordered_hash_map.h"
+#include "core/templates/hash_map.h"
#include "tests/test_macros.h"
-namespace TestOrderedHashMap {
+namespace TestHashMap {
-TEST_CASE("[OrderedHashMap] Insert element") {
- OrderedHashMap<int, int> map;
- OrderedHashMap<int, int>::Element e = map.insert(42, 84);
+TEST_CASE("[HashMap] Insert element") {
+ HashMap<int, int> map;
+ HashMap<int, int>::Iterator e = map.insert(42, 84);
CHECK(e);
- CHECK(e.key() == 42);
- CHECK(e.get() == 84);
- CHECK(e.value() == 84);
+ CHECK(e->key == 42);
+ CHECK(e->value == 84);
CHECK(map[42] == 84);
CHECK(map.has(42));
CHECK(map.find(42));
}
-TEST_CASE("[OrderedHashMap] Overwrite element") {
- OrderedHashMap<int, int> map;
+TEST_CASE("[HashMap] Overwrite element") {
+ HashMap<int, int> map;
map.insert(42, 84);
map.insert(42, 1234);
CHECK(map[42] == 1234);
}
-TEST_CASE("[OrderedHashMap] Erase via element") {
- OrderedHashMap<int, int> map;
- OrderedHashMap<int, int>::Element e = map.insert(42, 84);
-
- map.erase(e);
- CHECK(!e);
+TEST_CASE("[HashMap] Erase via element") {
+ HashMap<int, int> map;
+ HashMap<int, int>::Iterator e = map.insert(42, 84);
+ map.remove(e);
CHECK(!map.has(42));
CHECK(!map.find(42));
}
-TEST_CASE("[OrderedHashMap] Erase via key") {
- OrderedHashMap<int, int> map;
+TEST_CASE("[HashMap] Erase via key") {
+ HashMap<int, int> map;
map.insert(42, 84);
map.erase(42);
CHECK(!map.has(42));
CHECK(!map.find(42));
}
-TEST_CASE("[OrderedHashMap] Size") {
- OrderedHashMap<int, int> map;
+TEST_CASE("[HashMap] Size") {
+ HashMap<int, int> map;
map.insert(42, 84);
map.insert(123, 84);
map.insert(123, 84);
@@ -87,8 +84,8 @@ TEST_CASE("[OrderedHashMap] Size") {
CHECK(map.size() == 4);
}
-TEST_CASE("[OrderedHashMap] Iteration") {
- OrderedHashMap<int, int> map;
+TEST_CASE("[HashMap] Iteration") {
+ HashMap<int, int> map;
map.insert(42, 84);
map.insert(123, 12385);
map.insert(0, 12934);
@@ -102,34 +99,35 @@ TEST_CASE("[OrderedHashMap] Iteration") {
expected.push_back(Pair<int, int>(123485, 1238888));
int idx = 0;
- for (OrderedHashMap<int, int>::Element E = map.front(); E; E = E.next()) {
- CHECK(expected[idx] == Pair<int, int>(E.key(), E.value()));
+ for (const KeyValue<int, int> &E : map) {
+ CHECK(expected[idx] == Pair<int, int>(E.key, E.value));
++idx;
}
}
-TEST_CASE("[OrderedHashMap] Const iteration") {
- OrderedHashMap<int, int> map;
+TEST_CASE("[HashMap] Const iteration") {
+ HashMap<int, int> map;
map.insert(42, 84);
map.insert(123, 12385);
map.insert(0, 12934);
map.insert(123485, 1238888);
map.insert(123, 111111);
- const OrderedHashMap<int, int> const_map = map;
+ const HashMap<int, int> const_map = map;
Vector<Pair<int, int>> expected;
expected.push_back(Pair<int, int>(42, 84));
expected.push_back(Pair<int, int>(123, 111111));
expected.push_back(Pair<int, int>(0, 12934));
expected.push_back(Pair<int, int>(123485, 1238888));
+ expected.push_back(Pair<int, int>(123, 111111));
int idx = 0;
- for (OrderedHashMap<int, int>::ConstElement E = const_map.front(); E; E = E.next()) {
- CHECK(expected[idx] == Pair<int, int>(E.key(), E.value()));
+ for (const KeyValue<int, int> &E : const_map) {
+ CHECK(expected[idx] == Pair<int, int>(E.key, E.value));
++idx;
}
}
-} // namespace TestOrderedHashMap
+} // namespace TestHashMap
-#endif // TEST_ORDERED_HASH_MAP_H
+#endif // TEST_HASH_MAP_H
diff --git a/tests/scene/test_text_edit.h b/tests/scene/test_text_edit.h
index 249b645fae..a9a1a5fa71 100644
--- a/tests/scene/test_text_edit.h
+++ b/tests/scene/test_text_edit.h
@@ -42,8 +42,8 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
SceneTree::get_singleton()->get_root()->add_child(text_edit);
text_edit->grab_focus();
- Array empty_singal_args;
- empty_singal_args.push_back(Array());
+ Array empty_signal_args;
+ empty_signal_args.push_back(Array());
SUBCASE("[TextEdit] text entry") {
SIGNAL_WATCH(text_edit, "text_set");
@@ -65,7 +65,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_text() == "");
CHECK(text_edit->get_caret_column() == 0);
CHECK(text_edit->get_line_count() == 1);
- SIGNAL_CHECK("text_set", empty_singal_args);
+ SIGNAL_CHECK("text_set", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
SIGNAL_CHECK_FALSE("caret_changed");
SIGNAL_CHECK_FALSE("text_changed");
@@ -75,16 +75,16 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_text() == "test text");
CHECK(text_edit->get_caret_column() == 0);
CHECK(text_edit->get_line_count() == 1);
- SIGNAL_CHECK("text_set", empty_singal_args);
+ SIGNAL_CHECK("text_set", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
- SIGNAL_CHECK("caret_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_changed");
text_edit->clear();
MessageQueue::get_singleton()->flush();
CHECK(text_edit->get_text() == "");
CHECK(text_edit->get_caret_column() == 0);
- SIGNAL_CHECK("text_set", empty_singal_args);
+ SIGNAL_CHECK("text_set", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
SIGNAL_CHECK_FALSE("caret_changed");
SIGNAL_CHECK_FALSE("text_changed");
@@ -95,8 +95,8 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_text() == "test text");
CHECK(text_edit->get_caret_column() == 9);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
- SIGNAL_CHECK("caret_changed", empty_singal_args);
- SIGNAL_CHECK("text_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_set");
text_edit->redo();
@@ -104,8 +104,8 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_text() == "");
CHECK(text_edit->get_caret_column() == 0);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
- SIGNAL_CHECK("caret_changed", empty_singal_args);
- SIGNAL_CHECK("text_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_set");
// Cannot undo when not-editable but should still clear.
@@ -114,8 +114,8 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_text() == "test text");
CHECK(text_edit->get_caret_column() == 9);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
- SIGNAL_CHECK("caret_changed", empty_singal_args);
- SIGNAL_CHECK("text_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_set");
// Clear.
@@ -130,8 +130,8 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
MessageQueue::get_singleton()->flush();
CHECK(text_edit->get_text() == "");
CHECK(text_edit->get_caret_column() == 0);
- SIGNAL_CHECK("text_set", empty_singal_args);
- SIGNAL_CHECK("caret_changed", empty_singal_args);
+ SIGNAL_CHECK("text_set", empty_signal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_clear_args);
SIGNAL_CHECK_FALSE("text_changed");
@@ -153,9 +153,9 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
MessageQueue::get_singleton()->flush();
CHECK(text_edit->get_text() == "test text");
CHECK(text_edit->get_caret_column() == 0);
- SIGNAL_CHECK("text_set", empty_singal_args);
+ SIGNAL_CHECK("text_set", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
- SIGNAL_CHECK("caret_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_changed");
text_edit->set_editable(true);
@@ -165,7 +165,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_text() == "");
CHECK(text_edit->get_caret_column() == 0);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
- SIGNAL_CHECK("text_changed", empty_singal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("caret_changed");
SIGNAL_CHECK_FALSE("text_set");
@@ -173,14 +173,14 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
text_edit->set_text("test text");
MessageQueue::get_singleton()->flush();
text_edit->select_all();
- SIGNAL_CHECK("caret_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
MessageQueue::get_singleton()->flush();
CHECK(text_edit->get_text() == "test text");
CHECK(text_edit->get_caret_column() == 9);
CHECK(text_edit->has_selection());
- SIGNAL_CHECK("text_set", empty_singal_args);
+ SIGNAL_CHECK("text_set", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
- SIGNAL_CHECK("caret_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_changed");
text_edit->set_text("test");
@@ -188,14 +188,14 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_text() == "test");
CHECK(text_edit->get_caret_column() == 0);
CHECK_FALSE(text_edit->has_selection());
- SIGNAL_CHECK("text_set", empty_singal_args);
+ SIGNAL_CHECK("text_set", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
- SIGNAL_CHECK("caret_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_changed");
text_edit->select_all();
MessageQueue::get_singleton()->flush();
- SIGNAL_CHECK("caret_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
CHECK(text_edit->has_selection());
text_edit->clear();
@@ -203,9 +203,9 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_text() == "");
CHECK(text_edit->get_caret_column() == 0);
CHECK_FALSE(text_edit->has_selection());
- SIGNAL_CHECK("text_set", empty_singal_args);
+ SIGNAL_CHECK("text_set", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
- SIGNAL_CHECK("caret_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_changed");
}
@@ -225,7 +225,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_line(0) == "test");
CHECK(text_edit->get_line(1) == "");
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
- SIGNAL_CHECK("text_changed", empty_singal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_set");
SIGNAL_CHECK_FALSE("caret_changed");
@@ -233,7 +233,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
text_edit->select_all();
MessageQueue::get_singleton()->flush();
CHECK(text_edit->has_selection());
- SIGNAL_CHECK("caret_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
text_edit->set_line(0, "test text");
MessageQueue::get_singleton()->flush();
@@ -241,7 +241,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->has_selection());
CHECK(text_edit->get_selected_text() == "test");
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
- SIGNAL_CHECK("text_changed", empty_singal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("caret_changed");
SIGNAL_CHECK_FALSE("text_set");
@@ -253,8 +253,8 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->has_selection());
CHECK(text_edit->get_selected_text() == "te");
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
- SIGNAL_CHECK("caret_changed", empty_singal_args);
- SIGNAL_CHECK("text_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_set");
text_edit->set_editable(true);
@@ -264,8 +264,8 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_line(0) == "test text");
CHECK(text_edit->has_selection());
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
- SIGNAL_CHECK("caret_changed", empty_singal_args);
- SIGNAL_CHECK("text_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_set");
text_edit->redo();
@@ -273,8 +273,8 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_line(0) == "te");
CHECK_FALSE(text_edit->has_selection()); // Currently not handled.
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
- SIGNAL_CHECK("caret_changed", empty_singal_args);
- SIGNAL_CHECK("text_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_set");
// Out of range.
@@ -304,14 +304,14 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
text_edit->set_text("testing\nswap");
MessageQueue::get_singleton()->flush();
CHECK(text_edit->get_text() == "testing\nswap");
- SIGNAL_CHECK("text_set", empty_singal_args);
+ SIGNAL_CHECK("text_set", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
- SIGNAL_CHECK("caret_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_changed");
text_edit->set_caret_column(text_edit->get_line(0).length());
MessageQueue::get_singleton()->flush();
- SIGNAL_CHECK("caret_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
((Array)lines_edited_args[1])[1] = 0;
Array swap_args;
@@ -326,8 +326,8 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
MessageQueue::get_singleton()->flush();
CHECK(text_edit->get_text() == "swap\ntesting");
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
- SIGNAL_CHECK("caret_changed", empty_singal_args);
- SIGNAL_CHECK("text_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_set");
text_edit->set_editable(true);
@@ -338,8 +338,8 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
MessageQueue::get_singleton()->flush();
CHECK(text_edit->get_text() == "testing\nswap");
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
- SIGNAL_CHECK("caret_changed", empty_singal_args);
- SIGNAL_CHECK("text_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_set");
lines_edited_args.reverse();
@@ -348,8 +348,8 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
MessageQueue::get_singleton()->flush();
CHECK(text_edit->get_text() == "swap\ntesting");
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
- SIGNAL_CHECK("caret_changed", empty_singal_args);
- SIGNAL_CHECK("text_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_set");
// Out of range.
@@ -390,9 +390,9 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
text_edit->set_text("testing\nswap");
MessageQueue::get_singleton()->flush();
CHECK(text_edit->get_text() == "testing\nswap");
- SIGNAL_CHECK("text_set", empty_singal_args);
+ SIGNAL_CHECK("text_set", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
- SIGNAL_CHECK("caret_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_changed");
text_edit->select_all();
@@ -400,7 +400,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->has_selection());
CHECK(text_edit->get_selection_from_line() == 0);
CHECK(text_edit->get_selection_to_line() == 1);
- SIGNAL_CHECK("caret_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
// insert before should move caret and selecion, and works when not editable.
text_edit->set_editable(false);
@@ -414,8 +414,8 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_selection_from_line() == 1);
CHECK(text_edit->get_selection_to_line() == 2);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
- SIGNAL_CHECK("caret_changed", empty_singal_args);
- SIGNAL_CHECK("text_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_set");
text_edit->set_editable(true);
@@ -427,8 +427,8 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_text() == "testing\nswap");
CHECK_FALSE(text_edit->has_selection()); // Not currently handled.
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
- SIGNAL_CHECK("caret_changed", empty_singal_args);
- SIGNAL_CHECK("text_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_set");
((Array)lines_edited_args[0])[0] = 0;
@@ -438,8 +438,8 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_text() == "new\ntesting\nswap");
CHECK_FALSE(text_edit->has_selection()); // Not currently handled.
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
- SIGNAL_CHECK("caret_changed", empty_singal_args);
- SIGNAL_CHECK("text_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_set");
// Adding inside selection extends selection.
@@ -448,7 +448,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->has_selection());
CHECK(text_edit->get_selection_from_line() == 0);
CHECK(text_edit->get_selection_to_line() == 2);
- SIGNAL_CHECK("caret_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
((Array)lines_edited_args[0])[0] = 2;
((Array)lines_edited_args[0])[1] = 3;
@@ -461,8 +461,8 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_selection_from_line() == 0);
CHECK(text_edit->get_selection_to_line() == 3);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
- SIGNAL_CHECK("caret_changed", empty_singal_args);
- SIGNAL_CHECK("text_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_set");
// Out of range.
@@ -493,8 +493,8 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_caret_line() == 1);
CHECK(text_edit->get_caret_column() == text_edit->get_line(1).size() - 1);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
- SIGNAL_CHECK("text_changed", empty_singal_args);
- SIGNAL_CHECK("caret_changed", empty_singal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_set");
text_edit->set_caret_line(0, false);
@@ -508,8 +508,8 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_caret_line() == 0);
CHECK(text_edit->get_caret_column() == 5);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
- SIGNAL_CHECK("text_changed", empty_singal_args);
- SIGNAL_CHECK("caret_changed", empty_singal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_set");
text_edit->select(0, 0, 0, text_edit->get_line(0).length());
@@ -524,8 +524,8 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_caret_column() == text_edit->get_line(0).size() - 1);
CHECK_FALSE(text_edit->has_selection());
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
- SIGNAL_CHECK("text_changed", empty_singal_args);
- SIGNAL_CHECK("caret_changed", empty_singal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_set");
text_edit->set_editable(true);
@@ -536,8 +536,8 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_caret_column() == 10);
CHECK(text_edit->has_selection());
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
- SIGNAL_CHECK("text_changed", empty_singal_args);
- SIGNAL_CHECK("caret_changed", empty_singal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_set");
text_edit->redo();
@@ -547,8 +547,8 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_caret_column() == 8);
CHECK_FALSE(text_edit->has_selection());
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
- SIGNAL_CHECK("text_changed", empty_singal_args);
- SIGNAL_CHECK("caret_changed", empty_singal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_set");
}
@@ -615,7 +615,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_selection_mode() == TextEdit::SelectionMode::SELECTION_MODE_SHIFT);
CHECK(text_edit->get_caret_line() == 1);
CHECK(text_edit->get_caret_column() == 9);
- SIGNAL_CHECK("caret_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
text_edit->set_caret_line(0);
text_edit->set_caret_column(0);
@@ -660,7 +660,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_selection_to_column() == 4);
CHECK(text_edit->get_caret_line() == 0);
CHECK(text_edit->get_caret_column() == 4);
- SIGNAL_CHECK("caret_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
text_edit->set_selecting_enabled(false);
text_edit->select_word_under_caret();
@@ -817,7 +817,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_selection_to_column() == 3);
CHECK(text_edit->get_caret_line() == 1);
CHECK(text_edit->get_caret_column() == 3);
- SIGNAL_CHECK("caret_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
SEND_GUI_MOUSE_MOTION_EVENT(text_edit, text_edit->get_pos_at_line_column(0, 7), MouseButton::MASK_LEFT, Key::NONE);
CHECK(text_edit->has_selection());
@@ -829,7 +829,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_selection_to_column() == 13);
CHECK(text_edit->get_caret_line() == 1);
CHECK(text_edit->get_caret_column() == 13);
- SIGNAL_CHECK("caret_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
Point2i line_0 = text_edit->get_pos_at_line_column(0, 0);
line_0.y /= 2;
@@ -1098,8 +1098,8 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_text() == "this is\nsome");
CHECK(text_edit->get_caret_line() == 1);
CHECK(text_edit->get_caret_column() == 4);
- SIGNAL_CHECK("caret_changed", empty_singal_args);
- SIGNAL_CHECK("text_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
((Array)lines_edited_args[0])[0] = 1;
@@ -1109,8 +1109,8 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_text() == "this is\nsom");
CHECK(text_edit->get_caret_line() == 1);
CHECK(text_edit->get_caret_column() == 3);
- SIGNAL_CHECK("caret_changed", empty_singal_args);
- SIGNAL_CHECK("text_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
text_edit->end_complex_operation();
@@ -1121,8 +1121,8 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_text() == "this is\n");
CHECK(text_edit->get_caret_line() == 1);
CHECK(text_edit->get_caret_column() == 0);
- SIGNAL_CHECK("caret_changed", empty_singal_args);
- SIGNAL_CHECK("text_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
text_edit->set_editable(false);
@@ -1142,8 +1142,8 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_text() == "this is\nsom");
CHECK(text_edit->get_caret_line() == 1);
CHECK(text_edit->get_caret_column() == 3);
- SIGNAL_CHECK("caret_changed", empty_singal_args);
- SIGNAL_CHECK("text_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
}
@@ -1167,8 +1167,8 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_text() == "some\n");
CHECK(text_edit->get_caret_line() == 0);
CHECK(text_edit->get_caret_column() == 4);
- SIGNAL_CHECK("caret_changed", empty_singal_args);
- SIGNAL_CHECK("text_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
((Array)lines_edited_args[0])[0] = 0;
@@ -1178,8 +1178,8 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_text() == "this is\nsome\n");
CHECK(text_edit->get_caret_line() == 1);
CHECK(text_edit->get_caret_column() == 0);
- SIGNAL_CHECK("caret_changed", empty_singal_args);
- SIGNAL_CHECK("text_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
((Array)lines_edited_args[0])[0] = 1;
@@ -1190,7 +1190,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_caret_line() == 1);
CHECK(text_edit->get_caret_column() == 0);
SIGNAL_CHECK_FALSE("caret_changed");
- SIGNAL_CHECK("text_changed", empty_singal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
text_edit->set_text("this is\nsome\n");
@@ -1211,8 +1211,8 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_text() == "this \nsome\n");
CHECK(text_edit->get_caret_line() == 0);
CHECK(text_edit->get_caret_column() == 5);
- SIGNAL_CHECK("caret_changed", empty_singal_args);
- SIGNAL_CHECK("text_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
text_edit->set_editable(false);
@@ -1276,8 +1276,8 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_caret_line() == 0);
CHECK(text_edit->get_caret_column() == 0);
CHECK_FALSE(text_edit->has_selection());
- SIGNAL_CHECK("caret_changed", empty_singal_args);
- SIGNAL_CHECK("text_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
text_edit->set_caret_line(1);
@@ -1304,8 +1304,8 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_caret_line() == 1);
CHECK(text_edit->get_caret_column() == 0);
CHECK_FALSE(text_edit->has_selection());
- SIGNAL_CHECK("caret_changed", empty_singal_args);
- SIGNAL_CHECK("text_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
}
@@ -1327,8 +1327,8 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_caret_line() == 1);
CHECK(text_edit->get_caret_column() == 0);
CHECK_FALSE(text_edit->has_selection());
- SIGNAL_CHECK("caret_changed", empty_singal_args);
- SIGNAL_CHECK("text_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
text_edit->set_editable(false);
@@ -1363,8 +1363,8 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_caret_line() == 1);
CHECK(text_edit->get_caret_column() == 0);
CHECK_FALSE(text_edit->has_selection());
- SIGNAL_CHECK("caret_changed", empty_singal_args);
- SIGNAL_CHECK("text_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
text_edit->set_editable(false);
@@ -1405,8 +1405,8 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_caret_line() == 1);
CHECK(text_edit->get_caret_column() == 0);
CHECK_FALSE(text_edit->has_selection());
- SIGNAL_CHECK("caret_changed", empty_singal_args);
- SIGNAL_CHECK("text_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
((Array)lines_edited_args[0])[1] = 0;
@@ -1418,8 +1418,8 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_caret_line() == 0);
CHECK(text_edit->get_caret_column() == 0);
CHECK_FALSE(text_edit->has_selection());
- SIGNAL_CHECK("caret_changed", empty_singal_args);
- SIGNAL_CHECK("text_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
text_edit->set_caret_column(text_edit->get_line(0).length());
@@ -1450,8 +1450,8 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_caret_line() == 0);
CHECK(text_edit->get_caret_column() == 0);
CHECK_FALSE(text_edit->has_selection());
- SIGNAL_CHECK("caret_changed", empty_singal_args);
- SIGNAL_CHECK("text_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
InputMap::get_singleton()->action_erase_event("ui_text_backspace_all_to_left", tmpevent);
@@ -1479,8 +1479,8 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_caret_line() == 1);
CHECK(text_edit->get_caret_column() == 0);
CHECK_FALSE(text_edit->has_selection());
- SIGNAL_CHECK("caret_changed", empty_singal_args);
- SIGNAL_CHECK("text_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
text_edit->end_complex_operation();
@@ -1493,8 +1493,8 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_caret_line() == 0);
CHECK(text_edit->get_caret_column() == 0);
CHECK_FALSE(text_edit->has_selection());
- SIGNAL_CHECK("caret_changed", empty_singal_args);
- SIGNAL_CHECK("text_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
text_edit->set_editable(false);
@@ -1525,8 +1525,8 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_caret_line() == 0);
CHECK(text_edit->get_caret_column() == 14);
CHECK_FALSE(text_edit->has_selection());
- SIGNAL_CHECK("caret_changed", empty_singal_args);
- SIGNAL_CHECK("text_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
}
@@ -1552,8 +1552,8 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_caret_line() == 1);
CHECK(text_edit->get_caret_column() == 0);
CHECK_FALSE(text_edit->has_selection());
- SIGNAL_CHECK("caret_changed", empty_singal_args);
- SIGNAL_CHECK("text_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
((Array)lines_edited_args[0])[1] = 0;
@@ -1565,8 +1565,8 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_caret_line() == 0);
CHECK(text_edit->get_caret_column() == 0);
CHECK_FALSE(text_edit->has_selection());
- SIGNAL_CHECK("caret_changed", empty_singal_args);
- SIGNAL_CHECK("text_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
text_edit->set_caret_column(text_edit->get_line(0).length());
@@ -1597,8 +1597,29 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_caret_line() == 0);
CHECK(text_edit->get_caret_column() == 18);
CHECK_FALSE(text_edit->has_selection());
- SIGNAL_CHECK("caret_changed", empty_singal_args);
- SIGNAL_CHECK("text_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+
+ // Select the entire text, from right to left
+ text_edit->select(0, 18, 0, 0);
+ text_edit->set_caret_line(0);
+ text_edit->set_caret_column(0);
+ MessageQueue::get_singleton()->flush();
+
+ SIGNAL_DISCARD("text_set");
+ SIGNAL_DISCARD("text_changed");
+ SIGNAL_DISCARD("lines_edited_from");
+ SIGNAL_DISCARD("caret_changed");
+
+ ((Array)lines_edited_args[0])[0] = 0;
+
+ SEND_GUI_ACTION(text_edit, "ui_text_backspace");
+ CHECK(text_edit->get_text() == "");
+ CHECK(text_edit->get_caret_line() == 0);
+ CHECK(text_edit->get_caret_column() == 0);
+ SIGNAL_CHECK_FALSE("caret_changed");
+ SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
}
@@ -1624,8 +1645,8 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_caret_line() == 0);
CHECK(text_edit->get_caret_column() == 0);
CHECK_FALSE(text_edit->has_selection());
- SIGNAL_CHECK("caret_changed", empty_singal_args);
- SIGNAL_CHECK("text_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
// End of line should not do anything.
@@ -1674,7 +1695,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_caret_column() == 0);
CHECK_FALSE(text_edit->has_selection());
SIGNAL_CHECK_FALSE("caret_changed");
- SIGNAL_CHECK("text_changed", empty_singal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
InputMap::get_singleton()->action_erase_event("ui_text_delete_all_to_right", tmpevent);
@@ -1702,8 +1723,8 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_caret_line() == 0);
CHECK(text_edit->get_caret_column() == 0);
CHECK_FALSE(text_edit->has_selection());
- SIGNAL_CHECK("caret_changed", empty_singal_args);
- SIGNAL_CHECK("text_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
// With selection should be a normal delete.
@@ -1723,7 +1744,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_caret_column() == text_edit->get_line(0).length());
CHECK_FALSE(text_edit->has_selection());
SIGNAL_CHECK_FALSE("caret_changed");
- SIGNAL_CHECK("text_changed", empty_singal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
((Array)lines_edited_args[0])[0] = 0;
@@ -1754,7 +1775,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_caret_column() == 0);
CHECK_FALSE(text_edit->has_selection());
SIGNAL_CHECK_FALSE("caret_changed");
- SIGNAL_CHECK("text_changed", empty_singal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
}
@@ -1780,8 +1801,8 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_caret_line() == 0);
CHECK(text_edit->get_caret_column() == 0);
CHECK_FALSE(text_edit->has_selection());
- SIGNAL_CHECK("caret_changed", empty_singal_args);
- SIGNAL_CHECK("text_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
// With selection should be a normal delete.
@@ -1801,7 +1822,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_caret_column() == text_edit->get_line(0).length());
CHECK_FALSE(text_edit->has_selection());
SIGNAL_CHECK_FALSE("caret_changed");
- SIGNAL_CHECK("text_changed", empty_singal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
((Array)lines_edited_args[0])[0] = 0;
@@ -1832,7 +1853,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_caret_column() == 0);
CHECK_FALSE(text_edit->has_selection());
SIGNAL_CHECK_FALSE("caret_changed");
- SIGNAL_CHECK("text_changed", empty_singal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
SEND_GUI_ACTION(text_edit, "ui_text_delete");
@@ -1842,7 +1863,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_caret_column() == 0);
CHECK_FALSE(text_edit->has_selection());
SIGNAL_CHECK_FALSE("caret_changed");
- SIGNAL_CHECK("text_changed", empty_singal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
text_edit->set_caret_mid_grapheme_enabled(false);
@@ -1866,7 +1887,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_caret_column() == 0);
CHECK_FALSE(text_edit->has_selection());
SIGNAL_CHECK_FALSE("caret_changed");
- SIGNAL_CHECK("text_changed", empty_singal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
}
@@ -1892,7 +1913,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_caret_column() == 5);
CHECK(text_edit->get_selected_text() == "is");
CHECK(text_edit->has_selection());
- SIGNAL_CHECK("caret_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_changed");
SIGNAL_CHECK_FALSE("lines_edited_from");
@@ -1902,7 +1923,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_caret_line() == 1);
CHECK(text_edit->get_caret_column() == 0);
CHECK_FALSE(text_edit->has_selection());
- SIGNAL_CHECK("caret_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_changed");
SIGNAL_CHECK_FALSE("lines_edited_from");
@@ -1912,7 +1933,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_caret_line() == 0);
CHECK(text_edit->get_caret_column() == 0);
CHECK_FALSE(text_edit->has_selection());
- SIGNAL_CHECK("caret_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_changed");
SIGNAL_CHECK_FALSE("lines_edited_from");
}
@@ -1935,7 +1956,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_caret_line() == 1);
CHECK(text_edit->get_caret_column() == 2);
CHECK_FALSE(text_edit->has_selection());
- SIGNAL_CHECK("caret_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_changed");
SIGNAL_CHECK_FALSE("lines_edited_from");
@@ -1946,7 +1967,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_caret_column() == 1);
CHECK(text_edit->get_selected_text() == "h");
CHECK(text_edit->has_selection());
- SIGNAL_CHECK("caret_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_changed");
SIGNAL_CHECK_FALSE("lines_edited_from");
@@ -1966,7 +1987,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_caret_line() == 1);
CHECK(text_edit->get_caret_column() == 0);
CHECK_FALSE(text_edit->has_selection());
- SIGNAL_CHECK("caret_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_changed");
SIGNAL_CHECK_FALSE("lines_edited_from");
@@ -1976,7 +1997,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_caret_line() == 0);
CHECK(text_edit->get_caret_column() == 0);
CHECK_FALSE(text_edit->has_selection());
- SIGNAL_CHECK("caret_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_changed");
SIGNAL_CHECK_FALSE("lines_edited_from");
}
@@ -2003,7 +2024,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_caret_column() == 17);
CHECK(text_edit->get_selected_text() == "test");
CHECK(text_edit->has_selection());
- SIGNAL_CHECK("caret_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_changed");
SIGNAL_CHECK_FALSE("lines_edited_from");
@@ -2013,7 +2034,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_caret_line() == 0);
CHECK(text_edit->get_caret_column() == 22);
CHECK_FALSE(text_edit->has_selection());
- SIGNAL_CHECK("caret_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_changed");
SIGNAL_CHECK_FALSE("lines_edited_from");
@@ -2023,7 +2044,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_caret_line() == 1);
CHECK(text_edit->get_caret_column() == 0);
CHECK_FALSE(text_edit->has_selection());
- SIGNAL_CHECK("caret_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_changed");
SIGNAL_CHECK_FALSE("lines_edited_from");
}
@@ -2046,7 +2067,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_caret_line() == 0);
CHECK(text_edit->get_caret_column() == 20);
CHECK_FALSE(text_edit->has_selection());
- SIGNAL_CHECK("caret_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_changed");
SIGNAL_CHECK_FALSE("lines_edited_from");
@@ -2057,7 +2078,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_caret_column() == 21);
CHECK(text_edit->get_selected_text() == "x");
CHECK(text_edit->has_selection());
- SIGNAL_CHECK("caret_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_changed");
SIGNAL_CHECK_FALSE("lines_edited_from");
@@ -2077,7 +2098,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_caret_line() == 0);
CHECK(text_edit->get_caret_column() == 22);
CHECK_FALSE(text_edit->has_selection());
- SIGNAL_CHECK("caret_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_changed");
SIGNAL_CHECK_FALSE("lines_edited_from");
@@ -2087,7 +2108,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_caret_line() == 1);
CHECK(text_edit->get_caret_column() == 0);
CHECK_FALSE(text_edit->has_selection());
- SIGNAL_CHECK("caret_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_changed");
SIGNAL_CHECK_FALSE("lines_edited_from");
}
@@ -2114,7 +2135,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_caret_column() == 5);
CHECK(text_edit->get_selected_text() == "\ngo here");
CHECK(text_edit->has_selection());
- SIGNAL_CHECK("caret_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_changed");
SIGNAL_CHECK_FALSE("lines_edited_from");
@@ -2124,7 +2145,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_caret_line() == 1);
CHECK(text_edit->get_caret_column() == 8);
CHECK_FALSE(text_edit->has_selection());
- SIGNAL_CHECK("caret_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_changed");
SIGNAL_CHECK_FALSE("lines_edited_from");
@@ -2134,7 +2155,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_caret_line() == 0);
CHECK(text_edit->get_caret_column() == 12);
CHECK_FALSE(text_edit->has_selection());
- SIGNAL_CHECK("caret_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_changed");
SIGNAL_CHECK_FALSE("lines_edited_from");
text_edit->set_caret_column(12, false);
@@ -2145,7 +2166,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_caret_line() == 0);
CHECK(text_edit->get_caret_column() == 7);
CHECK_FALSE(text_edit->has_selection());
- SIGNAL_CHECK("caret_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_changed");
SIGNAL_CHECK_FALSE("lines_edited_from");
}
@@ -2172,7 +2193,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_caret_column() == 5);
CHECK(text_edit->get_selected_text() == "\nlines");
CHECK(text_edit->has_selection());
- SIGNAL_CHECK("caret_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_changed");
SIGNAL_CHECK_FALSE("lines_edited_from");
@@ -2182,7 +2203,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_caret_line() == 2);
CHECK(text_edit->get_caret_column() == 8);
CHECK_FALSE(text_edit->has_selection());
- SIGNAL_CHECK("caret_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_changed");
SIGNAL_CHECK_FALSE("lines_edited_from");
@@ -2192,7 +2213,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_caret_line() == 3);
CHECK(text_edit->get_caret_column() == 7);
CHECK_FALSE(text_edit->has_selection());
- SIGNAL_CHECK("caret_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_changed");
SIGNAL_CHECK_FALSE("lines_edited_from");
text_edit->set_caret_column(7, false);
@@ -2203,7 +2224,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_caret_line() == 3);
CHECK(text_edit->get_caret_column() == 12);
CHECK_FALSE(text_edit->has_selection());
- SIGNAL_CHECK("caret_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_changed");
SIGNAL_CHECK_FALSE("lines_edited_from");
}
@@ -2234,7 +2255,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_caret_column() == 0);
CHECK(text_edit->get_selected_text() == "this is some\nother test\nlines\ngo here");
CHECK(text_edit->has_selection());
- SIGNAL_CHECK("caret_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_changed");
SIGNAL_CHECK_FALSE("lines_edited_from");
@@ -2275,7 +2296,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_caret_column() == 12);
CHECK(text_edit->get_selected_text() == "go here\nlines\nother test\nthis is some");
CHECK(text_edit->has_selection());
- SIGNAL_CHECK("caret_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_changed");
SIGNAL_CHECK_FALSE("lines_edited_from");
@@ -2315,7 +2336,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_caret_column() == 10);
CHECK(text_edit->has_selection());
CHECK(text_edit->get_selected_text() == "some");
- SIGNAL_CHECK("caret_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_changed");
SIGNAL_CHECK_FALSE("lines_edited_from");
@@ -2324,7 +2345,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_caret_line() == 0);
CHECK(text_edit->get_caret_column() == 2);
CHECK_FALSE(text_edit->has_selection());
- SIGNAL_CHECK("caret_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_changed");
SIGNAL_CHECK_FALSE("lines_edited_from");
@@ -2333,7 +2354,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_caret_line() == 0);
CHECK(text_edit->get_caret_column() == 0);
CHECK_FALSE(text_edit->has_selection());
- SIGNAL_CHECK("caret_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_changed");
SIGNAL_CHECK_FALSE("lines_edited_from");
@@ -2342,7 +2363,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_caret_line() == 0);
CHECK(text_edit->get_caret_column() == 2);
CHECK_FALSE(text_edit->has_selection());
- SIGNAL_CHECK("caret_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_changed");
SIGNAL_CHECK_FALSE("lines_edited_from");
}
@@ -2372,7 +2393,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_caret_column() == 9);
CHECK(text_edit->has_selection());
CHECK(text_edit->get_selected_text() == " this is");
- SIGNAL_CHECK("caret_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_changed");
SIGNAL_CHECK_FALSE("lines_edited_from");
@@ -2381,7 +2402,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_caret_line() == 0);
CHECK(text_edit->get_caret_column() == text_edit->get_line(0).length());
CHECK_FALSE(text_edit->has_selection());
- SIGNAL_CHECK("caret_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_changed");
SIGNAL_CHECK_FALSE("lines_edited_from");
}
@@ -2399,8 +2420,8 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_viewport()->is_input_handled());
CHECK(text_edit->get_text() == "aA");
CHECK(text_edit->get_caret_column() == 2);
- SIGNAL_CHECK("caret_changed", empty_singal_args);
- SIGNAL_CHECK("text_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
text_edit->set_editable(false);
@@ -2420,8 +2441,8 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_viewport()->is_input_handled());
CHECK(text_edit->get_text() == "BA");
CHECK(text_edit->get_caret_column() == 1);
- SIGNAL_CHECK("caret_changed", empty_singal_args);
- SIGNAL_CHECK("text_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
SEND_GUI_ACTION(text_edit, "ui_text_toggle_insert_mode");
@@ -2431,8 +2452,8 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_viewport()->is_input_handled());
CHECK(text_edit->get_text() == "BB");
CHECK(text_edit->get_caret_column() == 2);
- SIGNAL_CHECK("caret_changed", empty_singal_args);
- SIGNAL_CHECK("text_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
text_edit->select(0, 0, 0, 1);
@@ -2440,8 +2461,8 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_viewport()->is_input_handled());
CHECK(text_edit->get_text() == "AB");
CHECK(text_edit->get_caret_column() == 1);
- SIGNAL_CHECK("caret_changed", empty_singal_args);
- SIGNAL_CHECK("text_changed", empty_singal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
text_edit->set_overtype_mode_enabled(false);
CHECK_FALSE(text_edit->is_overtype_mode_enabled());
@@ -3357,8 +3378,8 @@ TEST_CASE("[SceneTree][TextEdit] gutters") {
TextEdit *text_edit = memnew(TextEdit);
SceneTree::get_singleton()->get_root()->add_child(text_edit);
- Array empty_singal_args;
- empty_singal_args.push_back(Array());
+ Array empty_signal_args;
+ empty_signal_args.push_back(Array());
SIGNAL_WATCH(text_edit, "gutter_clicked");
SIGNAL_WATCH(text_edit, "gutter_added");
@@ -3367,7 +3388,7 @@ TEST_CASE("[SceneTree][TextEdit] gutters") {
SUBCASE("[TextEdit] gutter add and remove") {
text_edit->add_gutter();
CHECK(text_edit->get_gutter_count() == 1);
- SIGNAL_CHECK("gutter_added", empty_singal_args);
+ SIGNAL_CHECK("gutter_added", empty_signal_args);
text_edit->set_gutter_name(0, "test_gutter");
CHECK(text_edit->get_gutter_name(0) == "test_gutter");
@@ -3383,7 +3404,7 @@ TEST_CASE("[SceneTree][TextEdit] gutters") {
CHECK(text_edit->get_total_gutter_width() < 30);
CHECK(text_edit->get_gutter_count() == 2);
CHECK(text_edit->get_gutter_name(0) == "test_gutter");
- SIGNAL_CHECK("gutter_added", empty_singal_args);
+ SIGNAL_CHECK("gutter_added", empty_signal_args);
text_edit->set_gutter_draw(1, false);
CHECK(text_edit->get_total_gutter_width() > 10);
@@ -3392,22 +3413,22 @@ TEST_CASE("[SceneTree][TextEdit] gutters") {
text_edit->add_gutter(100);
CHECK(text_edit->get_gutter_count() == 3);
CHECK(text_edit->get_gutter_name(0) == "test_gutter");
- SIGNAL_CHECK("gutter_added", empty_singal_args);
+ SIGNAL_CHECK("gutter_added", empty_signal_args);
text_edit->add_gutter(0);
CHECK(text_edit->get_gutter_count() == 4);
CHECK(text_edit->get_gutter_name(1) == "test_gutter");
- SIGNAL_CHECK("gutter_added", empty_singal_args);
+ SIGNAL_CHECK("gutter_added", empty_signal_args);
text_edit->remove_gutter(2);
CHECK(text_edit->get_gutter_name(1) == "test_gutter");
CHECK(text_edit->get_gutter_count() == 3);
- SIGNAL_CHECK("gutter_removed", empty_singal_args);
+ SIGNAL_CHECK("gutter_removed", empty_signal_args);
text_edit->remove_gutter(0);
CHECK(text_edit->get_gutter_name(0) == "test_gutter");
CHECK(text_edit->get_gutter_count() == 2);
- SIGNAL_CHECK("gutter_removed", empty_singal_args);
+ SIGNAL_CHECK("gutter_removed", empty_signal_args);
ERR_PRINT_OFF;
text_edit->remove_gutter(-1);
@@ -3424,7 +3445,7 @@ TEST_CASE("[SceneTree][TextEdit] gutters") {
SUBCASE("[TextEdit] gutter data") {
text_edit->add_gutter();
CHECK(text_edit->get_gutter_count() == 1);
- SIGNAL_CHECK("gutter_added", empty_singal_args);
+ SIGNAL_CHECK("gutter_added", empty_signal_args);
text_edit->set_gutter_name(0, "test_gutter");
CHECK(text_edit->get_gutter_name(0) == "test_gutter");
diff --git a/tests/test_main.cpp b/tests/test_main.cpp
index be51afd83c..4cdc5ea1fa 100644
--- a/tests/test_main.cpp
+++ b/tests/test_main.cpp
@@ -59,10 +59,10 @@
#include "tests/core/string/test_string.h"
#include "tests/core/string/test_translation.h"
#include "tests/core/templates/test_command_queue.h"
+#include "tests/core/templates/test_hash_map.h"
#include "tests/core/templates/test_list.h"
#include "tests/core/templates/test_local_vector.h"
#include "tests/core/templates/test_lru.h"
-#include "tests/core/templates/test_ordered_hash_map.h"
#include "tests/core/templates/test_paged_array.h"
#include "tests/core/templates/test_vector.h"
#include "tests/core/test_crypto.h"
diff --git a/thirdparty/README.md b/thirdparty/README.md
index 41cd0230cc..d32db920ad 100644
--- a/thirdparty/README.md
+++ b/thirdparty/README.md
@@ -115,7 +115,7 @@ will limit its functionality to IPv4 only.
## etcpak
- Upstream: https://github.com/wolfpld/etcpak
-- Version: git (7c3cb6fe708d4ae330b0ab2af1ad472bae2a37a2, 2021)
+- Version: git (10fc4ce627f9a17ed49bf97fcc3796a712033ba1, 2022)
- License: BSD-3-Clause
Files extracted from upstream source:
diff --git a/thirdparty/etcpak/ProcessRGB.cpp b/thirdparty/etcpak/ProcessRGB.cpp
index d60164bcc8..f488f3b282 100644
--- a/thirdparty/etcpak/ProcessRGB.cpp
+++ b/thirdparty/etcpak/ProcessRGB.cpp
@@ -28,6 +28,10 @@
# define _bswap64(x) __builtin_bswap64(x)
#endif
+static const uint32_t MaxError = 1065369600; // ((38+76+14) * 255)^2
+// common T-/H-mode table
+static uint8_t tableTH[8] = { 3, 6, 11, 16, 23, 32, 41, 64 };
+
// thresholds for the early compression-mode decision scheme
// default: 0.03, 0.09, and 0.38
float ecmd_threshold[3] = { 0.03f, 0.09f, 0.38f };
@@ -36,13 +40,17 @@ static const uint8_t ModeUndecided = 0;
static const uint8_t ModePlanar = 0x1;
static const uint8_t ModeTH = 0x2;
+const unsigned int R = 2;
+const unsigned int G = 1;
+const unsigned int B = 0;
+
struct Luma
{
#ifdef __AVX2__
float max, min;
uint8_t minIdx = 255, maxIdx = 255;
__m128i luma8;
-#elif defined __ARM_NEON
+#elif defined __ARM_NEON && defined __aarch64__
float max, min;
uint8_t minIdx = 255, maxIdx = 255;
uint8x16_t luma8;
@@ -52,8 +60,206 @@ struct Luma
#endif
};
+#ifdef __AVX2__
+struct Plane
+{
+ uint64_t plane;
+ uint64_t error;
+ __m256i sum4;
+};
+#endif
+
+#if defined __AVX2__ || (defined __ARM_NEON && defined __aarch64__)
+struct Channels
+{
+#ifdef __AVX2__
+ __m128i r8, g8, b8;
+#elif defined __ARM_NEON && defined __aarch64__
+ uint8x16x2_t r, g, b;
+#endif
+};
+#endif
+
namespace
{
+static etcpak_force_inline uint8_t clamp( uint8_t min, int16_t val, uint8_t max )
+{
+ return val < min ? min : ( val > max ? max : val );
+}
+
+static etcpak_force_inline uint8_t clampMin( uint8_t min, int16_t val )
+{
+ return val < min ? min : val;
+}
+
+static etcpak_force_inline uint8_t clampMax( int16_t val, uint8_t max )
+{
+ return val > max ? max : val;
+}
+
+// slightly faster than std::sort
+static void insertionSort( uint8_t* arr1, uint8_t* arr2 )
+{
+ for( uint8_t i = 1; i < 16; ++i )
+ {
+ uint8_t value = arr1[i];
+ uint8_t hole = i;
+
+ for( ; hole > 0 && value < arr1[hole - 1]; --hole )
+ {
+ arr1[hole] = arr1[hole - 1];
+ arr2[hole] = arr2[hole - 1];
+ }
+ arr1[hole] = value;
+ arr2[hole] = i;
+ }
+}
+
+//converts indices from |a0|a1|e0|e1|i0|i1|m0|m1|b0|b1|f0|f1|j0|j1|n0|n1|c0|c1|g0|g1|k0|k1|o0|o1|d0|d1|h0|h1|l0|l1|p0|p1| previously used by T- and H-modes
+// into |p0|o0|n0|m0|l0|k0|j0|i0|h0|g0|f0|e0|d0|c0|b0|a0|p1|o1|n1|m1|l1|k1|j1|i1|h1|g1|f1|e1|d1|c1|b1|a1| which should be used for all modes.
+// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved.
+static etcpak_force_inline int indexConversion( int pixelIndices )
+{
+ int correctIndices = 0;
+ int LSB[4][4];
+ int MSB[4][4];
+ int shift = 0;
+ for( int y = 3; y >= 0; y-- )
+ {
+ for( int x = 3; x >= 0; x-- )
+ {
+ LSB[x][y] = ( pixelIndices >> shift ) & 1;
+ shift++;
+ MSB[x][y] = ( pixelIndices >> shift ) & 1;
+ shift++;
+ }
+ }
+ shift = 0;
+ for( int x = 0; x < 4; x++ )
+ {
+ for( int y = 0; y < 4; y++ )
+ {
+ correctIndices |= ( LSB[x][y] << shift );
+ correctIndices |= ( MSB[x][y] << ( 16 + shift ) );
+ shift++;
+ }
+ }
+ return correctIndices;
+}
+
+// Swapping two RGB-colors
+// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved.
+static etcpak_force_inline void swapColors( uint8_t( colors )[2][3] )
+{
+ uint8_t temp = colors[0][R];
+ colors[0][R] = colors[1][R];
+ colors[1][R] = temp;
+
+ temp = colors[0][G];
+ colors[0][G] = colors[1][G];
+ colors[1][G] = temp;
+
+ temp = colors[0][B];
+ colors[0][B] = colors[1][B];
+ colors[1][B] = temp;
+}
+
+
+// calculates quantized colors for T or H modes
+void compressColor( uint8_t( currColor )[2][3], uint8_t( quantColor )[2][3], bool t_mode )
+{
+ if( t_mode )
+ {
+ quantColor[0][R] = clampMax( 15 * ( currColor[0][R] + 8 ) / 255, 15 );
+ quantColor[0][G] = clampMax( 15 * ( currColor[0][G] + 8 ) / 255, 15 );
+ quantColor[0][B] = clampMax( 15 * ( currColor[0][B] + 8 ) / 255, 15 );
+ }
+ else // clamped to [1,14] to get a wider range
+ {
+ quantColor[0][R] = clamp( 1, 15 * ( currColor[0][R] + 8 ) / 255, 14 );
+ quantColor[0][G] = clamp( 1, 15 * ( currColor[0][G] + 8 ) / 255, 14 );
+ quantColor[0][B] = clamp( 1, 15 * ( currColor[0][B] + 8 ) / 255, 14 );
+ }
+
+ // clamped to [1,14] to get a wider range
+ quantColor[1][R] = clamp( 1, 15 * ( currColor[1][R] + 8 ) / 255, 14 );
+ quantColor[1][G] = clamp( 1, 15 * ( currColor[1][G] + 8 ) / 255, 14 );
+ quantColor[1][B] = clamp( 1, 15 * ( currColor[1][B] + 8 ) / 255, 14 );
+}
+
+// three decoding functions come from ETCPACK v2.74 and are slightly changed.
+static etcpak_force_inline void decompressColor( uint8_t( colorsRGB444 )[2][3], uint8_t( colors )[2][3] )
+{
+ // The color should be retrieved as:
+ //
+ // c = round(255/(r_bits^2-1))*comp_color
+ //
+ // This is similar to bit replication
+ //
+ // Note -- this code only work for bit replication from 4 bits and up --- 3 bits needs
+ // two copy operations.
+ colors[0][R] = ( colorsRGB444[0][R] << 4 ) | colorsRGB444[0][R];
+ colors[0][G] = ( colorsRGB444[0][G] << 4 ) | colorsRGB444[0][G];
+ colors[0][B] = ( colorsRGB444[0][B] << 4 ) | colorsRGB444[0][B];
+ colors[1][R] = ( colorsRGB444[1][R] << 4 ) | colorsRGB444[1][R];
+ colors[1][G] = ( colorsRGB444[1][G] << 4 ) | colorsRGB444[1][G];
+ colors[1][B] = ( colorsRGB444[1][B] << 4 ) | colorsRGB444[1][B];
+}
+
+// calculates the paint colors from the block colors
+// using a distance d and one of the H- or T-patterns.
+static void calculatePaintColors59T( uint8_t d, uint8_t( colors )[2][3], uint8_t( pColors )[4][3] )
+{
+ //////////////////////////////////////////////
+ //
+ // C3 C1 C4----C1---C2
+ // | | |
+ // | | |
+ // |-------| |
+ // | | |
+ // | | |
+ // C4 C2 C3
+ //
+ //////////////////////////////////////////////
+
+ // C4
+ pColors[3][R] = clampMin( 0, colors[1][R] - tableTH[d] );
+ pColors[3][G] = clampMin( 0, colors[1][G] - tableTH[d] );
+ pColors[3][B] = clampMin( 0, colors[1][B] - tableTH[d] );
+
+ // C3
+ pColors[0][R] = colors[0][R];
+ pColors[0][G] = colors[0][G];
+ pColors[0][B] = colors[0][B];
+ // C2
+ pColors[1][R] = clampMax( colors[1][R] + tableTH[d], 255 );
+ pColors[1][G] = clampMax( colors[1][G] + tableTH[d], 255 );
+ pColors[1][B] = clampMax( colors[1][B] + tableTH[d], 255 );
+ // C1
+ pColors[2][R] = colors[1][R];
+ pColors[2][G] = colors[1][G];
+ pColors[2][B] = colors[1][B];
+}
+
+static void calculatePaintColors58H( uint8_t d, uint8_t( colors )[2][3], uint8_t( pColors )[4][3] )
+{
+ pColors[3][R] = clampMin( 0, colors[1][R] - tableTH[d] );
+ pColors[3][G] = clampMin( 0, colors[1][G] - tableTH[d] );
+ pColors[3][B] = clampMin( 0, colors[1][B] - tableTH[d] );
+
+ // C1
+ pColors[0][R] = clampMax( colors[0][R] + tableTH[d], 255 );
+ pColors[0][G] = clampMax( colors[0][G] + tableTH[d], 255 );
+ pColors[0][B] = clampMax( colors[0][B] + tableTH[d], 255 );
+ // C2
+ pColors[1][R] = clampMin( 0, colors[0][R] - tableTH[d] );
+ pColors[1][G] = clampMin( 0, colors[0][G] - tableTH[d] );
+ pColors[1][B] = clampMin( 0, colors[0][B] - tableTH[d] );
+ // C3
+ pColors[2][R] = clampMax( colors[1][R] + tableTH[d], 255 );
+ pColors[2][G] = clampMax( colors[1][G] + tableTH[d], 255 );
+ pColors[2][B] = clampMax( colors[1][B] + tableTH[d], 255 );
+}
#if defined _MSC_VER && !defined __clang__
static etcpak_force_inline unsigned long _bit_scan_forward( unsigned long mask )
@@ -586,127 +792,107 @@ static etcpak_force_inline __m128i r6g7b6_AVX2(__m128 cof, __m128 chf, __m128 cv
return _mm_shuffle_epi8(cohv5, _mm_setr_epi8(6, 5, 4, -1, 2, 1, 0, -1, 10, 9, 8, -1, -1, -1, -1, -1));
}
-struct Plane
+static etcpak_force_inline Plane Planar_AVX2( const Channels& ch, uint8_t& mode, bool useHeuristics )
{
- uint64_t plane;
- uint64_t error;
- __m256i sum4;
-};
-
-static etcpak_force_inline Plane Planar_AVX2( const uint8_t* src, const uint8_t mode )
-{
- __m128i d0 = _mm_loadu_si128(((__m128i*)src) + 0);
- __m128i d1 = _mm_loadu_si128(((__m128i*)src) + 1);
- __m128i d2 = _mm_loadu_si128(((__m128i*)src) + 2);
- __m128i d3 = _mm_loadu_si128(((__m128i*)src) + 3);
-
- __m128i rgb0 = _mm_shuffle_epi8(d0, _mm_setr_epi8(0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, -1, -1, -1, -1));
- __m128i rgb1 = _mm_shuffle_epi8(d1, _mm_setr_epi8(0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, -1, -1, -1, -1));
- __m128i rgb2 = _mm_shuffle_epi8(d2, _mm_setr_epi8(0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, -1, -1, -1, -1));
- __m128i rgb3 = _mm_shuffle_epi8(d3, _mm_setr_epi8(0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, -1, -1, -1, -1));
-
- __m128i rg0 = _mm_unpacklo_epi32(rgb0, rgb1);
- __m128i rg1 = _mm_unpacklo_epi32(rgb2, rgb3);
- __m128i b0 = _mm_unpackhi_epi32(rgb0, rgb1);
- __m128i b1 = _mm_unpackhi_epi32(rgb2, rgb3);
-
- // swap channels
- __m128i b8 = _mm_unpacklo_epi64(rg0, rg1);
- __m128i g8 = _mm_unpackhi_epi64(rg0, rg1);
- __m128i r8 = _mm_unpacklo_epi64(b0, b1);
+ __m128i t0 = _mm_sad_epu8( ch.r8, _mm_setzero_si128() );
+ __m128i t1 = _mm_sad_epu8( ch.g8, _mm_setzero_si128() );
+ __m128i t2 = _mm_sad_epu8( ch.b8, _mm_setzero_si128() );
- __m128i t0 = _mm_sad_epu8(r8, _mm_setzero_si128());
- __m128i t1 = _mm_sad_epu8(g8, _mm_setzero_si128());
- __m128i t2 = _mm_sad_epu8(b8, _mm_setzero_si128());
+ __m128i r8s = _mm_shuffle_epi8( ch.r8, _mm_set_epi8( 0xF, 0xE, 0xB, 0xA, 0x7, 0x6, 0x3, 0x2, 0xD, 0xC, 0x9, 0x8, 0x5, 0x4, 0x1, 0x0 ) );
+ __m128i g8s = _mm_shuffle_epi8( ch.g8, _mm_set_epi8( 0xF, 0xE, 0xB, 0xA, 0x7, 0x6, 0x3, 0x2, 0xD, 0xC, 0x9, 0x8, 0x5, 0x4, 0x1, 0x0 ) );
+ __m128i b8s = _mm_shuffle_epi8( ch.b8, _mm_set_epi8( 0xF, 0xE, 0xB, 0xA, 0x7, 0x6, 0x3, 0x2, 0xD, 0xC, 0x9, 0x8, 0x5, 0x4, 0x1, 0x0 ) );
- __m128i r8s = _mm_shuffle_epi8(r8, _mm_set_epi8(0xF, 0xE, 0xB, 0xA, 0x7, 0x6, 0x3, 0x2, 0xD, 0xC, 0x9, 0x8, 0x5, 0x4, 0x1, 0x0));
- __m128i g8s = _mm_shuffle_epi8(g8, _mm_set_epi8(0xF, 0xE, 0xB, 0xA, 0x7, 0x6, 0x3, 0x2, 0xD, 0xC, 0x9, 0x8, 0x5, 0x4, 0x1, 0x0));
- __m128i b8s = _mm_shuffle_epi8(b8, _mm_set_epi8(0xF, 0xE, 0xB, 0xA, 0x7, 0x6, 0x3, 0x2, 0xD, 0xC, 0x9, 0x8, 0x5, 0x4, 0x1, 0x0));
+ __m128i s0 = _mm_sad_epu8( r8s, _mm_setzero_si128() );
+ __m128i s1 = _mm_sad_epu8( g8s, _mm_setzero_si128() );
+ __m128i s2 = _mm_sad_epu8( b8s, _mm_setzero_si128() );
- __m128i s0 = _mm_sad_epu8(r8s, _mm_setzero_si128());
- __m128i s1 = _mm_sad_epu8(g8s, _mm_setzero_si128());
- __m128i s2 = _mm_sad_epu8(b8s, _mm_setzero_si128());
+ __m256i sr0 = _mm256_insertf128_si256( _mm256_castsi128_si256( t0 ), s0, 1 );
+ __m256i sg0 = _mm256_insertf128_si256( _mm256_castsi128_si256( t1 ), s1, 1 );
+ __m256i sb0 = _mm256_insertf128_si256( _mm256_castsi128_si256( t2 ), s2, 1 );
- __m256i sr0 = _mm256_insertf128_si256(_mm256_castsi128_si256(t0), s0, 1);
- __m256i sg0 = _mm256_insertf128_si256(_mm256_castsi128_si256(t1), s1, 1);
- __m256i sb0 = _mm256_insertf128_si256(_mm256_castsi128_si256(t2), s2, 1);
+ __m256i sr1 = _mm256_slli_epi64( sr0, 32 );
+ __m256i sg1 = _mm256_slli_epi64( sg0, 16 );
- __m256i sr1 = _mm256_slli_epi64(sr0, 32);
- __m256i sg1 = _mm256_slli_epi64(sg0, 16);
+ __m256i srb = _mm256_or_si256( sr1, sb0 );
+ __m256i srgb = _mm256_or_si256( srb, sg1 );
- __m256i srb = _mm256_or_si256(sr1, sb0);
- __m256i srgb = _mm256_or_si256(srb, sg1);
+ if( mode != ModePlanar && useHeuristics )
+ {
+ Plane plane;
+ plane.sum4 = _mm256_permute4x64_epi64( srgb, _MM_SHUFFLE( 2, 3, 0, 1 ) );
+ return plane;
+ }
- __m128i t3 = _mm_castps_si128(_mm_shuffle_ps(_mm_castsi128_ps(t0), _mm_castsi128_ps(t1), _MM_SHUFFLE(2, 0, 2, 0)));
- __m128i t4 = _mm_shuffle_epi32(t2, _MM_SHUFFLE(3, 1, 2, 0));
- __m128i t5 = _mm_hadd_epi32(t3, t4);
- __m128i t6 = _mm_shuffle_epi32(t5, _MM_SHUFFLE(1, 1, 1, 1));
- __m128i t7 = _mm_shuffle_epi32(t5, _MM_SHUFFLE(2, 2, 2, 2));
+ __m128i t3 = _mm_castps_si128( _mm_shuffle_ps( _mm_castsi128_ps( t0 ), _mm_castsi128_ps( t1 ), _MM_SHUFFLE( 2, 0, 2, 0 ) ) );
+ __m128i t4 = _mm_shuffle_epi32( t2, _MM_SHUFFLE( 3, 1, 2, 0 ) );
+ __m128i t5 = _mm_hadd_epi32( t3, t4 );
+ __m128i t6 = _mm_shuffle_epi32( t5, _MM_SHUFFLE( 1, 1, 1, 1 ) );
+ __m128i t7 = _mm_shuffle_epi32( t5, _MM_SHUFFLE( 2, 2, 2, 2 ) );
- __m256i sr = _mm256_broadcastw_epi16(t5);
- __m256i sg = _mm256_broadcastw_epi16(t6);
- __m256i sb = _mm256_broadcastw_epi16(t7);
+ __m256i sr = _mm256_broadcastw_epi16( t5 );
+ __m256i sg = _mm256_broadcastw_epi16( t6 );
+ __m256i sb = _mm256_broadcastw_epi16( t7 );
- __m256i r08 = _mm256_cvtepu8_epi16(r8);
- __m256i g08 = _mm256_cvtepu8_epi16(g8);
- __m256i b08 = _mm256_cvtepu8_epi16(b8);
+ __m256i r08 = _mm256_cvtepu8_epi16( ch.r8 );
+ __m256i g08 = _mm256_cvtepu8_epi16( ch.g8 );
+ __m256i b08 = _mm256_cvtepu8_epi16( ch.b8 );
- __m256i r16 = _mm256_slli_epi16(r08, 4);
- __m256i g16 = _mm256_slli_epi16(g08, 4);
- __m256i b16 = _mm256_slli_epi16(b08, 4);
+ __m256i r16 = _mm256_slli_epi16( r08, 4 );
+ __m256i g16 = _mm256_slli_epi16( g08, 4 );
+ __m256i b16 = _mm256_slli_epi16( b08, 4 );
- __m256i difR0 = _mm256_sub_epi16(r16, sr);
- __m256i difG0 = _mm256_sub_epi16(g16, sg);
- __m256i difB0 = _mm256_sub_epi16(b16, sb);
+ __m256i difR0 = _mm256_sub_epi16( r16, sr );
+ __m256i difG0 = _mm256_sub_epi16( g16, sg );
+ __m256i difB0 = _mm256_sub_epi16( b16, sb );
- __m256i difRyz = _mm256_madd_epi16(difR0, _mm256_set_epi16(255, 85, -85, -255, 255, 85, -85, -255, 255, 85, -85, -255, 255, 85, -85, -255));
- __m256i difGyz = _mm256_madd_epi16(difG0, _mm256_set_epi16(255, 85, -85, -255, 255, 85, -85, -255, 255, 85, -85, -255, 255, 85, -85, -255));
- __m256i difByz = _mm256_madd_epi16(difB0, _mm256_set_epi16(255, 85, -85, -255, 255, 85, -85, -255, 255, 85, -85, -255, 255, 85, -85, -255));
+ __m256i difRyz = _mm256_madd_epi16( difR0, _mm256_set_epi16( 255, 85, -85, -255, 255, 85, -85, -255, 255, 85, -85, -255, 255, 85, -85, -255 ) );
+ __m256i difGyz = _mm256_madd_epi16( difG0, _mm256_set_epi16( 255, 85, -85, -255, 255, 85, -85, -255, 255, 85, -85, -255, 255, 85, -85, -255 ) );
+ __m256i difByz = _mm256_madd_epi16( difB0, _mm256_set_epi16( 255, 85, -85, -255, 255, 85, -85, -255, 255, 85, -85, -255, 255, 85, -85, -255 ) );
- __m256i difRxz = _mm256_madd_epi16(difR0, _mm256_set_epi16(255, 255, 255, 255, 85, 85, 85, 85, -85, -85, -85, -85, -255, -255, -255, -255));
- __m256i difGxz = _mm256_madd_epi16(difG0, _mm256_set_epi16(255, 255, 255, 255, 85, 85, 85, 85, -85, -85, -85, -85, -255, -255, -255, -255));
- __m256i difBxz = _mm256_madd_epi16(difB0, _mm256_set_epi16(255, 255, 255, 255, 85, 85, 85, 85, -85, -85, -85, -85, -255, -255, -255, -255));
+ __m256i difRxz = _mm256_madd_epi16( difR0, _mm256_set_epi16( 255, 255, 255, 255, 85, 85, 85, 85, -85, -85, -85, -85, -255, -255, -255, -255 ) );
+ __m256i difGxz = _mm256_madd_epi16( difG0, _mm256_set_epi16( 255, 255, 255, 255, 85, 85, 85, 85, -85, -85, -85, -85, -255, -255, -255, -255 ) );
+ __m256i difBxz = _mm256_madd_epi16( difB0, _mm256_set_epi16( 255, 255, 255, 255, 85, 85, 85, 85, -85, -85, -85, -85, -255, -255, -255, -255 ) );
- __m256i difRGyz = _mm256_hadd_epi32(difRyz, difGyz);
- __m256i difByzxz = _mm256_hadd_epi32(difByz, difBxz);
+ __m256i difRGyz = _mm256_hadd_epi32( difRyz, difGyz );
+ __m256i difByzxz = _mm256_hadd_epi32( difByz, difBxz );
- __m256i difRGxz = _mm256_hadd_epi32(difRxz, difGxz);
+ __m256i difRGxz = _mm256_hadd_epi32( difRxz, difGxz );
- __m128i sumRGyz = _mm_add_epi32(_mm256_castsi256_si128(difRGyz), _mm256_extracti128_si256(difRGyz, 1));
- __m128i sumByzxz = _mm_add_epi32(_mm256_castsi256_si128(difByzxz), _mm256_extracti128_si256(difByzxz, 1));
- __m128i sumRGxz = _mm_add_epi32(_mm256_castsi256_si128(difRGxz), _mm256_extracti128_si256(difRGxz, 1));
+ __m128i sumRGyz = _mm_add_epi32( _mm256_castsi256_si128( difRGyz ), _mm256_extracti128_si256( difRGyz, 1 ) );
+ __m128i sumByzxz = _mm_add_epi32( _mm256_castsi256_si128( difByzxz ), _mm256_extracti128_si256( difByzxz, 1 ) );
+ __m128i sumRGxz = _mm_add_epi32( _mm256_castsi256_si128( difRGxz ), _mm256_extracti128_si256( difRGxz, 1 ) );
- __m128i sumRGByz = _mm_hadd_epi32(sumRGyz, sumByzxz);
- __m128i sumRGByzxz = _mm_hadd_epi32(sumRGxz, sumByzxz);
+ __m128i sumRGByz = _mm_hadd_epi32( sumRGyz, sumByzxz );
+ __m128i sumRGByzxz = _mm_hadd_epi32( sumRGxz, sumByzxz );
- __m128i sumRGBxz = _mm_shuffle_epi32(sumRGByzxz, _MM_SHUFFLE(2, 3, 1, 0));
+ __m128i sumRGBxz = _mm_shuffle_epi32( sumRGByzxz, _MM_SHUFFLE( 2, 3, 1, 0 ) );
- __m128 sumRGByzf = _mm_cvtepi32_ps(sumRGByz);
- __m128 sumRGBxzf = _mm_cvtepi32_ps(sumRGBxz);
+ __m128 sumRGByzf = _mm_cvtepi32_ps( sumRGByz );
+ __m128 sumRGBxzf = _mm_cvtepi32_ps( sumRGBxz );
- const float value = (255 * 255 * 8.0f + 85 * 85 * 8.0f) * 16.0f;
+ const float value = ( 255 * 255 * 8.0f + 85 * 85 * 8.0f ) * 16.0f;
- __m128 scale = _mm_set1_ps(-4.0f / value);
+ __m128 scale = _mm_set1_ps( -4.0f / value );
- __m128 af = _mm_mul_ps(sumRGBxzf, scale);
- __m128 bf = _mm_mul_ps(sumRGByzf, scale);
+ __m128 af = _mm_mul_ps( sumRGBxzf, scale );
+ __m128 bf = _mm_mul_ps( sumRGByzf, scale );
- __m128 df = _mm_mul_ps(_mm_cvtepi32_ps(t5), _mm_set1_ps(4.0f / 16.0f));
+ __m128 df = _mm_mul_ps( _mm_cvtepi32_ps( t5 ), _mm_set1_ps( 4.0f / 16.0f ) );
// calculating the three colors RGBO, RGBH, and RGBV. RGB = df - af * x - bf * y;
- __m128 cof0 = _mm_fnmadd_ps(af, _mm_set1_ps(-255.0f), _mm_fnmadd_ps(bf, _mm_set1_ps(-255.0f), df));
- __m128 chf0 = _mm_fnmadd_ps(af, _mm_set1_ps( 425.0f), _mm_fnmadd_ps(bf, _mm_set1_ps(-255.0f), df));
- __m128 cvf0 = _mm_fnmadd_ps(af, _mm_set1_ps(-255.0f), _mm_fnmadd_ps(bf, _mm_set1_ps( 425.0f), df));
+ __m128 cof0 = _mm_fnmadd_ps( af, _mm_set1_ps( -255.0f ), _mm_fnmadd_ps( bf, _mm_set1_ps( -255.0f ), df ) );
+ __m128 chf0 = _mm_fnmadd_ps( af, _mm_set1_ps( 425.0f ), _mm_fnmadd_ps( bf, _mm_set1_ps( -255.0f ), df ) );
+ __m128 cvf0 = _mm_fnmadd_ps( af, _mm_set1_ps( -255.0f ), _mm_fnmadd_ps( bf, _mm_set1_ps( 425.0f ), df ) );
// convert to r6g7b6
- __m128i cohv = r6g7b6_AVX2(cof0, chf0, cvf0);
+ __m128i cohv = r6g7b6_AVX2( cof0, chf0, cvf0 );
- uint64_t rgbho = _mm_extract_epi64(cohv, 0);
- uint32_t rgbv0 = _mm_extract_epi32(cohv, 2);
+ uint64_t rgbho = _mm_extract_epi64( cohv, 0 );
+ uint32_t rgbv0 = _mm_extract_epi32( cohv, 2 );
// Error calculation
uint64_t error = 0;
- if( mode != ModePlanar )
+ if( !useHeuristics )
{
auto ro0 = ( rgbho >> 48 ) & 0x3F;
auto go0 = ( rgbho >> 40 ) & 0x7F;
@@ -820,7 +1006,15 @@ static etcpak_force_inline Plane Planar_AVX2( const uint8_t* src, const uint8_t
Plane plane;
plane.plane = result;
- plane.error = error;
+ if( useHeuristics )
+ {
+ plane.error = 0;
+ mode = ModePlanar;
+ }
+ else
+ {
+ plane.error = error;
+ }
plane.sum4 = _mm256_permute4x64_epi64(srgb, _MM_SHUFFLE(2, 3, 0, 1));
return plane;
@@ -1570,7 +1764,7 @@ static etcpak_force_inline uint8_t convert7(float f)
return (i + 9 - ((i + 9) >> 8) - ((i + 6) >> 8)) >> 2;
}
-static etcpak_force_inline std::pair<uint64_t, uint64_t> Planar( const uint8_t* src, const uint8_t mode )
+static etcpak_force_inline std::pair<uint64_t, uint64_t> Planar( const uint8_t* src, const uint8_t mode, bool useHeuristics )
{
int32_t r = 0;
int32_t g = 0;
@@ -1645,7 +1839,7 @@ static etcpak_force_inline std::pair<uint64_t, uint64_t> Planar( const uint8_t*
// Error calculation
uint64_t error = 0;
- if( ModePlanar != mode )
+ if( ModePlanar != mode && useHeuristics )
{
auto ro0 = coR;
auto go0 = coG;
@@ -1756,7 +1950,7 @@ static etcpak_force_inline int16x8_t Planar_NEON_SumWide( uint8x16_t src )
uint16x4_t accu2 = vpadd_u16( accu4, accu4 );
uint16x4_t accu1 = vpadd_u16( accu2, accu2 );
return vreinterpretq_s16_u16( vcombine_u16( accu1, accu1 ) );
-#else
+#else
return vdupq_n_s16( vaddvq_u16( accu8 ) );
#endif
}
@@ -1783,7 +1977,7 @@ static etcpak_force_inline int16x4_t convert7_NEON( int32x4_t x )
return vshr_n_s16( vsub_s16( vsub_s16( p9, vshr_n_s16( p9, 8 ) ), vshr_n_s16( p6, 8 ) ), 2 );
}
-static etcpak_force_inline std::pair<uint64_t, uint64_t> Planar_NEON( const uint8_t* src, const uint8_t mode )
+static etcpak_force_inline std::pair<uint64_t, uint64_t> Planar_NEON( const uint8_t* src, const uint8_t mode, bool useHeuristics )
{
uint8x16x4_t srcBlock = vld4q_u8( src );
@@ -1828,7 +2022,7 @@ static etcpak_force_inline std::pair<uint64_t, uint64_t> Planar_NEON( const uint
int16x4_t c_hvox_g_8 = vorr_s16( vshr_n_s16( c_hvox_g_7, 6 ), vshl_n_s16( c_hvox_g_7, 1 ) );
uint64_t error = 0;
- if( mode != ModePlanar )
+ if( mode != ModePlanar && useHeuristics )
{
int16x4_t rec_gxbr_o = vext_s16( c_hvox_g_8, vget_high_s16( c_hvoo_br_8 ), 3 );
@@ -1924,6 +2118,376 @@ static etcpak_force_inline std::pair<uint64_t, uint64_t> Planar_NEON( const uint
#endif
+#ifdef __AVX2__
+uint32_t calculateErrorTH( bool tMode, uint8_t( colorsRGB444 )[2][3], uint8_t& dist, uint32_t& pixIndices, uint8_t startDist, __m128i r8, __m128i g8, __m128i b8 )
+#else
+uint32_t calculateErrorTH( bool tMode, uint8_t* src, uint8_t( colorsRGB444 )[2][3], uint8_t& dist, uint32_t& pixIndices, uint8_t startDist )
+#endif
+{
+ uint32_t blockErr = 0, bestBlockErr = MaxError;
+
+ uint32_t pixColors;
+ uint8_t possibleColors[4][3];
+ uint8_t colors[2][3];
+
+ decompressColor( colorsRGB444, colors );
+
+#ifdef __AVX2__
+ __m128i reverseMask = _mm_set_epi8( 0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15 );
+#endif
+
+ // test distances
+ for( uint8_t d = startDist; d < 8; ++d )
+ {
+ if( d >= 2 && dist == d - 2 ) break;
+
+ blockErr = 0;
+ pixColors = 0;
+
+ if( tMode )
+ {
+ calculatePaintColors59T( d, colors, possibleColors );
+ }
+ else
+ {
+ calculatePaintColors58H( d, colors, possibleColors );
+ }
+
+#ifdef __AVX2__
+ // RGB ordering
+ __m128i b8Rev = _mm_shuffle_epi8( b8, reverseMask );
+ __m128i g8Rev = _mm_shuffle_epi8( g8, reverseMask );
+ __m128i r8Rev = _mm_shuffle_epi8( r8, reverseMask );
+
+ // extends 3x128 bits RGB into 3x256 bits RGB for error comparisions
+ static const __m128i zero = _mm_setzero_si128();
+ __m128i b8Lo = _mm_unpacklo_epi8( b8Rev, zero );
+ __m128i g8Lo = _mm_unpacklo_epi8( g8Rev, zero );
+ __m128i r8Lo = _mm_unpacklo_epi8( r8Rev, zero );
+ __m128i b8Hi = _mm_unpackhi_epi8( b8Rev, zero );
+ __m128i g8Hi = _mm_unpackhi_epi8( g8Rev, zero );
+ __m128i r8Hi = _mm_unpackhi_epi8( r8Rev, zero );
+
+ __m256i b8 = _mm256_set_m128i( b8Hi, b8Lo );
+ __m256i g8 = _mm256_set_m128i( g8Hi, g8Lo );
+ __m256i r8 = _mm256_set_m128i( r8Hi, r8Lo );
+
+ // caculates differences between the pixel colrs and the palette colors
+ __m256i diffb = _mm256_abs_epi16( _mm256_sub_epi16( b8, _mm256_set1_epi16( possibleColors[0][B] ) ) );
+ __m256i diffg = _mm256_abs_epi16( _mm256_sub_epi16( g8, _mm256_set1_epi16( possibleColors[0][G] ) ) );
+ __m256i diffr = _mm256_abs_epi16( _mm256_sub_epi16( r8, _mm256_set1_epi16( possibleColors[0][R] ) ) );
+
+ // luma-based error calculations
+ static const __m256i bWeight = _mm256_set1_epi16( 14 );
+ static const __m256i gWeight = _mm256_set1_epi16( 76 );
+ static const __m256i rWeight = _mm256_set1_epi16( 38 );
+
+ diffb = _mm256_mullo_epi16( diffb, bWeight );
+ diffg = _mm256_mullo_epi16( diffg, gWeight );
+ diffr = _mm256_mullo_epi16( diffr, rWeight );
+
+ // obtains the error with the current palette color
+ __m256i lowestPixErr = _mm256_add_epi16( _mm256_add_epi16( diffb, diffg ), diffr );
+
+ // error calucations with the remaining three palette colors
+ static const uint32_t masks[4] = { 0, 0x55555555, 0xAAAAAAAA, 0xFFFFFFFF };
+ for( uint8_t c = 1; c < 4; c++ )
+ {
+ __m256i diffb = _mm256_abs_epi16( _mm256_sub_epi16( b8, _mm256_set1_epi16( possibleColors[c][B] ) ) );
+ __m256i diffg = _mm256_abs_epi16( _mm256_sub_epi16( g8, _mm256_set1_epi16( possibleColors[c][G] ) ) );
+ __m256i diffr = _mm256_abs_epi16( _mm256_sub_epi16( r8, _mm256_set1_epi16( possibleColors[c][R] ) ) );
+
+ diffb = _mm256_mullo_epi16( diffb, bWeight );
+ diffg = _mm256_mullo_epi16( diffg, gWeight );
+ diffr = _mm256_mullo_epi16( diffr, rWeight );
+
+ // error comparison with the previous best color
+ __m256i pixErrors = _mm256_add_epi16( _mm256_add_epi16( diffb, diffg ), diffr );
+ __m256i minErr = _mm256_min_epu16( lowestPixErr, pixErrors );
+ __m256i cmpRes = _mm256_cmpeq_epi16( pixErrors, minErr );
+ lowestPixErr = minErr;
+
+ // update pixel colors
+ uint32_t updPixColors = _mm256_movemask_epi8( cmpRes );
+ uint32_t prevPixColors = pixColors & ~updPixColors;
+ uint32_t mskPixColors = masks[c] & updPixColors;
+ pixColors = prevPixColors | mskPixColors;
+ }
+
+ // accumulate the block error
+ alignas( 32 ) uint16_t pixErr16[16] = { 0, };
+ _mm256_storeu_si256( (__m256i*)pixErr16, lowestPixErr );
+ for( uint8_t p = 0; p < 16; p++ )
+ {
+ blockErr += (int)( pixErr16[p] ) * pixErr16[p];
+ }
+#else
+ for( size_t y = 0; y < 4; ++y )
+ {
+ for( size_t x = 0; x < 4; ++x )
+ {
+ uint32_t bestPixErr = MaxError;
+ pixColors <<= 2; // Make room for next value
+
+ // Loop possible block colors
+ for( uint8_t c = 0; c < 4; ++c )
+ {
+ int diff[3];
+ diff[R] = src[4 * ( x * 4 + y ) + R] - possibleColors[c][R];
+ diff[G] = src[4 * ( x * 4 + y ) + G] - possibleColors[c][G];
+ diff[B] = src[4 * ( x * 4 + y ) + B] - possibleColors[c][B];
+
+ const uint32_t err = 38 * abs( diff[R] ) + 76 * abs( diff[G] ) + 14 * abs( diff[B] );
+ uint32_t pixErr = err * err;
+
+ // Choose best error
+ if( pixErr < bestPixErr )
+ {
+ bestPixErr = pixErr;
+ pixColors ^= ( pixColors & 3 ); // Reset the two first bits
+ pixColors |= c;
+ }
+ }
+ blockErr += bestPixErr;
+ }
+ }
+#endif
+
+ if( blockErr < bestBlockErr )
+ {
+ bestBlockErr = blockErr;
+ dist = d;
+ pixIndices = pixColors;
+ }
+ }
+
+ return bestBlockErr;
+}
+
+
+// main T-/H-mode compression function
+#ifdef __AVX2__
+uint32_t compressBlockTH( uint8_t* src, Luma& l, uint32_t& compressed1, uint32_t& compressed2, bool& tMode, __m128i r8, __m128i g8, __m128i b8 )
+#else
+uint32_t compressBlockTH( uint8_t *src, Luma& l, uint32_t& compressed1, uint32_t& compressed2, bool &tMode )
+#endif
+{
+#ifdef __AVX2__
+ alignas( 8 ) uint8_t luma[16] = { 0, };
+ _mm_storeu_si128 ( (__m128i* )luma, l.luma8 );
+#elif defined __ARM_NEON && defined __aarch64__
+ alignas( 8 ) uint8_t luma[16] = { 0 };
+ vst1q_u8( luma, l.luma8 );
+#else
+ uint8_t* luma = l.val;
+#endif
+
+ uint8_t pixIdx[16] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
+
+ // 1) sorts the pairs of (luma, pix_idx)
+ insertionSort( luma, pixIdx );
+
+ // 2) finds the min (left+right)
+ uint8_t minSumRangeIdx = 0;
+ uint16_t minSumRangeValue;
+ uint16_t sum;
+ static const uint8_t diffBonus[15] = {8, 4, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 8};
+ const int16_t temp = luma[15] - luma[0];
+
+ minSumRangeValue = luma[15] - luma[1] + diffBonus[0];
+ for( uint8_t i = 1; i < 14; i++ )
+ {
+ sum = temp - luma[i+1] + luma[i] + diffBonus[i];
+ if( minSumRangeValue > sum )
+ {
+ minSumRangeValue = sum;
+ minSumRangeIdx = i;
+ }
+ }
+
+ sum = luma[14] - luma[0] + diffBonus[14];
+ if( minSumRangeValue > sum )
+ {
+ minSumRangeValue = sum;
+ minSumRangeIdx = 14;
+ }
+ uint8_t lRange, rRange;
+
+ lRange = luma[minSumRangeIdx] - luma[0];
+ rRange = luma[15] - luma[minSumRangeIdx + 1];
+
+ // 3) sets a proper mode
+ bool swap = false;
+ if( lRange >= rRange )
+ {
+ if( lRange >= rRange * 2 )
+ {
+ swap = true;
+ tMode = true;
+ }
+ }
+ else
+ {
+ if( lRange * 2 <= rRange ) tMode = true;
+ }
+ // 4) calculates the two base colors
+ uint8_t rangeIdx[4] = { pixIdx[0], pixIdx[minSumRangeIdx], pixIdx[minSumRangeIdx + 1], pixIdx[15] };
+
+ uint16_t r[4], g[4], b[4];
+ for( uint8_t i = 0; i < 4; ++i )
+ {
+ uint8_t idx = rangeIdx[i] * 4;
+ b[i] = src[idx];
+ g[i] = src[idx + 1];
+ r[i] = src[idx + 2];
+ }
+
+ uint8_t mid_rgb[2][3];
+ if( swap )
+ {
+ mid_rgb[1][B] = ( b[0] + b[1] ) / 2;
+ mid_rgb[1][G] = ( g[0] + g[1] ) / 2;
+ mid_rgb[1][R] = ( r[0] + r[1] ) / 2;
+
+ uint16_t sum_rgb[3] = { 0, 0, 0 };
+ for( uint8_t i = minSumRangeIdx + 1; i < 16; i++ )
+ {
+ uint8_t idx = pixIdx[i] * 4;
+ sum_rgb[B] += src[idx];
+ sum_rgb[G] += src[idx + 1];
+ sum_rgb[R] += src[idx + 2];
+ }
+ const uint8_t temp = 15 - minSumRangeIdx;
+ mid_rgb[0][B] = sum_rgb[B] / temp;
+ mid_rgb[0][G] = sum_rgb[G] / temp;
+ mid_rgb[0][R] = sum_rgb[R] / temp;
+ }
+ else
+ {
+ mid_rgb[0][B] = (b[0] + b[1]) / 2;
+ mid_rgb[0][G] = (g[0] + g[1]) / 2;
+ mid_rgb[0][R] = (r[0] + r[1]) / 2;
+ if( tMode )
+ {
+ uint16_t sum_rgb[3] = { 0, 0, 0 };
+ for( uint8_t i = minSumRangeIdx + 1; i < 16; i++ )
+ {
+ uint8_t idx = pixIdx[i] * 4;
+ sum_rgb[B] += src[idx];
+ sum_rgb[G] += src[idx + 1];
+ sum_rgb[R] += src[idx + 2];
+ }
+ const uint8_t temp = 15 - minSumRangeIdx;
+ mid_rgb[1][B] = sum_rgb[B] / temp;
+ mid_rgb[1][G] = sum_rgb[G] / temp;
+ mid_rgb[1][R] = sum_rgb[R] / temp;
+ }
+ else
+ {
+ mid_rgb[1][B] = (b[2] + b[3]) / 2;
+ mid_rgb[1][G] = (g[2] + g[3]) / 2;
+ mid_rgb[1][R] = (r[2] + r[3]) / 2;
+ }
+ }
+
+ // 5) sets the start distance index
+ uint32_t startDistCandidate;
+ uint32_t avgDist;
+ if( tMode )
+ {
+ if( swap )
+ {
+ avgDist = ( b[1] - b[0] + g[1] - g[0] + r[1] - r[0] ) / 6;
+ }
+ else
+ {
+ avgDist = ( b[3] - b[2] + g[3] - g[2] + r[3] - r[2] ) / 6;
+ }
+ }
+ else
+ {
+ avgDist = ( b[1] - b[0] + g[1] - g[0] + r[1] - r[0] + b[3] - b[2] + g[3] - g[2] + r[3] - r[2] ) / 12;
+ }
+
+ if( avgDist <= 16)
+ {
+ startDistCandidate = 0;
+ }
+ else if( avgDist <= 23 )
+ {
+ startDistCandidate = 1;
+ }
+ else if( avgDist <= 32 )
+ {
+ startDistCandidate = 2;
+ }
+ else if( avgDist <= 41 )
+ {
+ startDistCandidate = 3;
+ }
+ else
+ {
+ startDistCandidate = 4;
+ }
+
+ uint32_t bestErr = MaxError;
+ uint32_t bestPixIndices;
+ uint8_t bestDist = 10;
+ uint8_t colorsRGB444[2][3];
+ compressColor( mid_rgb, colorsRGB444, tMode );
+ compressed1 = 0;
+
+ // 6) finds the best candidate with the lowest error
+#ifdef __AVX2__
+ // Vectorized ver
+ bestErr = calculateErrorTH( tMode, colorsRGB444, bestDist, bestPixIndices, startDistCandidate, r8, g8, b8 );
+#else
+ // Scalar ver
+ bestErr = calculateErrorTH( tMode, src, colorsRGB444, bestDist, bestPixIndices, startDistCandidate );
+#endif
+
+ // 7) outputs the final T or H block
+ if( tMode )
+ {
+ // Put the compress params into the compression block
+ compressed1 |= ( colorsRGB444[0][R] & 0xf ) << 23;
+ compressed1 |= ( colorsRGB444[0][G] & 0xf ) << 19;
+ compressed1 |= ( colorsRGB444[0][B] ) << 15;
+ compressed1 |= ( colorsRGB444[1][R] ) << 11;
+ compressed1 |= ( colorsRGB444[1][G] ) << 7;
+ compressed1 |= ( colorsRGB444[1][B] ) << 3;
+ compressed1 |= bestDist & 0x7;
+ }
+ else
+ {
+ int bestRGB444ColPacked[2];
+ bestRGB444ColPacked[0] = (colorsRGB444[0][R] << 8) + (colorsRGB444[0][G] << 4) + colorsRGB444[0][B];
+ bestRGB444ColPacked[1] = (colorsRGB444[1][R] << 8) + (colorsRGB444[1][G] << 4) + colorsRGB444[1][B];
+ if( ( bestRGB444ColPacked[0] >= bestRGB444ColPacked[1] ) ^ ( ( bestDist & 1 ) == 1 ) )
+ {
+ swapColors( colorsRGB444 );
+ // Reshuffle pixel indices to to exchange C1 with C3, and C2 with C4
+ bestPixIndices = ( 0x55555555 & bestPixIndices ) | ( 0xaaaaaaaa & ( ~bestPixIndices ) );
+ }
+
+ // Put the compress params into the compression block
+ compressed1 |= ( colorsRGB444[0][R] & 0xf ) << 22;
+ compressed1 |= ( colorsRGB444[0][G] & 0xf ) << 18;
+ compressed1 |= ( colorsRGB444[0][B] & 0xf ) << 14;
+ compressed1 |= ( colorsRGB444[1][R] & 0xf ) << 10;
+ compressed1 |= ( colorsRGB444[1][G] & 0xf ) << 6;
+ compressed1 |= ( colorsRGB444[1][B] & 0xf ) << 2;
+ compressed1 |= ( bestDist >> 1 ) & 0x3;
+ }
+
+ bestPixIndices = indexConversion( bestPixIndices );
+ compressed2 = 0;
+ compressed2 = ( compressed2 & ~( ( 0x2 << 31 ) - 1 ) ) | ( bestPixIndices & ( ( 2 << 31 ) - 1 ) );
+
+ return bestErr;
+}
+//#endif
+
template<class T, class S>
static etcpak_force_inline uint64_t EncodeSelectors( uint64_t d, const T terr[2][8], const S tsel[16][8], const uint32_t* id, const uint64_t value, const uint64_t error)
{
@@ -2025,7 +2589,7 @@ static inline int16_t hMax( __m128i buffer, uint8_t& idx )
return result;
}
-#elif defined __ARM_NEON
+#elif defined __ARM_NEON && defined __aarch64__
static inline int16_t hMax( uint8x16_t buffer, uint8_t& idx )
{
const uint8_t max = vmaxvq_u8( buffer );
@@ -2072,7 +2636,7 @@ static inline int16_t hMin( __m128i buffer, uint8_t& idx )
idx = _tzcnt_u32( _mm_movemask_epi8( mask ) );
return result;
}
-#elif defined __ARM_NEON
+#elif defined __ARM_NEON && defined __aarch64__
static inline int16_t hMin( uint8x16_t buffer, uint8_t& idx )
{
const uint8_t min = vminvq_u8( buffer );
@@ -2109,8 +2673,153 @@ static inline int16_t hMin( uint8x16_t buffer, uint8_t& idx )
}
#endif
-static etcpak_force_inline void CalculateLuma( const uint8_t* src, Luma& luma )
+// During search it is not convenient to store the bits the way they are stored in the
+// file format. Hence, after search, it is converted to this format.
+// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved.
+static inline void stuff59bits( unsigned int thumbT59W1, unsigned int thumbT59W2, unsigned int& thumbTW1, unsigned int& thumbTW2 )
{
+ // Put bits in twotimer configuration for 59 (red overflows)
+ //
+ // Go from this bit layout:
+ //
+ // |63 62 61 60 59|58 57 56 55|54 53 52 51|50 49 48 47|46 45 44 43|42 41 40 39|38 37 36 35|34 33 32|
+ // |----empty-----|---red 0---|--green 0--|--blue 0---|---red 1---|--green 1--|--blue 1---|--dist--|
+ //
+ // |31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00|
+ // |----------------------------------------index bits---------------------------------------------|
+ //
+ //
+ // To this:
+ //
+ // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32
+ // -----------------------------------------------------------------------------------------------
+ // |// // //|R0a |//|R0b |G0 |B0 |R1 |G1 |B1 |da |df|db|
+ // -----------------------------------------------------------------------------------------------
+ //
+ // |31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00|
+ // |----------------------------------------index bits---------------------------------------------|
+ //
+ // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32
+ // -----------------------------------------------------------------------------------------------
+ // | base col1 | dcol 2 | base col1 | dcol 2 | base col 1 | dcol 2 | table | table |df|fp|
+ // | R1' (5 bits) | dR2 | G1' (5 bits) | dG2 | B1' (5 bits) | dB2 | cw 1 | cw 2 |bt|bt|
+ // ------------------------------------------------------------------------------------------------
+
+ uint8_t R0a;
+ uint8_t bit, a, b, c, d, bits;
+
+ R0a = ( thumbT59W1 >> 25 ) & 0x3;
+
+ // Fix middle part
+ thumbTW1 = thumbT59W1 << 1;
+ // Fix R0a (top two bits of R0)
+ thumbTW1 = ( thumbTW1 & ~( 0x3 << 27 ) ) | ( ( R0a & 0x3 ) << 27 );
+ // Fix db (lowest bit of d)
+ thumbTW1 = ( thumbTW1 & ~0x1 ) | ( thumbT59W1 & 0x1 );
+
+ // Make sure that red overflows:
+ a = ( thumbTW1 >> 28 ) & 0x1;
+ b = ( thumbTW1 >> 27 ) & 0x1;
+ c = ( thumbTW1 >> 25 ) & 0x1;
+ d = ( thumbTW1 >> 24 ) & 0x1;
+
+ // The following bit abcd bit sequences should be padded with ones: 0111, 1010, 1011, 1101, 1110, 1111
+ // The following logical expression checks for the presence of any of those:
+ bit = ( a & c ) | ( !a & b & c & d ) | ( a & b & !c & d );
+ bits = 0xf * bit;
+ thumbTW1 = ( thumbTW1 & ~( 0x7 << 29 ) ) | ( bits & 0x7 ) << 29;
+ thumbTW1 = ( thumbTW1 & ~( 0x1 << 26 ) ) | ( !bit & 0x1 ) << 26;
+
+ // Set diffbit
+ thumbTW1 = ( thumbTW1 & ~0x2 ) | 0x2;
+ thumbTW2 = thumbT59W2;
+}
+
+// During search it is not convenient to store the bits the way they are stored in the
+// file format. Hence, after search, it is converted to this format.
+// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2005-2013. All Rights Reserved.
+static inline void stuff58bits( unsigned int thumbH58W1, unsigned int thumbH58W2, unsigned int& thumbHW1, unsigned int& thumbHW2 )
+{
+ // Put bits in twotimer configuration for 58 (red doesn't overflow, green does)
+ //
+ // Go from this bit layout:
+ //
+ //
+ // |63 62 61 60 59 58|57 56 55 54|53 52 51 50|49 48 47 46|45 44 43 42|41 40 39 38|37 36 35 34|33 32|
+ // |-------empty-----|---red 0---|--green 0--|--blue 0---|---red 1---|--green 1--|--blue 1---|d2 d1|
+ //
+ // |31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00|
+ // |---------------------------------------index bits----------------------------------------------|
+ //
+ // To this:
+ //
+ // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32
+ // -----------------------------------------------------------------------------------------------
+ // |//|R0 |G0 |// // //|G0|B0|//|B0b |R1 |G1 |B0 |d2|df|d1|
+ // -----------------------------------------------------------------------------------------------
+ //
+ // |31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00|
+ // |---------------------------------------index bits----------------------------------------------|
+ //
+ // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32
+ // -----------------------------------------------------------------------------------------------
+ // | base col1 | dcol 2 | base col1 | dcol 2 | base col 1 | dcol 2 | table | table |df|fp|
+ // | R1' (5 bits) | dR2 | G1' (5 bits) | dG2 | B1' (5 bits) | dB2 | cw 1 | cw 2 |bt|bt|
+ // -----------------------------------------------------------------------------------------------
+ //
+ //
+ // Thus, what we are really doing is going from this bit layout:
+ //
+ //
+ // |63 62 61 60 59 58|57 56 55 54 53 52 51|50 49|48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33|32 |
+ // |-------empty-----|part0---------------|part1|part2------------------------------------------|part3|
+ //
+ // To this:
+ //
+ // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32
+ // --------------------------------------------------------------------------------------------------|
+ // |//|part0 |// // //|part1|//|part2 |df|part3|
+ // --------------------------------------------------------------------------------------------------|
+
+ unsigned int part0, part1, part2, part3;
+ uint8_t bit, a, b, c, d, bits;
+
+ // move parts
+ part0 = ( thumbH58W1 >> 19 ) & 0x7f;
+ part1 = ( thumbH58W1 >> 17 ) & 0x3;
+ part2 = ( thumbH58W1 >> 1 ) & 0xffff;
+ part3 = thumbH58W1 & 0x1;
+ thumbHW1 = 0;
+ thumbHW1 = ( thumbHW1 & ~( 0x7f << 24 ) ) | ( ( part0 & 0x7f ) << 24 );
+ thumbHW1 = ( thumbHW1 & ~( 0x3 << 19 ) ) | ( ( part1 & 0x3 ) << 19 );
+ thumbHW1 = ( thumbHW1 & ~( 0xffff << 2 ) ) | ( ( part2 & 0xffff ) << 2 );
+ thumbHW1 = ( thumbHW1 & ~0x1 ) | ( part3 & 0x1 );
+
+ // Make sure that red does not overflow:
+ bit = ( thumbHW1 >> 30 ) & 0x1;
+ thumbHW1 = ( thumbHW1 & ~( 0x1 << 31 ) ) | ( ( !bit & 0x1 ) << 31 );
+
+ // Make sure that green overflows:
+ a = ( thumbHW1 >> 20 ) & 0x1;
+ b = ( thumbHW1 >> 19 ) & 0x1;
+ c = ( thumbHW1 >> 17 ) & 0x1;
+ d = ( thumbHW1 >> 16 ) & 0x1;
+ // The following bit abcd bit sequences should be padded with ones: 0111, 1010, 1011, 1101, 1110, 1111
+ // The following logical expression checks for the presence of any of those:
+ bit = ( a & c ) | ( !a & b & c & d ) | ( a & b & !c & d );
+ bits = 0xf * bit;
+ thumbHW1 = ( thumbHW1 & ~( 0x7 << 21 ) ) | ( ( bits & 0x7 ) << 21 );
+ thumbHW1 = ( thumbHW1 & ~( 0x1 << 18 ) ) | ( ( !bit & 0x1 ) << 18 );
+
+ // Set diffbit
+ thumbHW1 = ( thumbHW1 & ~0x2 ) | 0x2;
+ thumbHW2 = thumbH58W2;
+}
+
+#if defined __AVX2__ || (defined __ARM_NEON && defined __aarch64__)
+static etcpak_force_inline Channels GetChannels( const uint8_t* src )
+{
+ Channels ch;
#ifdef __AVX2__
__m128i d0 = _mm_loadu_si128( ( (__m128i*)src ) + 0 );
__m128i d1 = _mm_loadu_si128( ( (__m128i*)src ) + 1 );
@@ -2128,30 +2837,10 @@ static etcpak_force_inline void CalculateLuma( const uint8_t* src, Luma& luma )
__m128i b1 = _mm_unpackhi_epi32( rgb2, rgb3 );
// swap channels
- __m128i b8 = _mm_unpacklo_epi64( rg0, rg1 );
- __m128i g8 = _mm_unpackhi_epi64( rg0, rg1 );
- __m128i r8 = _mm_unpacklo_epi64( b0, b1 );
-
- __m256i b16_luma = _mm256_mullo_epi16( _mm256_cvtepu8_epi16( b8 ), _mm256_set1_epi16( 14 ) );
- __m256i g16_luma = _mm256_mullo_epi16( _mm256_cvtepu8_epi16( g8 ), _mm256_set1_epi16( 76 ) );
- __m256i r16_luma = _mm256_mullo_epi16( _mm256_cvtepu8_epi16( r8 ), _mm256_set1_epi16( 38 ) );
-
- __m256i luma_16bit = _mm256_add_epi16( _mm256_add_epi16( g16_luma, r16_luma ), b16_luma );
- __m256i luma_8bit_m256i = _mm256_srli_epi16( luma_16bit, 7 );
- __m128i luma_8bit_lo = _mm256_extractf128_si256( luma_8bit_m256i, 0 );
- __m128i luma_8bit_hi = _mm256_extractf128_si256( luma_8bit_m256i, 1 );
-
- static const __m128i interleaving_mask_lo = _mm_set_epi8( 15, 13, 11, 9, 7, 5, 3, 1, 14, 12, 10, 8, 6, 4, 2, 0 );
- static const __m128i interleaving_mask_hi = _mm_set_epi8( 14, 12, 10, 8, 6, 4, 2, 0, 15, 13, 11, 9, 7, 5, 3, 1 );
- __m128i luma_8bit_lo_moved = _mm_shuffle_epi8( luma_8bit_lo, interleaving_mask_lo );
- __m128i luma_8bit_hi_moved = _mm_shuffle_epi8( luma_8bit_hi, interleaving_mask_hi );
- __m128i luma_8bit = _mm_or_si128( luma_8bit_hi_moved, luma_8bit_lo_moved );
- luma.luma8 = luma_8bit;
-
- // min/max calculation
- luma.min = hMin( luma_8bit, luma.minIdx ) * 0.00392156f;
- luma.max = hMax( luma_8bit, luma.maxIdx ) * 0.00392156f;
-#elif defined __ARM_NEON
+ ch.b8 = _mm_unpacklo_epi64( rg0, rg1 );
+ ch.g8 = _mm_unpackhi_epi64( rg0, rg1 );
+ ch.r8 = _mm_unpacklo_epi64( b0, b1 );
+#elif defined __ARM_NEON && defined __aarch64__
//load pixel data into 4 rows
uint8x16_t px0 = vld1q_u8( src + 0 );
uint8x16_t px1 = vld1q_u8( src + 16 );
@@ -2172,12 +2861,48 @@ static etcpak_force_inline void CalculateLuma( const uint8_t* src, Luma& luma )
uint8x16x2_t red = vzipq_u8( rr, uint8x16_t() );
uint8x16x2_t grn = vzipq_u8( gg, uint8x16_t() );
uint8x16x2_t blu = vzipq_u8( bb, uint8x16_t() );
- uint16x8_t red0 = vmulq_n_u16( vreinterpretq_u16_u8( red.val[0] ), 14 );
- uint16x8_t red1 = vmulq_n_u16( vreinterpretq_u16_u8( red.val[1] ), 14 );
- uint16x8_t grn0 = vmulq_n_u16( vreinterpretq_u16_u8( grn.val[0] ), 76 );
- uint16x8_t grn1 = vmulq_n_u16( vreinterpretq_u16_u8( grn.val[1] ), 76 );
- uint16x8_t blu0 = vmulq_n_u16( vreinterpretq_u16_u8( blu.val[0] ), 38 );
- uint16x8_t blu1 = vmulq_n_u16( vreinterpretq_u16_u8( blu.val[1] ), 38 );
+ ch.r = red;
+ ch.b = blu;
+ ch.g = grn;
+#endif
+ return ch;
+}
+#endif
+
+#if defined __AVX2__ || (defined __ARM_NEON && defined __aarch64__)
+static etcpak_force_inline void CalculateLuma( Channels& ch, Luma& luma )
+#else
+static etcpak_force_inline void CalculateLuma( const uint8_t* src, Luma& luma )
+#endif
+{
+#ifdef __AVX2__
+ __m256i b16_luma = _mm256_mullo_epi16( _mm256_cvtepu8_epi16( ch.b8 ), _mm256_set1_epi16( 14 ) );
+ __m256i g16_luma = _mm256_mullo_epi16( _mm256_cvtepu8_epi16( ch.g8 ), _mm256_set1_epi16( 76 ) );
+ __m256i r16_luma = _mm256_mullo_epi16( _mm256_cvtepu8_epi16( ch.r8 ), _mm256_set1_epi16( 38 ) );
+
+ __m256i luma_16bit = _mm256_add_epi16( _mm256_add_epi16( g16_luma, r16_luma ), b16_luma );
+ __m256i luma_8bit_m256i = _mm256_srli_epi16( luma_16bit, 7 );
+ __m128i luma_8bit_lo = _mm256_extractf128_si256( luma_8bit_m256i, 0 );
+ __m128i luma_8bit_hi = _mm256_extractf128_si256( luma_8bit_m256i, 1 );
+
+ static const __m128i interleaving_mask_lo = _mm_set_epi8( 15, 13, 11, 9, 7, 5, 3, 1, 14, 12, 10, 8, 6, 4, 2, 0 );
+ static const __m128i interleaving_mask_hi = _mm_set_epi8( 14, 12, 10, 8, 6, 4, 2, 0, 15, 13, 11, 9, 7, 5, 3, 1 );
+ __m128i luma_8bit_lo_moved = _mm_shuffle_epi8( luma_8bit_lo, interleaving_mask_lo );
+ __m128i luma_8bit_hi_moved = _mm_shuffle_epi8( luma_8bit_hi, interleaving_mask_hi );
+ __m128i luma_8bit = _mm_or_si128( luma_8bit_hi_moved, luma_8bit_lo_moved );
+ luma.luma8 = luma_8bit;
+
+ // min/max calculation
+ luma.min = hMin( luma_8bit, luma.minIdx ) * 0.00392156f;
+ luma.max = hMax( luma_8bit, luma.maxIdx ) * 0.00392156f;
+#elif defined __ARM_NEON && defined __aarch64__
+ //load pixel data into 4 rows
+ uint16x8_t red0 = vmulq_n_u16( vreinterpretq_u16_u8( ch.r.val[0] ), 14 );
+ uint16x8_t red1 = vmulq_n_u16( vreinterpretq_u16_u8( ch.r.val[1] ), 14 );
+ uint16x8_t grn0 = vmulq_n_u16( vreinterpretq_u16_u8( ch.g.val[0] ), 76 );
+ uint16x8_t grn1 = vmulq_n_u16( vreinterpretq_u16_u8( ch.g.val[1] ), 76 );
+ uint16x8_t blu0 = vmulq_n_u16( vreinterpretq_u16_u8( ch.b.val[0] ), 38 );
+ uint16x8_t blu1 = vmulq_n_u16( vreinterpretq_u16_u8( ch.b.val[1] ), 38 );
//calculate luma for rows 0,1 and 2,3
uint16x8_t lum_r01 = vaddq_u16( vaddq_u16( red0, grn0 ), blu0 );
@@ -2253,7 +2978,7 @@ static etcpak_force_inline uint8_t SelectModeETC2( const Luma& luma )
{
return ModeTH;
}
- return 0;
+ return ModeUndecided;
}
static etcpak_force_inline uint64_t ProcessRGB_ETC2( const uint8_t* src, bool useHeuristics )
@@ -2267,33 +2992,33 @@ static etcpak_force_inline uint64_t ProcessRGB_ETC2( const uint8_t* src, bool us
#endif
uint8_t mode = ModeUndecided;
+ Luma luma;
+#ifdef __AVX2__
+ Channels ch = GetChannels( src );
if( useHeuristics )
{
- Luma luma;
- CalculateLuma( src, luma );
+ CalculateLuma( ch, luma );
mode = SelectModeETC2( luma );
}
-#ifdef __AVX2__
- auto plane = Planar_AVX2( src, mode );
+ auto plane = Planar_AVX2( ch, mode, useHeuristics );
if( useHeuristics && mode == ModePlanar ) return plane.plane;
- alignas(32) v4i a[8];
-
+ alignas( 32 ) v4i a[8];
__m128i err0 = PrepareAverages_AVX2( a, plane.sum4 );
// Get index of minimum error (err0)
- __m128i err1 = _mm_shuffle_epi32(err0, _MM_SHUFFLE(2, 3, 0, 1));
+ __m128i err1 = _mm_shuffle_epi32( err0, _MM_SHUFFLE( 2, 3, 0, 1 ) );
__m128i errMin0 = _mm_min_epu32(err0, err1);
- __m128i errMin1 = _mm_shuffle_epi32(errMin0, _MM_SHUFFLE(1, 0, 3, 2));
- __m128i errMin2 = _mm_min_epu32(errMin1, errMin0);
+ __m128i errMin1 = _mm_shuffle_epi32( errMin0, _MM_SHUFFLE( 1, 0, 3, 2 ) );
+ __m128i errMin2 = _mm_min_epu32( errMin1, errMin0 );
- __m128i errMask = _mm_cmpeq_epi32(errMin2, err0);
+ __m128i errMask = _mm_cmpeq_epi32( errMin2, err0 );
- uint32_t mask = _mm_movemask_epi8(errMask);
+ uint32_t mask = _mm_movemask_epi8( errMask );
- size_t idx = _bit_scan_forward(mask) >> 2;
+ size_t idx = _bit_scan_forward( mask ) >> 2;
d = EncodeAverages_AVX2( a, idx );
@@ -2309,12 +3034,54 @@ static etcpak_force_inline uint64_t ProcessRGB_ETC2( const uint8_t* src, bool us
FindBestFit_2x4_AVX2( terr, tsel, a, idx * 2, src );
}
- return EncodeSelectors_AVX2( d, terr, tsel, (idx % 2) == 1, plane.plane, plane.error );
+ if( useHeuristics )
+ {
+ if( mode == ModeTH )
+ {
+ uint64_t result = 0;
+ uint64_t error = 0;
+ uint32_t compressed[4] = { 0, 0, 0, 0 };
+ bool tMode = false;
+
+ error = compressBlockTH( (uint8_t*)src, luma, compressed[0], compressed[1], tMode, ch.r8, ch.g8, ch.b8 );
+ if( tMode )
+ {
+ stuff59bits( compressed[0], compressed[1], compressed[2], compressed[3] );
+ }
+ else
+ {
+ stuff58bits( compressed[0], compressed[1], compressed[2], compressed[3] );
+ }
+
+ result = (uint32_t)_bswap( compressed[2] );
+ result |= static_cast<uint64_t>( _bswap( compressed[3] ) ) << 32;
+
+ plane.plane = result;
+ plane.error = error;
+ }
+ else
+ {
+ plane.plane = 0;
+ plane.error = MaxError;
+ }
+ }
+
+ return EncodeSelectors_AVX2( d, terr, tsel, ( idx % 2 ) == 1, plane.plane, plane.error );
#else
+ if( useHeuristics )
+ {
+#ifdef defined __ARM_NEON && defined __aarch64__
+ Channels ch = GetChannels( src );
+ CalculateLuma( ch, luma );
+#else
+ CalculateLuma( src, luma );
+#endif
+ mode = SelectModeETC2( luma );
+ }
#ifdef __ARM_NEON
- auto result = Planar_NEON( src, mode );
+ auto result = Planar_NEON( src, mode, useHeuristics );
#else
- auto result = Planar( src, mode );
+ auto result = Planar( src, mode, useHeuristics );
#endif
if( result.second == 0 ) return result.first;
@@ -2333,6 +3100,33 @@ static etcpak_force_inline uint64_t ProcessRGB_ETC2( const uint8_t* src, bool us
auto id = g_id[idx];
FindBestFit( terr, tsel, a, id, src );
+ if( useHeuristics )
+ {
+ if( mode == ModeTH )
+ {
+ uint32_t compressed[4] = { 0, 0, 0, 0 };
+ bool tMode = false;
+
+ result.second = compressBlockTH( (uint8_t*)src, luma, compressed[0], compressed[1], tMode );
+ if( tMode )
+ {
+ stuff59bits( compressed[0], compressed[1], compressed[2], compressed[3] );
+ }
+ else
+ {
+ stuff58bits( compressed[0], compressed[1], compressed[2], compressed[3] );
+ }
+
+ result.first = (uint32_t)_bswap( compressed[2] );
+ result.first |= static_cast<uint64_t>( _bswap( compressed[3] ) ) << 32;
+ }
+ else
+ {
+ result.first = 0;
+ result.second = MaxError;
+ }
+ }
+
return EncodeSelectors( d, terr, tsel, id, result.first, result.second );
#endif
}