summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/static_checks.yml3
-rw-r--r--CONTRIBUTING.md28
-rw-r--r--README.md3
-rw-r--r--SConstruct18
-rw-r--r--core/core_bind.cpp27
-rw-r--r--core/core_bind.h3
-rw-r--r--core/debugger/remote_debugger.cpp1
-rw-r--r--core/input/input.cpp16
-rw-r--r--core/input/input_map.cpp2
-rw-r--r--core/io/file_access_pack.h2
-rw-r--r--core/io/http_client.cpp5
-rw-r--r--core/io/json.cpp46
-rw-r--r--core/io/logger.cpp10
-rw-r--r--core/io/logger.h4
-rw-r--r--core/io/resource_importer.cpp6
-rw-r--r--core/io/resource_importer.h1
-rw-r--r--core/io/resource_loader.cpp2
-rw-r--r--core/io/xml_parser.cpp66
-rw-r--r--core/io/xml_parser.h2
-rw-r--r--core/math/color.cpp53
-rw-r--r--core/math/geometry_2d.cpp4
-rw-r--r--core/os/dir_access.cpp2
-rw-r--r--core/os/os.h3
-rw-r--r--core/os/thread.cpp25
-rw-r--r--core/os/thread.h5
-rw-r--r--core/os/threaded_array_processor.h2
-rw-r--r--core/string/translation.cpp19
-rw-r--r--core/string/ustring.cpp62
-rw-r--r--core/templates/cowdata.h2
-rw-r--r--core/templates/safe_refcount.h2
-rw-r--r--core/variant/variant_construct.cpp1
-rw-r--r--doc/Makefile11
-rw-r--r--doc/classes/Area2D.xml8
-rw-r--r--doc/classes/Area3D.xml8
-rw-r--r--doc/classes/Array.xml2
-rw-r--r--doc/classes/Callable.xml20
-rw-r--r--doc/classes/Control.xml2
-rw-r--r--doc/classes/Curve2D.xml2
-rw-r--r--doc/classes/Curve3D.xml2
-rw-r--r--doc/classes/FontData.xml52
-rw-r--r--doc/classes/ItemList.xml21
-rw-r--r--doc/classes/MarginContainer.xml20
-rw-r--r--doc/classes/OS.xml26
-rw-r--r--doc/classes/ProjectSettings.xml5
-rw-r--r--doc/classes/RayCast3D.xml7
-rw-r--r--doc/classes/RenderingServer.xml7
-rw-r--r--doc/classes/RichTextEffect.xml10
-rw-r--r--doc/classes/SceneTree.xml20
-rw-r--r--doc/classes/SceneTreeTimer.xml14
-rw-r--r--doc/classes/ScriptCreateDialog.xml20
-rw-r--r--doc/classes/SpinBox.xml13
-rw-r--r--doc/classes/Sprite2D.xml23
-rw-r--r--doc/classes/StreamPeer.xml18
-rw-r--r--doc/classes/String.xml48
-rw-r--r--doc/classes/StyleBoxFlat.xml8
-rw-r--r--doc/classes/SurfaceTool.xml13
-rw-r--r--doc/classes/TextEdit.xml19
-rw-r--r--doc/classes/TextServer.xml58
-rw-r--r--doc/classes/TileMap.xml14
-rw-r--r--doc/classes/Tree.xml37
-rw-r--r--doc/classes/Tween.xml13
-rw-r--r--doc/classes/UDPServer.xml86
-rw-r--r--doc/classes/UndoRedo.xml35
-rw-r--r--drivers/dummy/rasterizer_dummy.h2
-rw-r--r--editor/create_dialog.cpp5
-rw-r--r--editor/debugger/script_editor_debugger.cpp2
-rw-r--r--editor/editor_export.cpp5
-rw-r--r--editor/editor_file_system.cpp24
-rw-r--r--editor/editor_file_system.h2
-rw-r--r--editor/editor_node.cpp3
-rw-r--r--editor/fileserver/editor_file_server.cpp5
-rw-r--r--editor/fileserver/editor_file_server.h2
-rw-r--r--editor/icons/NodeDisabled.svg1
-rw-r--r--editor/import_defaults_editor.cpp216
-rw-r--r--editor/import_defaults_editor.h75
-rw-r--r--editor/node_3d_editor_gizmos.cpp82
-rw-r--r--editor/node_3d_editor_gizmos.h12
-rw-r--r--editor/plugins/canvas_item_editor_plugin.cpp59
-rw-r--r--editor/plugins/canvas_item_editor_plugin.h10
-rw-r--r--editor/plugins/node_3d_editor_plugin.cpp58
-rw-r--r--editor/plugins/node_3d_editor_plugin.h1
-rw-r--r--editor/plugins/script_editor_plugin.cpp12
-rw-r--r--editor/plugins/script_editor_plugin.h2
-rw-r--r--editor/plugins/tile_map_editor_plugin.cpp19
-rw-r--r--editor/project_settings_editor.cpp9
-rw-r--r--editor/project_settings_editor.h2
-rw-r--r--editor/scene_tree_dock.cpp84
-rw-r--r--editor/scene_tree_dock.h6
-rw-r--r--editor/shader_globals_editor.cpp3
-rw-r--r--main/main.cpp33
-rw-r--r--methods.py15
-rw-r--r--misc/dist/html/editor.html38
-rw-r--r--misc/dist/linux/org.godotengine.Godot.xml29
-rw-r--r--misc/dist/linux/x-godot-project.xml8
-rw-r--r--modules/bullet/collision_object_bullet.cpp8
-rw-r--r--modules/enet/doc_classes/NetworkedMultiplayerENet.xml16
-rw-r--r--modules/enet/networked_multiplayer_enet.cpp9
-rw-r--r--modules/enet/networked_multiplayer_enet.h1
-rw-r--r--modules/gdnative/include/text/godot_text.h4
-rw-r--r--modules/gdnative/text/text_server_gdnative.cpp22
-rw-r--r--modules/gdnative/text/text_server_gdnative.h5
-rw-r--r--modules/gdscript/gdscript_compiler.cpp69
-rw-r--r--modules/gdscript/gdscript_editor.cpp3
-rw-r--r--modules/gdscript/gdscript_parser.cpp22
-rw-r--r--modules/gdscript/gdscript_parser.h1
-rw-r--r--modules/gdscript/language_server/lsp.hpp2
-rw-r--r--modules/gridmap/doc_classes/GridMap.xml1
-rw-r--r--modules/mono/editor/bindings_generator.cpp78
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/PackedSceneExtensions.cs27
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj1
-rw-r--r--modules/mono/godotsharp_dirs.cpp10
-rw-r--r--modules/opensimplex/doc_classes/NoiseTexture.xml1
-rw-r--r--modules/opensimplex/doc_classes/OpenSimplexNoise.xml1
-rw-r--r--modules/text_server_adv/bitmap_font_adv.cpp86
-rw-r--r--modules/text_server_adv/bitmap_font_adv.h6
-rw-r--r--modules/text_server_adv/font_adv.h9
-rw-r--r--modules/text_server_adv/text_server_adv.cpp33
-rw-r--r--modules/text_server_adv/text_server_adv.h5
-rw-r--r--modules/text_server_fb/bitmap_font_fb.cpp85
-rw-r--r--modules/text_server_fb/bitmap_font_fb.h6
-rw-r--r--modules/text_server_fb/font_fb.h9
-rw-r--r--modules/text_server_fb/text_server_fb.cpp33
-rw-r--r--modules/text_server_fb/text_server_fb.h5
-rw-r--r--platform/android/export/export.cpp31
-rw-r--r--platform/android/export/gradle_export_util.h11
-rw-r--r--platform/android/java/app/AndroidManifest.xml10
-rw-r--r--platform/android/java/app/build.gradle2
-rw-r--r--platform/android/java/app/config.gradle59
-rw-r--r--platform/android/java/build.gradle6
-rw-r--r--platform/android/java/lib/AndroidManifest.xml5
-rw-r--r--platform/android/java/lib/build.gradle2
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/Godot.java42
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginRegistry.java38
-rw-r--r--platform/javascript/.eslintrc.engine.js3
-rw-r--r--platform/javascript/SCsub5
-rw-r--r--platform/javascript/export/export.cpp46
-rw-r--r--platform/javascript/http_client.h.inc13
-rw-r--r--platform/javascript/http_client_javascript.cpp183
-rw-r--r--platform/javascript/js/engine/config.js394
-rw-r--r--platform/javascript/js/engine/engine.externs.js1
-rw-r--r--platform/javascript/js/engine/engine.js358
-rw-r--r--platform/javascript/js/engine/preloader.js125
-rw-r--r--platform/javascript/js/engine/utils.js58
-rw-r--r--platform/javascript/js/jsdoc2rst/publish.js354
-rw-r--r--platform/javascript/js/libs/library_godot_display.js4
-rw-r--r--platform/javascript/js/libs/library_godot_fetch.js258
-rw-r--r--platform/javascript/js/libs/library_godot_http_request.js142
-rw-r--r--platform/javascript/js/libs/library_godot_runtime.js16
-rw-r--r--platform/javascript/package-lock.json154
-rw-r--r--platform/javascript/package.json12
-rw-r--r--platform/linuxbsd/detect.py7
-rw-r--r--platform/linuxbsd/display_server_x11.cpp20
-rw-r--r--platform/osx/detect.py7
-rw-r--r--platform/osx/export/export.cpp177
-rw-r--r--platform/server/detect.py7
-rw-r--r--platform/windows/godot.natvis10
-rw-r--r--scene/2d/area_2d.cpp8
-rw-r--r--scene/2d/camera_2d.cpp9
-rw-r--r--scene/2d/collision_polygon_2d.cpp55
-rw-r--r--scene/2d/cpu_particles_2d.cpp104
-rw-r--r--scene/2d/joints_2d.cpp18
-rw-r--r--scene/2d/joints_2d.h2
-rw-r--r--scene/2d/polygon_2d.cpp7
-rw-r--r--scene/2d/polygon_2d.h4
-rw-r--r--scene/2d/ray_cast_2d.cpp69
-rw-r--r--scene/2d/ray_cast_2d.h2
-rw-r--r--scene/2d/sprite_2d.cpp5
-rw-r--r--scene/3d/area_3d.cpp8
-rw-r--r--scene/3d/baked_lightmap.cpp1
-rw-r--r--scene/3d/collision_object_3d.cpp53
-rw-r--r--scene/3d/collision_object_3d.h7
-rw-r--r--scene/3d/collision_shape_3d.cpp30
-rw-r--r--scene/3d/collision_shape_3d.h4
-rw-r--r--scene/3d/decal.cpp9
-rw-r--r--scene/3d/decal.h4
-rw-r--r--scene/3d/gi_probe.cpp5
-rw-r--r--scene/3d/light_3d.cpp6
-rw-r--r--scene/3d/physics_joint_3d.cpp19
-rw-r--r--scene/3d/physics_joint_3d.h2
-rw-r--r--scene/3d/ray_cast_3d.cpp162
-rw-r--r--scene/3d/ray_cast_3d.h17
-rw-r--r--scene/3d/sprite_3d.cpp7
-rw-r--r--scene/animation/tween.cpp21
-rw-r--r--scene/animation/tween.h1
-rw-r--r--scene/gui/control.cpp2
-rw-r--r--scene/gui/graph_edit.cpp32
-rw-r--r--scene/gui/line_edit.cpp29
-rw-r--r--scene/gui/line_edit.h1
-rw-r--r--scene/gui/rich_text_label.cpp4
-rw-r--r--scene/gui/slider.cpp2
-rw-r--r--scene/gui/spin_box.cpp17
-rw-r--r--scene/gui/spin_box.h1
-rw-r--r--scene/gui/text_edit.cpp27
-rw-r--r--scene/gui/tree.cpp6
-rw-r--r--scene/main/canvas_layer.cpp9
-rw-r--r--scene/main/canvas_layer.h4
-rw-r--r--scene/main/http_request.cpp4
-rw-r--r--scene/main/node.cpp114
-rw-r--r--scene/main/node.h2
-rw-r--r--scene/main/scene_tree.cpp12
-rw-r--r--scene/main/scene_tree.h2
-rw-r--r--scene/main/viewport.cpp59
-rw-r--r--scene/main/viewport.h3
-rw-r--r--scene/main/window.cpp6
-rw-r--r--scene/resources/animation.h2
-rw-r--r--scene/resources/camera_effects.cpp11
-rw-r--r--scene/resources/camera_effects.h4
-rw-r--r--scene/resources/capsule_shape_2d.cpp8
-rw-r--r--scene/resources/circle_shape_2d.cpp8
-rw-r--r--scene/resources/convex_polygon_shape_2d.cpp12
-rw-r--r--scene/resources/default_theme/default_theme.cpp66
-rw-r--r--scene/resources/environment.cpp2
-rw-r--r--scene/resources/font.cpp33
-rw-r--r--scene/resources/font.h6
-rw-r--r--scene/resources/material.cpp20
-rw-r--r--scene/resources/mesh_data_tool.cpp38
-rw-r--r--scene/resources/navigation_mesh.cpp2
-rw-r--r--scene/resources/packed_scene.cpp17
-rw-r--r--scene/resources/rectangle_shape_2d.cpp32
-rw-r--r--scene/resources/shape_2d.cpp13
-rw-r--r--scene/resources/shape_2d.h3
-rw-r--r--scene/resources/texture.cpp2
-rw-r--r--scene/resources/visual_shader_nodes.cpp63
-rw-r--r--servers/audio/effects/audio_effect_chorus.cpp2
-rw-r--r--servers/physics_2d/joints_2d_sw.cpp19
-rw-r--r--servers/physics_2d/joints_2d_sw.h12
-rw-r--r--servers/physics_2d/space_2d_sw.cpp26
-rw-r--r--servers/physics_3d/collision_solver_3d_sat.cpp16
-rw-r--r--servers/physics_3d/joints_3d_sw.h9
-rw-r--r--servers/physics_3d/physics_server_3d_sw.cpp3
-rw-r--r--servers/physics_3d/shape_3d_sw.cpp36
-rw-r--r--servers/physics_3d/space_3d_sw.cpp22
-rw-r--r--servers/rendering/renderer_canvas_cull.cpp12
-rw-r--r--servers/rendering/renderer_rd/renderer_scene_environment_rd.cpp126
-rw-r--r--servers/rendering/renderer_rd/renderer_scene_environment_rd.h155
-rw-r--r--servers/rendering/renderer_rd/renderer_scene_gi_rd.cpp3383
-rw-r--r--servers/rendering/renderer_rd/renderer_scene_gi_rd.h653
-rw-r--r--servers/rendering/renderer_rd/renderer_scene_render_forward.cpp23
-rw-r--r--servers/rendering/renderer_rd/renderer_scene_render_rd.cpp5104
-rw-r--r--servers/rendering/renderer_rd/renderer_scene_render_rd.h956
-rw-r--r--servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp1491
-rw-r--r--servers/rendering/renderer_rd/renderer_scene_sky_rd.h292
-rw-r--r--servers/rendering/renderer_rd/renderer_storage_rd.h2
-rw-r--r--servers/rendering/renderer_storage.h2
-rw-r--r--servers/rendering/rendering_server_default.cpp2
-rw-r--r--servers/rendering/rendering_server_default.h2
-rw-r--r--servers/rendering/shader_language.cpp21
-rw-r--r--servers/rendering_server.h2
-rw-r--r--servers/text_server.cpp5
-rw-r--r--servers/text_server.h17
-rw-r--r--tests/test_geometry_3d.h417
-rw-r--r--tests/test_main.cpp2
-rw-r--r--tests/test_path_follow_2d.h36
-rw-r--r--tests/test_path_follow_3d.h220
-rw-r--r--tests/test_string.h46
-rw-r--r--tests/test_xml_parser.h (renamed from platform/javascript/http_request.h)65
256 files changed, 11723 insertions, 7782 deletions
diff --git a/.github/workflows/static_checks.yml b/.github/workflows/static_checks.yml
index 12270a848a..30468034ff 100644
--- a/.github/workflows/static_checks.yml
+++ b/.github/workflows/static_checks.yml
@@ -35,11 +35,12 @@ jobs:
run: |
bash ./misc/scripts/black_format.sh
- - name: JavaScript style checks via ESLint
+ - name: JavaScript style and documentation checks via ESLint and JSDoc
run: |
cd platform/javascript
npm ci
npm run lint
+ npm run docs -- -d dry-run
- name: Documentation checks
run: |
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 4387750f28..d7a4f976bf 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -241,25 +241,21 @@ discussions and support, others more for development discussions.
To communicate with developers (e.g. to discuss a feature you want to implement
or a bug you want to fix), the following channels can be used:
-- [GitHub issues](https://github.com/godotengine/godot/issues): If there is an
- existing issue about a topic you want to discuss, just add a comment to it -
- all developers watch the repository and will get an email notification. You
- can also create a new issue - please keep in mind to create issues only to
- discuss quite specific points about the development, and not general user
- feedback or support requests.
-- [#godotengine-devel IRC channel on
- Freenode](https://webchat.freenode.net/?channels=godotengine-devel): You will
- find most core developers there, so it's the go-to channel for direct chat
+- [Godot Contributors Chat](https://chat.godotengine.org): You will
+ find most core developers there, so it's the go-to platform for direct chat
about Godot Engine development. Feel free to start discussing something there
to get some early feedback before writing up a detailed proposal in a GitHub
issue.
-- [devel@godotengine.org mailing
- list](https://listengine.tuxfamily.org/godotengine.org/devel/): Mailing list
- for Godot developers, used primarily to announce developer meetings on IRC
- and other important discussions that need to reach people directly in their
- mailbox. See the [index
- page](https://listengine.tuxfamily.org/godotengine.org/devel/) for
- subscription instructions.
+- [Bug tracker](https://github.com/godotengine/godot/issues): If there is an
+ existing issue about a topic you want to discuss, just add a comment to it -
+ many developers watch the repository and will get a notification. You can
+ also create a new issue - please keep in mind to create issues only to
+ discuss quite specific points about the development, and not general user
+ feedback or support requests.
+- [Feature proposals](https://github.com/godotengine/godot-proposals/issues):
+ To propose a new feature, we have a dedicated issue tracker for that. Don't
+ hesitate to start by talking about your idea on the Godot Contributors Chat
+ to make sure that it makes sense in Godot's context.
Thanks for your interest in contributing!
diff --git a/README.md b/README.md
index 1f2aa00f08..72a85492fd 100644
--- a/README.md
+++ b/README.md
@@ -50,8 +50,7 @@ Godot is not only an engine but an ever-growing community of users and engine
developers. The main community channels are listed [on the homepage](https://godotengine.org/community).
To get in touch with the engine developers, the best way is to join the
-[#godotengine-devel IRC channel](https://webchat.freenode.net/?channels=godotengine-devel)
-on Freenode.
+[Godot Contributors Chat](https://chat.godotengine.org).
To get started contributing to the project, see the [contributing guide](CONTRIBUTING.md).
diff --git a/SConstruct b/SConstruct
index 90cc99dd47..f03fb72ff3 100644
--- a/SConstruct
+++ b/SConstruct
@@ -55,7 +55,7 @@ custom_tools = ["default"]
platform_arg = ARGUMENTS.get("platform", ARGUMENTS.get("p", False))
-if os.name == "nt" and (platform_arg == "android" or ARGUMENTS.get("use_mingw", False)):
+if os.name == "nt" and (platform_arg == "android" or methods.get_cmdline_bool("use_mingw", False)):
custom_tools = ["mingw"]
elif platform_arg == "javascript":
# Use generic POSIX build toolchain for Emscripten.
@@ -95,7 +95,7 @@ env_base.SConsignFile(".sconsign{0}.dblite".format(pickle.HIGHEST_PROTOCOL))
customs = ["custom.py"]
-profile = ARGUMENTS.get("profile", False)
+profile = ARGUMENTS.get("profile", "")
if profile:
if os.path.isfile(profile):
customs.append(profile)
@@ -325,17 +325,17 @@ if selected_platform in platform_list:
env.Alias("compiledb", env.CompilationDatabase())
# 'dev' and 'production' are aliases to set default options if they haven't been set
- # manually by the user. We use `ARGUMENTS.get()` to check if they were manually set.
+ # manually by the user.
if env["dev"]:
- env["verbose"] = ARGUMENTS.get("verbose", True)
+ env["verbose"] = methods.get_cmdline_bool("verbose", True)
env["warnings"] = ARGUMENTS.get("warnings", "extra")
- env["werror"] = ARGUMENTS.get("werror", True)
+ env["werror"] = methods.get_cmdline_bool("werror", True)
if env["tools"]:
- env["tests"] = ARGUMENTS.get("tests", True)
+ env["tests"] = methods.get_cmdline_bool("tests", True)
if env["production"]:
- env["use_static_cpp"] = ARGUMENTS.get("use_static_cpp", True)
- env["use_lto"] = ARGUMENTS.get("use_lto", True)
- env["debug_symbols"] = ARGUMENTS.get("debug_symbols", False)
+ env["use_static_cpp"] = methods.get_cmdline_bool("use_static_cpp", True)
+ env["use_lto"] = methods.get_cmdline_bool("use_lto", True)
+ env["debug_symbols"] = methods.get_cmdline_bool("debug_symbols", False)
if not env["tools"] and env["target"] == "debug":
print(
"WARNING: Requested `production` build with `tools=no target=debug`, "
diff --git a/core/core_bind.cpp b/core/core_bind.cpp
index 0da6680a7b..e7a77384da 100644
--- a/core/core_bind.cpp
+++ b/core/core_bind.cpp
@@ -276,6 +276,10 @@ String _OS::get_environment(const String &p_var) const {
return OS::get_singleton()->get_environment(p_var);
}
+bool _OS::set_environment(const String &p_var, const String &p_value) const {
+ return OS::get_singleton()->set_environment(p_var, p_value);
+}
+
String _OS::get_name() const {
return OS::get_singleton()->get_name();
}
@@ -318,18 +322,6 @@ uint64_t _OS::get_static_memory_peak_usage() const {
return OS::get_singleton()->get_static_memory_peak_usage();
}
-int _OS::get_exit_code() const {
- return OS::get_singleton()->get_exit_code();
-}
-
-void _OS::set_exit_code(int p_code) {
- if (p_code < 0 || p_code > 125) {
- WARN_PRINT("For portability reasons, the exit code should be set between 0 and 125 (inclusive).");
- }
-
- OS::get_singleton()->set_exit_code(p_code);
-}
-
/**
* Get current datetime with consideration for utc and
* dst
@@ -574,7 +566,7 @@ struct _OSCoreBindImg {
void _OS::print_all_textures_by_size() {
List<_OSCoreBindImg> imgs;
- int total = 0;
+ uint64_t total = 0;
{
List<Ref<Resource>> rsrc;
ResourceCache::get_cached_resources(&rsrc);
@@ -711,8 +703,9 @@ void _OS::_bind_methods() {
ClassDB::bind_method(D_METHOD("shell_open", "uri"), &_OS::shell_open);
ClassDB::bind_method(D_METHOD("get_process_id"), &_OS::get_process_id);
- ClassDB::bind_method(D_METHOD("get_environment", "environment"), &_OS::get_environment);
- ClassDB::bind_method(D_METHOD("has_environment", "environment"), &_OS::has_environment);
+ ClassDB::bind_method(D_METHOD("get_environment", "variable"), &_OS::get_environment);
+ ClassDB::bind_method(D_METHOD("set_environment", "variable", "value"), &_OS::set_environment);
+ ClassDB::bind_method(D_METHOD("has_environment", "variable"), &_OS::has_environment);
ClassDB::bind_method(D_METHOD("get_name"), &_OS::get_name);
ClassDB::bind_method(D_METHOD("get_cmdline_args"), &_OS::get_cmdline_args);
@@ -725,9 +718,6 @@ void _OS::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_datetime_from_unix_time", "unix_time_val"), &_OS::get_datetime_from_unix_time);
ClassDB::bind_method(D_METHOD("get_unix_time_from_datetime", "datetime"), &_OS::get_unix_time_from_datetime);
- ClassDB::bind_method(D_METHOD("get_exit_code"), &_OS::get_exit_code);
- ClassDB::bind_method(D_METHOD("set_exit_code", "code"), &_OS::set_exit_code);
-
ClassDB::bind_method(D_METHOD("delay_usec", "usec"), &_OS::delay_usec);
ClassDB::bind_method(D_METHOD("delay_msec", "msec"), &_OS::delay_msec);
ClassDB::bind_method(D_METHOD("get_ticks_msec"), &_OS::get_ticks_msec);
@@ -772,7 +762,6 @@ void _OS::_bind_methods() {
ClassDB::bind_method(D_METHOD("request_permissions"), &_OS::request_permissions);
ClassDB::bind_method(D_METHOD("get_granted_permissions"), &_OS::get_granted_permissions);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "exit_code"), "set_exit_code", "get_exit_code");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "low_processor_usage_mode"), "set_low_processor_usage_mode", "is_in_low_processor_usage_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "low_processor_usage_mode_sleep_usec"), "set_low_processor_usage_mode_sleep_usec", "get_low_processor_usage_mode_sleep_usec");
diff --git a/core/core_bind.h b/core/core_bind.h
index 8a4885b82b..3920116ca4 100644
--- a/core/core_bind.h
+++ b/core/core_bind.h
@@ -172,6 +172,7 @@ public:
bool has_environment(const String &p_var) const;
String get_environment(const String &p_var) const;
+ bool set_environment(const String &p_var, const String &p_value) const;
String get_name() const;
Vector<String> get_cmdline_args();
@@ -198,8 +199,6 @@ public:
void set_use_file_access_save_and_swap(bool p_enable);
- int get_exit_code() const;
- void set_exit_code(int p_code);
Dictionary get_date(bool utc) const;
Dictionary get_time(bool utc) const;
Dictionary get_datetime(bool utc) const;
diff --git a/core/debugger/remote_debugger.cpp b/core/debugger/remote_debugger.cpp
index 7392e6b9a6..bdbb7766fa 100644
--- a/core/debugger/remote_debugger.cpp
+++ b/core/debugger/remote_debugger.cpp
@@ -76,6 +76,7 @@ public:
NetworkProfiler() {}
int bandwidth_usage(const Vector<BandwidthFrame> &p_buffer, int p_pointer) {
+ ERR_FAIL_COND_V(p_buffer.size() == 0, 0);
int total_bandwidth = 0;
uint32_t timestamp = OS::get_singleton()->get_ticks_msec();
diff --git a/core/input/input.cpp b/core/input/input.cpp
index 94a18b5b4f..f928ae7654 100644
--- a/core/input/input.cpp
+++ b/core/input/input.cpp
@@ -241,10 +241,18 @@ bool Input::is_joy_button_pressed(int p_device, int p_button) const {
}
bool Input::is_action_pressed(const StringName &p_action, bool p_exact) const {
+#ifdef DEBUG_ENABLED
+ bool has_action = InputMap::get_singleton()->has_action(p_action);
+ ERR_FAIL_COND_V_MSG(!has_action, false, "Request for nonexistent InputMap action '" + String(p_action) + "'.");
+#endif
return action_state.has(p_action) && action_state[p_action].pressed && (p_exact ? action_state[p_action].exact : true);
}
bool Input::is_action_just_pressed(const StringName &p_action, bool p_exact) const {
+#ifdef DEBUG_ENABLED
+ bool has_action = InputMap::get_singleton()->has_action(p_action);
+ ERR_FAIL_COND_V_MSG(!has_action, false, "Request for nonexistent InputMap action '" + String(p_action) + "'.");
+#endif
const Map<StringName, Action>::Element *E = action_state.find(p_action);
if (!E) {
return false;
@@ -262,6 +270,10 @@ bool Input::is_action_just_pressed(const StringName &p_action, bool p_exact) con
}
bool Input::is_action_just_released(const StringName &p_action, bool p_exact) const {
+#ifdef DEBUG_ENABLED
+ bool has_action = InputMap::get_singleton()->has_action(p_action);
+ ERR_FAIL_COND_V_MSG(!has_action, false, "Request for nonexistent InputMap action '" + String(p_action) + "'.");
+#endif
const Map<StringName, Action>::Element *E = action_state.find(p_action);
if (!E) {
return false;
@@ -279,6 +291,10 @@ bool Input::is_action_just_released(const StringName &p_action, bool p_exact) co
}
float Input::get_action_strength(const StringName &p_action, bool p_exact) const {
+#ifdef DEBUG_ENABLED
+ bool has_action = InputMap::get_singleton()->has_action(p_action);
+ ERR_FAIL_COND_V_MSG(!has_action, false, "Request for nonexistent InputMap action '" + String(p_action) + "'.");
+#endif
const Map<StringName, Action>::Element *E = action_state.find(p_action);
if (!E) {
return 0.0f;
diff --git a/core/input/input_map.cpp b/core/input/input_map.cpp
index 029808ebbe..e0b25fa092 100644
--- a/core/input/input_map.cpp
+++ b/core/input/input_map.cpp
@@ -694,7 +694,7 @@ void InputMap::load_default() {
// For the editor, only add keyboard actions.
if (iek.is_valid()) {
- action_add_event(fullname, I->get());
+ action_add_event(name, I->get());
}
}
}
diff --git a/core/io/file_access_pack.h b/core/io/file_access_pack.h
index 3c84e6b656..343adbe592 100644
--- a/core/io/file_access_pack.h
+++ b/core/io/file_access_pack.h
@@ -92,7 +92,7 @@ private:
PathMD5() {}
- PathMD5(const Vector<uint8_t> p_buf) {
+ PathMD5(const Vector<uint8_t> &p_buf) {
a = *((uint64_t *)&p_buf[0]);
b = *((uint64_t *)&p_buf[8]);
}
diff --git a/core/io/http_client.cpp b/core/io/http_client.cpp
index 18afdc678e..3863dce0f6 100644
--- a/core/io/http_client.cpp
+++ b/core/io/http_client.cpp
@@ -96,6 +96,11 @@ Error HTTPClient::connect_to_host(const String &p_host, int p_port, bool p_ssl,
void HTTPClient::set_connection(const Ref<StreamPeer> &p_connection) {
ERR_FAIL_COND_MSG(p_connection.is_null(), "Connection is not a reference to a valid StreamPeer object.");
+ if (ssl) {
+ ERR_FAIL_NULL_MSG(Object::cast_to<StreamPeerSSL>(p_connection.ptr()),
+ "Connection is not a reference to a valid StreamPeerSSL object.");
+ }
+
if (connection == p_connection) {
return;
}
diff --git a/core/io/json.cpp b/core/io/json.cpp
index bc4527869b..0d9117fdda 100644
--- a/core/io/json.cpp
+++ b/core/io/json.cpp
@@ -234,6 +234,52 @@ Error JSON::_get_token(const char32_t *p_str, int &index, int p_len, Token &r_to
}
index += 4; //will add at the end anyway
+ if ((res & 0xfffffc00) == 0xd800) {
+ if (p_str[index + 1] != '\\' || p_str[index + 2] != 'u') {
+ r_err_str = "Invalid UTF-16 sequence in string, unpaired lead surrogate";
+ return ERR_PARSE_ERROR;
+ }
+ index += 2;
+ char32_t trail = 0;
+ for (int j = 0; j < 4; j++) {
+ char32_t c = p_str[index + j + 1];
+ if (c == 0) {
+ r_err_str = "Unterminated String";
+ return ERR_PARSE_ERROR;
+ }
+ if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))) {
+ r_err_str = "Malformed hex constant in string";
+ return ERR_PARSE_ERROR;
+ }
+ char32_t v;
+ if (c >= '0' && c <= '9') {
+ v = c - '0';
+ } else if (c >= 'a' && c <= 'f') {
+ v = c - 'a';
+ v += 10;
+ } else if (c >= 'A' && c <= 'F') {
+ v = c - 'A';
+ v += 10;
+ } else {
+ ERR_PRINT("Bug parsing hex constant.");
+ v = 0;
+ }
+
+ trail <<= 4;
+ trail |= v;
+ }
+ if ((trail & 0xfffffc00) == 0xdc00) {
+ res = (res << 10UL) + trail - ((0xd800 << 10UL) + 0xdc00 - 0x10000);
+ index += 4; //will add at the end anyway
+ } else {
+ r_err_str = "Invalid UTF-16 sequence in string, unpaired lead surrogate";
+ return ERR_PARSE_ERROR;
+ }
+ } else if ((res & 0xfffffc00) == 0xdc00) {
+ r_err_str = "Invalid UTF-16 sequence in string, unpaired trail surrogate";
+ return ERR_PARSE_ERROR;
+ }
+
} break;
default: {
res = next;
diff --git a/core/io/logger.cpp b/core/io/logger.cpp
index bd0285a7a9..8a07459a1d 100644
--- a/core/io/logger.cpp
+++ b/core/io/logger.cpp
@@ -43,6 +43,12 @@ bool Logger::should_log(bool p_err) {
return (!p_err || _print_error_enabled) && (p_err || _print_line_enabled);
}
+bool Logger::_flush_stdout_on_print = true;
+
+void Logger::set_flush_stdout_on_print(bool value) {
+ _flush_stdout_on_print = value;
+}
+
void Logger::log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, ErrorType p_type) {
if (!should_log(true)) {
return;
@@ -207,7 +213,7 @@ void RotatedFileLogger::logv(const char *p_format, va_list p_list, bool p_err) {
Memory::free_static(buf);
}
- if (p_err || !ProjectSettings::get_singleton() || GLOBAL_GET("application/run/flush_stdout_on_print")) {
+ if (p_err || _flush_stdout_on_print) {
// Don't always flush when printing stdout to avoid performance
// issues when `print()` is spammed in release builds.
file->flush();
@@ -228,7 +234,7 @@ void StdLogger::logv(const char *p_format, va_list p_list, bool p_err) {
vfprintf(stderr, p_format, p_list);
} else {
vprintf(p_format, p_list);
- if (!ProjectSettings::get_singleton() || GLOBAL_GET("application/run/flush_stdout_on_print")) {
+ if (_flush_stdout_on_print) {
// Don't always flush when printing stdout to avoid performance
// issues when `print()` is spammed in release builds.
fflush(stdout);
diff --git a/core/io/logger.h b/core/io/logger.h
index b8e615b436..a12945911c 100644
--- a/core/io/logger.h
+++ b/core/io/logger.h
@@ -41,6 +41,8 @@ class Logger {
protected:
bool should_log(bool p_err);
+ static bool _flush_stdout_on_print;
+
public:
enum ErrorType {
ERR_ERROR,
@@ -49,6 +51,8 @@ public:
ERR_SHADER
};
+ static void set_flush_stdout_on_print(bool value);
+
virtual void logv(const char *p_format, va_list p_list, bool p_err) _PRINTF_FORMAT_ATTRIBUTE_2_0 = 0;
virtual void log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, ErrorType p_type = ERR_ERROR);
diff --git a/core/io/resource_importer.cpp b/core/io/resource_importer.cpp
index 3e460726f6..5ca0eb884a 100644
--- a/core/io/resource_importer.cpp
+++ b/core/io/resource_importer.cpp
@@ -352,6 +352,12 @@ void ResourceFormatImporter::get_importers_for_extension(const String &p_extensi
}
}
+void ResourceFormatImporter::get_importers(List<Ref<ResourceImporter>> *r_importers) {
+ for (int i = 0; i < importers.size(); i++) {
+ r_importers->push_back(importers[i]);
+ }
+}
+
Ref<ResourceImporter> ResourceFormatImporter::get_importer_by_extension(const String &p_extension) const {
Ref<ResourceImporter> importer;
float priority = 0;
diff --git a/core/io/resource_importer.h b/core/io/resource_importer.h
index bda8b74b73..91efec5534 100644
--- a/core/io/resource_importer.h
+++ b/core/io/resource_importer.h
@@ -82,6 +82,7 @@ public:
Ref<ResourceImporter> get_importer_by_name(const String &p_name) const;
Ref<ResourceImporter> get_importer_by_extension(const String &p_extension) const;
void get_importers_for_extension(const String &p_extension, List<Ref<ResourceImporter>> *r_importers);
+ void get_importers(List<Ref<ResourceImporter>> *r_importers);
bool are_import_settings_valid(const String &p_path) const;
String get_import_settings_hash() const;
diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp
index cba9a47187..8275dd0ad4 100644
--- a/core/io/resource_loader.cpp
+++ b/core/io/resource_loader.cpp
@@ -518,7 +518,7 @@ RES ResourceLoader::load(const String &p_path, const String &p_type_hint, Resour
local_path = ProjectSettings::get_singleton()->localize_path(p_path);
}
- if (p_cache_mode == ResourceFormatLoader::CACHE_MODE_IGNORE) {
+ if (p_cache_mode != ResourceFormatLoader::CACHE_MODE_IGNORE) {
thread_load_mutex->lock();
//Is it already being loaded? poll until done
diff --git a/core/io/xml_parser.cpp b/core/io/xml_parser.cpp
index 905be6089d..1574634aad 100644
--- a/core/io/xml_parser.cpp
+++ b/core/io/xml_parser.cpp
@@ -36,63 +36,6 @@
VARIANT_ENUM_CAST(XMLParser::NodeType);
-static bool _equalsn(const char32_t *str1, const char32_t *str2, int len) {
- int i;
- for (i = 0; i < len && str1[i] && str2[i]; ++i) {
- if (str1[i] != str2[i]) {
- return false;
- }
- }
-
- // if one (or both) of the strings was smaller then they
- // are only equal if they have the same length
- return (i == len) || (str1[i] == 0 && str2[i] == 0);
-}
-
-String XMLParser::_replace_special_characters(const String &origstr) {
- int pos = origstr.find("&");
- int oldPos = 0;
-
- if (pos == -1) {
- return origstr;
- }
-
- String newstr;
-
- while (pos != -1 && pos < origstr.length() - 2) {
- // check if it is one of the special characters
-
- int specialChar = -1;
- for (int i = 0; i < (int)special_characters.size(); ++i) {
- const char32_t *p = &origstr[pos] + 1;
-
- if (_equalsn(&special_characters[i][1], p, special_characters[i].length() - 1)) {
- specialChar = i;
- break;
- }
- }
-
- if (specialChar != -1) {
- newstr += (origstr.substr(oldPos, pos - oldPos));
- newstr += (special_characters[specialChar][0]);
- pos += special_characters[specialChar].length();
- } else {
- newstr += (origstr.substr(oldPos, pos - oldPos + 1));
- pos += 1;
- }
-
- // find next &
- oldPos = pos;
- pos = origstr.find("&", pos);
- }
-
- if (oldPos < origstr.length() - 1) {
- newstr += (origstr.substr(oldPos, origstr.length() - oldPos));
- }
-
- return newstr;
-}
-
static inline bool _is_white_space(char c) {
return (c == ' ' || c == '\t' || c == '\n' || c == '\r');
}
@@ -116,7 +59,7 @@ bool XMLParser::_set_text(char *start, char *end) {
// set current text to the parsed text, and replace xml special characters
String s = String::utf8(start, (int)(end - start));
- node_name = _replace_special_characters(s);
+ node_name = s.xml_unescape();
// current XML node type is text
node_type = NODE_TEXT;
@@ -292,7 +235,7 @@ void XMLParser::_parse_opening_xml_element() {
String s = String::utf8(attributeValueBegin,
(int)(attributeValueEnd - attributeValueBegin));
- attr.value = _replace_special_characters(s);
+ attr.value = s.xml_unescape();
attributes.push_back(attr);
} else {
// tag is closed directly
@@ -555,11 +498,6 @@ int XMLParser::get_current_line() const {
}
XMLParser::XMLParser() {
- special_characters.push_back("&amp;");
- special_characters.push_back("<lt;");
- special_characters.push_back(">gt;");
- special_characters.push_back("\"quot;");
- special_characters.push_back("'apos;");
}
XMLParser::~XMLParser() {
diff --git a/core/io/xml_parser.h b/core/io/xml_parser.h
index 01af6a90ad..847edf958d 100644
--- a/core/io/xml_parser.h
+++ b/core/io/xml_parser.h
@@ -68,8 +68,6 @@ private:
char *data = nullptr;
char *P = nullptr;
uint64_t length = 0;
- void unescape(String &p_str);
- Vector<String> special_characters;
String node_name;
bool node_empty = false;
NodeType node_type = NODE_NONE;
diff --git a/core/math/color.cpp b/core/math/color.cpp
index e1b45cac9c..8affb07e8c 100644
--- a/core/math/color.cpp
+++ b/core/math/color.cpp
@@ -452,56 +452,9 @@ String Color::to_html(bool p_alpha) const {
}
Color Color::from_hsv(float p_h, float p_s, float p_v, float p_a) const {
- p_h = Math::fmod(p_h * 360.0f, 360.0f);
- if (p_h < 0.0) {
- p_h += 360.0f;
- }
-
- const float h_ = p_h / 60.0f;
- const float c = p_v * p_s;
- const float x = c * (1.0f - Math::abs(Math::fmod(h_, 2.0f) - 1.0f));
- float r, g, b;
-
- switch ((int)h_) {
- case 0: {
- r = c;
- g = x;
- b = 0;
- } break;
- case 1: {
- r = x;
- g = c;
- b = 0;
- } break;
- case 2: {
- r = 0;
- g = c;
- b = x;
- } break;
- case 3: {
- r = 0;
- g = x;
- b = c;
- } break;
- case 4: {
- r = x;
- g = 0;
- b = c;
- } break;
- case 5: {
- r = c;
- g = 0;
- b = x;
- } break;
- default: {
- r = 0;
- g = 0;
- b = 0;
- } break;
- }
-
- const float m = p_v - c;
- return Color(m + r, m + g, m + b, p_a);
+ Color c;
+ c.set_hsv(p_h, p_s, p_v, p_a);
+ return c;
}
Color::operator String() const {
diff --git a/core/math/geometry_2d.cpp b/core/math/geometry_2d.cpp
index 783750b9e6..d67be14d33 100644
--- a/core/math/geometry_2d.cpp
+++ b/core/math/geometry_2d.cpp
@@ -94,6 +94,10 @@ void Geometry2D::make_atlas(const Vector<Size2i> &p_rects, Vector<Point2i> &r_re
// 256x8192 atlas (won't work anywhere).
ERR_FAIL_COND(p_rects.size() == 0);
+ for (int i = 0; i < p_rects.size(); i++) {
+ ERR_FAIL_COND(p_rects[i].width <= 0);
+ ERR_FAIL_COND(p_rects[i].height <= 0);
+ }
Vector<_AtlasWorkRect> wrects;
wrects.resize(p_rects.size());
diff --git a/core/os/dir_access.cpp b/core/os/dir_access.cpp
index fb9da9fbed..b7c3a17ba9 100644
--- a/core/os/dir_access.cpp
+++ b/core/os/dir_access.cpp
@@ -170,7 +170,7 @@ Error DirAccess::make_dir_recursive(String p_dir) {
curpath = curpath.plus_file(subdirs[i]);
Error err = make_dir(curpath);
if (err != OK && err != ERR_ALREADY_EXISTS) {
- ERR_FAIL_V(err);
+ ERR_FAIL_V_MSG(err, "Could not create directory: " + curpath);
}
}
diff --git a/core/os/os.h b/core/os/os.h
index 77a54ba68a..e41d788e12 100644
--- a/core/os/os.h
+++ b/core/os/os.h
@@ -40,6 +40,7 @@
#include "core/templates/vector.h"
#include <stdarg.h>
+#include <stdlib.h>
class OS {
static OS *singleton;
@@ -53,7 +54,7 @@ class OS {
bool _debug_stdout = false;
String _local_clipboard;
bool _no_window = false;
- int _exit_code = 0;
+ int _exit_code = EXIT_FAILURE; // unexpected exit is marked as failure
int _orientation;
bool _allow_hidpi = false;
bool _allow_layered = false;
diff --git a/core/os/thread.cpp b/core/os/thread.cpp
index 88744eed63..f1fcfdf7e1 100644
--- a/core/os/thread.cpp
+++ b/core/os/thread.cpp
@@ -41,9 +41,13 @@ void (*Thread::set_priority_func)(Thread::Priority) = nullptr;
void (*Thread::init_func)() = nullptr;
void (*Thread::term_func)() = nullptr;
-Thread::ID Thread::main_thread_id = 1;
-SafeNumeric<Thread::ID> Thread::last_thread_id{ 1 };
-thread_local Thread::ID Thread::caller_id = 1;
+uint64_t Thread::_thread_id_hash(const std::thread::id &p_t) {
+ static std::hash<std::thread::id> hasher;
+ return hasher(p_t);
+}
+
+Thread::ID Thread::main_thread_id = _thread_id_hash(std::this_thread::get_id());
+thread_local Thread::ID Thread::caller_id = _thread_id_hash(std::this_thread::get_id());
void Thread::_set_platform_funcs(
Error (*p_set_name_func)(const String &),
@@ -57,7 +61,7 @@ void Thread::_set_platform_funcs(
}
void Thread::callback(Thread *p_self, const Settings &p_settings, Callback p_callback, void *p_userdata) {
- Thread::caller_id = p_self->id;
+ Thread::caller_id = _thread_id_hash(p_self->thread.get_id());
if (set_priority_func) {
set_priority_func(p_settings.priority);
}
@@ -73,7 +77,7 @@ void Thread::callback(Thread *p_self, const Settings &p_settings, Callback p_cal
}
void Thread::start(Thread::Callback p_callback, void *p_user, const Settings &p_settings) {
- if (id != 0) {
+ if (id != _thread_id_hash(std::thread::id())) {
#ifdef DEBUG_ENABLED
WARN_PRINT("A Thread object has been re-started without wait_to_finish() having been called on it. Please do so to ensure correct cleanup of the thread.");
#endif
@@ -81,21 +85,22 @@ void Thread::start(Thread::Callback p_callback, void *p_user, const Settings &p_
std::thread empty_thread;
thread.swap(empty_thread);
}
- id = last_thread_id.increment();
std::thread new_thread(&Thread::callback, this, p_settings, p_callback, p_user);
thread.swap(new_thread);
+ id = _thread_id_hash(thread.get_id());
}
bool Thread::is_started() const {
- return id != 0;
+ return id != _thread_id_hash(std::thread::id());
}
void Thread::wait_to_finish() {
- if (id != 0) {
+ if (id != _thread_id_hash(std::thread::id())) {
+ ERR_FAIL_COND_MSG(id == get_caller_id(), "A Thread can't wait for itself to finish.");
thread.join();
std::thread empty_thread;
thread.swap(empty_thread);
- id = 0;
+ id = _thread_id_hash(std::thread::id());
}
}
@@ -108,7 +113,7 @@ Error Thread::set_name(const String &p_name) {
}
Thread::~Thread() {
- if (id != 0) {
+ if (id != _thread_id_hash(std::thread::id())) {
#ifdef DEBUG_ENABLED
WARN_PRINT("A Thread object has been destroyed without wait_to_finish() having been called on it. Please do so to ensure correct cleanup of the thread.");
#endif
diff --git a/core/os/thread.h b/core/os/thread.h
index 76f5be182e..599585051f 100644
--- a/core/os/thread.h
+++ b/core/os/thread.h
@@ -62,9 +62,10 @@ private:
friend class Main;
static ID main_thread_id;
- static SafeNumeric<Thread::ID> last_thread_id;
- ID id = 0;
+ static uint64_t _thread_id_hash(const std::thread::id &p_t);
+
+ ID id = _thread_id_hash(std::thread::id());
static thread_local ID caller_id;
std::thread thread;
diff --git a/core/os/threaded_array_processor.h b/core/os/threaded_array_processor.h
index 4f270001d3..fec6473589 100644
--- a/core/os/threaded_array_processor.h
+++ b/core/os/threaded_array_processor.h
@@ -95,7 +95,7 @@ void thread_process_array(uint32_t p_elements, C *p_instance, M p_method, U p_us
data.method = p_method;
data.instance = p_instance;
data.userdata = p_userdata;
- data.index = 0;
+ data.index.set(0);
data.elements = p_elements;
for (uint32_t i = 0; i < p_elements; i++) {
data.process(i);
diff --git a/core/string/translation.cpp b/core/string/translation.cpp
index 0901944360..9cee218735 100644
--- a/core/string/translation.cpp
+++ b/core/string/translation.cpp
@@ -39,13 +39,14 @@
#include "main/main.h"
#endif
-// ISO 639-1 language codes, with the addition of glibc locales with their
-// regional identifiers. This list must match the language names (in English)
-// of locale_names.
+// ISO 639-1 language codes (and a couple of three-letter ISO 639-2 codes),
+// with the addition of glibc locales with their regional identifiers.
+// This list must match the language names (in English) of locale_names.
//
// References:
// - https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes
// - https://lh.2xlibre.net/locales/
+// - https://iso639-3.sil.org/
static const char *locale_list[] = {
"aa", // Afar
@@ -100,6 +101,7 @@ static const char *locale_list[] = {
"bo", // Tibetan
"bo_CN", // Tibetan (China)
"bo_IN", // Tibetan (India)
+ "br", // Breton
"br_FR", // Breton (France)
"brx_IN", // Bodo (India)
"bs_BA", // Bosnian (Bosnia and Herzegovina)
@@ -201,6 +203,7 @@ static const char *locale_list[] = {
"gd_GB", // Scottish Gaelic (United Kingdom)
"gez_ER", // Geez (Eritrea)
"gez_ET", // Geez (Ethiopia)
+ "gl", // Galician
"gl_ES", // Galician (Spain)
"gu_IN", // Gujarati (India)
"gv_GB", // Manx (United Kingdom)
@@ -273,6 +276,7 @@ static const char *locale_list[] = {
"ml_IN", // Malayalam (India)
"mni_IN", // Manipuri (India)
"mn_MN", // Mongolian (Mongolia)
+ "mr", // Marathi
"mr_IN", // Marathi (India)
"ms", // Malay
"ms_MY", // Malay (Malaysia)
@@ -302,6 +306,7 @@ static const char *locale_list[] = {
"om", // Oromo
"om_ET", // Oromo (Ethiopia)
"om_KE", // Oromo (Kenya)
+ "or", // Oriya
"or_IN", // Oriya (India)
"os_RU", // Ossetian (Russia)
"pa_IN", // Panjabi (India)
@@ -386,6 +391,8 @@ static const char *locale_list[] = {
"tr_TR", // Turkish (Turkey)
"ts_ZA", // Tsonga (South Africa)
"tt_RU", // Tatar (Russia)
+ "tzm", // Central Atlas Tamazight
+ "tzm_MA", // Central Atlas Tamazight (Marrocos)
"ug_CN", // Uighur (China)
"uk", // Ukrainian
"uk_UA", // Ukrainian (Ukraine)
@@ -468,6 +475,7 @@ static const char *locale_names[] = {
"Tibetan",
"Tibetan (China)",
"Tibetan (India)",
+ "Breton",
"Breton (France)",
"Bodo (India)",
"Bosnian (Bosnia and Herzegovina)",
@@ -569,6 +577,7 @@ static const char *locale_names[] = {
"Scottish Gaelic (United Kingdom)",
"Geez (Eritrea)",
"Geez (Ethiopia)",
+ "Galician",
"Galician (Spain)",
"Gujarati (India)",
"Manx (United Kingdom)",
@@ -641,6 +650,7 @@ static const char *locale_names[] = {
"Malayalam (India)",
"Manipuri (India)",
"Mongolian (Mongolia)",
+ "Marathi",
"Marathi (India)",
"Malay",
"Malay (Malaysia)",
@@ -670,6 +680,7 @@ static const char *locale_names[] = {
"Oromo",
"Oromo (Ethiopia)",
"Oromo (Kenya)",
+ "Oriya",
"Oriya (India)",
"Ossetian (Russia)",
"Panjabi (India)",
@@ -754,6 +765,8 @@ static const char *locale_names[] = {
"Turkish (Turkey)",
"Tsonga (South Africa)",
"Tatar (Russia)",
+ "Central Atlas Tamazight",
+ "Central Atlas Tamazight (Marrocos)",
"Uighur (China)",
"Ukrainian",
"Ukrainian (Ukraine)",
diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp
index 59fda65d43..a57c7b2504 100644
--- a/core/string/ustring.cpp
+++ b/core/string/ustring.cpp
@@ -3888,25 +3888,55 @@ static _FORCE_INLINE_ int _xml_unescape(const char32_t *p_src, int p_src_len, ch
if (p_src_len >= 4 && p_src[1] == '#') {
char32_t c = 0;
-
- for (int i = 2; i < p_src_len; i++) {
- eat = i + 1;
- char32_t ct = p_src[i];
- if (ct == ';') {
- break;
- } else if (ct >= '0' && ct <= '9') {
- ct = ct - '0';
- } else if (ct >= 'a' && ct <= 'f') {
- ct = (ct - 'a') + 10;
- } else if (ct >= 'A' && ct <= 'F') {
- ct = (ct - 'A') + 10;
- } else {
- continue;
+ bool overflow = false;
+ if (p_src[2] == 'x') {
+ // Hex entity &#x<num>;
+ for (int i = 3; i < p_src_len; i++) {
+ eat = i + 1;
+ char32_t ct = p_src[i];
+ if (ct == ';') {
+ break;
+ } else if (ct >= '0' && ct <= '9') {
+ ct = ct - '0';
+ } else if (ct >= 'a' && ct <= 'f') {
+ ct = (ct - 'a') + 10;
+ } else if (ct >= 'A' && ct <= 'F') {
+ ct = (ct - 'A') + 10;
+ } else {
+ break;
+ }
+ if (c > (UINT32_MAX >> 4)) {
+ overflow = true;
+ break;
+ }
+ c <<= 4;
+ c |= ct;
+ }
+ } else {
+ // Decimal entity &#<num>;
+ for (int i = 2; i < p_src_len; i++) {
+ eat = i + 1;
+ char32_t ct = p_src[i];
+ if (ct == ';' || ct < '0' || ct > '9') {
+ break;
+ }
+ }
+ if (p_src[eat - 1] == ';') {
+ int64_t val = String::to_int(p_src + 2, eat - 3);
+ if (val > 0 && val <= UINT32_MAX) {
+ c = (char32_t)val;
+ } else {
+ overflow = true;
+ }
}
- c <<= 4;
- c |= ct;
}
+ // Value must be non-zero, in the range of char32_t,
+ // actually end with ';'. If invalid, leave the entity as-is
+ if (c == '\0' || overflow || p_src[eat - 1] != ';') {
+ eat = 1;
+ c = *p_src;
+ }
if (p_dst) {
*p_dst = c;
}
diff --git a/core/templates/cowdata.h b/core/templates/cowdata.h
index 525d9e77cb..c985593473 100644
--- a/core/templates/cowdata.h
+++ b/core/templates/cowdata.h
@@ -142,7 +142,7 @@ public:
_FORCE_INLINE_ bool is_empty() const { return _ptr == nullptr; }
_FORCE_INLINE_ void set(int p_index, const T &p_elem) {
- CRASH_BAD_INDEX(p_index, size());
+ ERR_FAIL_INDEX(p_index, size());
_copy_on_write();
_get_data()[p_index] = p_elem;
}
diff --git a/core/templates/safe_refcount.h b/core/templates/safe_refcount.h
index cdc9908a5f..91a34ecd54 100644
--- a/core/templates/safe_refcount.h
+++ b/core/templates/safe_refcount.h
@@ -249,7 +249,7 @@ public:
}
_ALWAYS_INLINE_ T conditional_increment() {
- if (value != 0) {
+ if (value == 0) {
return 0;
} else {
return ++value;
diff --git a/core/variant/variant_construct.cpp b/core/variant/variant_construct.cpp
index 52f9f6060e..f0c9e52b46 100644
--- a/core/variant/variant_construct.cpp
+++ b/core/variant/variant_construct.cpp
@@ -773,6 +773,7 @@ void Variant::_unregister_variant_constructors() {
}
void Variant::construct(Variant::Type p_type, Variant &base, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
+ ERR_FAIL_INDEX(p_type, Variant::VARIANT_MAX);
uint32_t s = construct_data[p_type].size();
for (uint32_t i = 0; i < s; i++) {
int argc = construct_data[p_type][i].argument_count;
diff --git a/doc/Makefile b/doc/Makefile
index 9534da9bd5..d4bc53bcf9 100644
--- a/doc/Makefile
+++ b/doc/Makefile
@@ -2,6 +2,7 @@ BASEDIR = $(CURDIR)
CLASSES = $(BASEDIR)/classes/ $(BASEDIR)/../modules/
OUTPUTDIR = $(BASEDIR)/_build
TOOLSDIR = $(BASEDIR)/tools
+JSDIR = $(BASEDIR)/../platform/javascript
.ONESHELL:
@@ -16,6 +17,10 @@ doxygen:
rst:
rm -rf $(OUTPUTDIR)/rst
mkdir -p $(OUTPUTDIR)/rst
- pushd $(OUTPUTDIR)/rst
- python3 $(TOOLSDIR)/makerst.py $(CLASSES)
- popd
+ python3 $(TOOLSDIR)/makerst.py -o $(OUTPUTDIR)/rst $(CLASSES)
+
+rstjs:
+ rm -rf $(OUTPUTDIR)/rstjs
+ mkdir -p $(OUTPUTDIR)/rstjs
+ npm --prefix $(JSDIR) ci
+ npm --prefix $(JSDIR) run docs -- --destination $(OUTPUTDIR)/rstjs/html5_shell_classref.rst
diff --git a/doc/classes/Area2D.xml b/doc/classes/Area2D.xml
index a02f077cf7..9711a2a35b 100644
--- a/doc/classes/Area2D.xml
+++ b/doc/classes/Area2D.xml
@@ -187,7 +187,7 @@
</description>
</signal>
<signal name="body_entered">
- <argument index="0" name="body" type="Node">
+ <argument index="0" name="body" type="Node2D">
</argument>
<description>
Emitted when a [PhysicsBody2D] or [TileMap] enters this Area2D. Requires [member monitoring] to be set to [code]true[/code]. [TileMap]s are detected if the [TileSet] has Collision [Shape2D]s.
@@ -195,7 +195,7 @@
</description>
</signal>
<signal name="body_exited">
- <argument index="0" name="body" type="Node">
+ <argument index="0" name="body" type="Node2D">
</argument>
<description>
Emitted when a [PhysicsBody2D] or [TileMap] exits this Area2D. Requires [member monitoring] to be set to [code]true[/code]. [TileMap]s are detected if the [TileSet] has Collision [Shape2D]s.
@@ -205,7 +205,7 @@
<signal name="body_shape_entered">
<argument index="0" name="body_id" type="int">
</argument>
- <argument index="1" name="body" type="Node">
+ <argument index="1" name="body" type="Node2D">
</argument>
<argument index="2" name="body_shape" type="int">
</argument>
@@ -222,7 +222,7 @@
<signal name="body_shape_exited">
<argument index="0" name="body_id" type="int">
</argument>
- <argument index="1" name="body" type="Node">
+ <argument index="1" name="body" type="Node2D">
</argument>
<argument index="2" name="body_shape" type="int">
</argument>
diff --git a/doc/classes/Area3D.xml b/doc/classes/Area3D.xml
index bd43d619dd..4271769155 100644
--- a/doc/classes/Area3D.xml
+++ b/doc/classes/Area3D.xml
@@ -197,7 +197,7 @@
</description>
</signal>
<signal name="body_entered">
- <argument index="0" name="body" type="Node">
+ <argument index="0" name="body" type="Node3D">
</argument>
<description>
Emitted when a [PhysicsBody3D] or [GridMap] enters this Area3D. Requires [member monitoring] to be set to [code]true[/code]. [GridMap]s are detected if the [MeshLibrary] has Collision [Shape3D]s.
@@ -205,7 +205,7 @@
</description>
</signal>
<signal name="body_exited">
- <argument index="0" name="body" type="Node">
+ <argument index="0" name="body" type="Node3D">
</argument>
<description>
Emitted when a [PhysicsBody3D] or [GridMap] exits this Area3D. Requires [member monitoring] to be set to [code]true[/code]. [GridMap]s are detected if the [MeshLibrary] has Collision [Shape3D]s.
@@ -215,7 +215,7 @@
<signal name="body_shape_entered">
<argument index="0" name="body_id" type="int">
</argument>
- <argument index="1" name="body" type="Node">
+ <argument index="1" name="body" type="Node3D">
</argument>
<argument index="2" name="body_shape" type="int">
</argument>
@@ -232,7 +232,7 @@
<signal name="body_shape_exited">
<argument index="0" name="body_id" type="int">
</argument>
- <argument index="1" name="body" type="Node">
+ <argument index="1" name="body" type="Node3D">
</argument>
<argument index="2" name="body_shape" type="int">
</argument>
diff --git a/doc/classes/Array.xml b/doc/classes/Array.xml
index cea5360234..8fb688a8ae 100644
--- a/doc/classes/Array.xml
+++ b/doc/classes/Array.xml
@@ -197,7 +197,7 @@
</argument>
<description>
Finds the index of an existing value (or the insertion index that maintains sorting order, if the value is not yet present in the array) using binary search and a custom comparison method. Optionally, a [code]before[/code] specifier can be passed. If [code]false[/code], the returned index comes after all existing entries of the value in the array. The custom method receives two arguments (an element from the array and the value searched for) and must return [code]true[/code] if the first argument is less than the second, and return [code]false[/code] otherwise.
- [b]Note:[/b] Calling [method bsearch] on an unsorted array results in unexpected behavior.
+ [b]Note:[/b] Calling [method bsearch_custom] on an unsorted array results in unexpected behavior.
</description>
</method>
<method name="clear">
diff --git a/doc/classes/Callable.xml b/doc/classes/Callable.xml
index f137ede90f..b69768d33f 100644
--- a/doc/classes/Callable.xml
+++ b/doc/classes/Callable.xml
@@ -8,26 +8,26 @@
[b]Example:[/b]
[codeblocks]
[gdscript]
- var callable = Callable(self, "print_args")
func print_args(arg1, arg2, arg3 = ""):
prints(arg1, arg2, arg3)
func test():
- callable.call("hello", "world") # Prints "hello world".
+ var callable = Callable(self, "print_args")
+ callable.call("hello", "world") # Prints "hello world ".
callable.call(Vector2.UP, 42, callable) # Prints "(0, -1) 42 Node(Node.gd)::print_args".
callable.call("invalid") # Invalid call, should have at least 2 arguments.
[/gdscript]
[csharp]
- Callable callable = new Callable(this, "print_args");
- public void PrintArgs(object arg1, object arg2, object arg3 = "")
+ public void PrintArgs(object arg1, object arg2, object arg3 = null)
{
GD.PrintS(arg1, arg2, arg3);
}
public void Test()
{
- callable.Call("hello", "world"); // Prints "hello world".
- callable.Call(Vector2.Up, 42, callable); // Prints "(0, -1) 42 Node(Node.gd)::print_args".
+ Callable callable = new Callable(this, nameof(PrintArgs));
+ callable.Call("hello", "world"); // Prints "hello world null".
+ callable.Call(Vector2.Up, 42, callable); // Prints "(0, -1) 42 Node(Node.cs)::PrintArgs".
callable.Call("invalid"); // Invalid call, should have at least 2 arguments.
}
[/csharp]
@@ -67,6 +67,7 @@
<return type="Callable">
</return>
<description>
+ Returns a copy of this [Callable] with the arguments bound. Bound arguments are passed after the arguments supplied by [method call].
</description>
</method>
<method name="call" qualifiers="vararg">
@@ -108,24 +109,28 @@
<return type="int">
</return>
<description>
+ Returns the hash value of this [Callable]'s object.
</description>
</method>
<method name="is_custom">
<return type="bool">
</return>
<description>
+ Returns [code]true[/code] if this [Callable] is a custom callable whose behavior differs based on implementation details. Custom callables are used in the engine for various reasons. If [code]true[/code], you can't use [method get_method].
</description>
</method>
<method name="is_null">
<return type="bool">
</return>
<description>
+ Returns [code]true[/code] if this [Callable] has no target to call the method on.
</description>
</method>
<method name="is_standard">
<return type="bool">
</return>
<description>
+ Returns [code]true[/code] if this [Callable] is a standard callable, referencing an object and a method using a [StringName].
</description>
</method>
<method name="operator !=" qualifiers="operator">
@@ -134,6 +139,7 @@
<argument index="0" name="right" type="Callable">
</argument>
<description>
+ Returns [code]true[/code] if both [Callable]s invoke different targets.
</description>
</method>
<method name="operator ==" qualifiers="operator">
@@ -142,6 +148,7 @@
<argument index="0" name="right" type="Callable">
</argument>
<description>
+ Returns [code]true[/code] if both [Callable]s invoke the same custom target.
</description>
</method>
<method name="unbind">
@@ -150,6 +157,7 @@
<argument index="0" name="argcount" type="int">
</argument>
<description>
+ Returns a copy of this [Callable] with the arguments unbound. Calling the returned [Callable] will call the method without the extra arguments that are supplied in the [Callable] on which you are calling this method.
</description>
</method>
</methods>
diff --git a/doc/classes/Control.xml b/doc/classes/Control.xml
index e5285587eb..c5e820e9fe 100644
--- a/doc/classes/Control.xml
+++ b/doc/classes/Control.xml
@@ -869,7 +869,7 @@
<argument index="0" name="control" type="Control">
</argument>
<description>
- Shows the given control at the mouse pointer. A good time to call this method is in [method get_drag_data]. The control must not be in the scene tree.
+ Shows the given control at the mouse pointer. A good time to call this method is in [method get_drag_data]. The control must not be in the scene tree. You should not free the control, and you should not keep a reference to the control beyond the duration of the drag. It will be deleted automatically after the drag has ended.
[codeblocks]
[gdscript]
export (Color, RGBA) var color = Color(1, 0, 0, 1)
diff --git a/doc/classes/Curve2D.xml b/doc/classes/Curve2D.xml
index 2d50d98a74..b33f3b4ffc 100644
--- a/doc/classes/Curve2D.xml
+++ b/doc/classes/Curve2D.xml
@@ -63,7 +63,7 @@
<argument index="0" name="to_point" type="Vector2">
</argument>
<description>
- Returns the closest point (in curve's local space) to [code]to_point[/code].
+ Returns the closest baked point (in curve's local space) to [code]to_point[/code].
[code]to_point[/code] must be in this curve's local space.
</description>
</method>
diff --git a/doc/classes/Curve3D.xml b/doc/classes/Curve3D.xml
index bda04f010b..fcd150ad57 100644
--- a/doc/classes/Curve3D.xml
+++ b/doc/classes/Curve3D.xml
@@ -78,7 +78,7 @@
<argument index="0" name="to_point" type="Vector3">
</argument>
<description>
- Returns the closest point (in curve's local space) to [code]to_point[/code].
+ Returns the closest baked point (in curve's local space) to [code]to_point[/code].
[code]to_point[/code] must be in this curve's local space.
</description>
</method>
diff --git a/doc/classes/FontData.xml b/doc/classes/FontData.xml
index 7e99510124..6c54af05cd 100644
--- a/doc/classes/FontData.xml
+++ b/doc/classes/FontData.xml
@@ -11,6 +11,45 @@
<tutorials>
</tutorials>
<methods>
+ <method name="bitmap_add_char">
+ <return type="void">
+ </return>
+ <argument index="0" name="char" type="int">
+ </argument>
+ <argument index="1" name="texture_idx" type="int">
+ </argument>
+ <argument index="2" name="rect" type="Rect2">
+ </argument>
+ <argument index="3" name="align" type="Vector2">
+ </argument>
+ <argument index="4" name="advance" type="float">
+ </argument>
+ <description>
+ Adds a character to the font, where [code]character[/code] is the Unicode value, [code]texture[/code] is the texture index, [code]rect[/code] is the region in the texture (in pixels!), [code]align[/code] is the (optional) alignment for the character and [code]advance[/code] is the (optional) advance.
+ </description>
+ </method>
+ <method name="bitmap_add_kerning_pair">
+ <return type="void">
+ </return>
+ <argument index="0" name="A" type="int">
+ </argument>
+ <argument index="1" name="B" type="int">
+ </argument>
+ <argument index="2" name="kerning" type="int">
+ </argument>
+ <description>
+ Adds a kerning pair to the bitmap font as a difference. Kerning pairs are special cases where a typeface advance is determined by the next character.
+ </description>
+ </method>
+ <method name="bitmap_add_texture">
+ <return type="void">
+ </return>
+ <argument index="0" name="texture" type="Texture">
+ </argument>
+ <description>
+ Adds a texture to the bitmap font.
+ </description>
+ </method>
<method name="draw_glyph" qualifiers="const">
<return type="Vector2">
</return>
@@ -265,6 +304,19 @@
Note: For non-scalable fonts [code]base_size[/code] is ignored, use [method get_base_size] to check actual font size.
</description>
</method>
+ <method name="new_bitmap">
+ <return type="void">
+ </return>
+ <argument index="0" name="height" type="float">
+ </argument>
+ <argument index="1" name="ascent" type="float">
+ </argument>
+ <argument index="2" name="base_size" type="int">
+ </argument>
+ <description>
+ Creates new, empty bitmap font.
+ </description>
+ </method>
<method name="remove_language_support_override">
<return type="void">
</return>
diff --git a/doc/classes/ItemList.xml b/doc/classes/ItemList.xml
index ffeb45907d..0020cbf242 100644
--- a/doc/classes/ItemList.xml
+++ b/doc/classes/ItemList.xml
@@ -302,16 +302,7 @@
<argument index="1" name="custom_bg_color" type="Color">
</argument>
<description>
- [codeblocks]
- [gdscript]
- var itemList = ItemList.new()
- some_string.set_item_custom_bg_color(0, Color.red) # This will set the background color of the first item of the control to red.
- [/gdscript]
- [csharp]
- var itemList = new ItemList();
- itemList.SetItemCustomBgColor(0, Colors.Red); // This will set the background color of the first item of the control to red.
- [/csharp]
- [/codeblocks]
+ Sets the background color of the item specified by [code]idx[/code] index to the specified [Color].
</description>
</method>
<method name="set_item_custom_fg_color">
@@ -323,16 +314,6 @@
</argument>
<description>
Sets the foreground color of the item specified by [code]idx[/code] index to the specified [Color].
- [codeblocks]
- [gdscript]
- var item_list = ItemList.new()
- item_list.set_item_custom_fg_color(0, Color.red) # This will set the foreground color of the first item of the control to red.
- [/gdscript]
- [csharp]
- var itemList = new ItemList();
- itemList.SetItemCustomFgColor(0, Colors.Red); // This will set the foreground color of the first item of the control to red.
- [/csharp]
- [/codeblocks]
</description>
</method>
<method name="set_item_disabled">
diff --git a/doc/classes/MarginContainer.xml b/doc/classes/MarginContainer.xml
index c8eebd4677..a51632d5f1 100644
--- a/doc/classes/MarginContainer.xml
+++ b/doc/classes/MarginContainer.xml
@@ -5,21 +5,23 @@
</brief_description>
<description>
Adds a top, left, bottom, and right margin to all [Control] nodes that are direct children of the container. To control the [MarginContainer]'s margin, use the [code]margin_*[/code] theme properties listed below.
- [b]Note:[/b] Be careful, [Control] margin values are different than the constant margin values. If you want to change the custom margin values of the [MarginContainer] by code, you should use the following examples:
+ [b]Note:[/b] Be careful, [Control] margin values are different from the constant margin values. If you want to change the custom margin values of the [MarginContainer] by code, you should use the following examples:
[codeblocks]
[gdscript]
+ # This code sample assumes the current script is extending MarginContainer.
var margin_value = 100
- set("custom_constants/margin_top", margin_value)
- set("custom_constants/margin_left", margin_value)
- set("custom_constants/margin_bottom", margin_value)
- set("custom_constants/margin_right", margin_value)
+ add_theme_constant_override("margin_top", margin_value)
+ add_theme_constant_override("margin_left", margin_value)
+ add_theme_constant_override("margin_bottom", margin_value)
+ add_theme_constant_override("margin_right", margin_value)
[/gdscript]
[csharp]
+ // This code sample assumes the current script is extending MarginContainer.
int marginValue = 100;
- Set("custom_constants/margin_top", marginValue);
- Set("custom_constants/margin_left", marginValue);
- Set("custom_constants/margin_bottom", marginValue);
- Set("custom_constants/margin_right", marginValue);
+ AddThemeConstantOverride("margin_top", marginValue);
+ AddThemeConstantOverride("margin_left", marginValue);
+ AddThemeConstantOverride("margin_bottom", marginValue);
+ AddThemeConstantOverride("margin_right", marginValue);
[/csharp]
[/codeblocks]
</description>
diff --git a/doc/classes/OS.xml b/doc/classes/OS.xml
index f6602d5f4d..057a2b8d1a 100644
--- a/doc/classes/OS.xml
+++ b/doc/classes/OS.xml
@@ -207,10 +207,11 @@
<method name="get_environment" qualifiers="const">
<return type="String">
</return>
- <argument index="0" name="environment" type="String">
+ <argument index="0" name="variable" type="String">
</argument>
<description>
- Returns an environment variable.
+ Returns the value of an environment variable. Returns an empty string if the environment variable doesn't exist.
+ [b]Note:[/b] Double-check the casing of [code]variable[/code]. Environment variable names are case-sensitive on all platforms except Windows.
</description>
</method>
<method name="get_executable_path" qualifiers="const">
@@ -383,10 +384,11 @@
<method name="has_environment" qualifiers="const">
<return type="bool">
</return>
- <argument index="0" name="environment" type="String">
+ <argument index="0" name="variable" type="String">
</argument>
<description>
- Returns [code]true[/code] if an environment variable exists.
+ Returns [code]true[/code] if the environment variable with the name [code]variable[/code] exists.
+ [b]Note:[/b] Double-check the casing of [code]variable[/code]. Environment variable names are case-sensitive on all platforms except Windows.
</description>
</method>
<method name="has_feature" qualifiers="const">
@@ -501,6 +503,18 @@
[b]Note:[/b] This method is implemented on Android.
</description>
</method>
+ <method name="set_environment" qualifiers="const">
+ <return type="bool">
+ </return>
+ <argument index="0" name="variable" type="String">
+ </argument>
+ <argument index="1" name="value" type="String">
+ </argument>
+ <description>
+ Sets the value of the environment variable [code]variable[/code] to [code]value[/code]. The environment variable will be set for the Godot process and any process executed with [method execute] after running [method set_environment]. The environment variable will [i]not[/i] persist to processes run after the Godot process was terminated.
+ [b]Note:[/b] Double-check the casing of [code]variable[/code]. Environment variable names are case-sensitive on all platforms except Windows.
+ </description>
+ </method>
<method name="set_thread_name">
<return type="int" enum="Error">
</return>
@@ -535,10 +549,6 @@
</method>
</methods>
<members>
- <member name="exit_code" type="int" setter="set_exit_code" getter="get_exit_code" default="0">
- The exit code passed to the OS when the main loop exits. By convention, an exit code of [code]0[/code] indicates success whereas a non-zero exit code indicates an error. For portability reasons, the exit code should be set between 0 and 125 (inclusive).
- [b]Note:[/b] This value will be ignored if using [method SceneTree.quit] with an [code]exit_code[/code] argument passed.
- </member>
<member name="low_processor_usage_mode" type="bool" setter="set_low_processor_usage_mode" getter="is_in_low_processor_usage_mode" default="false">
If [code]true[/code], the engine optimizes for low processor usage by only refreshing the screen if needed. Can improve battery consumption on mobile.
</member>
diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml
index b056eff658..a1e5e60b4a 100644
--- a/doc/classes/ProjectSettings.xml
+++ b/doc/classes/ProjectSettings.xml
@@ -273,9 +273,11 @@
If [code]true[/code], flushes the standard output stream every time a line is printed. This affects both terminal logging and file logging.
When running a project, this setting must be enabled if you want logs to be collected by service managers such as systemd/journalctl. This setting is disabled by default on release builds, since flushing on every printed line will negatively affect performance if lots of lines are printed in a rapid succession. Also, if this setting is enabled, logged files will still be written successfully if the application crashes or is otherwise killed by the user (without being closed "normally").
[b]Note:[/b] Regardless of this setting, the standard error stream ([code]stderr[/code]) is always flushed when a line is printed to it.
+ Changes to this setting will only be applied upon restarting the application.
</member>
<member name="application/run/flush_stdout_on_print.debug" type="bool" setter="" getter="" default="true">
Debug build override for [member application/run/flush_stdout_on_print], as performance is less important during debugging.
+ Changes to this setting will only be applied upon restarting the application.
</member>
<member name="application/run/frame_delay_msec" type="int" setter="" getter="" default="0">
Forces a delay between frames in the main loop (in milliseconds). This may be useful if you plan to disable vertical synchronization.
@@ -460,6 +462,9 @@
<member name="debug/shapes/collision/contact_color" type="Color" setter="" getter="" default="Color( 1, 0.2, 0.1, 0.8 )">
Color of the contact points between collision shapes, visible when "Visible Collision Shapes" is enabled in the Debug menu.
</member>
+ <member name="debug/shapes/collision/draw_2d_outlines" type="bool" setter="" getter="" default="true">
+ Sets whether 2D physics will display collision outlines in game when "Visible Collision Shapes" is enabled in the Debug menu.
+ </member>
<member name="debug/shapes/collision/max_contacts_displayed" type="int" setter="" getter="" default="10000">
Maximum number of contact points between collision shapes to display when "Visible Collision Shapes" is enabled in the Debug menu.
</member>
diff --git a/doc/classes/RayCast3D.xml b/doc/classes/RayCast3D.xml
index d24e86a08b..681cfcd4ce 100644
--- a/doc/classes/RayCast3D.xml
+++ b/doc/classes/RayCast3D.xml
@@ -136,6 +136,13 @@
<member name="collision_mask" type="int" setter="set_collision_mask" getter="get_collision_mask" default="1">
The ray's collision mask. Only objects in at least one collision layer enabled in the mask will be detected. See [url=https://docs.godotengine.org/en/latest/tutorials/physics/physics_introduction.html#collision-layers-and-masks]Collision layers and masks[/url] in the documentation for more information.
</member>
+ <member name="debug_shape_custom_color" type="Color" setter="set_debug_shape__custom_color" getter="get_debug_shape_custom_color" default="Color( 0.0, 0.0, 0.0 )">
+ The custom color to use to draw the shape in the editor and at run-time if [b]Visible Collision Shapes[/b] is enabled in the [b]Debug[/b] menu. This color will be highlighted at run-time if the [RayCast3D] is colliding with something.
+ If set to [code]Color(0.0, 0.0, 0.0)[/code] (by default), the color set in [member ProjectSettings.debug/shapes/collision/shape_color] is used.
+ </member>
+ <member name="debug_shape_thickness" type="int" setter="set_debug_shape_thickness" getter="get_debug_shape_thickness" default="1">
+ If set to [code]1[/code], a line is used as the debug shape. Otherwise, a truncated pyramid is drawn to represent the [RayCast3D]. Requires [b]Visible Collision Shapes[/b] to be enabled in the [b]Debug[/b] menu for the debug shape to be visible at run-time.
+ </member>
<member name="enabled" type="bool" setter="set_enabled" getter="is_enabled" default="true">
If [code]true[/code], collisions will be reported.
</member>
diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml
index dfd4a5c2d5..d2b95fda20 100644
--- a/doc/classes/RenderingServer.xml
+++ b/doc/classes/RenderingServer.xml
@@ -2704,11 +2704,14 @@
<description>
Copies the viewport to a region of the screen specified by [code]rect[/code]. If [method viewport_set_render_direct_to_screen] is [code]true[/code], then the viewport does not use a framebuffer and the contents of the viewport are rendered directly to screen. However, note that the root viewport is drawn last, therefore it will draw over the screen. Accordingly, you must set the root viewport to an area that does not cover the area that you have attached this viewport to.
For example, you can set the root viewport to not render at all with the following code:
- [codeblock]
+ FIXME: The method seems to be non-existent.
+ [codeblocks]
+ [gdscript]
func _ready():
get_viewport().set_attach_to_screen_rect(Rect2())
$Viewport.set_attach_to_screen_rect(Rect2(0, 0, 600, 600))
- [/codeblock]
+ [/gdscript]
+ [/codeblocks]
Using this can result in significant optimization, especially on lower-end devices. However, it comes at the cost of having to manage your viewports manually. For a further optimization see, [method viewport_set_render_direct_to_screen].
</description>
</method>
diff --git a/doc/classes/RichTextEffect.xml b/doc/classes/RichTextEffect.xml
index 726b26fbc7..edab35f162 100644
--- a/doc/classes/RichTextEffect.xml
+++ b/doc/classes/RichTextEffect.xml
@@ -6,10 +6,16 @@
<description>
A custom effect for use with [RichTextLabel].
[b]Note:[/b] For a [RichTextEffect] to be usable, a BBCode tag must be defined as a member variable called [code]bbcode[/code] in the script.
- [codeblock]
+ [codeblocks]
+ [gdscript]
# The RichTextEffect will be usable like this: `[example]Some text[/example]`
var bbcode = "example"
- [/codeblock]
+ [/gdscript]
+ [csharp]
+ // The RichTextEffect will be usable like this: `[example]Some text[/example]`
+ public string bbcode = "example";
+ [/csharp]
+ [/codeblocks]
[b]Note:[/b] As soon as a [RichTextLabel] contains at least one [RichTextEffect], it will continuously process the effect unless the project is paused. This may impact battery life negatively.
</description>
<tutorials>
diff --git a/doc/classes/SceneTree.xml b/doc/classes/SceneTree.xml
index f65d013bfc..72a42202c7 100644
--- a/doc/classes/SceneTree.xml
+++ b/doc/classes/SceneTree.xml
@@ -69,12 +69,22 @@
<description>
Returns a [SceneTreeTimer] which will [signal SceneTreeTimer.timeout] after the given time in seconds elapsed in this [SceneTree]. If [code]process_always[/code] is set to [code]false[/code], pausing the [SceneTree] will also pause the timer.
Commonly used to create a one-shot delay timer as in the following example:
- [codeblock]
+ [codeblocks]
+ [gdscript]
func some_function():
print("start")
yield(get_tree().create_timer(1.0), "timeout")
print("end")
- [/codeblock]
+ [/gdscript]
+ [csharp]
+ public async void SomeFunction()
+ {
+ GD.Print("start");
+ await ToSignal(GetTree().CreateTimer(1.0f), "timeout");
+ GD.Print("end");
+ }
+ [/csharp]
+ [/codeblocks]
The timer will be automatically freed after its time elapses.
</description>
</method>
@@ -181,10 +191,12 @@
<method name="quit">
<return type="void">
</return>
- <argument index="0" name="exit_code" type="int" default="-1">
+ <argument index="0" name="exit_code" type="int" default="0">
</argument>
<description>
- Quits the application at the end of the current iteration. A process [code]exit_code[/code] can optionally be passed as an argument. If this argument is [code]0[/code] or greater, it will override the [member OS.exit_code] defined before quitting the application.
+ Quits the application at the end of the current iteration. Argument [code]exit_code[/code] can optionally be given (defaulting to 0) to customize the exit status code.
+ By convention, an exit code of [code]0[/code] indicates success whereas a non-zero exit code indicates an error.
+ For portability reasons, the exit code should be set between 0 and 125 (inclusive).
</description>
</method>
<method name="reload_current_scene">
diff --git a/doc/classes/SceneTreeTimer.xml b/doc/classes/SceneTreeTimer.xml
index a4a83fa65b..b223bf6821 100644
--- a/doc/classes/SceneTreeTimer.xml
+++ b/doc/classes/SceneTreeTimer.xml
@@ -6,12 +6,22 @@
<description>
A one-shot timer managed by the scene tree, which emits [signal timeout] on completion. See also [method SceneTree.create_timer].
As opposed to [Timer], it does not require the instantiation of a node. Commonly used to create a one-shot delay timer as in the following example:
- [codeblock]
+ [codeblocks]
+ [gdscript]
func some_function():
print("Timer started.")
yield(get_tree().create_timer(1.0), "timeout")
print("Timer ended.")
- [/codeblock]
+ [/gdscript]
+ [csharp]
+ public async void SomeFunction()
+ {
+ GD.Print("Timer started.");
+ await ToSignal(GetTree().CreateTimer(1.0f), "timeout");
+ GD.Print("Timer ended.");
+ }
+ [/csharp]
+ [/codeblocks]
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/ScriptCreateDialog.xml b/doc/classes/ScriptCreateDialog.xml
index aa60ecb12b..7185213c44 100644
--- a/doc/classes/ScriptCreateDialog.xml
+++ b/doc/classes/ScriptCreateDialog.xml
@@ -5,12 +5,24 @@
</brief_description>
<description>
The [ScriptCreateDialog] creates script files according to a given template for a given scripting language. The standard use is to configure its fields prior to calling one of the [method Window.popup] methods.
- [codeblock]
+ [codeblocks]
+ [gdscript]
func _ready():
- dialog.config("Node", "res://new_node.gd") # For in-engine types
- dialog.config("\"res://base_node.gd\"", "res://derived_node.gd") # For script types
+ var dialog = ScriptCreateDialog.new();
+ dialog.config("Node", "res://new_node.gd") # For in-engine types.
+ dialog.config("\"res://base_node.gd\"", "res://derived_node.gd") # For script types.
dialog.popup_centered()
- [/codeblock]
+ [/gdscript]
+ [csharp]
+ public override void _Ready()
+ {
+ var dialog = new ScriptCreateDialog();
+ dialog.Config("Node", "res://NewNode.cs"); // For in-engine types.
+ dialog.Config("\"res://BaseNode.cs\"", "res://DerivedNode.cs"); // For script types.
+ dialog.PopupCentered();
+ }
+ [/csharp]
+ [/codeblocks]
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/SpinBox.xml b/doc/classes/SpinBox.xml
index a08d4f5b44..7e2481f458 100644
--- a/doc/classes/SpinBox.xml
+++ b/doc/classes/SpinBox.xml
@@ -6,13 +6,22 @@
<description>
SpinBox is a numerical input text field. It allows entering integers and floats.
[b]Example:[/b]
- [codeblock]
+ [codeblocks]
+ [gdscript]
var spin_box = SpinBox.new()
add_child(spin_box)
var line_edit = spin_box.get_line_edit()
line_edit.context_menu_enabled = false
spin_box.align = LineEdit.ALIGN_RIGHT
- [/codeblock]
+ [/gdscript]
+ [csharp]
+ var spinBox = new SpinBox();
+ AddChild(spinBox);
+ var lineEdit = spinBox.GetLineEdit();
+ lineEdit.ContextMenuEnabled = false;
+ spinBox.Align = LineEdit.AlignEnum.Right;
+ [/csharp]
+ [/codeblocks]
The above code will create a [SpinBox], disable context menu on it and set the text alignment to right.
See [Range] class for more options over the [SpinBox].
[b]Note:[/b] [SpinBox] relies on an underlying [LineEdit] node. To theme a [SpinBox]'s background, add theme items for [LineEdit] and customize them.
diff --git a/doc/classes/Sprite2D.xml b/doc/classes/Sprite2D.xml
index c56596423d..e4df753674 100644
--- a/doc/classes/Sprite2D.xml
+++ b/doc/classes/Sprite2D.xml
@@ -15,12 +15,29 @@
</return>
<description>
Returns a [Rect2] representing the Sprite2D's boundary in local coordinates. Can be used to detect if the Sprite2D was clicked. Example:
- [codeblock]
+ [codeblocks]
+ [gdscript]
func _input(event):
if event is InputEventMouseButton and event.pressed and event.button_index == BUTTON_LEFT:
if get_rect().has_point(to_local(event.position)):
print("A click!")
- [/codeblock]
+ [/gdscript]
+ [csharp]
+ public override void _Input(InputEvent inputEvent)
+ {
+ if (inputEvent is InputEventMouseButton inputEventMouse)
+ {
+ if (inputEventMouse.Pressed &amp;&amp; inputEventMouse.ButtonIndex == (int)ButtonList.Left)
+ {
+ if (GetRect().HasPoint(ToLocal(inputEventMouse.Position)))
+ {
+ GD.Print("A click!");
+ }
+ }
+ }
+ }
+ [/csharp]
+ [/codeblocks]
</description>
</method>
<method name="is_pixel_opaque" qualifiers="const">
@@ -60,7 +77,7 @@
If [code]true[/code], texture is cut from a larger atlas texture. See [member region_rect].
</member>
<member name="region_filter_clip" type="bool" setter="set_region_filter_clip" getter="is_region_filter_clip_enabled" default="false">
- If [code]true[/code], the outermost pixels get blurred out.
+ If [code]true[/code], the outermost pixels get blurred out. [member region_enabled] must be [code]true[/code].
</member>
<member name="region_rect" type="Rect2" setter="set_region_rect" getter="get_region_rect" default="Rect2( 0, 0, 0, 0 )">
The region of the atlas texture to display. [member region_enabled] must be [code]true[/code].
diff --git a/doc/classes/StreamPeer.xml b/doc/classes/StreamPeer.xml
index a73d3c8212..a1b858acf6 100644
--- a/doc/classes/StreamPeer.xml
+++ b/doc/classes/StreamPeer.xml
@@ -212,9 +212,14 @@
<description>
Puts a zero-terminated ASCII string into the stream prepended by a 32-bit unsigned integer representing its size.
Note: To put an ASCII string without prepending its size, you can use [method put_data]:
- [codeblock]
+ [codeblocks]
+ [gdscript]
put_data("Hello world".to_ascii())
- [/codeblock]
+ [/gdscript]
+ [csharp]
+ PutData("Hello World".ToAscii());
+ [/csharp]
+ [/codeblocks]
</description>
</method>
<method name="put_u16">
@@ -261,9 +266,14 @@
<description>
Puts a zero-terminated UTF-8 string into the stream prepended by a 32 bits unsigned integer representing its size.
Note: To put an UTF-8 string without prepending its size, you can use [method put_data]:
- [codeblock]
+ [codeblocks]
+ [gdscript]
put_data("Hello world".to_utf8())
- [/codeblock]
+ [/gdscript]
+ [csharp]
+ PutData("Hello World".ToUTF8());
+ [/csharp]
+ [/codeblocks]
</description>
</method>
<method name="put_var">
diff --git a/doc/classes/String.xml b/doc/classes/String.xml
index c03f6357ab..a90bcd9eb7 100644
--- a/doc/classes/String.xml
+++ b/doc/classes/String.xml
@@ -163,11 +163,15 @@
<description>
Returns the index of the [b]first[/b] case-sensitive occurrence of the specified string in this instance, or [code]-1[/code]. Optionally, the starting search index can be specified, continuing to the end of the string.
[b]Note:[/b] If you just want to know whether a string contains a substring, use the [code]in[/code] operator as follows:
- [codeblock]
- # Will evaluate to `false`.
- if "i" in "team":
- pass
- [/codeblock]
+ [codeblocks]
+ [gdscript]
+ print("i" in "team") # Will print `false`.
+ [/gdscript]
+ [csharp]
+ // C# has no in operator, but we can use `Contains()`.
+ GD.Print("team".Contains("i")); // Will print `false`.
+ [/csharp]
+ [/codeblocks]
</description>
</method>
<method name="findn">
@@ -354,9 +358,14 @@
<description>
Return a [String] which is the concatenation of the [code]parts[/code]. The separator between elements is the string providing this method.
Example:
- [codeblock]
+ [codeblocks]
+ [gdscript]
print(", ".join(["One", "Two", "Three", "Four"]))
- [/codeblock]
+ [/gdscript]
+ [csharp]
+ GD.Print(String.Join(",", new string[] {"One", "Two", "Three", "Four"}));
+ [/csharp]
+ [/codeblocks]
</description>
</method>
<method name="json_escape">
@@ -654,13 +663,18 @@
The splits in the returned array are sorted in the same order as the original string, from left to right.
If [code]maxsplit[/code] is specified, it defines the number of splits to do from the right up to [code]maxsplit[/code]. The default value of 0 means that all items are split, thus giving the same result as [method split].
Example:
- [codeblock]
+ [codeblocks]
+ [gdscript]
var some_string = "One,Two,Three,Four"
var some_array = some_string.rsplit(",", true, 1)
print(some_array.size()) # Prints 2
print(some_array[0]) # Prints "Four"
print(some_array[1]) # Prints "Three,Two,One"
- [/codeblock]
+ [/gdscript]
+ [csharp]
+ // There is no Rsplit.
+ [/csharp]
+ [/codeblocks]
</description>
</method>
<method name="rstrip">
@@ -723,13 +737,21 @@
Splits the string by a [code]delimiter[/code] string and returns an array of the substrings. The [code]delimiter[/code] can be of any length.
If [code]maxsplit[/code] is specified, it defines the number of splits to do from the left up to [code]maxsplit[/code]. The default value of [code]0[/code] means that all items are split.
Example:
- [codeblock]
+ [codeblocks]
+ [gdscript]
var some_string = "One,Two,Three,Four"
var some_array = some_string.split(",", true, 1)
print(some_array.size()) # Prints 2
- print(some_array[0]) # Prints "One"
- print(some_array[1]) # Prints "Two,Three,Four"
- [/codeblock]
+ print(some_array[0]) # Prints "Four"
+ print(some_array[1]) # Prints "Three,Two,One"
+ [/gdscript]
+ [csharp]
+ var someString = "One,Two,Three,Four";
+ var someArray = someString.Split(",", true); // This is as close as it gets to Godots API.
+ GD.Print(someArray[0]); // Prints "Four"
+ GD.Print(someArray[1]); // Prints "Three,Two,One"
+ [/csharp]
+ [/codeblocks]
If you need to split strings with more complex rules, use the [RegEx] class instead.
</description>
</method>
diff --git a/doc/classes/StyleBoxFlat.xml b/doc/classes/StyleBoxFlat.xml
index 13ea7df294..d66ae210ec 100644
--- a/doc/classes/StyleBoxFlat.xml
+++ b/doc/classes/StyleBoxFlat.xml
@@ -4,12 +4,12 @@
Customizable [StyleBox] with a given set of parameters (no texture required).
</brief_description>
<description>
- This [StyleBox] can be used to achieve all kinds of looks without the need of a texture. Those properties are customizable:
+ This [StyleBox] can be used to achieve all kinds of looks without the need of a texture. The following properties are customizable:
- Color
- Border width (individual width for each border)
- Rounded corners (individual radius for each corner)
- Shadow (with blur and offset)
- Setting corner radius to high values is allowed. As soon as corners would overlap, the stylebox will switch to a relative system. Example:
+ Setting corner radius to high values is allowed. As soon as corners overlap, the stylebox will switch to a relative system. Example:
[codeblock]
height = 30
corner_radius_top_left = 50
@@ -178,8 +178,8 @@
Border width for the top border.
</member>
<member name="corner_detail" type="int" setter="set_corner_detail" getter="get_corner_detail" default="8">
- This sets the amount of vertices used for each corner. Higher values result in rounder corners but take more processing power to compute. When choosing a value, you should take the corner radius ([method set_corner_radius_all]) into account.
- For corner radii smaller than 10, [code]4[/code] or [code]5[/code] should be enough. For corner radii smaller than 30, values between [code]8[/code] and [code]12[/code] should be enough.
+ This sets the number of vertices used for each corner. Higher values result in rounder corners but take more processing power to compute. When choosing a value, you should take the corner radius ([method set_corner_radius_all]) into account.
+ For corner radii less than 10, [code]4[/code] or [code]5[/code] should be enough. For corner radii less than 30, values between [code]8[/code] and [code]12[/code] should be enough.
A corner detail of [code]1[/code] will result in chamfered corners instead of rounded corners, which is useful for some artistic effects.
</member>
<member name="corner_radius_bottom_left" type="int" setter="set_corner_radius" getter="get_corner_radius" default="0">
diff --git a/doc/classes/SurfaceTool.xml b/doc/classes/SurfaceTool.xml
index 331de4284e..d145b4ce97 100644
--- a/doc/classes/SurfaceTool.xml
+++ b/doc/classes/SurfaceTool.xml
@@ -5,13 +5,22 @@
</brief_description>
<description>
The [SurfaceTool] is used to construct a [Mesh] by specifying vertex attributes individually. It can be used to construct a [Mesh] from a script. All properties except indices need to be added before calling [method add_vertex]. For example, to add vertex colors and UVs:
- [codeblock]
+ [codeblocks]
+ [gdscript]
var st = SurfaceTool.new()
st.begin(Mesh.PRIMITIVE_TRIANGLES)
st.set_color(Color(1, 0, 0))
st.set_uv(Vector2(0, 0))
st.set_vertex(Vector3(0, 0, 0))
- [/codeblock]
+ [/gdscript]
+ [csharp]
+ var st = new SurfaceTool();
+ st.Begin(Mesh.PrimitiveType.Triangles);
+ st.SetColor(new Color(1, 0, 0));
+ st.SetUv(new Vector2(0, 0));
+ st.SetVertex(new Vector3(0, 0, 0));
+ [/csharp]
+ [/codeblocks]
The above [SurfaceTool] now contains one vertex of a triangle which has a UV coordinate and a specified [Color]. If another vertex were added without calling [method set_uv] or [method set_color], then the last values would be used.
Vertex attributes must be passed [b]before[/b] calling [method add_vertex]. Failure to do so will result in an error when committing the vertex information to a mesh.
Additionally, the attributes used before the first vertex is added determine the format of the mesh. For example, if you only add UVs to the first vertex, you cannot add color to any of the subsequent vertices.
diff --git a/doc/classes/TextEdit.xml b/doc/classes/TextEdit.xml
index 1d50cd60e5..6232a4e146 100644
--- a/doc/classes/TextEdit.xml
+++ b/doc/classes/TextEdit.xml
@@ -402,13 +402,24 @@
<description>
Perform a search inside the text. Search flags can be specified in the [enum SearchFlags] enum.
Returns an empty [code]Dictionary[/code] if no result was found. Otherwise, returns a [code]Dictionary[/code] containing [code]line[/code] and [code]column[/code] entries, e.g:
- [codeblock]
- var result = search(key, flags, line, column)
- if !result.is_empty():
+ [codeblocks]
+ [gdscript]
+ var result = search("print", SEARCH_WHOLE_WORDS, 0, 0)
+ if !result.empty():
# Result found.
var line_number = result.line
var column_number = result.column
- [/codeblock]
+ [/gdscript]
+ [csharp]
+ int[] result = Search("print", (uint)TextEdit.SearchFlags.WholeWords, 0, 0);
+ if (result.Length > 0)
+ {
+ // Result found.
+ int lineNumber = result[(int)TextEdit.SearchResult.Line];
+ int columnNumber = result[(int)TextEdit.SearchResult.Column];
+ }
+ [/csharp]
+ [/codeblocks]
</description>
</method>
<method name="select">
diff --git a/doc/classes/TextServer.xml b/doc/classes/TextServer.xml
index 79ab6e28e7..5635ec2be0 100644
--- a/doc/classes/TextServer.xml
+++ b/doc/classes/TextServer.xml
@@ -9,6 +9,19 @@
<tutorials>
</tutorials>
<methods>
+ <method name="create_font_bitmap">
+ <return type="RID">
+ </return>
+ <argument index="0" name="height" type="float">
+ </argument>
+ <argument index="1" name="ascent" type="float">
+ </argument>
+ <argument index="2" name="base_size" type="int">
+ </argument>
+ <description>
+ Creates new, empty bitmap font. To free the resulting font, use [method free_rid] method.
+ </description>
+ </method>
<method name="create_font_memory">
<return type="RID">
</return>
@@ -78,6 +91,51 @@
Draws box displaying character hexadecimal code. Used for replacing missing characters.
</description>
</method>
+ <method name="font_bitmap_add_char">
+ <return type="void">
+ </return>
+ <argument index="0" name="font" type="RID">
+ </argument>
+ <argument index="1" name="char" type="int">
+ </argument>
+ <argument index="2" name="texture_idx" type="int">
+ </argument>
+ <argument index="3" name="rect" type="Rect2">
+ </argument>
+ <argument index="4" name="align" type="Vector2">
+ </argument>
+ <argument index="5" name="advance" type="float">
+ </argument>
+ <description>
+ Adds a character to the font, where [code]character[/code] is the Unicode value, [code]texture[/code] is the texture index, [code]rect[/code] is the region in the texture (in pixels!), [code]align[/code] is the (optional) alignment for the character and [code]advance[/code] is the (optional) advance.
+ </description>
+ </method>
+ <method name="font_bitmap_add_kerning_pair">
+ <return type="void">
+ </return>
+ <argument index="0" name="font" type="RID">
+ </argument>
+ <argument index="1" name="A" type="int">
+ </argument>
+ <argument index="2" name="B" type="int">
+ </argument>
+ <argument index="3" name="kerning" type="int">
+ </argument>
+ <description>
+ Adds a kerning pair to the bitmap font as a difference. Kerning pairs are special cases where a typeface advance is determined by the next character.
+ </description>
+ </method>
+ <method name="font_bitmap_add_texture">
+ <return type="void">
+ </return>
+ <argument index="0" name="font" type="RID">
+ </argument>
+ <argument index="1" name="texture" type="Texture">
+ </argument>
+ <description>
+ Adds a texture to the bitmap font.
+ </description>
+ </method>
<method name="font_draw_glyph" qualifiers="const">
<return type="Vector2">
</return>
diff --git a/doc/classes/TileMap.xml b/doc/classes/TileMap.xml
index a12ef614e3..c0ee004ca6 100644
--- a/doc/classes/TileMap.xml
+++ b/doc/classes/TileMap.xml
@@ -172,12 +172,22 @@
[b]Note:[/b] Data such as navigation polygons and collision shapes are not immediately updated for performance reasons.
If you need these to be immediately updated, you can call [method update_dirty_quadrants].
Overriding this method also overrides it internally, allowing custom logic to be implemented when tiles are placed/removed:
- [codeblock]
+ [codeblocks]
+ [gdscript]
func set_cell(x, y, tile, flip_x=false, flip_y=false, transpose=false, autotile_coord=Vector2())
# Write your custom logic here.
# To call the default method:
.set_cell(x, y, tile, flip_x, flip_y, transpose, autotile_coord)
- [/codeblock]
+ [/gdscript]
+ [csharp]
+ public void SetCell(int x, int y, int tile, bool flipX = false, bool flipY = false, bool transpose = false, Vector2 autotileCoord = new Vector2())
+ {
+ // Write your custom logic here.
+ // To call the default method:
+ base.SetCell(x, y, tile, flipX, flipY, transpose, autotileCoord);
+ }
+ [/csharp]
+ [/codeblocks]
</description>
</method>
<method name="set_cellv">
diff --git a/doc/classes/Tree.xml b/doc/classes/Tree.xml
index 8502707096..a09f1bf470 100644
--- a/doc/classes/Tree.xml
+++ b/doc/classes/Tree.xml
@@ -6,16 +6,30 @@
<description>
This shows a tree of items that can be selected, expanded and collapsed. The tree can have multiple columns with custom controls like text editing, buttons and popups. It can be useful for structured displays and interactions.
Trees are built via code, using [TreeItem] objects to create the structure. They have a single root but multiple roots can be simulated if a dummy hidden root is added.
- [codeblock]
+ [codeblocks]
+ [gdscript]
func _ready():
var tree = Tree.new()
var root = tree.create_item()
- tree.set_hide_root(true)
+ tree.hide_root = true
var child1 = tree.create_item(root)
var child2 = tree.create_item(root)
var subchild1 = tree.create_item(child1)
subchild1.set_text(0, "Subchild1")
- [/codeblock]
+ [/gdscript]
+ [csharp]
+ public override void _Ready()
+ {
+ var tree = new Tree();
+ TreeItem root = tree.CreateItem();
+ tree.HideRoot = true;
+ TreeItem child1 = tree.CreateItem(root);
+ TreeItem child2 = tree.CreateItem(root);
+ TreeItem subchild1 = tree.CreateItem(child1);
+ subchild1.SetText(0, "Subchild1");
+ }
+ [/csharp]
+ [/codeblocks]
To iterate over all the [TreeItem] objects in a [Tree] object, use [method TreeItem.get_next] and [method TreeItem.get_children] after getting the root through [method get_root]. You can use [method Object.free] on a [TreeItem] to remove it from the [Tree].
</description>
<tutorials>
@@ -152,13 +166,26 @@
</return>
<description>
Returns the currently edited item. Can be used with [signal item_edited] to get the item that was modified.
- [codeblock]
+ [codeblocks]
+ [gdscript]
func _ready():
$Tree.item_edited.connect(on_Tree_item_edited)
func on_Tree_item_edited():
print($Tree.get_edited()) # This item just got edited (e.g. checked).
- [/codeblock]
+ [/gdscript]
+ [csharp]
+ public override void _Ready()
+ {
+ GetNode&lt;Tree&gt;("Tree").ItemEdited += OnTreeItemEdited;
+ }
+
+ public void OnTreeItemEdited()
+ {
+ GD.Print(GetNode&lt;Tree&gt;("Tree").GetEdited()); // This item just got edited (e.g. checked).
+ }
+ [/csharp]
+ [/codeblocks]
</description>
</method>
<method name="get_edited_column" qualifiers="const">
diff --git a/doc/classes/Tween.xml b/doc/classes/Tween.xml
index 56ccaaf383..b32bb1e2d9 100644
--- a/doc/classes/Tween.xml
+++ b/doc/classes/Tween.xml
@@ -7,13 +7,22 @@
Tweens are useful for animations requiring a numerical property to be interpolated over a range of values. The name [i]tween[/i] comes from [i]in-betweening[/i], an animation technique where you specify [i]keyframes[/i] and the computer interpolates the frames that appear between them.
[Tween] is more suited than [AnimationPlayer] for animations where you don't know the final values in advance. For example, interpolating a dynamically-chosen camera zoom value is best done with a [Tween] node; it would be difficult to do the same thing with an [AnimationPlayer] node.
Here is a brief usage example that makes a 2D node move smoothly between two positions:
- [codeblock]
+ [codeblocks]
+ [gdscript]
var tween = get_node("Tween")
tween.interpolate_property($Node2D, "position",
Vector2(0, 0), Vector2(100, 100), 1,
Tween.TRANS_LINEAR, Tween.EASE_IN_OUT)
tween.start()
- [/codeblock]
+ [/gdscript]
+ [csharp]
+ var tween = GetNode&lt;Tween&gt;("Tween");
+ tween.InterpolateProperty(GetNode&lt;Node2D&gt;("Node2D"), "position",
+ new Vector2(0, 0), new Vector2(100, 100), 1,
+ Tween.TransitionType.Linear, Tween.EaseType.InOut);
+ tween.Start();
+ [/csharp]
+ [/codeblocks]
Many methods require a property name, such as [code]"position"[/code] above. You can find the correct property name by hovering over the property in the Inspector. You can also provide the components of a property directly by using [code]"property:component"[/code] (eg. [code]position:x[/code]), where it would only apply to that particular component.
Many of the methods accept [code]trans_type[/code] and [code]ease_type[/code]. The first accepts an [enum TransitionType] constant, and refers to the way the timing of the animation is handled (see [url=https://easings.net/]easings.net[/url] for some examples). The second accepts an [enum EaseType] constant, and controls where the [code]trans_type[/code] is applied to the interpolation (in the beginning, the end, or both). If you don't know which transition and easing to pick, you can try different [enum TransitionType] constants with [constant EASE_IN_OUT], and use the one that looks best.
[url=https://raw.githubusercontent.com/godotengine/godot-docs/master/img/tween_cheatsheet.png]Tween easing and transition types cheatsheet[/url]
diff --git a/doc/classes/UDPServer.xml b/doc/classes/UDPServer.xml
index aabfed85f0..0fc00f67f8 100644
--- a/doc/classes/UDPServer.xml
+++ b/doc/classes/UDPServer.xml
@@ -7,8 +7,9 @@
A simple server that opens a UDP socket and returns connected [PacketPeerUDP] upon receiving new packets. See also [method PacketPeerUDP.connect_to_host].
After starting the server ([method listen]), you will need to [method poll] it at regular intervals (e.g. inside [method Node._process]) for it to process new packets, delivering them to the appropriate [PacketPeerUDP], and taking new connections.
Below a small example of how it can be used:
- [codeblock]
- # server.gd
+ [codeblocks]
+ [gdscript]
+ class_name Server
extends Node
var server := UDPServer.new()
@@ -21,20 +22,57 @@
server.poll() # Important!
if server.is_connection_available():
var peer : PacketPeerUDP = server.take_connection()
- var pkt = peer.get_packet()
+ var packet = peer.get_packet()
print("Accepted peer: %s:%s" % [peer.get_packet_ip(), peer.get_packet_port()])
- print("Received data: %s" % [pkt.get_string_from_utf8()])
+ print("Received data: %s" % [packet.get_string_from_utf8()])
# Reply so it knows we received the message.
- peer.put_packet(pkt)
+ peer.put_packet(packet)
# Keep a reference so we can keep contacting the remote peer.
peers.append(peer)
for i in range(0, peers.size()):
pass # Do something with the connected peers.
+ [/gdscript]
+ [csharp]
+ using Godot;
+ using System;
+ using System.Collections.Generic;
- [/codeblock]
- [codeblock]
- # client.gd
+ public class Server : Node
+ {
+ public UDPServer Server = new UDPServer();
+ public List&lt;PacketPeerUDP&gt; Peers = new List&lt;PacketPeerUDP&gt;();
+
+ public override void _Ready()
+ {
+ Server.Listen(4242);
+ }
+
+ public override void _Process(float delta)
+ {
+ Server.Poll(); // Important!
+ if (Server.IsConnectionAvailable())
+ {
+ PacketPeerUDP peer = Server.TakeConnection();
+ byte[] packet = peer.GetPacket();
+ GD.Print($"Accepted Peer: {peer.GetPacketIp()}:{peer.GetPacketPort()}");
+ GD.Print($"Received Data: {packet.GetStringFromUTF8()}");
+ // Reply so it knows we received the message.
+ peer.PutPacket(packet);
+ // Keep a reference so we can keep contacting the remote peer.
+ Peers.Add(peer);
+ }
+ foreach (var peer in Peers)
+ {
+ // Do something with the peers.
+ }
+ }
+ }
+ [/csharp]
+ [/codeblocks]
+ [codeblocks]
+ [gdscript]
+ class_name Client
extends Node
var udp := PacketPeerUDP.new()
@@ -50,7 +88,37 @@
if udp.get_available_packet_count() &gt; 0:
print("Connected: %s" % udp.get_packet().get_string_from_utf8())
connected = true
- [/codeblock]
+ [/gdscript]
+ [csharp]
+ using Godot;
+ using System;
+
+ public class Client : Node
+ {
+ public PacketPeerUDP Udp = new PacketPeerUDP();
+ public bool Connected = false;
+
+ public override void _Ready()
+ {
+ Udp.ConnectToHost("127.0.0.1", 4242);
+ }
+
+ public override void _Process(float delta)
+ {
+ if (!Connected)
+ {
+ // Try to contact server
+ Udp.PutPacket("The Answer Is..42!".ToUTF8());
+ }
+ if (Udp.GetAvailablePacketCount() &gt; 0)
+ {
+ GD.Print($"Connected: {Udp.GetPacket().GetStringFromUTF8()}");
+ Connected = true;
+ }
+ }
+ }
+ [/csharp]
+ [/codeblocks]
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/UndoRedo.xml b/doc/classes/UndoRedo.xml
index e8124d0e6a..a0330de4fa 100644
--- a/doc/classes/UndoRedo.xml
+++ b/doc/classes/UndoRedo.xml
@@ -7,7 +7,8 @@
Helper to manage undo/redo operations in the editor or custom tools. It works by registering methods and property changes inside "actions".
Common behavior is to create an action, then add do/undo calls to functions or property changes, then committing the action.
Here's an example on how to add an action to the Godot editor's own [UndoRedo], from a plugin:
- [codeblock]
+ [codeblocks]
+ [gdscript]
var undo_redo = get_undo_redo() # Method of EditorPlugin.
func do_something():
@@ -24,7 +25,37 @@
undo_redo.add_do_property(node, "position", Vector2(100,100))
undo_redo.add_undo_property(node, "position", node.position)
undo_redo.commit_action()
- [/codeblock]
+ [/gdscript]
+ [csharp]
+ public UndoRedo UndoRedo;
+
+ public override void _Ready()
+ {
+ UndoRedo = GetUndoRedo(); // Method of EditorPlugin.
+ }
+
+ public void DoSomething()
+ {
+ // Put your code here.
+ }
+
+ public void UndoSomething()
+ {
+ // Put here the code that reverts what's done by "DoSomething()".
+ }
+
+ private void OnMyButtonPressed()
+ {
+ var node = GetNode&lt;Node2D&gt;("MyNode2D");
+ UndoRedo.CreateAction("Move the node");
+ UndoRedo.AddDoMethod(this, nameof(DoSomething));
+ UndoRedo.AddUndoMethod(this, nameof(UndoSomething));
+ UndoRedo.AddDoProperty(node, "position", new Vector2(100, 100));
+ UndoRedo.AddUndoProperty(node, "position", node.Position);
+ UndoRedo.CommitAction();
+ }
+ [/csharp]
+ [/codeblocks]
[method create_action], [method add_do_method], [method add_undo_method], [method add_do_property], [method add_undo_property], and [method commit_action] should be called one after the other, like in the example. Not doing so could lead to crashes.
If you don't need to register a method, you can leave [method add_do_method] and [method add_undo_method] out; the same goes for properties. You can also register more than one method/property.
</description>
diff --git a/drivers/dummy/rasterizer_dummy.h b/drivers/dummy/rasterizer_dummy.h
index a76520dcdb..9d6be1a802 100644
--- a/drivers/dummy/rasterizer_dummy.h
+++ b/drivers/dummy/rasterizer_dummy.h
@@ -674,7 +674,7 @@ public:
void render_info_end_capture() override {}
int get_captured_render_info(RS::RenderInfo p_info) override { return 0; }
- int get_render_info(RS::RenderInfo p_info) override { return 0; }
+ uint64_t get_render_info(RS::RenderInfo p_info) override { return 0; }
String get_video_adapter_name() const override { return String(); }
String get_video_adapter_vendor() const override { return String(); }
diff --git a/editor/create_dialog.cpp b/editor/create_dialog.cpp
index 3a63100012..711072f4b2 100644
--- a/editor/create_dialog.cpp
+++ b/editor/create_dialog.cpp
@@ -170,6 +170,7 @@ void CreateDialog::_update_search() {
root->set_text(0, base_type);
root->set_icon(0, search_options->get_theme_icon(icon_fallback, "EditorIcons"));
search_options_types[base_type] = root;
+ _configure_search_option_item(root, base_type, ClassDB::class_exists(base_type));
const String search_text = search_box->get_text();
bool empty_search = search_text == "";
@@ -236,7 +237,10 @@ void CreateDialog::_configure_search_option_item(TreeItem *r_item, const String
bool can_instance = (p_cpp_type && ClassDB::can_instance(p_type)) || !p_cpp_type;
if (!can_instance) {
r_item->set_custom_color(0, search_options->get_theme_color("disabled_font_color", "Editor"));
+ r_item->set_icon(0, EditorNode::get_singleton()->get_class_icon(p_type, "NodeDisabled"));
r_item->set_selectable(0, false);
+ } else {
+ r_item->set_icon(0, EditorNode::get_singleton()->get_class_icon(p_type, icon_fallback));
}
if (search_box->get_text() != "") {
@@ -253,7 +257,6 @@ void CreateDialog::_configure_search_option_item(TreeItem *r_item, const String
const String &description = DTR(EditorHelp::get_doc_data()->class_list[p_type].brief_description);
r_item->set_tooltip(0, description);
- r_item->set_icon(0, EditorNode::get_singleton()->get_class_icon(p_type, icon_fallback));
if (!p_cpp_type && !script_type) {
Ref<Texture2D> icon = EditorNode::get_editor_data().get_custom_types()[custom_type_parents[p_type]][custom_type_indices[p_type]].icon;
diff --git a/editor/debugger/script_editor_debugger.cpp b/editor/debugger/script_editor_debugger.cpp
index be2b98bf1a..c92e94270e 100644
--- a/editor/debugger/script_editor_debugger.cpp
+++ b/editor/debugger/script_editor_debugger.cpp
@@ -343,7 +343,7 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da
DebuggerMarshalls::ResourceUsage usage;
usage.deserialize(p_data);
- int total = 0;
+ uint64_t total = 0;
for (List<DebuggerMarshalls::ResourceInfo>::Element *E = usage.infos.front(); E; E = E->next()) {
TreeItem *it = vmem_tree->create_item(root);
diff --git a/editor/editor_export.cpp b/editor/editor_export.cpp
index 949306de42..4f60258d95 100644
--- a/editor/editor_export.cpp
+++ b/editor/editor_export.cpp
@@ -507,6 +507,11 @@ void EditorExportPlatform::_edit_files_with_filter(DirAccess *da, const Vector<S
if (dir.begins_with(".")) {
continue;
}
+
+ if (EditorFileSystem::_should_skip_directory(cur_dir + dir)) {
+ continue;
+ }
+
da->change_dir(dir);
_edit_files_with_filter(da, p_filters, r_list, exclude);
da->change_dir("..");
diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp
index 3c6649a66a..dce022e86e 100644
--- a/editor/editor_file_system.cpp
+++ b/editor/editor_file_system.cpp
@@ -669,10 +669,7 @@ void EditorFileSystem::_scan_new_dir(EditorFileSystemDirectory *p_dir, DirAccess
continue;
}
- if (FileAccess::exists(cd.plus_file(f).plus_file("project.godot"))) { // skip if another project inside this
- continue;
- }
- if (FileAccess::exists(cd.plus_file(f).plus_file(".gdignore"))) { // skip if another project inside this
+ if (_should_skip_directory(cd.plus_file(f))) {
continue;
}
@@ -874,10 +871,7 @@ void EditorFileSystem::_scan_fs_changes(EditorFileSystemDirectory *p_dir, const
int idx = p_dir->find_dir_index(f);
if (idx == -1) {
- if (FileAccess::exists(cd.plus_file(f).plus_file("project.godot"))) { // skip if another project inside this
- continue;
- }
- if (FileAccess::exists(cd.plus_file(f).plus_file(".gdignore"))) { // skip if another project inside this
+ if (_should_skip_directory(cd.plus_file(f))) {
continue;
}
@@ -1979,6 +1973,20 @@ Error EditorFileSystem::_resource_import(const String &p_path) {
return OK;
}
+bool EditorFileSystem::_should_skip_directory(const String &p_path) {
+ if (FileAccess::exists(p_path.plus_file("project.godot"))) {
+ // skip if another project inside this
+ return true;
+ }
+
+ if (FileAccess::exists(p_path.plus_file(".gdignore"))) {
+ // skip if a `.gdignore` file is inside this
+ return true;
+ }
+
+ return false;
+}
+
bool EditorFileSystem::is_group_file(const String &p_path) const {
return group_file_cache.has(p_path);
}
diff --git a/editor/editor_file_system.h b/editor/editor_file_system.h
index dec2330256..59bde238a8 100644
--- a/editor/editor_file_system.h
+++ b/editor/editor_file_system.h
@@ -262,6 +262,8 @@ public:
bool is_group_file(const String &p_path) const;
void move_group_file(const String &p_path, const String &p_new_path);
+ static bool _should_skip_directory(const String &p_path);
+
EditorFileSystem();
~EditorFileSystem();
};
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 4cf85a8caf..f0e53e7ef5 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -902,7 +902,8 @@ void EditorNode::_scan_external_changes() {
// Check if any edited scene has changed.
for (int i = 0; i < editor_data.get_edited_scene_count(); i++) {
- if (editor_data.get_scene_path(i) == "") {
+ DirAccessRef da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
+ if (editor_data.get_scene_path(i) == "" || !da->file_exists(editor_data.get_scene_path(i))) {
continue;
}
diff --git a/editor/fileserver/editor_file_server.cpp b/editor/fileserver/editor_file_server.cpp
index 07edae833d..d80003a12a 100644
--- a/editor/fileserver/editor_file_server.cpp
+++ b/editor/fileserver/editor_file_server.cpp
@@ -43,7 +43,7 @@ void EditorFileServer::_close_client(ClientData *cd) {
cd->connection->disconnect_from_host();
{
MutexLock lock(cd->efs->wait_mutex);
- cd->efs->to_wait.insert(&cd->thread);
+ cd->efs->to_wait.insert(cd->thread);
}
while (cd->files.size()) {
memdelete(cd->files.front()->get());
@@ -278,7 +278,8 @@ void EditorFileServer::_thread_start(void *s) {
cd->connection = self->server->take_connection();
cd->efs = self;
cd->quit = false;
- cd->thread.start(_subthread_start, cd);
+ cd->thread = memnew(Thread);
+ cd->thread->start(_subthread_start, cd);
}
}
diff --git a/editor/fileserver/editor_file_server.h b/editor/fileserver/editor_file_server.h
index e4c8327d76..8ffd4f9692 100644
--- a/editor/fileserver/editor_file_server.h
+++ b/editor/fileserver/editor_file_server.h
@@ -47,7 +47,7 @@ class EditorFileServer : public Object {
};
struct ClientData {
- Thread thread;
+ Thread *thread;
Ref<StreamPeerTCP> connection;
Map<int, FileAccess *> files;
EditorFileServer *efs = nullptr;
diff --git a/editor/icons/NodeDisabled.svg b/editor/icons/NodeDisabled.svg
new file mode 100644
index 0000000000..b2d51fc4fb
--- /dev/null
+++ b/editor/icons/NodeDisabled.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m8 2a6 6 0 0 0 -6 6 6 6 0 0 0 6 6 6 6 0 0 0 6-6 6 6 0 0 0 -6-6zm0 2a4 4 0 0 1 4 4 4 4 0 0 1 -4 4 4 4 0 0 1 -4-4 4 4 0 0 1 4-4z" fill="#999"/></svg>
diff --git a/editor/import_defaults_editor.cpp b/editor/import_defaults_editor.cpp
new file mode 100644
index 0000000000..43b97eb910
--- /dev/null
+++ b/editor/import_defaults_editor.cpp
@@ -0,0 +1,216 @@
+/*************************************************************************/
+/* import_defaults_editor.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 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 "import_defaults_editor.h"
+
+class ImportDefaultsEditorSettings : public Object {
+ GDCLASS(ImportDefaultsEditorSettings, Object)
+ friend class ImportDefaultsEditor;
+ List<PropertyInfo> properties;
+ Map<StringName, Variant> values;
+ Map<StringName, Variant> default_values;
+
+ Ref<ResourceImporter> importer;
+
+protected:
+ bool _set(const StringName &p_name, const Variant &p_value) {
+ if (values.has(p_name)) {
+ values[p_name] = p_value;
+ return true;
+ } else {
+ return false;
+ }
+ }
+ bool _get(const StringName &p_name, Variant &r_ret) const {
+ if (values.has(p_name)) {
+ r_ret = values[p_name];
+ return true;
+ } else {
+ r_ret = Variant();
+ return false;
+ }
+ }
+ void _get_property_list(List<PropertyInfo> *p_list) const {
+ if (importer.is_null()) {
+ return;
+ }
+ for (const List<PropertyInfo>::Element *E = properties.front(); E; E = E->next()) {
+ if (importer->get_option_visibility(E->get().name, values)) {
+ p_list->push_back(E->get());
+ }
+ }
+ }
+};
+
+void ImportDefaultsEditor::_notification(int p_what) {
+ if (p_what == NOTIFICATION_PREDELETE) {
+ inspector->edit(nullptr);
+ }
+}
+
+void ImportDefaultsEditor::_reset() {
+ if (settings->importer.is_valid()) {
+ settings->values = settings->default_values;
+ settings->notify_property_list_changed();
+ }
+}
+
+void ImportDefaultsEditor::_save() {
+ if (settings->importer.is_valid()) {
+ Dictionary modified;
+
+ for (Map<StringName, Variant>::Element *E = settings->values.front(); E; E = E->next()) {
+ if (E->get() != settings->default_values[E->key()]) {
+ modified[E->key()] = E->get();
+ }
+ }
+
+ if (modified.size()) {
+ ProjectSettings::get_singleton()->set("importer_defaults/" + settings->importer->get_importer_name(), modified);
+ } else {
+ ProjectSettings::get_singleton()->set("importer_defaults/" + settings->importer->get_importer_name(), Variant());
+ }
+
+ emit_signal("project_settings_changed");
+ }
+}
+
+void ImportDefaultsEditor::_update_importer() {
+ List<Ref<ResourceImporter>> importer_list;
+ ResourceFormatImporter::get_singleton()->get_importers(&importer_list);
+ Ref<ResourceImporter> importer;
+ for (List<Ref<ResourceImporter>>::Element *E = importer_list.front(); E; E = E->next()) {
+ if (E->get()->get_visible_name() == importers->get_item_text(importers->get_selected())) {
+ importer = E->get();
+ break;
+ }
+ }
+
+ settings->properties.clear();
+ settings->values.clear();
+ settings->importer = importer;
+
+ if (importer.is_valid()) {
+ List<ResourceImporter::ImportOption> options;
+ importer->get_import_options(&options);
+ Dictionary d;
+ if (ProjectSettings::get_singleton()->has_setting("importer_defaults/" + importer->get_importer_name())) {
+ d = ProjectSettings::get_singleton()->get("importer_defaults/" + importer->get_importer_name());
+ }
+
+ for (List<ResourceImporter::ImportOption>::Element *E = options.front(); E; E = E->next()) {
+ settings->properties.push_back(E->get().option);
+ if (d.has(E->get().option.name)) {
+ settings->values[E->get().option.name] = d[E->get().option.name];
+ } else {
+ settings->values[E->get().option.name] = E->get().default_value;
+ }
+ settings->default_values[E->get().option.name] = E->get().default_value;
+ }
+
+ save_defaults->set_disabled(false);
+ reset_defaults->set_disabled(false);
+
+ } else {
+ save_defaults->set_disabled(true);
+ reset_defaults->set_disabled(true);
+ }
+
+ settings->notify_property_list_changed();
+
+ inspector->edit(settings);
+}
+
+void ImportDefaultsEditor::_importer_selected(int p_index) {
+ _update_importer();
+}
+
+void ImportDefaultsEditor::clear() {
+ String last_selected;
+ if (importers->get_selected() > 0) {
+ last_selected = importers->get_item_text(importers->get_selected());
+ }
+
+ importers->clear();
+
+ importers->add_item("<" + TTR("Select Importer") + ">");
+ importers->set_item_disabled(0, true);
+
+ List<Ref<ResourceImporter>> importer_list;
+ ResourceFormatImporter::get_singleton()->get_importers(&importer_list);
+ Vector<String> names;
+ for (List<Ref<ResourceImporter>>::Element *E = importer_list.front(); E; E = E->next()) {
+ String vn = E->get()->get_visible_name();
+ names.push_back(vn);
+ }
+ names.sort();
+
+ for (int i = 0; i < names.size(); i++) {
+ importers->add_item(names[i]);
+
+ if (names[i] == last_selected) {
+ importers->select(i + 1);
+ }
+ }
+}
+
+void ImportDefaultsEditor::_bind_methods() {
+ ADD_SIGNAL(MethodInfo("project_settings_changed"));
+}
+
+ImportDefaultsEditor::ImportDefaultsEditor() {
+ HBoxContainer *hb = memnew(HBoxContainer);
+ hb->add_child(memnew(Label(TTR("Importer:"))));
+ importers = memnew(OptionButton);
+ hb->add_child(importers);
+ hb->add_spacer();
+ importers->connect("item_selected", callable_mp(this, &ImportDefaultsEditor::_importer_selected));
+ reset_defaults = memnew(Button);
+ reset_defaults->set_text(TTR("Reset to Defaults"));
+ reset_defaults->set_disabled(true);
+ reset_defaults->connect("pressed", callable_mp(this, &ImportDefaultsEditor::_reset));
+ hb->add_child(reset_defaults);
+ add_child(hb);
+ inspector = memnew(EditorInspector);
+ add_child(inspector);
+ inspector->set_v_size_flags(SIZE_EXPAND_FILL);
+ CenterContainer *cc = memnew(CenterContainer);
+ save_defaults = memnew(Button);
+ save_defaults->set_text(TTR("Save"));
+ save_defaults->connect("pressed", callable_mp(this, &ImportDefaultsEditor::_save));
+ cc->add_child(save_defaults);
+ add_child(cc);
+
+ settings = memnew(ImportDefaultsEditorSettings);
+}
+
+ImportDefaultsEditor::~ImportDefaultsEditor() {
+ memdelete(settings);
+}
diff --git a/editor/import_defaults_editor.h b/editor/import_defaults_editor.h
new file mode 100644
index 0000000000..c1becac5e9
--- /dev/null
+++ b/editor/import_defaults_editor.h
@@ -0,0 +1,75 @@
+/*************************************************************************/
+/* import_defaults_editor.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 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 IMPORT_DEFAULTS_EDITOR_H
+#define IMPORT_DEFAULTS_EDITOR_H
+
+#include "core/object/undo_redo.h"
+#include "editor/action_map_editor.h"
+#include "editor/editor_data.h"
+#include "editor/editor_plugin_settings.h"
+#include "editor/editor_sectioned_inspector.h"
+#include "editor/localization_editor.h"
+#include "editor/shader_globals_editor.h"
+#include "editor_autoload_settings.h"
+#include "scene/gui/center_container.h"
+#include "scene/gui/option_button.h"
+
+class ImportDefaultsEditorSettings;
+
+class ImportDefaultsEditor : public VBoxContainer {
+ GDCLASS(ImportDefaultsEditor, VBoxContainer)
+
+ OptionButton *importers;
+ Button *save_defaults;
+ Button *reset_defaults;
+
+ EditorInspector *inspector;
+
+ ImportDefaultsEditorSettings *settings;
+
+ void _update_importer();
+ void _importer_selected(int p_index);
+
+ void _reset();
+ void _save();
+
+protected:
+ void _notification(int p_what);
+ static void _bind_methods();
+
+public:
+ void clear();
+
+ ImportDefaultsEditor();
+ ~ImportDefaultsEditor();
+};
+
+#endif // IMPORT_DEFAULTS_EDITOR_H
diff --git a/editor/node_3d_editor_gizmos.cpp b/editor/node_3d_editor_gizmos.cpp
index 9e2fb01bb8..16eefb1ad3 100644
--- a/editor/node_3d_editor_gizmos.cpp
+++ b/editor/node_3d_editor_gizmos.cpp
@@ -198,7 +198,11 @@ void EditorNode3DGizmo::add_mesh(const Ref<ArrayMesh> &p_mesh, bool p_billboard,
}
void EditorNode3DGizmo::add_lines(const Vector<Vector3> &p_lines, const Ref<Material> &p_material, bool p_billboard, const Color &p_modulate) {
- if (p_lines.is_empty()) {
+ add_vertices(p_lines, p_material, Mesh::PRIMITIVE_LINES, p_billboard, p_modulate);
+}
+
+void EditorNode3DGizmo::add_vertices(const Vector<Vector3> &p_vertices, const Ref<Material> &p_material, Mesh::PrimitiveType p_primitive_type, bool p_billboard, const Color &p_modulate) {
+ if (p_vertices.is_empty()) {
return;
}
@@ -209,13 +213,13 @@ void EditorNode3DGizmo::add_lines(const Vector<Vector3> &p_lines, const Ref<Mate
Array a;
a.resize(Mesh::ARRAY_MAX);
- a[Mesh::ARRAY_VERTEX] = p_lines;
+ a[Mesh::ARRAY_VERTEX] = p_vertices;
Vector<Color> color;
- color.resize(p_lines.size());
+ color.resize(p_vertices.size());
{
Color *w = color.ptrw();
- for (int i = 0; i < p_lines.size(); i++) {
+ for (int i = 0; i < p_vertices.size(); i++) {
if (is_selected()) {
w[i] = Color(1, 1, 1, 0.8) * p_modulate;
} else {
@@ -226,13 +230,13 @@ void EditorNode3DGizmo::add_lines(const Vector<Vector3> &p_lines, const Ref<Mate
a[Mesh::ARRAY_COLOR] = color;
- mesh->add_surface_from_arrays(Mesh::PRIMITIVE_LINES, a);
+ mesh->add_surface_from_arrays(p_primitive_type, a);
mesh->surface_set_material(0, p_material);
if (p_billboard) {
float md = 0;
- for (int i = 0; i < p_lines.size(); i++) {
- md = MAX(0, p_lines[i].length());
+ for (int i = 0; i < p_vertices.size(); i++) {
+ md = MAX(0, p_vertices[i].length());
}
if (md) {
mesh->set_custom_aabb(AABB(Vector3(-md, -md, -md), Vector3(md, md, md) * 2.0));
@@ -1906,16 +1910,15 @@ void RayCast3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
p_gizmo->clear();
- Vector<Vector3> lines;
+ const Ref<StandardMaterial3D> material = raycast->is_enabled() ? raycast->get_debug_material() : get_material("shape_material_disabled");
- lines.push_back(Vector3());
- lines.push_back(raycast->get_target_position());
+ p_gizmo->add_lines(raycast->get_debug_line_vertices(), material);
- const Ref<StandardMaterial3D> material =
- get_material(raycast->is_enabled() ? "shape_material" : "shape_material_disabled", p_gizmo);
+ if (raycast->get_debug_shape_thickness() > 1) {
+ p_gizmo->add_vertices(raycast->get_debug_shape_vertices(), material, Mesh::PRIMITIVE_TRIANGLE_STRIP);
+ }
- p_gizmo->add_lines(lines, material);
- p_gizmo->add_collision_segments(lines);
+ p_gizmo->add_collision_segments(raycast->get_debug_line_vertices());
}
/////
@@ -3505,6 +3508,57 @@ void LightmapProbeGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
////
+CollisionObject3DGizmoPlugin::CollisionObject3DGizmoPlugin() {
+ const Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/shape", Color(0.5, 0.7, 1));
+ create_material("shape_material", gizmo_color);
+ const float gizmo_value = gizmo_color.get_v();
+ const Color gizmo_color_disabled = Color(gizmo_value, gizmo_value, gizmo_value, 0.65);
+ create_material("shape_material_disabled", gizmo_color_disabled);
+}
+
+bool CollisionObject3DGizmoPlugin::has_gizmo(Node3D *p_spatial) {
+ return Object::cast_to<CollisionObject3D>(p_spatial) != nullptr;
+}
+
+String CollisionObject3DGizmoPlugin::get_gizmo_name() const {
+ return "CollisionObject3D";
+}
+
+int CollisionObject3DGizmoPlugin::get_priority() const {
+ return -1;
+}
+
+void CollisionObject3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
+ CollisionObject3D *co = Object::cast_to<CollisionObject3D>(p_gizmo->get_spatial_node());
+
+ p_gizmo->clear();
+
+ List<uint32_t> owners;
+ co->get_shape_owners(&owners);
+ for (List<uint32_t>::Element *E = owners.front(); E; E = E->next()) {
+ uint32_t owner_id = E->get();
+ Transform xform = co->shape_owner_get_transform(owner_id);
+ Object *owner = co->shape_owner_get_owner(owner_id);
+ // Exclude CollisionShape3D and CollisionPolygon3D as they have their gizmo.
+ if (!Object::cast_to<CollisionShape3D>(owner) && !Object::cast_to<CollisionPolygon3D>(owner)) {
+ Ref<Material> material = get_material(!co->is_shape_owner_disabled(owner_id) ? "shape_material" : "shape_material_disabled", p_gizmo);
+ for (int shape_id = 0; shape_id < co->shape_owner_get_shape_count(owner_id); shape_id++) {
+ Ref<Shape3D> s = co->shape_owner_get_shape(owner_id, shape_id);
+ if (s.is_null()) {
+ continue;
+ }
+ SurfaceTool st;
+ st.append_from(s->get_debug_mesh(), 0, xform);
+
+ p_gizmo->add_mesh(st.commit(), false, Ref<SkinReference>(), material);
+ p_gizmo->add_collision_segments(s->get_debug_mesh_lines());
+ }
+ }
+ }
+}
+
+////
+
CollisionShape3DGizmoPlugin::CollisionShape3DGizmoPlugin() {
const Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/shape", Color(0.5, 0.7, 1));
create_material("shape_material", gizmo_color);
diff --git a/editor/node_3d_editor_gizmos.h b/editor/node_3d_editor_gizmos.h
index df4ed15a8e..6f98d3a08c 100644
--- a/editor/node_3d_editor_gizmos.h
+++ b/editor/node_3d_editor_gizmos.h
@@ -355,6 +355,18 @@ public:
LightmapProbeGizmoPlugin();
};
+class CollisionObject3DGizmoPlugin : public EditorNode3DGizmoPlugin {
+ GDCLASS(CollisionObject3DGizmoPlugin, EditorNode3DGizmoPlugin);
+
+public:
+ bool has_gizmo(Node3D *p_spatial) override;
+ String get_gizmo_name() const override;
+ int get_priority() const override;
+ void redraw(EditorNode3DGizmo *p_gizmo) override;
+
+ CollisionObject3DGizmoPlugin();
+};
+
class CollisionShape3DGizmoPlugin : public EditorNode3DGizmoPlugin {
GDCLASS(CollisionShape3DGizmoPlugin, EditorNode3DGizmoPlugin);
diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp
index 6fa3c923eb..82ebf48242 100644
--- a/editor/plugins/canvas_item_editor_plugin.cpp
+++ b/editor/plugins/canvas_item_editor_plugin.cpp
@@ -1021,6 +1021,32 @@ void CanvasItemEditor::_selection_menu_hide() {
selection_menu->set_size(Vector2(0, 0));
}
+void CanvasItemEditor::_add_node_pressed(int p_result) {
+ if (p_result == AddNodeOption::ADD_NODE) {
+ editor->get_scene_tree_dock()->open_add_child_dialog();
+ } else if (p_result == AddNodeOption::ADD_INSTANCE) {
+ editor->get_scene_tree_dock()->open_instance_child_dialog();
+ }
+}
+
+void CanvasItemEditor::_node_created(Node *p_node) {
+ if (node_create_position == Point2()) {
+ return;
+ }
+
+ CanvasItem *c = Object::cast_to<CanvasItem>(p_node);
+ if (c) {
+ Transform2D xform = c->get_global_transform_with_canvas().affine_inverse() * c->get_transform();
+ c->_edit_set_position(xform.xform(node_create_position));
+ }
+
+ call_deferred("_reset_create_position"); // Defer the call in case more than one node is added.
+}
+
+void CanvasItemEditor::_reset_create_position() {
+ node_create_position = Point2();
+}
+
bool CanvasItemEditor::_gui_input_rulers_and_guides(const Ref<InputEvent> &p_event) {
Ref<InputEventMouseButton> b = p_event;
Ref<InputEventMouseMotion> m = p_event;
@@ -2463,6 +2489,14 @@ bool CanvasItemEditor::_gui_input_select(const Ref<InputEvent> &p_event) {
}
}
+ if (b.is_valid() && b->is_pressed() && b->get_button_index() == BUTTON_RIGHT && b->get_control()) {
+ add_node_menu->set_position(get_global_transform().xform(get_local_mouse_position()));
+ add_node_menu->set_size(Vector2(1, 1));
+ add_node_menu->popup();
+ node_create_position = transform.affine_inverse().xform((get_local_mouse_position()));
+ return true;
+ }
+
if (b.is_valid() && b->get_button_index() == BUTTON_LEFT && b->is_pressed() && tool == TOOL_SELECT) {
// Single item selection
Point2 click = transform.affine_inverse().xform(b->get_position());
@@ -2868,21 +2902,22 @@ void CanvasItemEditor::_draw_guides() {
// Dragged guide
Color text_color = get_theme_color("font_color", "Editor");
- text_color.a = 0.5;
+ Color outline_color = text_color.inverted();
+ const float outline_size = 2;
if (drag_type == DRAG_DOUBLE_GUIDE || drag_type == DRAG_V_GUIDE) {
String str = TS->format_number(vformat("%d px", Math::round(xform.affine_inverse().xform(dragged_guide_pos).x)));
- Ref<Font> font = get_theme_font("font", "Label");
- int font_size = get_theme_font_size("font_size", "Label");
+ Ref<Font> font = get_theme_font("bold", "EditorFonts");
+ int font_size = get_theme_font_size("bold_size", "EditorFonts");
Size2 text_size = font->get_string_size(str, font_size);
- viewport->draw_string(font, Point2(dragged_guide_pos.x + 10, RULER_WIDTH + text_size.y / 2 + 10), str, HALIGN_LEFT, -1, font_size, text_color);
+ viewport->draw_string(font, Point2(dragged_guide_pos.x + 10, RULER_WIDTH + text_size.y / 2 + 10), str, HALIGN_LEFT, -1, font_size, text_color, outline_size, outline_color);
viewport->draw_line(Point2(dragged_guide_pos.x, 0), Point2(dragged_guide_pos.x, viewport->get_size().y), guide_color, Math::round(EDSCALE));
}
if (drag_type == DRAG_DOUBLE_GUIDE || drag_type == DRAG_H_GUIDE) {
String str = TS->format_number(vformat("%d px", Math::round(xform.affine_inverse().xform(dragged_guide_pos).y)));
- Ref<Font> font = get_theme_font("font", "Label");
- int font_size = get_theme_font_size("font_size", "Label");
+ Ref<Font> font = get_theme_font("bold", "EditorFonts");
+ int font_size = get_theme_font_size("bold_size", "EditorFonts");
Size2 text_size = font->get_string_size(str, font_size);
- viewport->draw_string(font, Point2(RULER_WIDTH + 10, dragged_guide_pos.y + text_size.y / 2 + 10), str, HALIGN_LEFT, -1, font_size, text_color);
+ viewport->draw_string(font, Point2(RULER_WIDTH + 10, dragged_guide_pos.y + text_size.y / 2 + 10), str, HALIGN_LEFT, -1, font_size, text_color, outline_size, outline_color);
viewport->draw_line(Point2(0, dragged_guide_pos.y), Point2(viewport->get_size().x, dragged_guide_pos.y), guide_color, Math::round(EDSCALE));
}
}
@@ -5363,6 +5398,7 @@ void CanvasItemEditor::_bind_methods() {
ClassDB::bind_method("_unhandled_key_input", &CanvasItemEditor::_unhandled_key_input);
ClassDB::bind_method("_queue_update_bone_list", &CanvasItemEditor::_update_bone_list);
ClassDB::bind_method("_update_bone_list", &CanvasItemEditor::_update_bone_list);
+ ClassDB::bind_method("_reset_create_position", &CanvasItemEditor::_reset_create_position);
ClassDB::bind_method(D_METHOD("set_state"), &CanvasItemEditor::set_state);
ClassDB::bind_method(D_METHOD("update_viewport"), &CanvasItemEditor::update_viewport);
@@ -5684,6 +5720,9 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
editor_selection->connect("selection_changed", callable_mp((CanvasItem *)this, &CanvasItem::update));
editor_selection->connect("selection_changed", callable_mp(this, &CanvasItemEditor::_selection_changed));
+ editor->get_scene_tree_dock()->connect("node_created", callable_mp(this, &CanvasItemEditor::_node_created));
+ editor->get_scene_tree_dock()->connect("add_node_used", callable_mp(this, &CanvasItemEditor::_reset_create_position));
+
editor->call_deferred("connect", "play_pressed", Callable(this, "_update_override_camera_button"), make_binds(true));
editor->call_deferred("connect", "stop_pressed", Callable(this, "_update_override_camera_button"), make_binds(false));
@@ -6105,6 +6144,12 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
selection_menu->connect("id_pressed", callable_mp(this, &CanvasItemEditor::_selection_result_pressed));
selection_menu->connect("popup_hide", callable_mp(this, &CanvasItemEditor::_selection_menu_hide));
+ add_node_menu = memnew(PopupMenu);
+ add_child(add_node_menu);
+ add_node_menu->add_icon_item(editor->get_scene_tree_dock()->get_theme_icon("Add", "EditorIcons"), TTR("Add Node Here"));
+ add_node_menu->add_icon_item(editor->get_scene_tree_dock()->get_theme_icon("Instance", "EditorIcons"), TTR("Instance Scene Here"));
+ add_node_menu->connect("id_pressed", callable_mp(this, &CanvasItemEditor::_add_node_pressed));
+
multiply_grid_step_shortcut = ED_SHORTCUT("canvas_item_editor/multiply_grid_step", TTR("Multiply grid step by 2"), KEY_KP_MULTIPLY);
divide_grid_step_shortcut = ED_SHORTCUT("canvas_item_editor/divide_grid_step", TTR("Divide grid step by 2"), KEY_KP_DIVIDE);
pan_view_shortcut = ED_SHORTCUT("canvas_item_editor/pan_view", TTR("Pan View"), KEY_SPACE);
diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h
index 24149a57b0..62a9b1e162 100644
--- a/editor/plugins/canvas_item_editor_plugin.h
+++ b/editor/plugins/canvas_item_editor_plugin.h
@@ -79,6 +79,11 @@ public:
TOOL_MAX
};
+ enum AddNodeOption {
+ ADD_NODE,
+ ADD_INSTANCE,
+ };
+
private:
EditorNode *editor;
@@ -284,6 +289,7 @@ private:
bool ruler_tool_active;
Point2 ruler_tool_origin;
+ Point2 node_create_position;
MenuOption last_option;
@@ -376,6 +382,7 @@ private:
Button *key_auto_insert_button;
PopupMenu *selection_menu;
+ PopupMenu *add_node_menu;
Control *top_ruler;
Control *left_ruler;
@@ -436,6 +443,9 @@ private:
void _snap_changed();
void _selection_result_pressed(int);
void _selection_menu_hide();
+ void _add_node_pressed(int p_result);
+ void _node_created(Node *p_node);
+ void _reset_create_position();
UndoRedo *undo_redo;
bool _build_bones_list(Node *p_node);
diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp
index 1d3835add9..9643881f96 100644
--- a/editor/plugins/node_3d_editor_plugin.cpp
+++ b/editor/plugins/node_3d_editor_plugin.cpp
@@ -535,7 +535,10 @@ ObjectID Node3DEditorViewport::_select_ray(const Point2 &p_pos, bool p_append, b
}
if (dist < closest_dist) {
- item = edited_scene->get_deepest_editable_node(Object::cast_to<Node>(spat));
+ item = Object::cast_to<Node>(spat);
+ if (item != edited_scene) {
+ item = edited_scene->get_deepest_editable_node(item);
+ }
closest = item->get_instance_id();
closest_dist = dist;
@@ -694,7 +697,10 @@ void Node3DEditorViewport::_select_region() {
continue;
}
- Node *item = edited_scene->get_deepest_editable_node(Object::cast_to<Node>(sp));
+ Node *item = Object::cast_to<Node>(sp);
+ if (item != edited_scene) {
+ item = edited_scene->get_deepest_editable_node(item);
+ }
// Replace the node by the group if grouped
if (item->is_class("Node3D")) {
@@ -4707,7 +4713,7 @@ Dictionary Node3DEditor::get_state() const {
continue;
}
int state = gizmos_menu->get_item_state(gizmos_menu->get_item_index(i));
- String name = gizmo_plugins_by_name[i]->get_name();
+ String name = gizmo_plugins_by_name[i]->get_gizmo_name();
gizmos_status[name] = state;
}
@@ -4833,7 +4839,7 @@ void Node3DEditor::set_state(const Dictionary &p_state) {
}
int state = EditorNode3DGizmoPlugin::VISIBLE;
for (int i = 0; i < keys.size(); i++) {
- if (gizmo_plugins_by_name.write[j]->get_name() == String(keys[i])) {
+ if (gizmo_plugins_by_name.write[j]->get_gizmo_name() == String(keys[i])) {
state = gizmos_status[keys[i]];
break;
}
@@ -5744,7 +5750,7 @@ void Node3DEditor::_update_gizmos_menu() {
if (!gizmo_plugins_by_name[i]->can_be_hidden()) {
continue;
}
- String plugin_name = gizmo_plugins_by_name[i]->get_name();
+ String plugin_name = gizmo_plugins_by_name[i]->get_gizmo_name();
const int plugin_state = gizmo_plugins_by_name[i]->get_state();
gizmos_menu->add_multistate_item(plugin_name, 3, plugin_state, i);
const int idx = gizmos_menu->get_item_index(i);
@@ -6243,12 +6249,14 @@ void Node3DEditor::_notification(int p_what) {
sun_button->set_icon(get_theme_icon("DirectionalLight3D", "EditorIcons"));
environ_button->set_icon(get_theme_icon("WorldEnvironment", "EditorIcons"));
- sun_environ_settings->set_icon(get_theme_icon("GuiTabMenu", "EditorIcons"));
+ sun_environ_settings->set_icon(get_theme_icon("GuiTabMenuHl", "EditorIcons"));
_update_preview_environment();
sun_title->add_theme_font_override("font", get_theme_font("title_font", "Window"));
environ_title->add_theme_font_override("font", get_theme_font("title_font", "Window"));
+ sun_state->set_custom_minimum_size(sun_vb->get_combined_minimum_size());
+ environ_state->set_custom_minimum_size(environ_vb->get_combined_minimum_size());
} else if (p_what == NOTIFICATION_ENTER_TREE) {
_register_all_gizmos();
_update_gizmos_menu();
@@ -6450,6 +6458,7 @@ void Node3DEditor::_register_all_gizmos() {
add_gizmo_plugin(Ref<GIProbeGizmoPlugin>(memnew(GIProbeGizmoPlugin)));
add_gizmo_plugin(Ref<BakedLightmapGizmoPlugin>(memnew(BakedLightmapGizmoPlugin)));
add_gizmo_plugin(Ref<LightmapProbeGizmoPlugin>(memnew(LightmapProbeGizmoPlugin)));
+ add_gizmo_plugin(Ref<CollisionObject3DGizmoPlugin>(memnew(CollisionObject3DGizmoPlugin)));
add_gizmo_plugin(Ref<CollisionShape3DGizmoPlugin>(memnew(CollisionShape3DGizmoPlugin)));
add_gizmo_plugin(Ref<CollisionPolygon3DGizmoPlugin>(memnew(CollisionPolygon3DGizmoPlugin)));
add_gizmo_plugin(Ref<NavigationRegion3DGizmoPlugin>(memnew(NavigationRegion3DGizmoPlugin)));
@@ -6563,9 +6572,9 @@ void Node3DEditor::_update_preview_environment() {
}
if (directional_light_count > 0) {
- sun_state->set_text(TTR("Scene contains\nDirectionalLight3D.\nPreview Disabled."));
+ sun_state->set_text(TTR("Scene contains\nDirectionalLight3D.\nPreview disabled."));
} else {
- sun_state->set_text(TTR("Preview Disabled."));
+ sun_state->set_text(TTR("Preview disabled."));
}
} else {
@@ -6587,9 +6596,9 @@ void Node3DEditor::_update_preview_environment() {
environ_vb->hide();
}
if (world_env_count > 0) {
- environ_state->set_text(TTR("Scene contains\nWorldEnvironment.\nPreview Disabled."));
+ environ_state->set_text(TTR("Scene contains\nWorldEnvironment.\nPreview disabled."));
} else {
- environ_state->set_text(TTR("Preview Disabled."));
+ environ_state->set_text(TTR("Preview disabled."));
}
} else {
@@ -7027,10 +7036,6 @@ Node3DEditor::Node3DEditor(EditorNode *p_editor) {
sun_title->set_text(TTR("Preview Sun"));
sun_title->set_align(Label::ALIGN_CENTER);
- sun_state = memnew(Label);
- sun_environ_hb->add_child(sun_state);
- sun_state->show();
-
CenterContainer *sun_direction_center = memnew(CenterContainer);
sun_direction = memnew(Control);
sun_direction->set_custom_minimum_size(Size2i(128, 128) * EDSCALE);
@@ -7071,6 +7076,12 @@ Node3DEditor::Node3DEditor(EditorNode *p_editor) {
sun_vb->add_spacer();
sun_vb->add_child(sun_add_to_scene);
+ sun_state = memnew(Label);
+ sun_environ_hb->add_child(sun_state);
+ sun_state->set_align(Label::ALIGN_CENTER);
+ sun_state->set_valign(Label::VALIGN_CENTER);
+ sun_state->set_h_size_flags(SIZE_EXPAND_FILL);
+
VSeparator *sc = memnew(VSeparator);
sc->set_custom_minimum_size(Size2(50 * EDSCALE, 0));
sc->set_v_size_flags(SIZE_EXPAND_FILL);
@@ -7078,13 +7089,8 @@ Node3DEditor::Node3DEditor(EditorNode *p_editor) {
environ_vb = memnew(VBoxContainer);
sun_environ_hb->add_child(environ_vb);
- environ_vb->hide();
-
environ_vb->set_custom_minimum_size(Size2(200 * EDSCALE, 0));
-
- environ_state = memnew(Label);
- sun_environ_hb->add_child(environ_state);
- environ_state->show();
+ environ_vb->hide();
environ_title = memnew(Label);
environ_vb->add_child(environ_title);
@@ -7134,6 +7140,12 @@ Node3DEditor::Node3DEditor(EditorNode *p_editor) {
environ_vb->add_spacer();
environ_vb->add_child(environ_add_to_scene);
+ environ_state = memnew(Label);
+ sun_environ_hb->add_child(environ_state);
+ environ_state->set_align(Label::ALIGN_CENTER);
+ environ_state->set_valign(Label::VALIGN_CENTER);
+ environ_state->set_h_size_flags(SIZE_EXPAND_FILL);
+
preview_sun = memnew(DirectionalLight3D);
preview_sun->set_shadow(true);
preview_sun->set_shadow_mode(DirectionalLight3D::SHADOW_PARALLEL_4_SPLITS);
@@ -7240,7 +7252,7 @@ void Node3DEditorPlugin::snap_cursor_to_plane(const Plane &p_plane) {
struct _GizmoPluginPriorityComparator {
bool operator()(const Ref<EditorNode3DGizmoPlugin> &p_a, const Ref<EditorNode3DGizmoPlugin> &p_b) const {
if (p_a->get_priority() == p_b->get_priority()) {
- return p_a->get_name() < p_b->get_name();
+ return p_a->get_gizmo_name() < p_b->get_gizmo_name();
}
return p_a->get_priority() > p_b->get_priority();
}
@@ -7248,7 +7260,7 @@ struct _GizmoPluginPriorityComparator {
struct _GizmoPluginNameComparator {
bool operator()(const Ref<EditorNode3DGizmoPlugin> &p_a, const Ref<EditorNode3DGizmoPlugin> &p_b) const {
- return p_a->get_name() < p_b->get_name();
+ return p_a->get_gizmo_name() < p_b->get_gizmo_name();
}
};
@@ -7459,7 +7471,7 @@ void EditorNode3DGizmoPlugin::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_material", "name", "gizmo"), &EditorNode3DGizmoPlugin::get_material, DEFVAL(Ref<EditorNode3DGizmo>()));
- BIND_VMETHOD(MethodInfo(Variant::STRING, "get_name"));
+ BIND_VMETHOD(MethodInfo(Variant::STRING, "get_gizmo_name"));
BIND_VMETHOD(MethodInfo(Variant::INT, "get_priority"));
BIND_VMETHOD(MethodInfo(Variant::BOOL, "can_be_hidden"));
BIND_VMETHOD(MethodInfo(Variant::BOOL, "is_selectable_when_hidden"));
diff --git a/editor/plugins/node_3d_editor_plugin.h b/editor/plugins/node_3d_editor_plugin.h
index cf4aa33257..ff4a941b06 100644
--- a/editor/plugins/node_3d_editor_plugin.h
+++ b/editor/plugins/node_3d_editor_plugin.h
@@ -99,6 +99,7 @@ protected:
public:
void add_lines(const Vector<Vector3> &p_lines, const Ref<Material> &p_material, bool p_billboard = false, const Color &p_modulate = Color(1, 1, 1));
+ void add_vertices(const Vector<Vector3> &p_vertices, const Ref<Material> &p_material, Mesh::PrimitiveType p_primitive_type, bool p_billboard = false, const Color &p_modulate = Color(1, 1, 1));
void add_mesh(const Ref<ArrayMesh> &p_mesh, bool p_billboard = false, const Ref<SkinReference> &p_skin_reference = Ref<SkinReference>(), const Ref<Material> &p_material = Ref<Material>());
void add_collision_segments(const Vector<Vector3> &p_lines);
void add_collision_triangles(const Ref<TriangleMesh> &p_tmesh);
diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp
index a6afd45686..8bf5d0611d 100644
--- a/editor/plugins/script_editor_plugin.cpp
+++ b/editor/plugins/script_editor_plugin.cpp
@@ -755,8 +755,8 @@ void ScriptEditor::_close_tab(int p_idx, bool p_save, bool p_history_back) {
_save_layout();
}
-void ScriptEditor::_close_current_tab() {
- _close_tab(tab_container->get_current_tab());
+void ScriptEditor::_close_current_tab(bool p_save) {
+ _close_tab(tab_container->get_current_tab(), p_save);
}
void ScriptEditor::_close_discard_current_tab(const String &p_str) {
@@ -802,7 +802,7 @@ void ScriptEditor::_close_other_tabs() {
}
}
- _close_current_tab();
+ _close_current_tab(false);
}
}
@@ -820,7 +820,7 @@ void ScriptEditor::_close_all_tabs() {
}
}
- _close_current_tab();
+ _close_current_tab(false);
}
}
@@ -1372,7 +1372,7 @@ void ScriptEditor::_menu_option(int p_option) {
if (current->is_unsaved()) {
_ask_close_current_unsaved_tab(current);
} else {
- _close_current_tab();
+ _close_current_tab(false);
}
} break;
case FILE_COPY_PATH: {
@@ -3497,7 +3497,7 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) {
erase_tab_confirm = memnew(ConfirmationDialog);
erase_tab_confirm->get_ok_button()->set_text(TTR("Save"));
erase_tab_confirm->add_button(TTR("Discard"), DisplayServer::get_singleton()->get_swap_cancel_ok(), "discard");
- erase_tab_confirm->connect("confirmed", callable_mp(this, &ScriptEditor::_close_current_tab));
+ erase_tab_confirm->connect("confirmed", callable_mp(this, &ScriptEditor::_close_current_tab), varray(true));
erase_tab_confirm->connect("custom_action", callable_mp(this, &ScriptEditor::_close_discard_current_tab));
add_child(erase_tab_confirm);
diff --git a/editor/plugins/script_editor_plugin.h b/editor/plugins/script_editor_plugin.h
index 09ed3854ea..b2172e7f10 100644
--- a/editor/plugins/script_editor_plugin.h
+++ b/editor/plugins/script_editor_plugin.h
@@ -325,7 +325,7 @@ class ScriptEditor : public PanelContainer {
void _close_tab(int p_idx, bool p_save = true, bool p_history_back = true);
- void _close_current_tab();
+ void _close_current_tab(bool p_save = true);
void _close_discard_current_tab(const String &p_str);
void _close_docs_tab();
void _close_other_tabs();
diff --git a/editor/plugins/tile_map_editor_plugin.cpp b/editor/plugins/tile_map_editor_plugin.cpp
index 6a16aa28a9..74b01b3c36 100644
--- a/editor/plugins/tile_map_editor_plugin.cpp
+++ b/editor/plugins/tile_map_editor_plugin.cpp
@@ -40,9 +40,12 @@
void TileMapEditor::_node_removed(Node *p_node) {
if (p_node == node && node) {
- // Fixes #44824, which describes a situation where you can reselect a TileMap node without first de-selecting it when switching scenes.
- node->disconnect("settings_changed", callable_mp(this, &TileMapEditor::_tileset_settings_changed));
+ Callable callable_tileset_settings_changed = callable_mp(this, &TileMapEditor::_tileset_settings_changed);
+ if (node->is_connected("settings_changed", callable_tileset_settings_changed)) {
+ // Fixes #44824, which describes a situation where you can reselect a TileMap node without first de-selecting it when switching scenes.
+ node->disconnect("settings_changed", callable_tileset_settings_changed);
+ }
node = nullptr;
}
}
@@ -1870,7 +1873,11 @@ void TileMapEditor::edit(Node *p_tile_map) {
}
if (node) {
- node->disconnect("settings_changed", callable_mp(this, &TileMapEditor::_tileset_settings_changed));
+ Callable callable_tileset_settings_changed = callable_mp(this, &TileMapEditor::_tileset_settings_changed);
+
+ if (node->is_connected("settings_changed", callable_tileset_settings_changed)) {
+ node->disconnect("settings_changed", callable_tileset_settings_changed);
+ }
}
if (p_tile_map) {
node = Object::cast_to<TileMap>(p_tile_map);
@@ -1897,7 +1904,11 @@ void TileMapEditor::edit(Node *p_tile_map) {
}
if (node) {
- node->connect("settings_changed", callable_mp(this, &TileMapEditor::_tileset_settings_changed));
+ Callable callable_tileset_settings_changed = callable_mp(this, &TileMapEditor::_tileset_settings_changed);
+
+ if (!node->is_connected("settings_changed", callable_tileset_settings_changed)) {
+ node->connect("settings_changed", callable_tileset_settings_changed);
+ }
}
_clear_bucket_cache();
diff --git a/editor/project_settings_editor.cpp b/editor/project_settings_editor.cpp
index 6e2cd72796..de7996eaa2 100644
--- a/editor/project_settings_editor.cpp
+++ b/editor/project_settings_editor.cpp
@@ -52,6 +52,7 @@ void ProjectSettingsEditor::popup_project_settings() {
localization_editor->update_translations();
autoload_settings->update_autoload();
plugin_settings->update_plugins();
+ import_defaults_editor->clear();
}
void ProjectSettingsEditor::queue_save() {
@@ -257,6 +258,7 @@ void ProjectSettingsEditor::_add_feature_overrides() {
}
void ProjectSettingsEditor::_editor_restart() {
+ ProjectSettings::get_singleton()->save();
EditorNode::get_singleton()->save_all_scenes();
EditorNode::get_singleton()->restart_editor();
}
@@ -648,7 +650,7 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) {
action_map->connect("action_removed", callable_mp(this, &ProjectSettingsEditor::_action_removed));
action_map->connect("action_renamed", callable_mp(this, &ProjectSettingsEditor::_action_renamed));
action_map->connect("action_reordered", callable_mp(this, &ProjectSettingsEditor::_action_reordered));
- action_map->set_toggle_editable_label(TTR("Show built-in Actions"));
+ action_map->set_toggle_editable_label(TTR("Show Built-in Actions"));
action_map->set_show_uneditable(false);
tab_container->add_child(action_map);
@@ -692,4 +694,9 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) {
}
inspector->set_restrict_to_basic_settings(!use_advanced);
+
+ import_defaults_editor = memnew(ImportDefaultsEditor);
+ import_defaults_editor->set_name(TTR("Import Defaults"));
+ tab_container->add_child(import_defaults_editor);
+ import_defaults_editor->connect("project_settings_changed", callable_mp(this, &ProjectSettingsEditor::queue_save));
}
diff --git a/editor/project_settings_editor.h b/editor/project_settings_editor.h
index c28785bb27..cde46ac4c4 100644
--- a/editor/project_settings_editor.h
+++ b/editor/project_settings_editor.h
@@ -36,6 +36,7 @@
#include "editor/editor_data.h"
#include "editor/editor_plugin_settings.h"
#include "editor/editor_sectioned_inspector.h"
+#include "editor/import_defaults_editor.h"
#include "editor/localization_editor.h"
#include "editor/shader_globals_editor.h"
#include "editor_autoload_settings.h"
@@ -75,6 +76,7 @@ class ProjectSettingsEditor : public AcceptDialog {
PanelContainer *restart_container;
Button *restart_close_button;
+ ImportDefaultsEditor *import_defaults_editor;
EditorData *data;
UndoRedo *undo_redo;
diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp
index 48c4d33184..16a0576af4 100644
--- a/editor/scene_tree_dock.cpp
+++ b/editor/scene_tree_dock.cpp
@@ -219,6 +219,9 @@ void SceneTreeDock::_perform_instance_scenes(const Vector<String> &p_files, Node
editor_data->get_undo_redo().commit_action();
editor->push_item(instances[instances.size() - 1]);
+ for (int i = 0; i < instances.size(); i++) {
+ emit_signal("node_created", instances[i]);
+ }
}
void SceneTreeDock::_replace_with_branch_scene(const String &p_file, Node *base) {
@@ -347,6 +350,11 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
break;
}
+ if (reset_create_dialog) {
+ create_dialog->set_base_type("Node");
+ reset_create_dialog = false;
+ }
+
// Prefer nodes that inherit from the current scene root.
Node *current_edited_scene_root = EditorNode::get_singleton()->get_edited_scene();
if (current_edited_scene_root) {
@@ -367,6 +375,9 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
}
create_dialog->popup_create(true);
+ if (!p_confirm_override) {
+ emit_signal("add_node_used");
+ }
} break;
case TOOL_INSTANCE: {
if (!profile_allow_editing) {
@@ -381,7 +392,9 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
quick_open->popup_dialog("PackedScene", true);
quick_open->set_title(TTR("Instance Child Scene"));
-
+ if (!p_confirm_override) {
+ emit_signal("add_node_used");
+ }
} break;
case TOOL_EXPAND_COLLAPSE: {
if (!scene_tree->get_selected()) {
@@ -514,6 +527,14 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
break;
}
+ if (!_validate_no_foreign()) {
+ break;
+ }
+
+ if (!_validate_no_instance()) {
+ break;
+ }
+
Node *selected = scene_tree->get_selected();
if (!selected && !editor_selection->get_selected_node_list().is_empty()) {
selected = editor_selection->get_selected_node_list().front()->get();
@@ -1615,6 +1636,20 @@ bool SceneTreeDock::_validate_no_foreign() {
return true;
}
+bool SceneTreeDock::_validate_no_instance() {
+ List<Node *> selection = editor_selection->get_selected_node_list();
+
+ for (List<Node *>::Element *E = selection.front(); E; E = E->next()) {
+ if (E->get() != edited_scene && E->get()->get_filename() != "") {
+ accept->set_text(TTR("This operation can't be done on instanced scenes."));
+ accept->popup_centered();
+ return false;
+ }
+ }
+
+ return true;
+}
+
void SceneTreeDock::_node_reparent(NodePath p_path, bool p_keep_global_xform) {
Node *new_parent = scene_root->get_node(p_path);
ERR_FAIL_COND(!new_parent);
@@ -2079,6 +2114,8 @@ void SceneTreeDock::_do_create(Node *p_parent) {
}
ct->set_size(ms);
}
+
+ emit_signal("node_created", c);
}
void SceneTreeDock::_create() {
@@ -2299,10 +2336,14 @@ void SceneTreeDock::_new_scene_from(String p_file) {
Node *base = selection.front()->get();
- Map<Node *, Node *> reown;
- reown[editor_data->get_edited_scene_root()] = base;
- Node *copy = base->duplicate_and_reown(reown);
+ Map<const Node *, Node *> duplimap;
+ Node *copy = base->duplicate_from_editor(duplimap);
+
if (copy) {
+ for (int i = 0; i < copy->get_child_count(); i++) {
+ _set_node_owner_recursive(copy->get_child(i), copy);
+ }
+
Ref<PackedScene> sdata = memnew(PackedScene);
Error err = sdata->pack(copy);
memdelete(copy);
@@ -2332,6 +2373,16 @@ void SceneTreeDock::_new_scene_from(String p_file) {
}
}
+void SceneTreeDock::_set_node_owner_recursive(Node *p_node, Node *p_owner) {
+ if (!p_node->get_owner()) {
+ p_node->set_owner(p_owner);
+ }
+
+ for (int i = 0; i < p_node->get_child_count(); i++) {
+ _set_node_owner_recursive(p_node->get_child(i), p_owner);
+ }
+}
+
static bool _is_node_visible(Node *p_node) {
if (!p_node->get_owner()) {
return false;
@@ -2581,7 +2632,18 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) {
if (full_selection.size() == 1) {
menu->add_icon_shortcut(get_theme_icon("Rename", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/rename"), TOOL_RENAME);
}
- menu->add_icon_shortcut(get_theme_icon("Reload", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/change_node_type"), TOOL_REPLACE);
+
+ bool can_replace = true;
+ for (List<Node *>::Element *E = selection.front(); E; E = E->next()) {
+ if (E->get() != edited_scene && (E->get()->get_owner() != edited_scene || E->get()->get_filename() != "")) {
+ can_replace = false;
+ break;
+ }
+ }
+
+ if (can_replace) {
+ menu->add_icon_shortcut(get_theme_icon("Reload", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/change_node_type"), TOOL_REPLACE);
+ }
if (scene_tree->get_selected() != edited_scene) {
menu->add_separator();
@@ -2743,6 +2805,16 @@ void SceneTreeDock::open_script_dialog(Node *p_for_node, bool p_extend) {
}
}
+void SceneTreeDock::open_add_child_dialog() {
+ create_dialog->set_base_type("CanvasItem");
+ _tool_selected(TOOL_NEW, true);
+ reset_create_dialog = true;
+}
+
+void SceneTreeDock::open_instance_child_dialog() {
+ _tool_selected(TOOL_INSTANCE, true);
+}
+
void SceneTreeDock::add_remote_tree_editor(Control *p_remote) {
ERR_FAIL_COND(remote_tree != nullptr);
add_child(p_remote);
@@ -2942,6 +3014,8 @@ void SceneTreeDock::_bind_methods() {
ClassDB::bind_method(D_METHOD("replace_node"), &SceneTreeDock::replace_node);
ADD_SIGNAL(MethodInfo("remote_tree_selected"));
+ ADD_SIGNAL(MethodInfo("add_node_used"));
+ ADD_SIGNAL(MethodInfo("node_created", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node")));
}
SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSelection *p_editor_selection, EditorData &p_editor_data) {
diff --git a/editor/scene_tree_dock.h b/editor/scene_tree_dock.h
index 9bc281c7fb..aa62c93cb5 100644
--- a/editor/scene_tree_dock.h
+++ b/editor/scene_tree_dock.h
@@ -100,6 +100,7 @@ class SceneTreeDock : public VBoxContainer {
Vector<ObjectID> subresources;
bool restore_script_editor_on_drag;
+ bool reset_create_dialog = false;
int current_option;
CreateDialog *create_dialog;
@@ -203,8 +204,10 @@ class SceneTreeDock : public VBoxContainer {
void _import_subscene();
void _new_scene_from(String p_file);
+ void _set_node_owner_recursive(Node *p_node, Node *p_owner);
bool _validate_no_foreign();
+ bool _validate_no_instance();
void _selection_changed();
void _update_script_button();
@@ -272,6 +275,9 @@ public:
void attach_script_to_selected(bool p_extend);
void open_script_dialog(Node *p_for_node, bool p_extend);
+ void open_add_child_dialog();
+ void open_instance_child_dialog();
+
ScriptCreateDialog *get_script_create_dialog() { return script_create_dialog; }
SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSelection *p_editor_selection, EditorData &p_editor_data);
diff --git a/editor/shader_globals_editor.cpp b/editor/shader_globals_editor.cpp
index a61b4aa3b9..ebef5be9ed 100644
--- a/editor/shader_globals_editor.cpp
+++ b/editor/shader_globals_editor.cpp
@@ -483,8 +483,5 @@ ShaderGlobalsEditor::ShaderGlobalsEditor() {
}
ShaderGlobalsEditor::~ShaderGlobalsEditor() {
- if (is_visible_in_tree()) {
- inspector->edit(nullptr);
- }
memdelete(interface);
}
diff --git a/main/main.cpp b/main/main.cpp
index 71dd3cf1ad..9c8909f8fb 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -538,8 +538,8 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
// Only flush stdout in debug builds by default, as spamming `print()` will
// decrease performance if this is enabled.
- GLOBAL_DEF("application/run/flush_stdout_on_print", false);
- GLOBAL_DEF("application/run/flush_stdout_on_print.debug", true);
+ GLOBAL_DEF_RST("application/run/flush_stdout_on_print", false);
+ GLOBAL_DEF_RST("application/run/flush_stdout_on_print.debug", true);
GLOBAL_DEF("debug/settings/crash_handler/message",
String("Please include this when reporting the bug on https://github.com/godotengine/godot/issues"));
@@ -1174,6 +1174,8 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
_print_line_enabled = false;
}
+ Logger::set_flush_stdout_on_print(ProjectSettings::get_singleton()->get("application/run/flush_stdout_on_print"));
+
OS::get_singleton()->set_cmdline(execpath, main_args);
GLOBAL_DEF("rendering/driver/driver_name", "Vulkan");
@@ -1742,6 +1744,19 @@ Error Main::setup2(Thread::ID p_main_tid_override) {
register_scene_types();
+#ifdef TOOLS_ENABLED
+ ClassDB::set_current_api(ClassDB::API_EDITOR);
+ EditorNode::register_editor_types();
+
+ ClassDB::set_current_api(ClassDB::API_CORE);
+
+#endif
+
+ MAIN_PRINT("Main: Load Modules, Physics, Drivers, Scripts");
+
+ register_platform_apis();
+ register_module_types();
+
GLOBAL_DEF("display/mouse_cursor/custom_image", String());
GLOBAL_DEF("display/mouse_cursor/custom_image_hotspot", Vector2());
GLOBAL_DEF("display/mouse_cursor/tooltip_position_offset", Point2(10, 10));
@@ -1758,18 +1773,6 @@ Error Main::setup2(Thread::ID p_main_tid_override) {
Input::get_singleton()->set_custom_mouse_cursor(cursor, Input::CURSOR_ARROW, hotspot);
}
}
-#ifdef TOOLS_ENABLED
- ClassDB::set_current_api(ClassDB::API_EDITOR);
- EditorNode::register_editor_types();
-
- ClassDB::set_current_api(ClassDB::API_CORE);
-
-#endif
-
- MAIN_PRINT("Main: Load Modules, Physics, Drivers, Scripts");
-
- register_platform_apis();
- register_module_types();
camera_server = CameraServer::create();
@@ -1992,7 +1995,7 @@ bool Main::start() {
if (check_only) {
if (!script_res->is_valid()) {
- OS::get_singleton()->set_exit_code(1);
+ OS::get_singleton()->set_exit_code(EXIT_FAILURE);
}
return false;
}
diff --git a/methods.py b/methods.py
index 45cfe00959..9b29eadc16 100644
--- a/methods.py
+++ b/methods.py
@@ -6,9 +6,11 @@ from collections import OrderedDict
# We need to define our own `Action` method to control the verbosity of output
# and whenever we need to run those commands in a subprocess on some platforms.
-from SCons.Script import Action
from SCons import Node
+from SCons.Script import Action
+from SCons.Script import ARGUMENTS
from SCons.Script import Glob
+from SCons.Variables.BoolVariable import _text2bool
from platform_methods import run_in_subprocess
@@ -145,6 +147,17 @@ def parse_cg_file(fname, uniforms, sizes, conditionals):
fs.close()
+def get_cmdline_bool(option, default):
+ """We use `ARGUMENTS.get()` to check if options were manually overridden on the command line,
+ and SCons' _text2bool helper to convert them to booleans, otherwise they're handled as strings.
+ """
+ cmdline_val = ARGUMENTS.get(option)
+ if cmdline_val is not None:
+ return _text2bool(cmdline_val)
+ else:
+ return default
+
+
def detect_modules(search_path, recursive=False):
"""Detects and collects a list of C++ modules at specified path
diff --git a/misc/dist/html/editor.html b/misc/dist/html/editor.html
index b4a8c69cc6..535721f418 100644
--- a/misc/dist/html/editor.html
+++ b/misc/dist/html/editor.html
@@ -4,9 +4,8 @@
<meta charset='utf-8' />
<meta name='viewport' content='width=device-width, user-scalable=no' />
<link id='-gd-engine-icon' rel='icon' type='image/png' href='favicon.png' />
- <title>Godot Engine Web Editor ($GODOT_VERSION)</title>
- <style type='text/css'>
-
+ <title>Godot Engine Web Editor (@GODOT_VERSION@)</title>
+ <style>
*:focus {
/* More visible outline for better keyboard navigation. */
outline: 0.125rem solid hsl(220, 100%, 62.5%);
@@ -40,13 +39,30 @@
filter: brightness(82.5%);
}
+ #tabs-buttons {
+ /* Match the default background color of the editor window for a seamless appearance. */
+ background-color: #202531;
+ }
+
+ #tab-game {
+ /* Use a pure black background to better distinguish the running project */
+ /* from the editor window, and to use a more neutral background color (no tint). */
+ background-color: black;
+ /* Make the background span the entire page height. */
+ min-height: 100vh;
+ }
+
#canvas, #gameCanvas {
display: block;
margin: 0;
color: white;
}
- #canvas:focus, #gameCanvas:focus {
+ /* Don't show distracting focus outlines for the main tabs' contents. */
+ #tab-editor canvas:focus,
+ #tab-game canvas:focus,
+ #canvas:focus,
+ #gameCanvas:focus {
outline: none;
}
@@ -189,7 +205,7 @@
<br />
<img src="logo.svg" width="1024" height="414" style="width: auto; height: auto; max-width: 85%; max-height: 250px" />
<br />
- $GODOT_VERSION
+ @GODOT_VERSION@
<br />
<a href="releases/">Need an old version?</a>
<br />
@@ -200,9 +216,11 @@
<a href="demo.zip">(Try this for example)</a>
<br />
<br />
- <button id="startButton" class="btn" style="margin-bottom: 4rem">Start Godot editor</button>
+ <button id="startButton" class="btn" style="margin-bottom: 4rem; font-weight: 700">Start Godot editor</button>
+ <br />
+ <button class="btn" onclick="clearPersistence()" style="margin-bottom: 1.5rem">Clear persistent data</button>
<br />
- <button class="btn" onclick="clearPersistence()">Clear persistent data</button>
+ <a href="https://docs.godotengine.org/en/latest/tutorials/editor/using_the_web_editor.html">Web editor documentation</a>
</div>
</div>
<div id='tab-editor' style="display: none;">
@@ -233,8 +251,8 @@
</div>
</div>
- <script type='text/javascript' src='godot.tools.js'></script>
- <script type='text/javascript'>//<![CDATA[
+ <script src='godot.tools.js'></script>
+ <script>//<![CDATA[
var editor = null;
var game = null;
@@ -258,7 +276,7 @@
});
}
- if (!window.confirm("Are you sure you want to delete all the locally stored files?")) {
+ if (!window.confirm("Are you sure you want to delete all the locally stored files?\nClicking \"OK\" will permanently remove your projects and editor settings!")) {
return;
}
Promise.all([
diff --git a/misc/dist/linux/org.godotengine.Godot.xml b/misc/dist/linux/org.godotengine.Godot.xml
new file mode 100644
index 0000000000..2f647f71a6
--- /dev/null
+++ b/misc/dist/linux/org.godotengine.Godot.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0"?>
+<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
+ <mime-type type="application/x-godot-project">
+ <comment>Godot Engine project</comment>
+ <icon name="x-godot-project" />
+ <glob pattern="*.godot"/>
+ </mime-type>
+
+ <mime-type type="application/x-godot-resource">
+ <comment>Godot Engine resource</comment>
+ <icon name="x-godot-resource" />
+ <glob pattern="*.res"/>
+ <glob pattern="*.tres"/>
+ </mime-type>
+
+ <mime-type type="application/x-godot-scene">
+ <comment>Godot Engine scene</comment>
+ <icon name="x-godot-scene" />
+ <glob pattern="*.scn"/>
+ <glob pattern="*.tscn"/>
+ <glob pattern="*.escn"/>
+ </mime-type>
+
+ <mime-type type="application/x-gdscript">
+ <comment>GDScript script</comment>
+ <icon name="x-gdscript" />
+ <glob pattern="*.gd"/>
+ </mime-type>
+</mime-info>
diff --git a/misc/dist/linux/x-godot-project.xml b/misc/dist/linux/x-godot-project.xml
deleted file mode 100644
index 9f28bab2ae..0000000000
--- a/misc/dist/linux/x-godot-project.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-<?xml version="1.0"?>
-<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
- <mime-type type="application/x-godot-project">
- <comment>Godot Engine project</comment>
- <icon name="godot" />
- <glob pattern="*.godot" weight="100" />
- </mime-type>
-</mime-info>
diff --git a/modules/bullet/collision_object_bullet.cpp b/modules/bullet/collision_object_bullet.cpp
index bce8ec8076..d9f5beb5a1 100644
--- a/modules/bullet/collision_object_bullet.cpp
+++ b/modules/bullet/collision_object_bullet.cpp
@@ -148,6 +148,9 @@ void CollisionObjectBullet::add_collision_exception(const CollisionObjectBullet
void CollisionObjectBullet::remove_collision_exception(const CollisionObjectBullet *p_ignoreCollisionObject) {
exceptions.erase(p_ignoreCollisionObject->get_self());
+ if (!bt_collision_object) {
+ return;
+ }
bt_collision_object->setIgnoreCollisionCheck(p_ignoreCollisionObject->bt_collision_object, false);
if (space) {
space->get_broadphase()->getOverlappingPairCache()->cleanProxyFromPairs(bt_collision_object->getBroadphaseHandle(), space->get_dispatcher());
@@ -155,11 +158,14 @@ void CollisionObjectBullet::remove_collision_exception(const CollisionObjectBull
}
bool CollisionObjectBullet::has_collision_exception(const CollisionObjectBullet *p_otherCollisionObject) const {
- return !bt_collision_object->checkCollideWith(p_otherCollisionObject->bt_collision_object);
+ return exceptions.has(p_otherCollisionObject->get_self());
}
void CollisionObjectBullet::set_collision_enabled(bool p_enabled) {
collisionsEnabled = p_enabled;
+ if (!bt_collision_object) {
+ return;
+ }
if (collisionsEnabled) {
bt_collision_object->setCollisionFlags(bt_collision_object->getCollisionFlags() & (~btCollisionObject::CF_NO_CONTACT_RESPONSE));
} else {
diff --git a/modules/enet/doc_classes/NetworkedMultiplayerENet.xml b/modules/enet/doc_classes/NetworkedMultiplayerENet.xml
index 6c713fa1ce..c8f32ffde6 100644
--- a/modules/enet/doc_classes/NetworkedMultiplayerENet.xml
+++ b/modules/enet/doc_classes/NetworkedMultiplayerENet.xml
@@ -124,6 +124,22 @@
Configure the [CryptoKey] to use when [member use_dtls] is [code]true[/code]. Remember to also call [method set_dtls_certificate] to setup your [X509Certificate].
</description>
</method>
+ <method name="set_peer_timeout">
+ <return type="void">
+ </return>
+ <argument index="0" name="id" type="int">
+ </argument>
+ <argument index="1" name="timeout_limit" type="int">
+ </argument>
+ <argument index="2" name="timeout_min" type="int">
+ </argument>
+ <argument index="3" name="timeout_max" type="int">
+ </argument>
+ <description>
+ Sets the timeout parameters for a peer. The timeout parameters control how and when a peer will timeout from a failure to acknowledge reliable traffic. Timeout values are expressed in milliseconds.
+ The [code]timeout_limit[/code] is a factor that, multiplied by a value based on the avarage round trip time, will determine the timeout limit for a reliable packet. When that limit is reached, the timeout will be doubled, and the peer will be disconnected if that limit has reached [code]timeout_min[/code]. The [code]timeout_max[/code] parameter, on the other hand, defines a fixed timeout for which any packet must be acknowledged or the peer will be dropped.
+ </description>
+ </method>
</methods>
<members>
<member name="always_ordered" type="bool" setter="set_always_ordered" getter="is_always_ordered" default="false">
diff --git a/modules/enet/networked_multiplayer_enet.cpp b/modules/enet/networked_multiplayer_enet.cpp
index 91984b8928..276f13e553 100644
--- a/modules/enet/networked_multiplayer_enet.cpp
+++ b/modules/enet/networked_multiplayer_enet.cpp
@@ -784,6 +784,14 @@ int NetworkedMultiplayerENet::get_peer_port(int p_peer_id) const {
#endif
}
+void NetworkedMultiplayerENet::set_peer_timeout(int p_peer_id, int p_timeout_limit, int p_timeout_min, int p_timeout_max) {
+ ERR_FAIL_COND_MSG(!peer_map.has(p_peer_id), vformat("Peer ID %d not found in the list of peers.", p_peer_id));
+ ERR_FAIL_COND_MSG(!is_server() && p_peer_id != 1, "Can't change the timeout of peers other then the server when acting as a client.");
+ ERR_FAIL_COND_MSG(peer_map[p_peer_id] == nullptr, vformat("Peer ID %d found in the list of peers, but is null.", p_peer_id));
+ ERR_FAIL_COND_MSG(p_timeout_limit > p_timeout_min || p_timeout_min > p_timeout_max, "Timeout limit must be less than minimum timeout, which itself must be less then maximum timeout");
+ enet_peer_timeout(peer_map[p_peer_id], p_timeout_limit, p_timeout_min, p_timeout_max);
+}
+
void NetworkedMultiplayerENet::set_transfer_channel(int p_channel) {
ERR_FAIL_COND_MSG(p_channel < -1 || p_channel >= channel_count, vformat("The transfer channel must be set between 0 and %d, inclusive (got %d).", channel_count - 1, p_channel));
ERR_FAIL_COND_MSG(p_channel == SYSCH_CONFIG, vformat("The channel %d is reserved.", SYSCH_CONFIG));
@@ -838,6 +846,7 @@ void NetworkedMultiplayerENet::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_dtls_verify_enabled"), &NetworkedMultiplayerENet::is_dtls_verify_enabled);
ClassDB::bind_method(D_METHOD("get_peer_address", "id"), &NetworkedMultiplayerENet::get_peer_address);
ClassDB::bind_method(D_METHOD("get_peer_port", "id"), &NetworkedMultiplayerENet::get_peer_port);
+ ClassDB::bind_method(D_METHOD("set_peer_timeout", "id", "timeout_limit", "timeout_min", "timeout_max"), &NetworkedMultiplayerENet::set_peer_timeout);
ClassDB::bind_method(D_METHOD("get_packet_channel"), &NetworkedMultiplayerENet::get_packet_channel);
ClassDB::bind_method(D_METHOD("get_last_packet_channel"), &NetworkedMultiplayerENet::get_last_packet_channel);
diff --git a/modules/enet/networked_multiplayer_enet.h b/modules/enet/networked_multiplayer_enet.h
index eb70d71c2c..b99b14d218 100644
--- a/modules/enet/networked_multiplayer_enet.h
+++ b/modules/enet/networked_multiplayer_enet.h
@@ -127,6 +127,7 @@ public:
virtual IP_Address get_peer_address(int p_peer_id) const;
virtual int get_peer_port(int p_peer_id) const;
+ void set_peer_timeout(int p_peer_id, int p_timeout_limit, int p_timeout_min, int p_timeout_max);
Error create_server(int p_port, int p_max_clients = 32, int p_in_bandwidth = 0, int p_out_bandwidth = 0);
Error create_client(const String &p_address, int p_port, int p_in_bandwidth = 0, int p_out_bandwidth = 0, int p_client_port = 0);
diff --git a/modules/gdnative/include/text/godot_text.h b/modules/gdnative/include/text/godot_text.h
index ef60633a3d..86fc745134 100644
--- a/modules/gdnative/include/text/godot_text.h
+++ b/modules/gdnative/include/text/godot_text.h
@@ -74,6 +74,10 @@ typedef struct {
godot_rid (*create_font_system)(void *, const godot_string *, int);
godot_rid (*create_font_resource)(void *, const godot_string *, int);
godot_rid (*create_font_memory)(void *, const uint8_t *, size_t, godot_string *, int);
+ godot_rid (*create_font_bitmap)(void *, float, float, int);
+ void (*font_bitmap_add_texture)(void *, godot_rid *, const godot_object *);
+ void (*font_bitmap_add_char)(void *, godot_rid *, char32_t, int, const godot_rect2 *, const godot_vector2 *, float);
+ void (*font_bitmap_add_kerning_pair)(void *, godot_rid *, char32_t, char32_t, int);
float (*font_get_height)(void *, godot_rid *, int);
float (*font_get_ascent)(void *, godot_rid *, int);
float (*font_get_descent)(void *, godot_rid *, int);
diff --git a/modules/gdnative/text/text_server_gdnative.cpp b/modules/gdnative/text/text_server_gdnative.cpp
index 7584568dd8..7cd8de5f2e 100644
--- a/modules/gdnative/text/text_server_gdnative.cpp
+++ b/modules/gdnative/text/text_server_gdnative.cpp
@@ -113,6 +113,28 @@ RID TextServerGDNative::create_font_memory(const uint8_t *p_data, size_t p_size,
return rid;
}
+RID TextServerGDNative::create_font_bitmap(float p_height, float p_ascent, int p_base_size) {
+ ERR_FAIL_COND_V(interface == nullptr, RID());
+ godot_rid result = interface->create_font_bitmap(data, p_height, p_ascent, p_base_size);
+ RID rid = *(RID *)&result;
+ return rid;
+}
+
+void TextServerGDNative::font_bitmap_add_texture(RID p_font, const Ref<Texture> &p_texture) {
+ ERR_FAIL_COND(interface == nullptr);
+ interface->font_bitmap_add_texture(data, (godot_rid *)&p_font, (const godot_object *)p_texture.ptr());
+}
+
+void TextServerGDNative::font_bitmap_add_char(RID p_font, char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance) {
+ ERR_FAIL_COND(interface == nullptr);
+ interface->font_bitmap_add_char(data, (godot_rid *)&p_font, p_char, p_texture_idx, (const godot_rect2 *)&p_rect, (const godot_vector2 *)&p_align, p_advance);
+}
+
+void TextServerGDNative::font_bitmap_add_kerning_pair(RID p_font, char32_t p_A, char32_t p_B, int p_kerning) {
+ ERR_FAIL_COND(interface == nullptr);
+ interface->font_bitmap_add_kerning_pair(data, (godot_rid *)&p_font, p_A, p_B, p_kerning);
+}
+
float TextServerGDNative::font_get_height(RID p_font, int p_size) const {
ERR_FAIL_COND_V(interface == nullptr, 0.f);
return interface->font_get_height(data, (godot_rid *)&p_font, p_size);
diff --git a/modules/gdnative/text/text_server_gdnative.h b/modules/gdnative/text/text_server_gdnative.h
index 9783b65431..931bb44885 100644
--- a/modules/gdnative/text/text_server_gdnative.h
+++ b/modules/gdnative/text/text_server_gdnative.h
@@ -64,6 +64,11 @@ public:
virtual RID create_font_system(const String &p_name, int p_base_size = 16) override;
virtual RID create_font_resource(const String &p_filename, int p_base_size = 16) override;
virtual RID create_font_memory(const uint8_t *p_data, size_t p_size, const String &p_type, int p_base_size = 16) override;
+ virtual RID create_font_bitmap(float p_height, float p_ascent, int p_base_size = 16) override;
+
+ virtual void font_bitmap_add_texture(RID p_font, const Ref<Texture> &p_texture) override;
+ virtual void font_bitmap_add_char(RID p_font, char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance) override;
+ virtual void font_bitmap_add_kerning_pair(RID p_font, char32_t p_A, char32_t p_B, int p_kerning) override;
virtual float font_get_height(RID p_font, int p_size) const override;
virtual float font_get_ascent(RID p_font, int p_size) const override;
diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp
index b491440d4c..06d628d23f 100644
--- a/modules/gdscript/gdscript_compiler.cpp
+++ b/modules/gdscript/gdscript_compiler.cpp
@@ -255,36 +255,59 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
}
// Try class constants.
- GDScript *owner = codegen.script;
- while (owner) {
- GDScript *scr = owner;
- GDScriptNativeClass *nc = nullptr;
- while (scr) {
- if (scr->constants.has(identifier)) {
- return GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::CLASS_CONSTANT, gen->add_or_get_name(identifier)); // TODO: Get type here.
+ {
+ GDScript *owner = codegen.script;
+ while (owner) {
+ GDScript *scr = owner;
+ GDScriptNativeClass *nc = nullptr;
+ while (scr) {
+ if (scr->constants.has(identifier)) {
+ return GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::CLASS_CONSTANT, gen->add_or_get_name(identifier)); // TODO: Get type here.
+ }
+ if (scr->native.is_valid()) {
+ nc = scr->native.ptr();
+ }
+ scr = scr->_base;
}
- if (scr->native.is_valid()) {
- nc = scr->native.ptr();
+
+ // Class C++ integer constant.
+ if (nc) {
+ bool success = false;
+ int constant = ClassDB::get_integer_constant(nc->get_name(), identifier, &success);
+ if (success) {
+ return codegen.add_constant(constant);
+ }
}
- scr = scr->_base;
+
+ owner = owner->_owner;
}
+ }
- // Class C++ integer constant.
- if (nc) {
- bool success = false;
- int constant = ClassDB::get_integer_constant(nc->get_name(), identifier, &success);
- if (success) {
- return codegen.add_constant(constant);
+ // Try signals and methods (can be made callables).
+ {
+ if (codegen.class_node->members_indices.has(identifier)) {
+ const GDScriptParser::ClassNode::Member &member = codegen.class_node->members[codegen.class_node->members_indices[identifier]];
+ if (member.type == GDScriptParser::ClassNode::Member::FUNCTION || member.type == GDScriptParser::ClassNode::Member::SIGNAL) {
+ // Get like it was a property.
+ GDScriptCodeGenerator::Address temp = codegen.add_temporary(); // TODO: Get type here.
+ GDScriptCodeGenerator::Address self(GDScriptCodeGenerator::Address::SELF);
+
+ gen->write_get_named(temp, identifier, self);
+ return temp;
}
}
- owner = owner->_owner;
- }
+ // Try in native base.
+ GDScript *scr = codegen.script;
+ GDScriptNativeClass *nc = nullptr;
+ while (scr) {
+ if (scr->native.is_valid()) {
+ nc = scr->native.ptr();
+ }
+ scr = scr->_base;
+ }
- // Try signals and methods (can be made callables);
- if (codegen.class_node->members_indices.has(identifier)) {
- const GDScriptParser::ClassNode::Member &member = codegen.class_node->members[codegen.class_node->members_indices[identifier]];
- if (member.type == GDScriptParser::ClassNode::Member::FUNCTION || member.type == GDScriptParser::ClassNode::Member::SIGNAL) {
+ if (nc && (ClassDB::has_signal(nc->get_name(), identifier) || ClassDB::has_method(nc->get_name(), identifier))) {
// Get like it was a property.
GDScriptCodeGenerator::Address temp = codegen.add_temporary(); // TODO: Get type here.
GDScriptCodeGenerator::Address self(GDScriptCodeGenerator::Address::SELF);
@@ -1153,7 +1176,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c
codegen.generator->write_and_left_operand(result_addr);
// Check value equality.
- codegen.generator->write_binary_operator(result_addr, Variant::OP_EQUAL, p_value_addr, expr_addr);
+ codegen.generator->write_binary_operator(equality_test_addr, Variant::OP_EQUAL, p_value_addr, expr_addr);
codegen.generator->write_and_right_operand(equality_test_addr);
// AND both type and value equality.
diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp
index b17971cf93..a9975c8602 100644
--- a/modules/gdscript/gdscript_editor.cpp
+++ b/modules/gdscript/gdscript_editor.cpp
@@ -2261,10 +2261,9 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
}
r_arghint = _make_arguments_hint(info, p_argidx);
- return;
}
- if (ClassDB::is_parent_class(class_name, "Node") && (p_method == "get_node" || p_method == "has_node") && p_argidx == 0) {
+ if (p_argidx == 0 && ClassDB::is_parent_class(class_name, "Node") && (p_method == "get_node" || p_method == "has_node")) {
// Get autoloads
List<PropertyInfo> props;
ProjectSettings::get_singleton()->get_property_list(&props);
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index a77fb14064..08645d371c 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -2080,6 +2080,17 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_unary_operator(ExpressionN
return operation;
}
+GDScriptParser::ExpressionNode *GDScriptParser::parse_binary_not_in_operator(ExpressionNode *p_previous_operand, bool p_can_assign) {
+ // check that NOT is followed by IN by consuming it before calling parse_binary_operator which will only receive a plain IN
+ consume(GDScriptTokenizer::Token::IN, R"(Expected "in" after "not" in content-test operator.)");
+ ExpressionNode *in_operation = parse_binary_operator(p_previous_operand, p_can_assign);
+ UnaryOpNode *operation = alloc_node<UnaryOpNode>();
+ operation->operation = UnaryOpNode::OP_LOGIC_NOT;
+ operation->variant_op = Variant::OP_NOT;
+ operation->operand = in_operation;
+ return operation;
+}
+
GDScriptParser::ExpressionNode *GDScriptParser::parse_binary_operator(ExpressionNode *p_previous_operand, bool p_can_assign) {
GDScriptTokenizer::Token op = previous;
BinaryOpNode *operation = alloc_node<BinaryOpNode>();
@@ -2906,7 +2917,7 @@ GDScriptParser::ParseRule *GDScriptParser::get_rule(GDScriptTokenizer::Token::Ty
// Logical
{ nullptr, &GDScriptParser::parse_binary_operator, PREC_LOGIC_AND }, // AND,
{ nullptr, &GDScriptParser::parse_binary_operator, PREC_LOGIC_OR }, // OR,
- { &GDScriptParser::parse_unary_operator, nullptr, PREC_NONE }, // NOT,
+ { &GDScriptParser::parse_unary_operator, &GDScriptParser::parse_binary_not_in_operator, PREC_CONTENT_TEST }, // NOT,
{ nullptr, &GDScriptParser::parse_binary_operator, PREC_LOGIC_AND }, // AMPERSAND_AMPERSAND,
{ nullptr, &GDScriptParser::parse_binary_operator, PREC_LOGIC_OR }, // PIPE_PIPE,
{ &GDScriptParser::parse_unary_operator, nullptr, PREC_NONE }, // BANG,
@@ -3157,11 +3168,16 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
push_error(R"(Cannot use "@export" annotation with variable without type or initializer, since type can't be inferred.)", p_annotation);
return false;
}
- if (variable->initializer->type != Node::LITERAL) {
+ if (variable->initializer->type == Node::LITERAL) {
+ variable->export_info.type = static_cast<LiteralNode *>(variable->initializer)->value.get_type();
+ } else if (variable->initializer->type == Node::ARRAY) {
+ variable->export_info.type = Variant::ARRAY;
+ } else if (variable->initializer->type == Node::DICTIONARY) {
+ variable->export_info.type = Variant::DICTIONARY;
+ } else {
push_error(R"(To use "@export" annotation with type-less variable, the default value must be a literal.)", p_annotation);
return false;
}
- variable->export_info.type = static_cast<LiteralNode *>(variable->initializer)->value.get_type();
} // else: Actual type will be set by the analyzer, which can infer the proper type.
}
diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h
index d59b68b602..a4b1d4c866 100644
--- a/modules/gdscript/gdscript_parser.h
+++ b/modules/gdscript/gdscript_parser.h
@@ -1285,6 +1285,7 @@ private:
ExpressionNode *parse_builtin_constant(ExpressionNode *p_previous_operand, bool p_can_assign);
ExpressionNode *parse_unary_operator(ExpressionNode *p_previous_operand, bool p_can_assign);
ExpressionNode *parse_binary_operator(ExpressionNode *p_previous_operand, bool p_can_assign);
+ ExpressionNode *parse_binary_not_in_operator(ExpressionNode *p_previous_operand, bool p_can_assign);
ExpressionNode *parse_ternary_operator(ExpressionNode *p_previous_operand, bool p_can_assign);
ExpressionNode *parse_assignment(ExpressionNode *p_previous_operand, bool p_can_assign);
ExpressionNode *parse_array(ExpressionNode *p_previous_operand, bool p_can_assign);
diff --git a/modules/gdscript/language_server/lsp.hpp b/modules/gdscript/language_server/lsp.hpp
index 96744a15d7..6635098be2 100644
--- a/modules/gdscript/language_server/lsp.hpp
+++ b/modules/gdscript/language_server/lsp.hpp
@@ -1661,7 +1661,7 @@ struct ServerCapabilities {
signatureHelpProvider.triggerCharacters.push_back(",");
signatureHelpProvider.triggerCharacters.push_back("(");
dict["signatureHelpProvider"] = signatureHelpProvider.to_json();
- dict["codeLensProvider"] = false; // codeLensProvider.to_json();
+ //dict["codeLensProvider"] = codeLensProvider.to_json();
dict["documentOnTypeFormattingProvider"] = documentOnTypeFormattingProvider.to_json();
dict["renameProvider"] = renameProvider.to_json();
dict["documentLinkProvider"] = documentLinkProvider.to_json();
diff --git a/modules/gridmap/doc_classes/GridMap.xml b/modules/gridmap/doc_classes/GridMap.xml
index 134aadb75e..e28cc57f9b 100644
--- a/modules/gridmap/doc_classes/GridMap.xml
+++ b/modules/gridmap/doc_classes/GridMap.xml
@@ -41,6 +41,7 @@
<return type="Array">
</return>
<description>
+ Returns an array of [ArrayMesh]es and [Transform] references of all bake meshes that exist within the current GridMap.
</description>
</method>
<method name="get_cell_item" qualifiers="const">
diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp
index a569dfc207..b1875aec3f 100644
--- a/modules/mono/editor/bindings_generator.cpp
+++ b/modules/mono/editor/bindings_generator.cpp
@@ -3036,9 +3036,9 @@ bool BindingsGenerator::_arg_default_value_from_variant(const Variant &p_val, Ar
}
break;
case Variant::FLOAT:
-#ifndef REAL_T_IS_DOUBLE
- r_iarg.default_argument += "f";
-#endif
+ if (r_iarg.type.cname == name_cache.type_float) {
+ r_iarg.default_argument += "f";
+ }
break;
case Variant::STRING:
case Variant::STRING_NAME:
@@ -3051,23 +3051,32 @@ bool BindingsGenerator::_arg_default_value_from_variant(const Variant &p_val, Ar
r_iarg.default_argument = "\"" + r_iarg.default_argument + "\"";
}
break;
- case Variant::TRANSFORM:
- if (p_val.operator Transform() == Transform()) {
- r_iarg.default_argument.clear();
- }
- r_iarg.default_argument = "new %s(" + r_iarg.default_argument + ")";
+ case Variant::PLANE: {
+ Plane plane = p_val.operator Plane();
+ r_iarg.default_argument = "new Plane(new Vector3(" + plane.normal.operator String() + "), " + rtos(plane.d) + ")";
r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL;
- break;
- case Variant::PLANE:
- case Variant::AABB:
+ } break;
+ case Variant::AABB: {
+ AABB aabb = p_val.operator ::AABB();
+ r_iarg.default_argument = "new AABB(new Vector3(" + aabb.position.operator String() + "), new Vector3(" + aabb.position.operator String() + "))";
+ r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL;
+ } break;
+ case Variant::RECT2: {
+ Rect2 rect = p_val.operator Rect2();
+ r_iarg.default_argument = "new Rect2(new Vector2(" + rect.position.operator String() + "), new Vector2(" + rect.position.operator String() + "))";
+ r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL;
+ } break;
+ case Variant::RECT2I: {
+ Rect2i rect = p_val.operator Rect2i();
+ r_iarg.default_argument = "new Rect2i(new Vector2i(" + rect.position.operator String() + "), new Vector2i(" + rect.position.operator String() + "))";
+ r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL;
+ } break;
case Variant::COLOR:
- r_iarg.default_argument = "new Color(1, 1, 1, 1)";
+ r_iarg.default_argument = "new %s(" + r_iarg.default_argument + ")";
r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL;
break;
case Variant::VECTOR2:
case Variant::VECTOR2I:
- case Variant::RECT2:
- case Variant::RECT2I:
case Variant::VECTOR3:
case Variant::VECTOR3I:
r_iarg.default_argument = "new %s" + r_iarg.default_argument;
@@ -3105,12 +3114,43 @@ bool BindingsGenerator::_arg_default_value_from_variant(const Variant &p_val, Ar
r_iarg.default_argument = "new %s {}";
r_iarg.def_param_mode = ArgumentInterface::NULLABLE_REF;
break;
- case Variant::TRANSFORM2D:
- case Variant::BASIS:
- case Variant::QUAT:
- r_iarg.default_argument = Variant::get_type_name(p_val.get_type()) + ".Identity";
+ case Variant::TRANSFORM2D: {
+ Transform2D transform = p_val.operator Transform2D();
+ if (transform == Transform2D()) {
+ r_iarg.default_argument = "Transform2D.Identity";
+ } else {
+ r_iarg.default_argument = "new Transform2D(new Vector2" + transform.elements[0].operator String() + ", new Vector2" + transform.elements[1].operator String() + ", new Vector2" + transform.elements[2].operator String() + ")";
+ }
r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL;
- break;
+ } break;
+ case Variant::TRANSFORM: {
+ Transform transform = p_val.operator Transform();
+ if (transform == Transform()) {
+ r_iarg.default_argument = "Transform.Identity";
+ } else {
+ Basis basis = transform.basis;
+ r_iarg.default_argument = "new Transform(new Vector3" + basis.get_column(0).operator String() + ", new Vector3" + basis.get_column(1).operator String() + ", new Vector3" + basis.get_column(2).operator String() + ", new Vector3" + transform.origin.operator String() + ")";
+ }
+ r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL;
+ } break;
+ case Variant::BASIS: {
+ Basis basis = p_val.operator Basis();
+ if (basis == Basis()) {
+ r_iarg.default_argument = "Basis.Identity";
+ } else {
+ r_iarg.default_argument = "new Basis(new Vector3" + basis.get_column(0).operator String() + ", new Vector3" + basis.get_column(1).operator String() + ", new Vector3" + basis.get_column(2).operator String() + ")";
+ }
+ r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL;
+ } break;
+ case Variant::QUAT: {
+ Quat quat = p_val.operator Quat();
+ if (quat == Quat()) {
+ r_iarg.default_argument = "Quat.Identity";
+ } else {
+ r_iarg.default_argument = "new Quat" + quat.operator String();
+ }
+ r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL;
+ } break;
case Variant::CALLABLE:
case Variant::SIGNAL:
CRASH_NOW_MSG("Parameter of type '" + String(r_iarg.type.cname) + "' cannot have a default value.");
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/PackedSceneExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/PackedSceneExtensions.cs
new file mode 100644
index 0000000000..763f470504
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/PackedSceneExtensions.cs
@@ -0,0 +1,27 @@
+namespace Godot
+{
+ public partial class PackedScene
+ {
+ /// <summary>
+ /// Instantiates the scene's node hierarchy, erroring on failure.
+ /// Triggers child scene instantiation(s). Triggers a
+ /// `Node.NotificationInstanced` notification on the root node.
+ /// </summary>
+ /// <typeparam name="T">The type to cast to. Should be a descendant of Node.</typeparam>
+ public T Instance<T>(PackedScene.GenEditState editState = (PackedScene.GenEditState)0) where T : class
+ {
+ return (T)(object)Instance(editState);
+ }
+
+ /// <summary>
+ /// Instantiates the scene's node hierarchy, returning null on failure.
+ /// Triggers child scene instantiation(s). Triggers a
+ /// `Node.NotificationInstanced` notification on the root node.
+ /// </summary>
+ /// <typeparam name="T">The type to cast to. Should be a descendant of Node.</typeparam>
+ public T InstanceOrNull<T>(PackedScene.GenEditState editState = (PackedScene.GenEditState)0) where T : class
+ {
+ return Instance(editState) as T;
+ }
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj
index a2f05d296e..e98eabaff2 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj
+++ b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj
@@ -33,6 +33,7 @@
<Compile Include="Core\DynamicObject.cs" />
<Compile Include="Core\Extensions\NodeExtensions.cs" />
<Compile Include="Core\Extensions\ObjectExtensions.cs" />
+ <Compile Include="Core\Extensions\PackedSceneExtensions.cs" />
<Compile Include="Core\Extensions\ResourceLoaderExtensions.cs" />
<Compile Include="Core\Extensions\SceneTreeExtensions.cs" />
<Compile Include="Core\GD.cs" />
diff --git a/modules/mono/godotsharp_dirs.cpp b/modules/mono/godotsharp_dirs.cpp
index a39a6fe381..020a40575c 100644
--- a/modules/mono/godotsharp_dirs.cpp
+++ b/modules/mono/godotsharp_dirs.cpp
@@ -179,16 +179,16 @@ private:
#ifdef OSX_ENABLED
if (!DirAccess::exists(data_editor_tools_dir)) {
- data_editor_tools_dir = exe_dir.plus_file("../Frameworks/GodotSharp/Tools");
+ data_editor_tools_dir = exe_dir.plus_file("../Resources/GodotSharp/Tools");
}
if (!DirAccess::exists(data_editor_prebuilt_api_dir)) {
- data_editor_prebuilt_api_dir = exe_dir.plus_file("../Frameworks/GodotSharp/Api");
+ data_editor_prebuilt_api_dir = exe_dir.plus_file("../Resources/GodotSharp/Api");
}
if (!DirAccess::exists(data_mono_root_dir)) {
data_mono_etc_dir = exe_dir.plus_file("../Resources/GodotSharp/Mono/etc");
- data_mono_lib_dir = exe_dir.plus_file("../Frameworks/GodotSharp/Mono/lib");
+ data_mono_lib_dir = exe_dir.plus_file("../Resources/GodotSharp/Mono/lib");
}
#endif
@@ -218,11 +218,11 @@ private:
#ifdef OSX_ENABLED
if (!DirAccess::exists(data_mono_root_dir)) {
data_mono_etc_dir = exe_dir.plus_file("../Resources/GodotSharp/Mono/etc");
- data_mono_lib_dir = exe_dir.plus_file("../Frameworks/GodotSharp/Mono/lib");
+ data_mono_lib_dir = exe_dir.plus_file("../Resources/GodotSharp/Mono/lib");
}
if (!DirAccess::exists(data_game_assemblies_dir)) {
- data_game_assemblies_dir = exe_dir.plus_file("../Frameworks/GodotSharp/Assemblies");
+ data_game_assemblies_dir = exe_dir.plus_file("../Resources/GodotSharp/Assemblies");
}
#endif
diff --git a/modules/opensimplex/doc_classes/NoiseTexture.xml b/modules/opensimplex/doc_classes/NoiseTexture.xml
index 7df261d2ba..86e7f9cc08 100644
--- a/modules/opensimplex/doc_classes/NoiseTexture.xml
+++ b/modules/opensimplex/doc_classes/NoiseTexture.xml
@@ -32,6 +32,7 @@
</member>
<member name="seamless" type="bool" setter="set_seamless" getter="get_seamless" default="false">
Whether the texture can be tiled without visible seams or not. Seamless textures take longer to generate.
+ [b]Note:[/b] Seamless noise has a lower contrast compared to non-seamless noise. This is due to the way noise uses higher dimensions for generating seamless noise.
</member>
<member name="width" type="int" setter="set_width" getter="get_width" default="512">
Width of the generated texture.
diff --git a/modules/opensimplex/doc_classes/OpenSimplexNoise.xml b/modules/opensimplex/doc_classes/OpenSimplexNoise.xml
index dcda5c2324..ad82f87213 100644
--- a/modules/opensimplex/doc_classes/OpenSimplexNoise.xml
+++ b/modules/opensimplex/doc_classes/OpenSimplexNoise.xml
@@ -109,6 +109,7 @@
</argument>
<description>
Generate a tileable noise image in [constant Image.FORMAT_L8] format, based on the current noise parameters. Generated seamless images are always square ([code]size[/code] × [code]size[/code]).
+ [b]Note:[/b] Seamless noise has a lower contrast compared to non-seamless noise. This is due to the way noise uses higher dimensions for generating seamless noise.
</description>
</method>
</methods>
diff --git a/modules/text_server_adv/bitmap_font_adv.cpp b/modules/text_server_adv/bitmap_font_adv.cpp
index df771301e6..e33556b232 100644
--- a/modules/text_server_adv/bitmap_font_adv.cpp
+++ b/modules/text_server_adv/bitmap_font_adv.cpp
@@ -367,61 +367,63 @@ Error BitmapFontDataAdvanced::load_from_file(const String &p_filename, int p_bas
return OK;
}
-Error BitmapFontDataAdvanced::load_from_memory(const uint8_t *p_data, size_t p_size, int p_base_size) {
- _THREAD_SAFE_METHOD_
- ERR_FAIL_COND_V(p_data == nullptr, ERR_CANT_CREATE);
- ERR_FAIL_COND_V(p_size != sizeof(TextServer::BitmapFontData), ERR_CANT_CREATE);
+Error BitmapFontDataAdvanced::bitmap_new(float p_height, float p_ascent, int p_base_size) {
+ height = p_height;
+ ascent = p_ascent;
- const TextServer::BitmapFontData *data = (const TextServer::BitmapFontData *)p_data;
+ base_size = p_base_size;
+ if (base_size == 0) {
+ base_size = height;
+ }
- if (RenderingServer::get_singleton() != nullptr) {
- Ref<Image> image = memnew(Image(data->img));
- Ref<ImageTexture> tex = memnew(ImageTexture);
- tex->create_from_image(image);
+ char_map.clear();
+ textures.clear();
+ kerning_map.clear();
- textures.push_back(tex);
+ for (Map<float, hb_font_t *>::Element *E = cache.front(); E; E = E->next()) {
+ hb_font_destroy(E->get());
}
+ cache.clear();
- for (int i = 0; i < data->charcount; i++) {
- const int *c = &data->char_rects[i * 8];
+ valid = true;
- Character chr;
- chr.rect.position.x = c[1];
- chr.rect.position.y = c[2];
- chr.rect.size.x = c[3];
- chr.rect.size.y = c[4];
- if (c[7] < 0) {
- chr.advance.x = c[3];
- } else {
- chr.advance.x = c[7];
- }
- chr.align = Vector2(c[6], c[5]);
- char_map[c[0]] = chr;
- }
+ return OK;
+}
- for (int i = 0; i < data->kerning_count; i++) {
- KerningPairKey kpk;
- kpk.A = data->kernings[i * 3 + 0];
- kpk.B = data->kernings[i * 3 + 1];
+void BitmapFontDataAdvanced::bitmap_add_texture(const Ref<Texture> &p_texture) {
+ ERR_FAIL_COND(!valid);
+ ERR_FAIL_COND_MSG(p_texture.is_null(), "It's not a reference to a valid Texture object.");
- if (data->kernings[i * 3 + 2] == 0 && kerning_map.has(kpk)) {
- kerning_map.erase(kpk);
- } else {
- kerning_map[kpk] = data->kernings[i * 3 + 2];
- }
- }
+ textures.push_back(p_texture);
+}
- height = data->height;
- ascent = data->ascent;
+void BitmapFontDataAdvanced::bitmap_add_char(char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance) {
+ ERR_FAIL_COND(!valid);
- base_size = p_base_size;
- if (base_size == 0) {
- base_size = height;
+ Character chr;
+ chr.rect = p_rect;
+ chr.texture_idx = p_texture_idx;
+ if (p_advance < 0) {
+ chr.advance.x = chr.rect.size.x;
+ } else {
+ chr.advance.x = p_advance;
}
+ chr.align = p_align;
+ char_map[p_char] = chr;
+}
- valid = true;
+void BitmapFontDataAdvanced::bitmap_add_kerning_pair(char32_t p_A, char32_t p_B, int p_kerning) {
+ ERR_FAIL_COND(!valid);
- return OK;
+ KerningPairKey kpk;
+ kpk.A = p_A;
+ kpk.B = p_B;
+
+ if (p_kerning == 0 && kerning_map.has(kpk)) {
+ kerning_map.erase(kpk);
+ } else {
+ kerning_map[kpk] = p_kerning;
+ }
}
float BitmapFontDataAdvanced::get_height(int p_size) const {
diff --git a/modules/text_server_adv/bitmap_font_adv.h b/modules/text_server_adv/bitmap_font_adv.h
index c314f1b087..da7c2b00ac 100644
--- a/modules/text_server_adv/bitmap_font_adv.h
+++ b/modules/text_server_adv/bitmap_font_adv.h
@@ -74,7 +74,11 @@ public:
virtual void clear_cache() override{};
virtual Error load_from_file(const String &p_filename, int p_base_size) override;
- virtual Error load_from_memory(const uint8_t *p_data, size_t p_size, int p_base_size) override;
+ virtual Error bitmap_new(float p_height, float p_ascent, int p_base_size) override;
+
+ virtual void bitmap_add_texture(const Ref<Texture> &p_texture) override;
+ virtual void bitmap_add_char(char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance) override;
+ virtual void bitmap_add_kerning_pair(char32_t p_A, char32_t p_B, int p_kerning) override;
virtual float get_height(int p_size) const override;
virtual float get_ascent(int p_size) const override;
diff --git a/modules/text_server_adv/font_adv.h b/modules/text_server_adv/font_adv.h
index 2bb09c11ad..2b6d977451 100644
--- a/modules/text_server_adv/font_adv.h
+++ b/modules/text_server_adv/font_adv.h
@@ -44,8 +44,13 @@ struct FontDataAdvanced {
virtual void clear_cache() = 0;
- virtual Error load_from_file(const String &p_filename, int p_base_size) = 0;
- virtual Error load_from_memory(const uint8_t *p_data, size_t p_size, int p_base_size) = 0;
+ virtual Error load_from_file(const String &p_filename, int p_base_size) { return ERR_CANT_CREATE; };
+ virtual Error load_from_memory(const uint8_t *p_data, size_t p_size, int p_base_size) { return ERR_CANT_CREATE; };
+ virtual Error bitmap_new(float p_height, float p_ascent, int p_base_size) { return ERR_CANT_CREATE; };
+
+ virtual void bitmap_add_texture(const Ref<Texture> &p_texture) { ERR_FAIL_MSG("Not supported."); };
+ virtual void bitmap_add_char(char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance) { ERR_FAIL_MSG("Not supported."); };
+ virtual void bitmap_add_kerning_pair(char32_t p_A, char32_t p_B, int p_kerning) { ERR_FAIL_MSG("Not supported."); };
virtual float get_height(int p_size) const = 0;
virtual float get_ascent(int p_size) const = 0;
diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp
index 2af31f4043..2e3c2d1cab 100644
--- a/modules/text_server_adv/text_server_adv.cpp
+++ b/modules/text_server_adv/text_server_adv.cpp
@@ -570,6 +570,39 @@ RID TextServerAdvanced::create_font_memory(const uint8_t *p_data, size_t p_size,
return font_owner.make_rid(fd);
}
+RID TextServerAdvanced::create_font_bitmap(float p_height, float p_ascent, int p_base_size) {
+ _THREAD_SAFE_METHOD_
+ FontDataAdvanced *fd = memnew(BitmapFontDataAdvanced);
+ Error err = fd->bitmap_new(p_height, p_ascent, p_base_size);
+ if (err != OK) {
+ memdelete(fd);
+ return RID();
+ }
+
+ return font_owner.make_rid(fd);
+}
+
+void TextServerAdvanced::font_bitmap_add_texture(RID p_font, const Ref<Texture> &p_texture) {
+ _THREAD_SAFE_METHOD_
+ FontDataAdvanced *fd = font_owner.getornull(p_font);
+ ERR_FAIL_COND(!fd);
+ fd->bitmap_add_texture(p_texture);
+}
+
+void TextServerAdvanced::font_bitmap_add_char(RID p_font, char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance) {
+ _THREAD_SAFE_METHOD_
+ FontDataAdvanced *fd = font_owner.getornull(p_font);
+ ERR_FAIL_COND(!fd);
+ fd->bitmap_add_char(p_char, p_texture_idx, p_rect, p_align, p_advance);
+}
+
+void TextServerAdvanced::font_bitmap_add_kerning_pair(RID p_font, char32_t p_A, char32_t p_B, int p_kerning) {
+ _THREAD_SAFE_METHOD_
+ FontDataAdvanced *fd = font_owner.getornull(p_font);
+ ERR_FAIL_COND(!fd);
+ fd->bitmap_add_kerning_pair(p_A, p_B, p_kerning);
+}
+
float TextServerAdvanced::font_get_height(RID p_font, int p_size) const {
_THREAD_SAFE_METHOD_
const FontDataAdvanced *fd = font_owner.getornull(p_font);
diff --git a/modules/text_server_adv/text_server_adv.h b/modules/text_server_adv/text_server_adv.h
index c5ebe61bc3..b53b5716e5 100644
--- a/modules/text_server_adv/text_server_adv.h
+++ b/modules/text_server_adv/text_server_adv.h
@@ -126,6 +126,11 @@ public:
virtual RID create_font_system(const String &p_name, int p_base_size = 16) override;
virtual RID create_font_resource(const String &p_filename, int p_base_size = 16) override;
virtual RID create_font_memory(const uint8_t *p_data, size_t p_size, const String &p_type, int p_base_size = 16) override;
+ virtual RID create_font_bitmap(float p_height, float p_ascent, int p_base_size = 16) override;
+
+ virtual void font_bitmap_add_texture(RID p_font, const Ref<Texture> &p_texture) override;
+ virtual void font_bitmap_add_char(RID p_font, char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance) override;
+ virtual void font_bitmap_add_kerning_pair(RID p_font, char32_t p_A, char32_t p_B, int p_kerning) override;
virtual float font_get_height(RID p_font, int p_size) const override;
virtual float font_get_ascent(RID p_font, int p_size) const override;
diff --git a/modules/text_server_fb/bitmap_font_fb.cpp b/modules/text_server_fb/bitmap_font_fb.cpp
index c9a9cc6eba..c58f8cbba1 100644
--- a/modules/text_server_fb/bitmap_font_fb.cpp
+++ b/modules/text_server_fb/bitmap_font_fb.cpp
@@ -175,61 +175,58 @@ Error BitmapFontDataFallback::load_from_file(const String &p_filename, int p_bas
return OK;
}
-Error BitmapFontDataFallback::load_from_memory(const uint8_t *p_data, size_t p_size, int p_base_size) {
- _THREAD_SAFE_METHOD_
- ERR_FAIL_COND_V(p_data == nullptr, ERR_CANT_CREATE);
- ERR_FAIL_COND_V(p_size != sizeof(TextServer::BitmapFontData), ERR_CANT_CREATE);
+Error BitmapFontDataFallback::bitmap_new(float p_height, float p_ascent, int p_base_size) {
+ height = p_height;
+ ascent = p_ascent;
- const TextServer::BitmapFontData *data = (const TextServer::BitmapFontData *)p_data;
+ base_size = p_base_size;
+ if (base_size == 0) {
+ base_size = height;
+ }
- if (RenderingServer::get_singleton() != nullptr) {
- Ref<Image> image = memnew(Image(data->img));
- Ref<ImageTexture> tex = memnew(ImageTexture);
- tex->create_from_image(image);
+ char_map.clear();
+ textures.clear();
+ kerning_map.clear();
- textures.push_back(tex);
- }
+ valid = true;
- for (int i = 0; i < data->charcount; i++) {
- const int *c = &data->char_rects[i * 8];
-
- Character chr;
- chr.rect.position.x = c[1];
- chr.rect.position.y = c[2];
- chr.rect.size.x = c[3];
- chr.rect.size.y = c[4];
- if (c[7] < 0) {
- chr.advance.x = c[3];
- } else {
- chr.advance.x = c[7];
- }
- chr.align = Vector2(c[6], c[5]);
- char_map[c[0]] = chr;
- }
+ return OK;
+}
- for (int i = 0; i < data->kerning_count; i++) {
- KerningPairKey kpk;
- kpk.A = data->kernings[i * 3 + 0];
- kpk.B = data->kernings[i * 3 + 1];
+void BitmapFontDataFallback::bitmap_add_texture(const Ref<Texture> &p_texture) {
+ ERR_FAIL_COND(!valid);
+ ERR_FAIL_COND_MSG(p_texture.is_null(), "It's not a reference to a valid Texture object.");
- if (data->kernings[i * 3 + 2] == 0 && kerning_map.has(kpk)) {
- kerning_map.erase(kpk);
- } else {
- kerning_map[kpk] = data->kernings[i * 3 + 2];
- }
- }
+ textures.push_back(p_texture);
+}
- height = data->height;
- ascent = data->ascent;
+void BitmapFontDataFallback::bitmap_add_char(char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance) {
+ ERR_FAIL_COND(!valid);
- base_size = p_base_size;
- if (base_size == 0) {
- base_size = height;
+ Character chr;
+ chr.rect = p_rect;
+ chr.texture_idx = p_texture_idx;
+ if (p_advance < 0) {
+ chr.advance.x = chr.rect.size.x;
+ } else {
+ chr.advance.x = p_advance;
}
+ chr.align = p_align;
+ char_map[p_char] = chr;
+}
- valid = true;
+void BitmapFontDataFallback::bitmap_add_kerning_pair(char32_t p_A, char32_t p_B, int p_kerning) {
+ ERR_FAIL_COND(!valid);
- return OK;
+ KerningPairKey kpk;
+ kpk.A = p_A;
+ kpk.B = p_B;
+
+ if (p_kerning == 0 && kerning_map.has(kpk)) {
+ kerning_map.erase(kpk);
+ } else {
+ kerning_map[kpk] = p_kerning;
+ }
}
float BitmapFontDataFallback::get_height(int p_size) const {
diff --git a/modules/text_server_fb/bitmap_font_fb.h b/modules/text_server_fb/bitmap_font_fb.h
index 33401b85fa..7cd7507ebc 100644
--- a/modules/text_server_fb/bitmap_font_fb.h
+++ b/modules/text_server_fb/bitmap_font_fb.h
@@ -70,7 +70,11 @@ public:
virtual void clear_cache() override{};
virtual Error load_from_file(const String &p_filename, int p_base_size) override;
- virtual Error load_from_memory(const uint8_t *p_data, size_t p_size, int p_base_size) override;
+ virtual Error bitmap_new(float p_height, float p_ascent, int p_base_size) override;
+
+ virtual void bitmap_add_texture(const Ref<Texture> &p_texture) override;
+ virtual void bitmap_add_char(char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance) override;
+ virtual void bitmap_add_kerning_pair(char32_t p_A, char32_t p_B, int p_kerning) override;
virtual float get_height(int p_size) const override;
virtual float get_ascent(int p_size) const override;
diff --git a/modules/text_server_fb/font_fb.h b/modules/text_server_fb/font_fb.h
index 7fccbe06b5..218f3df03a 100644
--- a/modules/text_server_fb/font_fb.h
+++ b/modules/text_server_fb/font_fb.h
@@ -42,8 +42,13 @@ struct FontDataFallback {
virtual void clear_cache() = 0;
- virtual Error load_from_file(const String &p_filename, int p_base_size) = 0;
- virtual Error load_from_memory(const uint8_t *p_data, size_t p_size, int p_base_size) = 0;
+ virtual Error load_from_file(const String &p_filename, int p_base_size) { return ERR_CANT_CREATE; };
+ virtual Error load_from_memory(const uint8_t *p_data, size_t p_size, int p_base_size) { return ERR_CANT_CREATE; };
+ virtual Error bitmap_new(float p_height, float p_ascent, int p_base_size) { return ERR_CANT_CREATE; };
+
+ virtual void bitmap_add_texture(const Ref<Texture> &p_texture) { ERR_FAIL_MSG("Not supported."); };
+ virtual void bitmap_add_char(char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance) { ERR_FAIL_MSG("Not supported."); };
+ virtual void bitmap_add_kerning_pair(char32_t p_A, char32_t p_B, int p_kerning) { ERR_FAIL_MSG("Not supported."); };
virtual float get_height(int p_size) const = 0;
virtual float get_ascent(int p_size) const = 0;
diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp
index a732c6184c..60ab14738a 100644
--- a/modules/text_server_fb/text_server_fb.cpp
+++ b/modules/text_server_fb/text_server_fb.cpp
@@ -148,6 +148,39 @@ RID TextServerFallback::create_font_memory(const uint8_t *p_data, size_t p_size,
return font_owner.make_rid(fd);
}
+RID TextServerFallback::create_font_bitmap(float p_height, float p_ascent, int p_base_size) {
+ _THREAD_SAFE_METHOD_
+ FontDataFallback *fd = memnew(BitmapFontDataFallback);
+ Error err = fd->bitmap_new(p_height, p_ascent, p_base_size);
+ if (err != OK) {
+ memdelete(fd);
+ return RID();
+ }
+
+ return font_owner.make_rid(fd);
+}
+
+void TextServerFallback::font_bitmap_add_texture(RID p_font, const Ref<Texture> &p_texture) {
+ _THREAD_SAFE_METHOD_
+ FontDataFallback *fd = font_owner.getornull(p_font);
+ ERR_FAIL_COND(!fd);
+ fd->bitmap_add_texture(p_texture);
+}
+
+void TextServerFallback::font_bitmap_add_char(RID p_font, char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance) {
+ _THREAD_SAFE_METHOD_
+ FontDataFallback *fd = font_owner.getornull(p_font);
+ ERR_FAIL_COND(!fd);
+ fd->bitmap_add_char(p_char, p_texture_idx, p_rect, p_align, p_advance);
+}
+
+void TextServerFallback::font_bitmap_add_kerning_pair(RID p_font, char32_t p_A, char32_t p_B, int p_kerning) {
+ _THREAD_SAFE_METHOD_
+ FontDataFallback *fd = font_owner.getornull(p_font);
+ ERR_FAIL_COND(!fd);
+ fd->bitmap_add_kerning_pair(p_A, p_B, p_kerning);
+}
+
float TextServerFallback::font_get_height(RID p_font, int p_size) const {
_THREAD_SAFE_METHOD_
const FontDataFallback *fd = font_owner.getornull(p_font);
diff --git a/modules/text_server_fb/text_server_fb.h b/modules/text_server_fb/text_server_fb.h
index c14a444872..b10369d172 100644
--- a/modules/text_server_fb/text_server_fb.h
+++ b/modules/text_server_fb/text_server_fb.h
@@ -81,6 +81,11 @@ public:
virtual RID create_font_system(const String &p_name, int p_base_size = 16) override;
virtual RID create_font_resource(const String &p_filename, int p_base_size = 16) override;
virtual RID create_font_memory(const uint8_t *p_data, size_t p_size, const String &p_type, int p_base_size = 16) override;
+ virtual RID create_font_bitmap(float p_height, float p_ascent, int p_base_size = 16) override;
+
+ virtual void font_bitmap_add_texture(RID p_font, const Ref<Texture> &p_texture) override;
+ virtual void font_bitmap_add_char(RID p_font, char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance) override;
+ virtual void font_bitmap_add_kerning_pair(RID p_font, char32_t p_A, char32_t p_B, int p_kerning) override;
virtual float font_get_height(RID p_font, int p_size) const override;
virtual float font_get_ascent(RID p_font, int p_size) const override;
diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp
index 888b1546e4..326e513261 100644
--- a/platform/android/export/export.cpp
+++ b/platform/android/export/export.cpp
@@ -806,8 +806,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
manifest_text += _get_xr_features_tag(p_preset);
manifest_text += _get_instrumentation_tag(p_preset);
- String plugins_names = get_plugins_names(get_enabled_plugins(p_preset));
- manifest_text += _get_application_tag(p_preset, plugins_names);
+ manifest_text += _get_application_tag(p_preset);
manifest_text += "</manifest>\n";
String manifest_path = vformat("res://android/build/src/%s/AndroidManifest.xml", (p_debug ? "debug" : "release"));
@@ -856,8 +855,6 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
int xr_mode_index = p_preset->get("xr_features/xr_mode");
bool focus_awareness = p_preset->get("xr_features/focus_awareness");
- String plugins_names = get_plugins_names(get_enabled_plugins(p_preset));
-
Vector<String> perms;
// Write permissions into the perms variable.
_get_permissions(p_preset, p_give_internet, perms);
@@ -995,11 +992,6 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
encode_uint32(xr_mode_index == /* XRMode.OVR */ 1 && focus_awareness ? 0xFFFFFFFF : 0, &p_manifest.write[iofs + 16]);
}
- if (tname == "meta-data" && attrname == "value" && value == "plugins_value" && !plugins_names.is_empty()) {
- // Update the meta-data 'android:value' attribute with the list of enabled plugins.
- string_table.write[attr_value] = plugins_names;
- }
-
is_focus_aware_metadata = tname == "meta-data" && attrname == "name" && value == "com.oculus.vr.focusaware";
iofs += 20;
}
@@ -2003,6 +1995,7 @@ public:
String template_err;
bool dvalid = false;
bool rvalid = false;
+ bool has_export_templates = false;
if (p_preset->get("custom_template/debug") != "") {
dvalid = FileAccess::exists(p_preset->get("custom_template/debug"));
@@ -2010,7 +2003,7 @@ public:
template_err += TTR("Custom debug template not found.") + "\n";
}
} else {
- dvalid = exists_export_template("android_debug.apk", &template_err);
+ has_export_templates |= exists_export_template("android_debug.apk", &template_err);
}
if (p_preset->get("custom_template/release") != "") {
@@ -2019,22 +2012,24 @@ public:
template_err += TTR("Custom release template not found.") + "\n";
}
} else {
- rvalid = exists_export_template("android_release.apk", &template_err);
+ has_export_templates |= exists_export_template("android_release.apk", &template_err);
}
- valid = dvalid || rvalid;
+ r_missing_templates = !has_export_templates;
+ valid = dvalid || rvalid || has_export_templates;
if (!valid) {
err += template_err;
}
} else {
- valid = exists_export_template("android_source.zip", &err);
+ r_missing_templates = !exists_export_template("android_source.zip", &err);
- if (!FileAccess::exists("res://android/build/build.gradle")) {
+ bool installed_android_build_template = FileAccess::exists("res://android/build/build.gradle");
+ if (!installed_android_build_template) {
err += TTR("Android build template not installed in the project. Install it from the Project menu.") + "\n";
- valid = false;
}
+
+ valid = installed_android_build_template && !r_missing_templates;
}
- r_missing_templates = !valid;
// Validate the rest of the configuration.
@@ -2414,6 +2409,9 @@ public:
print_verbose("- custom build enabled: " + bool_to_string(use_custom_build));
print_verbose("- apk expansion enabled: " + bool_to_string(apk_expansion));
print_verbose("- enabled abis: " + String(",").join(enabled_abis));
+ print_verbose("- export filter: " + itos(p_preset->get_export_filter()));
+ print_verbose("- include filter: " + p_preset->get_include_filter());
+ print_verbose("- exclude filter: " + p_preset->get_exclude_filter());
Ref<Image> splash_image;
Ref<Image> splash_bg_color_image;
@@ -2553,6 +2551,7 @@ public:
cmdline.push_back("-Pplugins_maven_repos=" + custom_maven_repos); // argument to specify the list of custom maven repos for the plugins dependencies.
cmdline.push_back("-Pperform_zipalign=" + zipalign_flag); // argument to specify whether the build should be zipaligned.
cmdline.push_back("-Pperform_signing=" + sign_flag); // argument to specify whether the build should be signed.
+ cmdline.push_back("-Pgodot_editor_version=" + String(VERSION_FULL_CONFIG));
// NOTE: The release keystore is not included in the verbose logging
// to avoid accidentally leaking sensitive information when sharing verbose logs for troubleshooting.
diff --git a/platform/android/export/gradle_export_util.h b/platform/android/export/gradle_export_util.h
index ce6a3c96db..097a2391ee 100644
--- a/platform/android/export/gradle_export_util.h
+++ b/platform/android/export/gradle_export_util.h
@@ -269,14 +269,6 @@ String _get_instrumentation_tag(const Ref<EditorExportPreset> &p_preset) {
return manifest_instrumentation_text;
}
-String _get_plugins_tag(const String &plugins_names) {
- if (!plugins_names.is_empty()) {
- return vformat(" <meta-data tools:node=\"replace\" android:name=\"plugins\" android:value=\"%s\" />\n", plugins_names);
- } else {
- return " <meta-data tools:node=\"remove\" android:name=\"plugins\" />\n";
- }
-}
-
String _get_activity_tag(const Ref<EditorExportPreset> &p_preset) {
bool uses_xr = (int)(p_preset->get("xr_features/xr_mode")) == 1;
String orientation = _get_android_orientation_label(_get_screen_orientation());
@@ -295,7 +287,7 @@ String _get_activity_tag(const Ref<EditorExportPreset> &p_preset) {
return manifest_activity_text;
}
-String _get_application_tag(const Ref<EditorExportPreset> &p_preset, const String &plugins_names) {
+String _get_application_tag(const Ref<EditorExportPreset> &p_preset) {
bool uses_xr = (int)(p_preset->get("xr_features/xr_mode")) == 1;
String manifest_application_text =
" <application android:label=\"@string/godot_project_name_string\"\n"
@@ -303,7 +295,6 @@ String _get_application_tag(const Ref<EditorExportPreset> &p_preset, const Strin
" android:icon=\"@mipmap/icon\">\n\n"
" <meta-data tools:node=\"remove\" android:name=\"xr_mode_metadata_name\" />\n";
- manifest_application_text += _get_plugins_tag(plugins_names);
if (uses_xr) {
manifest_application_text += " <meta-data tools:node=\"replace\" android:name=\"com.samsung.android.vr.application.mode\" android:value=\"vr_only\" />\n";
}
diff --git a/platform/android/java/app/AndroidManifest.xml b/platform/android/java/app/AndroidManifest.xml
index e94681659c..948fa8c00b 100644
--- a/platform/android/java/app/AndroidManifest.xml
+++ b/platform/android/java/app/AndroidManifest.xml
@@ -22,6 +22,11 @@
tools:ignore="GoogleAppIndexingWarning"
android:icon="@mipmap/icon" >
+ <!-- Records the version of the Godot editor used for building -->
+ <meta-data
+ android:name="org.godotengine.editor.version"
+ android:value="${godotEditorVersion}" />
+
<!-- The following metadata values are replaced when Godot exports, modifying them here has no effect. -->
<!-- Do these changes in the export preset. Adding new ones is fine. -->
@@ -30,11 +35,6 @@
android:name="xr_mode_metadata_name"
android:value="xr_mode_metadata_value" />
- <!-- Metadata populated at export time and used by Godot to figure out which plugins must be enabled. -->
- <meta-data
- android:name="plugins"
- android:value="plugins_value"/>
-
<activity
android:name=".GodotApp"
android:label="@string/godot_project_name_string"
diff --git a/platform/android/java/app/build.gradle b/platform/android/java/app/build.gradle
index 814cc30613..934c4bf441 100644
--- a/platform/android/java/app/build.gradle
+++ b/platform/android/java/app/build.gradle
@@ -85,6 +85,8 @@ android {
abiFilters export_abi_list
}
+ manifestPlaceholders = [godotEditorVersion: getGodotEditorVersion()]
+
// Feel free to modify the application id to your own.
applicationId getExportPackageName()
versionCode getExportVersionCode()
diff --git a/platform/android/java/app/config.gradle b/platform/android/java/app/config.gradle
index 202b3c35c0..585e517631 100644
--- a/platform/android/java/app/config.gradle
+++ b/platform/android/java/app/config.gradle
@@ -50,7 +50,56 @@ ext.getExportVersionName = { ->
return versionName
}
-final String PLUGIN_VALUE_SEPARATOR_REGEX = "\\|"
+ext.getGodotEditorVersion = { ->
+ String editorVersion = project.hasProperty("godot_editor_version") ? project.property("godot_editor_version") : ""
+ if (editorVersion == null || editorVersion.isEmpty()) {
+ // Try the library version first
+ editorVersion = getGodotLibraryVersion()
+
+ if (editorVersion.isEmpty()) {
+ // Fallback value.
+ editorVersion = "custom_build"
+ }
+ }
+ return editorVersion
+}
+
+ext.getGodotLibraryVersion = { ->
+ // Attempt to read the version from the `version.py` file.
+ String libraryVersion = ""
+
+ File versionFile = new File("../../../version.py")
+ if (versionFile.isFile()) {
+ List<String> requiredKeys = ["major", "minor", "patch", "status", "module_config"]
+ def map = [:]
+
+ List<String> lines = versionFile.readLines()
+ for (String line in lines) {
+ String[] keyValue = line.split("=")
+ String key = keyValue[0].trim()
+ String value = keyValue[1].trim().replaceAll("\"", "")
+
+ if (requiredKeys.contains(key)) {
+ if (!value.isEmpty()) {
+ map[key] = value
+ }
+ requiredKeys.remove(key)
+ }
+ }
+
+ if (requiredKeys.empty) {
+ libraryVersion = map.values().join(".")
+ }
+ }
+
+ if (libraryVersion.isEmpty()) {
+ // Fallback value in case we're unable to read the file.
+ libraryVersion = "custom_build"
+ }
+ return libraryVersion
+}
+
+final String VALUE_SEPARATOR_REGEX = "\\|"
// get the list of ABIs the project should be exported to
ext.getExportEnabledABIs = { ->
@@ -59,7 +108,7 @@ ext.getExportEnabledABIs = { ->
enabledABIs = "armeabi-v7a|arm64-v8a|x86|x86_64|"
}
Set<String> exportAbiFilter = [];
- for (String abi_name : enabledABIs.split(PLUGIN_VALUE_SEPARATOR_REGEX)) {
+ for (String abi_name : enabledABIs.split(VALUE_SEPARATOR_REGEX)) {
if (!abi_name.trim().isEmpty()){
exportAbiFilter.add(abi_name);
}
@@ -94,7 +143,7 @@ ext.getGodotPluginsMavenRepos = { ->
if (project.hasProperty("plugins_maven_repos")) {
String mavenReposProperty = project.property("plugins_maven_repos")
if (mavenReposProperty != null && !mavenReposProperty.trim().isEmpty()) {
- for (String mavenRepoUrl : mavenReposProperty.split(PLUGIN_VALUE_SEPARATOR_REGEX)) {
+ for (String mavenRepoUrl : mavenReposProperty.split(VALUE_SEPARATOR_REGEX)) {
mavenRepos += mavenRepoUrl.trim()
}
}
@@ -114,7 +163,7 @@ ext.getGodotPluginsRemoteBinaries = { ->
if (project.hasProperty("plugins_remote_binaries")) {
String remoteDepsList = project.property("plugins_remote_binaries")
if (remoteDepsList != null && !remoteDepsList.trim().isEmpty()) {
- for (String dep: remoteDepsList.split(PLUGIN_VALUE_SEPARATOR_REGEX)) {
+ for (String dep: remoteDepsList.split(VALUE_SEPARATOR_REGEX)) {
remoteDeps += dep.trim()
}
}
@@ -133,7 +182,7 @@ ext.getGodotPluginsLocalBinaries = { ->
if (project.hasProperty("plugins_local_binaries")) {
String pluginsList = project.property("plugins_local_binaries")
if (pluginsList != null && !pluginsList.trim().isEmpty()) {
- for (String plugin : pluginsList.split(PLUGIN_VALUE_SEPARATOR_REGEX)) {
+ for (String plugin : pluginsList.split(VALUE_SEPARATOR_REGEX)) {
binDeps += plugin.trim()
}
}
diff --git a/platform/android/java/build.gradle b/platform/android/java/build.gradle
index 73c136ed0e..ec02b0fc7a 100644
--- a/platform/android/java/build.gradle
+++ b/platform/android/java/build.gradle
@@ -165,12 +165,6 @@ task cleanGodotTemplates(type: Delete) {
// Delete the library generated AAR files
delete("lib/build/outputs/aar")
- // Delete the godotpayment libs directory contents
- delete("plugins/godotpayment/libs")
-
- // Delete the generated godotpayment aar
- delete("plugins/godotpayment/build/outputs/aar")
-
// Delete the app libs directory contents
delete("app/libs")
diff --git a/platform/android/java/lib/AndroidManifest.xml b/platform/android/java/lib/AndroidManifest.xml
index fa39bc0f1d..3034794d69 100644
--- a/platform/android/java/lib/AndroidManifest.xml
+++ b/platform/android/java/lib/AndroidManifest.xml
@@ -6,6 +6,11 @@
<application>
+ <!-- Records the version of the Godot library -->
+ <meta-data
+ android:name="org.godotengine.library.version"
+ android:value="${godotLibraryVersion}" />
+
<service android:name=".GodotDownloaderService" />
</application>
diff --git a/platform/android/java/lib/build.gradle b/platform/android/java/lib/build.gradle
index ca5153f7f6..6fc9a11a08 100644
--- a/platform/android/java/lib/build.gradle
+++ b/platform/android/java/lib/build.gradle
@@ -18,6 +18,8 @@ android {
defaultConfig {
minSdkVersion versions.minSdk
targetSdkVersion versions.targetSdk
+
+ manifestPlaceholders = [godotLibraryVersion: getGodotLibraryVersion()]
}
compileOptions {
diff --git a/platform/android/java/lib/src/org/godotengine/godot/Godot.java b/platform/android/java/lib/src/org/godotengine/godot/Godot.java
index 7d396b402e..0891904dff 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/Godot.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.java
@@ -464,7 +464,9 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
}
@Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle icicle) {
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
final Activity activity = getActivity();
Window window = activity.getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
@@ -572,24 +574,11 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
if (startResult != DownloaderClientMarshaller.NO_DOWNLOAD_REQUIRED) {
// This is where you do set up to display the download
- // progress (next step)
+ // progress (next step in onCreateView)
mDownloaderClientStub = DownloaderClientMarshaller.CreateStub(this,
GodotDownloaderService.class);
- View downloadingExpansionView =
- inflater.inflate(R.layout.downloading_expansion, container, false);
- mPB = (ProgressBar)downloadingExpansionView.findViewById(R.id.progressBar);
- mStatusText = (TextView)downloadingExpansionView.findViewById(R.id.statusText);
- mProgressFraction = (TextView)downloadingExpansionView.findViewById(R.id.progressAsFraction);
- mProgressPercent = (TextView)downloadingExpansionView.findViewById(R.id.progressAsPercentage);
- mAverageSpeed = (TextView)downloadingExpansionView.findViewById(R.id.progressAverageSpeed);
- mTimeRemaining = (TextView)downloadingExpansionView.findViewById(R.id.progressTimeRemaining);
- mDashboard = downloadingExpansionView.findViewById(R.id.downloaderDashboard);
- mCellMessage = downloadingExpansionView.findViewById(R.id.approveCellular);
- mPauseButton = (Button)downloadingExpansionView.findViewById(R.id.pauseButton);
- mWiFiSettingsButton = (Button)downloadingExpansionView.findViewById(R.id.wifiSettingsButton);
-
- return downloadingExpansionView;
+ return;
}
} catch (NameNotFoundException e) {
// TODO Auto-generated catch block
@@ -600,6 +589,27 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
mCurrentIntent = activity.getIntent();
initializeGodot();
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle icicle) {
+ if (mDownloaderClientStub != null) {
+ View downloadingExpansionView =
+ inflater.inflate(R.layout.downloading_expansion, container, false);
+ mPB = (ProgressBar)downloadingExpansionView.findViewById(R.id.progressBar);
+ mStatusText = (TextView)downloadingExpansionView.findViewById(R.id.statusText);
+ mProgressFraction = (TextView)downloadingExpansionView.findViewById(R.id.progressAsFraction);
+ mProgressPercent = (TextView)downloadingExpansionView.findViewById(R.id.progressAsPercentage);
+ mAverageSpeed = (TextView)downloadingExpansionView.findViewById(R.id.progressAverageSpeed);
+ mTimeRemaining = (TextView)downloadingExpansionView.findViewById(R.id.progressTimeRemaining);
+ mDashboard = downloadingExpansionView.findViewById(R.id.downloaderDashboard);
+ mCellMessage = downloadingExpansionView.findViewById(R.id.approveCellular);
+ mPauseButton = (Button)downloadingExpansionView.findViewById(R.id.pauseButton);
+ mWiFiSettingsButton = (Button)downloadingExpansionView.findViewById(R.id.wifiSettingsButton);
+
+ return downloadingExpansionView;
+ }
+
return containerLayout;
}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginRegistry.java b/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginRegistry.java
index 99811f72ed..5b41205253 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginRegistry.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginRegistry.java
@@ -44,8 +44,6 @@ import androidx.annotation.Nullable;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Collection;
-import java.util.HashSet;
-import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
@@ -56,13 +54,6 @@ public final class GodotPluginRegistry {
private static final String GODOT_PLUGIN_V1_NAME_PREFIX = "org.godotengine.plugin.v1.";
- /**
- * Name for the metadata containing the list of Godot plugins to enable.
- */
- private static final String GODOT_ENABLED_PLUGINS_LABEL = "plugins";
-
- private static final String PLUGIN_VALUE_SEPARATOR_REGEX = "\\|";
-
private static GodotPluginRegistry instance;
private final ConcurrentHashMap<String, GodotPlugin> registry;
@@ -132,37 +123,11 @@ public final class GodotPluginRegistry {
return;
}
- // When using the Godot editor for building and exporting the apk, this is used to check
- // which plugins to enable.
- // When using a custom process to generate the apk, the metadata is not needed since
- // it's assumed that the developer is aware of the dependencies included in the apk.
- final Set<String> enabledPluginsSet;
- if (metaData.containsKey(GODOT_ENABLED_PLUGINS_LABEL)) {
- String enabledPlugins = metaData.getString(GODOT_ENABLED_PLUGINS_LABEL, "");
- String[] enabledPluginsList = enabledPlugins.split(PLUGIN_VALUE_SEPARATOR_REGEX);
- if (enabledPluginsList.length == 0) {
- // No plugins to enable. Aborting early.
- return;
- }
-
- enabledPluginsSet = new HashSet<>();
- for (String enabledPlugin : enabledPluginsList) {
- enabledPluginsSet.add(enabledPlugin.trim());
- }
- } else {
- enabledPluginsSet = null;
- }
-
int godotPluginV1NamePrefixLength = GODOT_PLUGIN_V1_NAME_PREFIX.length();
for (String metaDataName : metaData.keySet()) {
// Parse the meta-data looking for entry with the Godot plugin name prefix.
if (metaDataName.startsWith(GODOT_PLUGIN_V1_NAME_PREFIX)) {
String pluginName = metaDataName.substring(godotPluginV1NamePrefixLength).trim();
- if (enabledPluginsSet != null && !enabledPluginsSet.contains(pluginName)) {
- Log.w(TAG, "Plugin " + pluginName + " is listed in the dependencies but is not enabled.");
- continue;
- }
-
Log.i(TAG, "Initializing Godot plugin " + pluginName);
// Retrieve the plugin class full name.
@@ -177,8 +142,7 @@ public final class GodotPluginRegistry {
.getConstructor(Godot.class);
GodotPlugin pluginHandle = pluginConstructor.newInstance(godot);
- // Load the plugin initializer into the registry using the plugin name
- // as key.
+ // Load the plugin initializer into the registry using the plugin name as key.
if (!pluginName.equals(pluginHandle.getPluginName())) {
Log.w(TAG,
"Meta-data plugin name does not match the value returned by the plugin handle: " + pluginName + " =/= " + pluginHandle.getPluginName());
diff --git a/platform/javascript/.eslintrc.engine.js b/platform/javascript/.eslintrc.engine.js
index 3725cf164e..78df6d41d9 100644
--- a/platform/javascript/.eslintrc.engine.js
+++ b/platform/javascript/.eslintrc.engine.js
@@ -3,9 +3,8 @@ module.exports = {
"./.eslintrc.js",
],
"globals": {
- "EngineConfig": true,
+ "InternalConfig": true,
"Godot": true,
"Preloader": true,
- "Utils": true,
},
};
diff --git a/platform/javascript/SCsub b/platform/javascript/SCsub
index ab527ef419..72e999b38d 100644
--- a/platform/javascript/SCsub
+++ b/platform/javascript/SCsub
@@ -17,7 +17,7 @@ sys_env.AddJSLibraries(
[
"js/libs/library_godot_audio.js",
"js/libs/library_godot_display.js",
- "js/libs/library_godot_http_request.js",
+ "js/libs/library_godot_fetch.js",
"js/libs/library_godot_os.js",
"js/libs/library_godot_runtime.js",
]
@@ -73,7 +73,6 @@ sys_env.Depends(build[0], sys_env["JS_EXTERNS"])
engine = [
"js/engine/preloader.js",
- "js/engine/utils.js",
"js/engine/config.js",
"js/engine/engine.js",
]
@@ -97,7 +96,7 @@ out_files = [
]
html_file = "#misc/dist/html/full-size.html"
if env["tools"]:
- subst_dict = {"\$GODOT_VERSION": env.GetBuildVersion()}
+ subst_dict = {"@GODOT_VERSION@": env.GetBuildVersion()}
html_file = env.Substfile(
target="#bin/godot${PROGSUFFIX}.html", source="#misc/dist/html/editor.html", SUBST_DICT=subst_dict
)
diff --git a/platform/javascript/export/export.cpp b/platform/javascript/export/export.cpp
index 353cc49ef8..46d0458ca1 100644
--- a/platform/javascript/export/export.cpp
+++ b/platform/javascript/export/export.cpp
@@ -242,7 +242,7 @@ class EditorExportPlatformJavaScript : public EditorExportPlatform {
return name;
}
- void _fix_html(Vector<uint8_t> &p_html, const Ref<EditorExportPreset> &p_preset, const String &p_name, bool p_debug, int p_flags, const Vector<SharedObject> p_shared_objects);
+ void _fix_html(Vector<uint8_t> &p_html, const Ref<EditorExportPreset> &p_preset, const String &p_name, bool p_debug, int p_flags, const Vector<SharedObject> p_shared_objects, const Dictionary &p_file_sizes);
static void _server_thread_poll(void *data);
@@ -281,7 +281,7 @@ public:
~EditorExportPlatformJavaScript();
};
-void EditorExportPlatformJavaScript::_fix_html(Vector<uint8_t> &p_html, const Ref<EditorExportPreset> &p_preset, const String &p_name, bool p_debug, int p_flags, const Vector<SharedObject> p_shared_objects) {
+void EditorExportPlatformJavaScript::_fix_html(Vector<uint8_t> &p_html, const Ref<EditorExportPreset> &p_preset, const String &p_name, bool p_debug, int p_flags, const Vector<SharedObject> p_shared_objects, const Dictionary &p_file_sizes) {
String str_template = String::utf8(reinterpret_cast<const char *>(p_html.ptr()), p_html.size());
String str_export;
Vector<String> lines = str_template.split("\n");
@@ -300,6 +300,7 @@ void EditorExportPlatformJavaScript::_fix_html(Vector<uint8_t> &p_html, const Re
config["gdnativeLibs"] = libs;
config["executable"] = p_name;
config["args"] = args;
+ config["fileSizes"] = p_file_sizes;
const String str_config = JSON::print(config);
for (int i = 0; i < lines.size(); i++) {
@@ -472,6 +473,8 @@ Error EditorExportPlatformJavaScript::export_project(const Ref<EditorExportPrese
return ERR_FILE_CORRUPT;
}
+ Vector<uint8_t> html;
+ Dictionary file_sizes;
do {
//get filename
unz_file_info info;
@@ -480,6 +483,16 @@ Error EditorExportPlatformJavaScript::export_project(const Ref<EditorExportPrese
String file = fname;
+ // HTML is handled later
+ if (file == "godot.html") {
+ if (custom_html.is_empty()) {
+ html.resize(info.uncompressed_size);
+ unzOpenCurrentFile(pkg);
+ unzReadCurrentFile(pkg, html.ptrw(), html.size());
+ unzCloseCurrentFile(pkg);
+ }
+ continue;
+ }
Vector<uint8_t> data;
data.resize(info.uncompressed_size);
@@ -490,14 +503,7 @@ Error EditorExportPlatformJavaScript::export_project(const Ref<EditorExportPrese
//write
- if (file == "godot.html") {
- if (!custom_html.is_empty()) {
- continue;
- }
- _fix_html(data, p_preset, p_path.get_file().get_basename(), p_debug, p_flags, shared_objects);
- file = p_path.get_file();
-
- } else if (file == "godot.js") {
+ if (file == "godot.js") {
file = p_path.get_file().get_basename() + ".js";
} else if (file == "godot.worker.js") {
@@ -511,6 +517,7 @@ Error EditorExportPlatformJavaScript::export_project(const Ref<EditorExportPrese
} else if (file == "godot.wasm") {
file = p_path.get_file().get_basename() + ".wasm";
+ file_sizes[file.get_file()] = (uint64_t)info.uncompressed_size;
}
String dst = p_path.get_base_dir().plus_file(file);
@@ -532,19 +539,26 @@ Error EditorExportPlatformJavaScript::export_project(const Ref<EditorExportPrese
EditorNode::get_singleton()->show_warning(TTR("Could not read custom HTML shell:") + "\n" + custom_html);
return ERR_FILE_CANT_READ;
}
- Vector<uint8_t> buf;
- buf.resize(f->get_len());
- f->get_buffer(buf.ptrw(), buf.size());
+ html.resize(f->get_len());
+ f->get_buffer(html.ptrw(), html.size());
memdelete(f);
- _fix_html(buf, p_preset, p_path.get_file().get_basename(), p_debug, p_flags, shared_objects);
-
+ }
+ {
+ FileAccess *f = FileAccess::open(pck_path, FileAccess::READ);
+ if (f) {
+ file_sizes[pck_path.get_file()] = (uint64_t)f->get_len();
+ memdelete(f);
+ f = NULL;
+ }
+ _fix_html(html, p_preset, p_path.get_file().get_basename(), p_debug, p_flags, shared_objects, file_sizes);
f = FileAccess::open(p_path, FileAccess::WRITE);
if (!f) {
EditorNode::get_singleton()->show_warning(TTR("Could not write file:") + "\n" + p_path);
return ERR_FILE_CANT_WRITE;
}
- f->store_buffer(buf.ptr(), buf.size());
+ f->store_buffer(html.ptr(), html.size());
memdelete(f);
+ html.resize(0);
}
Ref<Image> splash;
diff --git a/platform/javascript/http_client.h.inc b/platform/javascript/http_client.h.inc
index 2ef1ad5b83..842a93fcba 100644
--- a/platform/javascript/http_client.h.inc
+++ b/platform/javascript/http_client.h.inc
@@ -30,24 +30,21 @@
// HTTPClient's additional private members in the javascript platform
-Error prepare_request(Method p_method, const String &p_url, const Vector<String> &p_headers);
+Error make_request(Method p_method, const String &p_url, const Vector<String> &p_headers, const uint8_t *p_body, int p_body_len);
+static void _parse_headers(int p_len, const char **p_headers, void *p_ref);
-int xhr_id;
+int js_id = 0;
int read_limit = 4096;
-int response_read_offset = 0;
Status status = STATUS_DISCONNECTED;
String host;
int port = -1;
bool use_tls = false;
-String username;
-String password;
int polled_response_code = 0;
-String polled_response_header;
-PackedByteArray polled_response;
+Vector<String> response_headers;
+Vector<uint8_t> response_buffer;
#ifdef DEBUG_ENABLED
-bool has_polled = false;
uint64_t last_polling_frame = 0;
#endif
diff --git a/platform/javascript/http_client_javascript.cpp b/platform/javascript/http_client_javascript.cpp
index 1136ea299d..b79c965854 100644
--- a/platform/javascript/http_client_javascript.cpp
+++ b/platform/javascript/http_client_javascript.cpp
@@ -30,7 +30,38 @@
#include "core/io/http_client.h"
-#include "http_request.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "stddef.h"
+
+typedef enum {
+ GODOT_JS_FETCH_STATE_REQUESTING = 0,
+ GODOT_JS_FETCH_STATE_BODY = 1,
+ GODOT_JS_FETCH_STATE_DONE = 2,
+ GODOT_JS_FETCH_STATE_ERROR = -1,
+} godot_js_fetch_state_t;
+
+extern int godot_js_fetch_create(const char *p_method, const char *p_url, const char **p_headers, int p_headers_len, const uint8_t *p_body, int p_body_len);
+extern int godot_js_fetch_read_headers(int p_id, void (*parse_callback)(int p_size, const char **p_headers, void *p_ref), void *p_ref);
+extern int godot_js_fetch_read_chunk(int p_id, uint8_t *p_buf, int p_buf_size);
+extern void godot_js_fetch_free(int p_id);
+extern godot_js_fetch_state_t godot_js_fetch_state_get(int p_id);
+extern int godot_js_fetch_body_length_get(int p_id);
+extern int godot_js_fetch_http_status_get(int p_id);
+extern int godot_js_fetch_is_chunked(int p_id);
+
+#ifdef __cplusplus
+}
+#endif
+
+void HTTPClient::_parse_headers(int p_len, const char **p_headers, void *p_ref) {
+ HTTPClient *client = static_cast<HTTPClient *>(p_ref);
+ for (int i = 0; i < p_len; i++) {
+ client->response_headers.push_back(String::utf8(p_headers[i]));
+ }
+}
Error HTTPClient::connect_to_host(const String &p_host, int p_port, bool p_ssl, bool p_verify_host) {
close();
@@ -74,7 +105,7 @@ Ref<StreamPeer> HTTPClient::get_connection() const {
ERR_FAIL_V_MSG(REF(), "Accessing an HTTPClient's StreamPeer is not supported for the HTML5 platform.");
}
-Error HTTPClient::prepare_request(Method p_method, const String &p_url, const Vector<String> &p_headers) {
+Error HTTPClient::make_request(Method p_method, const String &p_url, const Vector<String> &p_headers, const uint8_t *p_body, int p_body_len) {
ERR_FAIL_INDEX_V(p_method, METHOD_MAX, ERR_INVALID_PARAMETER);
ERR_FAIL_COND_V_MSG(p_method == METHOD_TRACE || p_method == METHOD_CONNECT, ERR_UNAVAILABLE, "HTTP methods TRACE and CONNECT are not supported for the HTML5 platform.");
ERR_FAIL_COND_V(status != STATUS_CONNECTED, ERR_INVALID_PARAMETER);
@@ -83,46 +114,33 @@ Error HTTPClient::prepare_request(Method p_method, const String &p_url, const Ve
ERR_FAIL_COND_V(!p_url.begins_with("/"), ERR_INVALID_PARAMETER);
String url = (use_tls ? "https://" : "http://") + host + ":" + itos(port) + p_url;
- godot_xhr_reset(xhr_id);
- godot_xhr_open(xhr_id, _methods[p_method], url.utf8().get_data(),
- username.is_empty() ? nullptr : username.utf8().get_data(),
- password.is_empty() ? nullptr : password.utf8().get_data());
-
+ Vector<CharString> keeper;
+ Vector<const char *> c_strings;
for (int i = 0; i < p_headers.size(); i++) {
- int header_separator = p_headers[i].find(": ");
- ERR_FAIL_COND_V(header_separator < 0, ERR_INVALID_PARAMETER);
- godot_xhr_set_request_header(xhr_id,
- p_headers[i].left(header_separator).utf8().get_data(),
- p_headers[i].right(header_separator + 2).utf8().get_data());
+ keeper.push_back(p_headers[i].utf8());
+ c_strings.push_back(keeper[i].get_data());
+ }
+ if (js_id) {
+ godot_js_fetch_free(js_id);
}
- response_read_offset = 0;
+ js_id = godot_js_fetch_create(_methods[p_method], url.utf8().get_data(), c_strings.ptrw(), c_strings.size(), p_body, p_body_len);
status = STATUS_REQUESTING;
return OK;
}
Error HTTPClient::request_raw(Method p_method, const String &p_url, const Vector<String> &p_headers, const Vector<uint8_t> &p_body) {
- Error err = prepare_request(p_method, p_url, p_headers);
- if (err != OK)
- return err;
if (p_body.is_empty()) {
- godot_xhr_send(xhr_id, nullptr, 0);
- } else {
- godot_xhr_send(xhr_id, p_body.ptr(), p_body.size());
+ return make_request(p_method, p_url, p_headers, nullptr, 0);
}
- return OK;
+ return make_request(p_method, p_url, p_headers, p_body.ptr(), p_body.size());
}
Error HTTPClient::request(Method p_method, const String &p_url, const Vector<String> &p_headers, const String &p_body) {
- Error err = prepare_request(p_method, p_url, p_headers);
- if (err != OK)
- return err;
if (p_body.is_empty()) {
- godot_xhr_send(xhr_id, nullptr, 0);
- } else {
- const CharString cs = p_body.utf8();
- godot_xhr_send(xhr_id, cs.get_data(), cs.length());
+ return make_request(p_method, p_url, p_headers, nullptr, 0);
}
- return OK;
+ const CharString cs = p_body.utf8();
+ return make_request(p_method, p_url, p_headers, (const uint8_t *)cs.get_data(), cs.size() - 1);
}
void HTTPClient::close() {
@@ -130,10 +148,13 @@ void HTTPClient::close() {
port = -1;
use_tls = false;
status = STATUS_DISCONNECTED;
- polled_response.resize(0);
polled_response_code = 0;
- polled_response_header = String();
- godot_xhr_reset(xhr_id);
+ response_headers.resize(0);
+ response_buffer.resize(0);
+ if (js_id) {
+ godot_js_fetch_free(js_id);
+ js_id = 0;
+ }
}
HTTPClient::Status HTTPClient::get_status() const {
@@ -141,12 +162,11 @@ HTTPClient::Status HTTPClient::get_status() const {
}
bool HTTPClient::has_response() const {
- return !polled_response_header.is_empty();
+ return response_headers.size() > 0;
}
bool HTTPClient::is_response_chunked() const {
- // TODO evaluate using moz-chunked-arraybuffer, fetch & ReadableStream
- return false;
+ return godot_js_fetch_is_chunked(js_id);
}
int HTTPClient::get_response_code() const {
@@ -154,36 +174,42 @@ int HTTPClient::get_response_code() const {
}
Error HTTPClient::get_response_headers(List<String> *r_response) {
- if (polled_response_header.is_empty())
+ if (!response_headers.size()) {
return ERR_INVALID_PARAMETER;
-
- Vector<String> header_lines = polled_response_header.split("\r\n", false);
- for (int i = 0; i < header_lines.size(); ++i) {
- r_response->push_back(header_lines[i]);
}
- polled_response_header = String();
+ for (int i = 0; i < response_headers.size(); i++) {
+ r_response->push_back(response_headers[i]);
+ }
+ response_headers.clear();
return OK;
}
int HTTPClient::get_response_body_length() const {
- return polled_response.size();
+ return godot_js_fetch_body_length_get(js_id);
}
PackedByteArray HTTPClient::read_response_body_chunk() {
ERR_FAIL_COND_V(status != STATUS_BODY, PackedByteArray());
- int to_read = MIN(read_limit, polled_response.size() - response_read_offset);
- PackedByteArray chunk;
- chunk.resize(to_read);
- memcpy(chunk.ptrw(), polled_response.ptr() + response_read_offset, to_read);
- response_read_offset += to_read;
-
- if (response_read_offset == polled_response.size()) {
- status = STATUS_CONNECTED;
- polled_response.resize(0);
- godot_xhr_reset(xhr_id);
+ if (response_buffer.size() != read_limit) {
+ response_buffer.resize(read_limit);
+ }
+ int read = godot_js_fetch_read_chunk(js_id, response_buffer.ptrw(), read_limit);
+
+ // Check if the stream is over.
+ godot_js_fetch_state_t state = godot_js_fetch_state_get(js_id);
+ if (state == GODOT_JS_FETCH_STATE_DONE) {
+ status = STATUS_DISCONNECTED;
+ } else if (state != GODOT_JS_FETCH_STATE_BODY) {
+ status = STATUS_CONNECTION_ERROR;
}
+ PackedByteArray chunk;
+ if (!read) {
+ return chunk;
+ }
+ chunk.resize(read);
+ copymem(chunk.ptrw(), response_buffer.ptr(), read);
return chunk;
}
@@ -217,48 +243,48 @@ Error HTTPClient::poll() {
return OK;
case STATUS_CONNECTED:
- case STATUS_BODY:
return OK;
+ case STATUS_BODY: {
+ godot_js_fetch_state_t state = godot_js_fetch_state_get(js_id);
+ if (state == GODOT_JS_FETCH_STATE_DONE) {
+ status = STATUS_DISCONNECTED;
+ } else if (state != GODOT_JS_FETCH_STATE_BODY) {
+ status = STATUS_CONNECTION_ERROR;
+ return ERR_CONNECTION_ERROR;
+ }
+ return OK;
+ }
+
case STATUS_CONNECTION_ERROR:
return ERR_CONNECTION_ERROR;
case STATUS_REQUESTING: {
#ifdef DEBUG_ENABLED
- if (!has_polled) {
- has_polled = true;
- } else {
- // forcing synchronous requests is not possible on the web
- if (last_polling_frame == Engine::get_singleton()->get_process_frames()) {
- WARN_PRINT("HTTPClient polled multiple times in one frame, "
- "but request cannot progress more than once per "
- "frame on the HTML5 platform.");
- }
+ // forcing synchronous requests is not possible on the web
+ if (last_polling_frame == Engine::get_singleton()->get_process_frames()) {
+ WARN_PRINT("HTTPClient polled multiple times in one frame, "
+ "but request cannot progress more than once per "
+ "frame on the HTML5 platform.");
}
last_polling_frame = Engine::get_singleton()->get_process_frames();
#endif
- polled_response_code = godot_xhr_get_status(xhr_id);
- if (godot_xhr_get_ready_state(xhr_id) != XHR_READY_STATE_DONE) {
+ polled_response_code = godot_js_fetch_http_status_get(js_id);
+ godot_js_fetch_state_t js_state = godot_js_fetch_state_get(js_id);
+ if (js_state == GODOT_JS_FETCH_STATE_REQUESTING) {
return OK;
- } else if (!polled_response_code) {
+ } else if (js_state == GODOT_JS_FETCH_STATE_ERROR) {
+ // Fetch is in error state.
+ status = STATUS_CONNECTION_ERROR;
+ return ERR_CONNECTION_ERROR;
+ }
+ if (godot_js_fetch_read_headers(js_id, &_parse_headers, this)) {
+ // Failed to parse headers.
status = STATUS_CONNECTION_ERROR;
return ERR_CONNECTION_ERROR;
}
-
status = STATUS_BODY;
-
- PackedByteArray bytes;
- int len = godot_xhr_get_response_headers_length(xhr_id);
- bytes.resize(len + 1);
-
- godot_xhr_get_response_headers(xhr_id, reinterpret_cast<char *>(bytes.ptrw()), len);
- bytes.ptrw()[len] = 0;
-
- polled_response_header = String::utf8(reinterpret_cast<const char *>(bytes.ptr()));
-
- polled_response.resize(godot_xhr_get_response_length(xhr_id));
- godot_xhr_get_response(xhr_id, polled_response.ptrw(), polled_response.size());
break;
}
@@ -269,9 +295,8 @@ Error HTTPClient::poll() {
}
HTTPClient::HTTPClient() {
- xhr_id = godot_xhr_new();
}
HTTPClient::~HTTPClient() {
- godot_xhr_free(xhr_id);
+ close();
}
diff --git a/platform/javascript/js/engine/config.js b/platform/javascript/js/engine/config.js
index 97fd718815..82ff273ecf 100644
--- a/platform/javascript/js/engine/config.js
+++ b/platform/javascript/js/engine/config.js
@@ -1,100 +1,320 @@
-/** @constructor */
-function EngineConfig(opts) {
- // Module config
- this.unloadAfterInit = true;
- this.onPrintError = function () {
- console.error.apply(console, Array.from(arguments)); // eslint-disable-line no-console
- };
- this.onPrint = function () {
- console.log.apply(console, Array.from(arguments)); // eslint-disable-line no-console
+/**
+ * An object used to configure the Engine instance based on godot export options, and to override those in custom HTML
+ * templates if needed.
+ *
+ * @header Engine configuration
+ * @summary The Engine configuration object. This is just a typedef, create it like a regular object, e.g.:
+ *
+ * ``const MyConfig = { executable: 'godot', unloadAfterInit: false }``
+ *
+ * @typedef {Object} EngineConfig
+ */
+const EngineConfig = {}; // eslint-disable-line no-unused-vars
+
+/**
+ * @struct
+ * @constructor
+ * @ignore
+ */
+const InternalConfig = function (initConfig) { // eslint-disable-line no-unused-vars
+ const cfg = /** @lends {InternalConfig.prototype} */ {
+ /**
+ * Whether the unload the engine automatically after the instance is initialized.
+ *
+ * @memberof EngineConfig
+ * @default
+ * @type {boolean}
+ */
+ unloadAfterInit: true,
+ /**
+ * The HTML DOM Canvas object to use.
+ *
+ * By default, the first canvas element in the document will be used is none is specified.
+ *
+ * @memberof EngineConfig
+ * @default
+ * @type {?HTMLCanvasElement}
+ */
+ canvas: null,
+ /**
+ * The name of the WASM file without the extension. (Set by Godot Editor export process).
+ *
+ * @memberof EngineConfig
+ * @default
+ * @type {string}
+ */
+ executable: '',
+ /**
+ * An alternative name for the game pck to load. The executable name is used otherwise.
+ *
+ * @memberof EngineConfig
+ * @default
+ * @type {?string}
+ */
+ mainPack: null,
+ /**
+ * Specify a language code to select the proper localization for the game.
+ *
+ * The browser locale will be used if none is specified. See complete list of
+ * :ref:`supported locales <doc_locales>`.
+ *
+ * @memberof EngineConfig
+ * @type {?string}
+ * @default
+ */
+ locale: null,
+ /**
+ * The canvas resize policy determines how the canvas should be resized by Godot.
+ *
+ * ``0`` means Godot won't do any resizing. This is useful if you want to control the canvas size from
+ * javascript code in your template.
+ *
+ * ``1`` means Godot will resize the canvas on start, and when changing window size via engine functions.
+ *
+ * ``2`` means Godot will adapt the canvas size to match the whole browser window.
+ *
+ * @memberof EngineConfig
+ * @type {number}
+ * @default
+ */
+ canvasResizePolicy: 2,
+ /**
+ * The arguments to be passed as command line arguments on startup.
+ *
+ * See :ref:`command line tutorial <doc_command_line_tutorial>`.
+ *
+ * **Note**: :js:meth:`startGame <Engine.prototype.startGame>` will always add the ``--main-pack`` argument.
+ *
+ * @memberof EngineConfig
+ * @type {Array<string>}
+ * @default
+ */
+ args: [],
+ /**
+ * @ignore
+ * @type {Array.<string>}
+ */
+ persistentPaths: ['/userfs'],
+ /**
+ * @ignore
+ * @type {Array.<string>}
+ */
+ gdnativeLibs: [],
+ /**
+ * @ignore
+ * @type {Array.<string>}
+ */
+ fileSizes: [],
+ /**
+ * A callback function for handling Godot's ``OS.execute`` calls.
+ *
+ * This is for example used in the Web Editor template to switch between project manager and editor, and for running the game.
+ *
+ * @callback EngineConfig.onExecute
+ * @param {string} path The path that Godot's wants executed.
+ * @param {Array.<string>} args The arguments of the "command" to execute.
+ */
+ /**
+ * @ignore
+ * @type {?function(string, Array.<string>)}
+ */
+ onExecute: null,
+ /**
+ * A callback function for being notified when the Godot instance quits.
+ *
+ * **Note**: This function will not be called if the engine crashes or become unresponsive.
+ *
+ * @callback EngineConfig.onExit
+ * @param {number} status_code The status code returned by Godot on exit.
+ */
+ /**
+ * @ignore
+ * @type {?function(number)}
+ */
+ onExit: null,
+ /**
+ * A callback function for displaying download progress.
+ *
+ * The function is called once per frame while downloading files, so the usage of ``requestAnimationFrame()``
+ * is not necessary.
+ *
+ * If the callback function receives a total amount of bytes as 0, this means that it is impossible to calculate.
+ * Possible reasons include:
+ *
+ * - Files are delivered with server-side chunked compression
+ * - Files are delivered with server-side compression on Chromium
+ * - Not all file downloads have started yet (usually on servers without multi-threading)
+ *
+ * @callback EngineConfig.onProgress
+ * @param {number} current The current amount of downloaded bytes so far.
+ * @param {number} total The total amount of bytes to be downloaded.
+ */
+ /**
+ * @ignore
+ * @type {?function(number, number)}
+ */
+ onProgress: null,
+ /**
+ * A callback function for handling the standard output stream. This method should usually only be used in debug pages.
+ *
+ * By default, ``console.log()`` is used.
+ *
+ * @callback EngineConfig.onPrint
+ * @param {...*} [var_args] A variadic number of arguments to be printed.
+ */
+ /**
+ * @ignore
+ * @type {?function(...*)}
+ */
+ onPrint: function () {
+ console.log.apply(console, Array.from(arguments)); // eslint-disable-line no-console
+ },
+ /**
+ * A callback function for handling the standard error stream. This method should usually only be used in debug pages.
+ *
+ * By default, ``console.error()`` is used.
+ *
+ * @callback EngineConfig.onPrintError
+ * @param {...*} [var_args] A variadic number of arguments to be printed as errors.
+ */
+ /**
+ * @ignore
+ * @type {?function(...*)}
+ */
+ onPrintError: function (var_args) {
+ console.error.apply(console, Array.from(arguments)); // eslint-disable-line no-console
+ },
};
- this.onProgress = null;
- // Godot Config
- this.canvas = null;
- this.executable = '';
- this.mainPack = null;
- this.locale = null;
- this.canvasResizePolicy = false;
- this.persistentPaths = ['/userfs'];
- this.gdnativeLibs = [];
- this.args = [];
- this.onExecute = null;
- this.onExit = null;
- this.update(opts);
-}
+ /**
+ * @ignore
+ * @struct
+ * @constructor
+ * @param {EngineConfig} opts
+ */
+ function Config(opts) {
+ this.update(opts);
+ }
+
+ Config.prototype = cfg;
-EngineConfig.prototype.update = function (opts) {
- const config = opts || {};
- function parse(key, def) {
- if (typeof (config[key]) === 'undefined') {
- return def;
+ /**
+ * @ignore
+ * @param {EngineConfig} opts
+ */
+ Config.prototype.update = function (opts) {
+ const config = opts || {};
+ function parse(key, def) {
+ if (typeof (config[key]) === 'undefined') {
+ return def;
+ }
+ return config[key];
}
- return config[key];
- }
- // Module config
- this.unloadAfterInit = parse('unloadAfterInit', this.unloadAfterInit);
- this.onPrintError = parse('onPrintError', this.onPrintError);
- this.onPrint = parse('onPrint', this.onPrint);
- this.onProgress = parse('onProgress', this.onProgress);
+ // Module config
+ this.unloadAfterInit = parse('unloadAfterInit', this.unloadAfterInit);
+ this.onPrintError = parse('onPrintError', this.onPrintError);
+ this.onPrint = parse('onPrint', this.onPrint);
+ this.onProgress = parse('onProgress', this.onProgress);
- // Godot config
- this.canvas = parse('canvas', this.canvas);
- this.executable = parse('executable', this.executable);
- this.mainPack = parse('mainPack', this.mainPack);
- this.locale = parse('locale', this.locale);
- this.canvasResizePolicy = parse('canvasResizePolicy', this.canvasResizePolicy);
- this.persistentPaths = parse('persistentPaths', this.persistentPaths);
- this.gdnativeLibs = parse('gdnativeLibs', this.gdnativeLibs);
- this.args = parse('args', this.args);
- this.onExecute = parse('onExecute', this.onExecute);
- this.onExit = parse('onExit', this.onExit);
-};
+ // Godot config
+ this.canvas = parse('canvas', this.canvas);
+ this.executable = parse('executable', this.executable);
+ this.mainPack = parse('mainPack', this.mainPack);
+ this.locale = parse('locale', this.locale);
+ this.canvasResizePolicy = parse('canvasResizePolicy', this.canvasResizePolicy);
+ this.persistentPaths = parse('persistentPaths', this.persistentPaths);
+ this.gdnativeLibs = parse('gdnativeLibs', this.gdnativeLibs);
+ this.fileSizes = parse('fileSizes', this.fileSizes);
+ this.args = parse('args', this.args);
+ this.onExecute = parse('onExecute', this.onExecute);
+ this.onExit = parse('onExit', this.onExit);
+ };
-EngineConfig.prototype.getModuleConfig = function (loadPath, loadPromise) {
- const me = this;
- return {
- 'print': this.onPrint,
- 'printErr': this.onPrintError,
- 'locateFile': Utils.createLocateRewrite(loadPath),
- 'instantiateWasm': Utils.createInstantiatePromise(loadPromise),
- 'thisProgram': me.executable,
- 'noExitRuntime': true,
- 'dynamicLibraries': [`${me.executable}.side.wasm`],
+ /**
+ * @ignore
+ * @param {string} loadPath
+ * @param {Response} response
+ */
+ Config.prototype.getModuleConfig = function (loadPath, response) {
+ let r = response;
+ return {
+ 'print': this.onPrint,
+ 'printErr': this.onPrintError,
+ 'thisProgram': this.executable,
+ 'noExitRuntime': true,
+ 'dynamicLibraries': [`${loadPath}.side.wasm`],
+ 'instantiateWasm': function (imports, onSuccess) {
+ function done(result) {
+ onSuccess(result['instance'], result['module']);
+ }
+ if (typeof (WebAssembly.instantiateStreaming) !== 'undefined') {
+ WebAssembly.instantiateStreaming(Promise.resolve(r), imports).then(done);
+ } else {
+ r.arrayBuffer().then(function (buffer) {
+ WebAssembly.instantiate(buffer, imports).then(done);
+ });
+ }
+ r = null;
+ return {};
+ },
+ 'locateFile': function (path) {
+ if (path.endsWith('.worker.js')) {
+ return `${loadPath}.worker.js`;
+ } else if (path.endsWith('.audio.worklet.js')) {
+ return `${loadPath}.audio.worklet.js`;
+ } else if (path.endsWith('.js')) {
+ return `${loadPath}.js`;
+ } else if (path.endsWith('.side.wasm')) {
+ return `${loadPath}.side.wasm`;
+ } else if (path.endsWith('.wasm')) {
+ return `${loadPath}.wasm`;
+ }
+ return path;
+ },
+ };
};
-};
-EngineConfig.prototype.getGodotConfig = function (cleanup) {
- if (!(this.canvas instanceof HTMLCanvasElement)) {
- this.canvas = Utils.findCanvas();
- if (!this.canvas) {
- throw new Error('No canvas found in page');
+ /**
+ * @ignore
+ * @param {function()} cleanup
+ */
+ Config.prototype.getGodotConfig = function (cleanup) {
+ // Try to find a canvas
+ if (!(this.canvas instanceof HTMLCanvasElement)) {
+ const nodes = document.getElementsByTagName('canvas');
+ if (nodes.length && nodes[0] instanceof HTMLCanvasElement) {
+ this.canvas = nodes[0];
+ }
+ if (!this.canvas) {
+ throw new Error('No canvas found in page');
+ }
+ }
+ // Canvas can grab focus on click, or key events won't work.
+ if (this.canvas.tabIndex < 0) {
+ this.canvas.tabIndex = 0;
}
- }
- // Canvas can grab focus on click, or key events won't work.
- if (this.canvas.tabIndex < 0) {
- this.canvas.tabIndex = 0;
- }
+ // Browser locale, or custom one if defined.
+ let locale = this.locale;
+ if (!locale) {
+ locale = navigator.languages ? navigator.languages[0] : navigator.language;
+ locale = locale.split('.')[0];
+ }
+ const onExit = this.onExit;
- // Browser locale, or custom one if defined.
- let locale = this.locale;
- if (!locale) {
- locale = navigator.languages ? navigator.languages[0] : navigator.language;
- locale = locale.split('.')[0];
- }
- const onExit = this.onExit;
- // Godot configuration.
- return {
- 'canvas': this.canvas,
- 'canvasResizePolicy': this.canvasResizePolicy,
- 'locale': locale,
- 'onExecute': this.onExecute,
- 'onExit': function (p_code) {
- cleanup(); // We always need to call the cleanup callback to free memory.
- if (typeof (onExit) === 'function') {
- onExit(p_code);
- }
- },
+ // Godot configuration.
+ return {
+ 'canvas': this.canvas,
+ 'canvasResizePolicy': this.canvasResizePolicy,
+ 'locale': locale,
+ 'onExecute': this.onExecute,
+ 'onExit': function (p_code) {
+ cleanup(); // We always need to call the cleanup callback to free memory.
+ if (typeof (onExit) === 'function') {
+ onExit(p_code);
+ }
+ },
+ };
};
+ return new Config(initConfig);
};
diff --git a/platform/javascript/js/engine/engine.externs.js b/platform/javascript/js/engine/engine.externs.js
index 1a94dd15ec..35a66a93ae 100644
--- a/platform/javascript/js/engine/engine.externs.js
+++ b/platform/javascript/js/engine/engine.externs.js
@@ -1,3 +1,4 @@
var Godot;
var WebAssembly = {};
WebAssembly.instantiate = function(buffer, imports) {};
+WebAssembly.instantiateStreaming = function(response, imports) {};
diff --git a/platform/javascript/js/engine/engine.js b/platform/javascript/js/engine/engine.js
index d14e0e5806..19a0552c8c 100644
--- a/platform/javascript/js/engine/engine.js
+++ b/platform/javascript/js/engine/engine.js
@@ -1,3 +1,13 @@
+/**
+ * Projects exported for the Web expose the :js:class:`Engine` class to the JavaScript environment, that allows
+ * fine control over the engine's start-up process.
+ *
+ * This API is built in an asynchronous manner and requires basic understanding
+ * of `Promises <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises>`__.
+ *
+ * @module Engine
+ * @header HTML5 shell class reference
+ */
const Engine = (function () {
const preloader = new Preloader();
@@ -5,139 +15,259 @@ const Engine = (function () {
let loadPath = '';
let initPromise = null;
- function load(basePath) {
+ /**
+ * @classdesc The ``Engine`` class provides methods for loading and starting exported projects on the Web. For default export
+ * settings, this is already part of the exported HTML page. To understand practical use of the ``Engine`` class,
+ * see :ref:`Custom HTML page for Web export <doc_customizing_html5_shell>`.
+ *
+ * @description Create a new Engine instance with the given configuration.
+ *
+ * @global
+ * @constructor
+ * @param {EngineConfig} initConfig The initial config for this instance.
+ */
+ function Engine(initConfig) { // eslint-disable-line no-shadow
+ this.config = new InternalConfig(initConfig);
+ this.rtenv = null;
+ }
+
+ /**
+ * Load the engine from the specified base path.
+ *
+ * @param {string} basePath Base path of the engine to load.
+ * @param {number=} [size=0] The file size if known.
+ * @returns {Promise} A Promise that resolves once the engine is loaded.
+ *
+ * @function Engine.load
+ */
+ Engine.load = function (basePath, size) {
if (loadPromise == null) {
loadPath = basePath;
- loadPromise = preloader.loadPromise(`${loadPath}.wasm`);
+ loadPromise = preloader.loadPromise(`${loadPath}.wasm`, size, true);
requestAnimationFrame(preloader.animateProgress);
}
return loadPromise;
- }
+ };
- function unload() {
+ /**
+ * Unload the engine to free memory.
+ *
+ * This method will be called automatically depending on the configuration. See :js:attr:`unloadAfterInit`.
+ *
+ * @function Engine.unload
+ */
+ Engine.unload = function () {
loadPromise = null;
- }
+ };
- /** @constructor */
- function Engine(opts) { // eslint-disable-line no-shadow
- this.config = new EngineConfig(opts);
- this.rtenv = null;
- }
+ /**
+ * Check whether WebGL is available. Optionally, specify a particular version of WebGL to check for.
+ *
+ * @param {number=} [majorVersion=1] The major WebGL version to check for.
+ * @returns {boolean} If the given major version of WebGL is available.
+ * @function Engine.isWebGLAvailable
+ */
+ Engine.isWebGLAvailable = function (majorVersion = 1) {
+ try {
+ return !!document.createElement('canvas').getContext(['webgl', 'webgl2'][majorVersion - 1]);
+ } catch (e) { /* Not available */ }
+ return false;
+ };
- Engine.prototype.init = /** @param {string=} basePath */ function (basePath) {
- if (initPromise) {
- return initPromise;
- }
- if (loadPromise == null) {
- if (!basePath) {
- initPromise = Promise.reject(new Error('A base path must be provided when calling `init` and the engine is not loaded.'));
+ /**
+ * Safe Engine constructor, creates a new prototype for every new instance to avoid prototype pollution.
+ * @ignore
+ * @constructor
+ */
+ function SafeEngine(initConfig) {
+ const proto = /** @lends Engine.prototype */ {
+ /**
+ * Initialize the engine instance. Optionally, pass the base path to the engine to load it,
+ * if it hasn't been loaded yet. See :js:meth:`Engine.load`.
+ *
+ * @param {string=} basePath Base path of the engine to load.
+ * @return {Promise} A ``Promise`` that resolves once the engine is loaded and initialized.
+ */
+ init: function (basePath) {
+ if (initPromise) {
+ return initPromise;
+ }
+ if (loadPromise == null) {
+ if (!basePath) {
+ initPromise = Promise.reject(new Error('A base path must be provided when calling `init` and the engine is not loaded.'));
+ return initPromise;
+ }
+ Engine.load(basePath, this.config.fileSizes[`${basePath}.wasm`]);
+ }
+ const me = this;
+ function doInit(promise) {
+ return promise.then(function (response) {
+ return Godot(me.config.getModuleConfig(loadPath, response.clone()));
+ }).then(function (module) {
+ const paths = me.config.persistentPaths;
+ return module['initFS'](paths).then(function (err) {
+ return Promise.resolve(module);
+ });
+ }).then(function (module) {
+ me.rtenv = module;
+ if (me.config.unloadAfterInit) {
+ Engine.unload();
+ }
+ return Promise.resolve();
+ });
+ }
+ preloader.setProgressFunc(this.config.onProgress);
+ initPromise = doInit(loadPromise);
return initPromise;
- }
- load(basePath);
- }
- preloader.setProgressFunc(this.config.onProgress);
- let config = this.config.getModuleConfig(loadPath, loadPromise);
- const me = this;
- initPromise = new Promise(function (resolve, reject) {
- Godot(config).then(function (module) {
- module['initFS'](me.config.persistentPaths).then(function (fs_err) {
- me.rtenv = module;
- if (me.config.unloadAfterInit) {
- unload();
+ },
+
+ /**
+ * Load a file so it is available in the instance's file system once it runs. Must be called **before** starting the
+ * instance.
+ *
+ * If not provided, the ``path`` is derived from the URL of the loaded file.
+ *
+ * @param {string|ArrayBuffer} file The file to preload.
+ *
+ * If a ``string`` the file will be loaded from that path.
+ *
+ * If an ``ArrayBuffer`` or a view on one, the buffer will used as the content of the file.
+ *
+ * @param {string=} path Path by which the file will be accessible. Required, if ``file`` is not a string.
+ *
+ * @returns {Promise} A Promise that resolves once the file is loaded.
+ */
+ preloadFile: function (file, path) {
+ return preloader.preload(file, path, this.config.fileSizes[file]);
+ },
+
+ /**
+ * Start the engine instance using the given override configuration (if any).
+ * :js:meth:`startGame <Engine.prototype.startGame>` can be used in typical cases instead.
+ *
+ * This will initialize the instance if it is not initialized. For manual initialization, see :js:meth:`init <Engine.prototype.init>`.
+ * The engine must be loaded beforehand.
+ *
+ * Fails if a canvas cannot be found on the page, or not specified in the configuration.
+ *
+ * @param {EngineConfig} override An optional configuration override.
+ * @return {Promise} Promise that resolves once the engine started.
+ */
+ start: function (override) {
+ this.config.update(override);
+ const me = this;
+ return me.init().then(function () {
+ if (!me.rtenv) {
+ return Promise.reject(new Error('The engine must be initialized before it can be started'));
}
- resolve();
- config = null;
- });
- });
- });
- return initPromise;
- };
- /** @type {function(string, string):Object} */
- Engine.prototype.preloadFile = function (file, path) {
- return preloader.preload(file, path);
- };
+ let config = {};
+ try {
+ config = me.config.getGodotConfig(function () {
+ me.rtenv = null;
+ });
+ } catch (e) {
+ return Promise.reject(e);
+ }
+ // Godot configuration.
+ me.rtenv['initConfig'](config);
- /** @type {function(...string):Object} */
- Engine.prototype.start = function (override) {
- this.config.update(override);
- const me = this;
- return me.init().then(function () {
- if (!me.rtenv) {
- return Promise.reject(new Error('The engine must be initialized before it can be started'));
- }
-
- let config = {};
- try {
- config = me.config.getGodotConfig(function () {
- me.rtenv = null;
- });
- } catch (e) {
- return Promise.reject(e);
- }
- // Godot configuration.
- me.rtenv['initConfig'](config);
-
- // Preload GDNative libraries.
- const libs = [];
- me.config.gdnativeLibs.forEach(function (lib) {
- libs.push(me.rtenv['loadDynamicLibrary'](lib, { 'loadAsync': true }));
- });
- return Promise.all(libs).then(function () {
- return new Promise(function (resolve, reject) {
- preloader.preloadedFiles.forEach(function (file) {
- me.rtenv['copyToFS'](file.path, file.buffer);
+ // Preload GDNative libraries.
+ const libs = [];
+ me.config.gdnativeLibs.forEach(function (lib) {
+ libs.push(me.rtenv['loadDynamicLibrary'](lib, { 'loadAsync': true }));
+ });
+ return Promise.all(libs).then(function () {
+ return new Promise(function (resolve, reject) {
+ preloader.preloadedFiles.forEach(function (file) {
+ me.rtenv['copyToFS'](file.path, file.buffer);
+ });
+ preloader.preloadedFiles.length = 0; // Clear memory
+ me.rtenv['callMain'](me.config.args);
+ initPromise = null;
+ resolve();
+ });
});
- preloader.preloadedFiles.length = 0; // Clear memory
- me.rtenv['callMain'](me.config.args);
- initPromise = null;
- resolve();
});
- });
- });
- };
+ },
- Engine.prototype.startGame = function (override) {
- this.config.update(override);
- // Add main-pack argument.
- const exe = this.config.executable;
- const pack = this.config.mainPack || `${exe}.pck`;
- this.config.args = ['--main-pack', pack].concat(this.config.args);
- // Start and init with execName as loadPath if not inited.
- const me = this;
- return Promise.all([
- this.init(exe),
- this.preloadFile(pack, pack),
- ]).then(function () {
- return me.start.apply(me);
- });
- };
+ /**
+ * Start the game instance using the given configuration override (if any).
+ *
+ * This will initialize the instance if it is not initialized. For manual initialization, see :js:meth:`init <Engine.prototype.init>`.
+ *
+ * This will load the engine if it is not loaded, and preload the main pck.
+ *
+ * This method expects the initial config (or the override) to have both the :js:attr:`executable` and :js:attr:`mainPack`
+ * properties set (normally done by the editor during export).
+ *
+ * @param {EngineConfig} override An optional configuration override.
+ * @return {Promise} Promise that resolves once the game started.
+ */
+ startGame: function (override) {
+ this.config.update(override);
+ // Add main-pack argument.
+ const exe = this.config.executable;
+ const pack = this.config.mainPack || `${exe}.pck`;
+ this.config.args = ['--main-pack', pack].concat(this.config.args);
+ // Start and init with execName as loadPath if not inited.
+ const me = this;
+ return Promise.all([
+ this.init(exe),
+ this.preloadFile(pack, pack),
+ ]).then(function () {
+ return me.start.apply(me);
+ });
+ },
- Engine.prototype.copyToFS = function (path, buffer) {
- if (this.rtenv == null) {
- throw new Error('Engine must be inited before copying files');
- }
- this.rtenv['copyToFS'](path, buffer);
- };
+ /**
+ * Create a file at the specified ``path`` with the passed as ``buffer`` in the instance's file system.
+ *
+ * @param {string} path The location where the file will be created.
+ * @param {ArrayBuffer} buffer The content of the file.
+ */
+ copyToFS: function (path, buffer) {
+ if (this.rtenv == null) {
+ throw new Error('Engine must be inited before copying files');
+ }
+ this.rtenv['copyToFS'](path, buffer);
+ },
- Engine.prototype.requestQuit = function () {
- if (this.rtenv) {
- this.rtenv['request_quit']();
- }
- };
+ /**
+ * Request that the current instance quit.
+ *
+ * This is akin the user pressing the close button in the window manager, and will
+ * have no effect if the engine has crashed, or is stuck in a loop.
+ *
+ */
+ requestQuit: function () {
+ if (this.rtenv) {
+ this.rtenv['request_quit']();
+ }
+ },
+ };
+
+ Engine.prototype = proto;
+ // Closure compiler exported instance methods.
+ Engine.prototype['init'] = Engine.prototype.init;
+ Engine.prototype['preloadFile'] = Engine.prototype.preloadFile;
+ Engine.prototype['start'] = Engine.prototype.start;
+ Engine.prototype['startGame'] = Engine.prototype.startGame;
+ Engine.prototype['copyToFS'] = Engine.prototype.copyToFS;
+ Engine.prototype['requestQuit'] = Engine.prototype.requestQuit;
+ // Also expose static methods as instance methods
+ Engine.prototype['load'] = Engine.load;
+ Engine.prototype['unload'] = Engine.unload;
+ Engine.prototype['isWebGLAvailable'] = Engine.isWebGLAvailable;
+ return new Engine(initConfig);
+ }
+
+ // Closure compiler exported static methods.
+ SafeEngine['load'] = Engine.load;
+ SafeEngine['unload'] = Engine.unload;
+ SafeEngine['isWebGLAvailable'] = Engine.isWebGLAvailable;
- // Closure compiler exported engine methods.
- /** @export */
- Engine['isWebGLAvailable'] = Utils.isWebGLAvailable;
- Engine['load'] = load;
- Engine['unload'] = unload;
- Engine.prototype['init'] = Engine.prototype.init;
- Engine.prototype['preloadFile'] = Engine.prototype.preloadFile;
- Engine.prototype['start'] = Engine.prototype.start;
- Engine.prototype['startGame'] = Engine.prototype.startGame;
- Engine.prototype['copyToFS'] = Engine.prototype.copyToFS;
- Engine.prototype['requestQuit'] = Engine.prototype.requestQuit;
- return Engine;
+ return SafeEngine;
}());
if (typeof window !== 'undefined') {
window['Engine'] = Engine;
diff --git a/platform/javascript/js/engine/preloader.js b/platform/javascript/js/engine/preloader.js
index ec34fb93f2..3535fdb361 100644
--- a/platform/javascript/js/engine/preloader.js
+++ b/platform/javascript/js/engine/preloader.js
@@ -1,54 +1,79 @@
const Preloader = /** @constructor */ function () { // eslint-disable-line no-unused-vars
- const loadXHR = function (resolve, reject, file, tracker, attempts) {
- const xhr = new XMLHttpRequest();
+ function getTrackedResponse(response, load_status) {
+ let clen = 0;
+ let compressed = false;
+ response.headers.forEach(function (value, header) {
+ const h = header.toLowerCase().trim();
+ // We can't accurately compute compressed stream length.
+ if (h === 'content-encoding') {
+ compressed = true;
+ } else if (h === 'content-length') {
+ const length = parseInt(value, 10);
+ if (!Number.isNaN(length) && length > 0) {
+ clen = length;
+ }
+ }
+ });
+ if (!compressed && clen) {
+ load_status.total = clen;
+ }
+ function onloadprogress(reader, controller) {
+ return reader.read().then(function (result) {
+ if (load_status.done) {
+ return Promise.resolve();
+ }
+ if (result.value) {
+ controller.enqueue(result.value);
+ load_status.loaded += result.value.length;
+ }
+ if (!result.done) {
+ return onloadprogress(reader, controller);
+ }
+ load_status.done = true;
+ return Promise.resolve();
+ });
+ }
+ const reader = response.body.getReader();
+ return new Response(new ReadableStream({
+ start: function (controller) {
+ onloadprogress(reader, controller).then(function () {
+ controller.close();
+ });
+ },
+ }), { headers: response.headers });
+ }
+
+ function loadFetch(file, tracker, fileSize, raw) {
tracker[file] = {
- total: 0,
+ total: fileSize || 0,
loaded: 0,
- final: false,
+ done: false,
};
- xhr.onerror = function () {
+ return fetch(file).then(function (response) {
+ if (!response.ok) {
+ return Promise.reject(new Error(`Failed loading file '${file}'`));
+ }
+ const tr = getTrackedResponse(response, tracker[file]);
+ if (raw) {
+ return Promise.resolve(tr);
+ }
+ return tr.arrayBuffer();
+ });
+ }
+
+ function retry(func, attempts = 1) {
+ function onerror(err) {
if (attempts <= 1) {
- reject(new Error(`Failed loading file '${file}'`));
- } else {
+ return Promise.reject(err);
+ }
+ return new Promise(function (resolve, reject) {
setTimeout(function () {
- loadXHR(resolve, reject, file, tracker, attempts - 1);
+ retry(func, attempts - 1).then(resolve).catch(reject);
}, 1000);
- }
- };
- xhr.onabort = function () {
- tracker[file].final = true;
- reject(new Error(`Loading file '${file}' was aborted.`));
- };
- xhr.onloadstart = function (ev) {
- tracker[file].total = ev.total;
- tracker[file].loaded = ev.loaded;
- };
- xhr.onprogress = function (ev) {
- tracker[file].loaded = ev.loaded;
- tracker[file].total = ev.total;
- };
- xhr.onload = function () {
- if (xhr.status >= 400) {
- if (xhr.status < 500 || attempts <= 1) {
- reject(new Error(`Failed loading file '${file}': ${xhr.statusText}`));
- xhr.abort();
- } else {
- setTimeout(function () {
- loadXHR(resolve, reject, file, tracker, attempts - 1);
- }, 1000);
- }
- } else {
- tracker[file].final = true;
- resolve(xhr);
- }
- };
- // Make request.
- xhr.open('GET', file);
- if (!file.endsWith('.js')) {
- xhr.responseType = 'arraybuffer';
+ });
}
- xhr.send();
- };
+ return func().catch(onerror);
+ }
const DOWNLOAD_ATTEMPTS_MAX = 4;
const loadingFiles = {};
@@ -63,7 +88,7 @@ const Preloader = /** @constructor */ function () { // eslint-disable-line no-un
Object.keys(loadingFiles).forEach(function (file) {
const stat = loadingFiles[file];
- if (!stat.final) {
+ if (!stat.done) {
progressIsFinal = false;
}
if (!totalIsValid || stat.total === 0) {
@@ -92,21 +117,19 @@ const Preloader = /** @constructor */ function () { // eslint-disable-line no-un
progressFunc = callback;
};
- this.loadPromise = function (file) {
- return new Promise(function (resolve, reject) {
- loadXHR(resolve, reject, file, loadingFiles, DOWNLOAD_ATTEMPTS_MAX);
- });
+ this.loadPromise = function (file, fileSize, raw = false) {
+ return retry(loadFetch.bind(null, file, loadingFiles, fileSize, raw), DOWNLOAD_ATTEMPTS_MAX);
};
this.preloadedFiles = [];
- this.preload = function (pathOrBuffer, destPath) {
+ this.preload = function (pathOrBuffer, destPath, fileSize) {
let buffer = null;
if (typeof pathOrBuffer === 'string') {
const me = this;
- return this.loadPromise(pathOrBuffer).then(function (xhr) {
+ return this.loadPromise(pathOrBuffer, fileSize).then(function (buf) {
me.preloadedFiles.push({
path: destPath || pathOrBuffer,
- buffer: xhr.response,
+ buffer: buf,
});
return Promise.resolve();
});
diff --git a/platform/javascript/js/engine/utils.js b/platform/javascript/js/engine/utils.js
deleted file mode 100644
index 9273bbad42..0000000000
--- a/platform/javascript/js/engine/utils.js
+++ /dev/null
@@ -1,58 +0,0 @@
-const Utils = { // eslint-disable-line no-unused-vars
-
- createLocateRewrite: function (execName) {
- function rw(path) {
- if (path.endsWith('.worker.js')) {
- return `${execName}.worker.js`;
- } else if (path.endsWith('.audio.worklet.js')) {
- return `${execName}.audio.worklet.js`;
- } else if (path.endsWith('.js')) {
- return `${execName}.js`;
- } else if (path.endsWith('.side.wasm')) {
- return `${execName}.side.wasm`;
- } else if (path.endsWith('.wasm')) {
- return `${execName}.wasm`;
- }
- return path;
- }
- return rw;
- },
-
- createInstantiatePromise: function (wasmLoader) {
- let loader = wasmLoader;
- function instantiateWasm(imports, onSuccess) {
- loader.then(function (xhr) {
- WebAssembly.instantiate(xhr.response, imports).then(function (result) {
- onSuccess(result['instance'], result['module']);
- });
- });
- loader = null;
- return {};
- }
-
- return instantiateWasm;
- },
-
- findCanvas: function () {
- const nodes = document.getElementsByTagName('canvas');
- if (nodes.length && nodes[0] instanceof HTMLCanvasElement) {
- return nodes[0];
- }
- return null;
- },
-
- isWebGLAvailable: function (majorVersion = 1) {
- let testContext = false;
- try {
- const testCanvas = document.createElement('canvas');
- if (majorVersion === 1) {
- testContext = testCanvas.getContext('webgl') || testCanvas.getContext('experimental-webgl');
- } else if (majorVersion === 2) {
- testContext = testCanvas.getContext('webgl2') || testCanvas.getContext('experimental-webgl2');
- }
- } catch (e) {
- // Not available
- }
- return !!testContext;
- },
-};
diff --git a/platform/javascript/js/jsdoc2rst/publish.js b/platform/javascript/js/jsdoc2rst/publish.js
new file mode 100644
index 0000000000..ad9c0fbaaa
--- /dev/null
+++ b/platform/javascript/js/jsdoc2rst/publish.js
@@ -0,0 +1,354 @@
+/* eslint-disable strict */
+
+'use strict';
+
+const fs = require('fs');
+
+class JSDoclet {
+ constructor(doc) {
+ this.doc = doc;
+ this.description = doc['description'] || '';
+ this.name = doc['name'] || 'unknown';
+ this.longname = doc['longname'] || '';
+ this.types = [];
+ if (doc['type'] && doc['type']['names']) {
+ this.types = doc['type']['names'].slice();
+ }
+ this.type = this.types.length > 0 ? this.types.join('\\|') : '*';
+ this.variable = doc['variable'] || false;
+ this.kind = doc['kind'] || '';
+ this.memberof = doc['memberof'] || null;
+ this.scope = doc['scope'] || '';
+ this.members = [];
+ this.optional = doc['optional'] || false;
+ this.defaultvalue = doc['defaultvalue'];
+ this.summary = doc['summary'] || null;
+ this.classdesc = doc['classdesc'] || null;
+
+ // Parameters (functions)
+ this.params = [];
+ this.returns = doc['returns'] ? doc['returns'][0]['type']['names'][0] : 'void';
+ this.returns_desc = doc['returns'] ? doc['returns'][0]['description'] : null;
+
+ this.params = (doc['params'] || []).slice().map((p) => new JSDoclet(p));
+
+ // Custom tags
+ this.tags = doc['tags'] || [];
+ this.header = this.tags.filter((t) => t['title'] === 'header').map((t) => t['text']).pop() || null;
+ }
+
+ add_member(obj) {
+ this.members.push(obj);
+ }
+
+ is_static() {
+ return this.scope === 'static';
+ }
+
+ is_instance() {
+ return this.scope === 'instance';
+ }
+
+ is_object() {
+ return this.kind === 'Object' || (this.kind === 'typedef' && this.type === 'Object');
+ }
+
+ is_class() {
+ return this.kind === 'class';
+ }
+
+ is_function() {
+ return this.kind === 'function' || (this.kind === 'typedef' && this.type === 'function');
+ }
+
+ is_module() {
+ return this.kind === 'module';
+ }
+}
+
+function format_table(f, data, depth = 0) {
+ if (!data.length) {
+ return;
+ }
+
+ const column_sizes = new Array(data[0].length).fill(0);
+
+ data.forEach((row) => {
+ row.forEach((e, idx) => {
+ column_sizes[idx] = Math.max(e.length, column_sizes[idx]);
+ });
+ });
+
+ const indent = ' '.repeat(depth);
+ let sep = indent;
+ column_sizes.forEach((size) => {
+ sep += '+';
+ sep += '-'.repeat(size + 2);
+ });
+ sep += '+\n';
+ f.write(sep);
+
+ data.forEach((row) => {
+ let row_text = `${indent}|`;
+ row.forEach((entry, idx) => {
+ row_text += ` ${entry.padEnd(column_sizes[idx])} |`;
+ });
+ row_text += '\n';
+ f.write(row_text);
+ f.write(sep);
+ });
+
+ f.write('\n');
+}
+
+function make_header(header, sep) {
+ return `${header}\n${sep.repeat(header.length)}\n\n`;
+}
+
+function indent_multiline(text, depth) {
+ const indent = ' '.repeat(depth);
+ return text.split('\n').map((l) => (l === '' ? l : indent + l)).join('\n');
+}
+
+function make_rst_signature(obj, types = false, style = false) {
+ let out = '';
+ const fmt = style ? '*' : '';
+ obj.params.forEach((arg, idx) => {
+ if (idx > 0) {
+ if (arg.optional) {
+ out += ` ${fmt}[`;
+ }
+ out += ', ';
+ } else {
+ out += ' ';
+ if (arg.optional) {
+ out += `${fmt}[ `;
+ }
+ }
+ if (types) {
+ out += `${arg.type} `;
+ }
+ const variable = arg.variable ? '...' : '';
+ const defval = arg.defaultvalue !== undefined ? `=${arg.defaultvalue}` : '';
+ out += `${variable}${arg.name}${defval}`;
+ if (arg.optional) {
+ out += ` ]${fmt}`;
+ }
+ });
+ out += ' ';
+ return out;
+}
+
+function make_rst_param(f, obj, depth = 0) {
+ const indent = ' '.repeat(depth * 3);
+ f.write(indent);
+ f.write(`:param ${obj.type} ${obj.name}:\n`);
+ f.write(indent_multiline(obj.description, (depth + 1) * 3));
+ f.write('\n\n');
+}
+
+function make_rst_attribute(f, obj, depth = 0, brief = false) {
+ const indent = ' '.repeat(depth * 3);
+ f.write(indent);
+ f.write(`.. js:attribute:: ${obj.name}\n\n`);
+
+ if (brief) {
+ if (obj.summary) {
+ f.write(indent_multiline(obj.summary, (depth + 1) * 3));
+ }
+ f.write('\n\n');
+ return;
+ }
+
+ f.write(indent_multiline(obj.description, (depth + 1) * 3));
+ f.write('\n\n');
+
+ f.write(indent);
+ f.write(` :type: ${obj.type}\n\n`);
+
+ if (obj.defaultvalue !== undefined) {
+ let defval = obj.defaultvalue;
+ if (defval === '') {
+ defval = '""';
+ }
+ f.write(indent);
+ f.write(` :value: \`\`${defval}\`\`\n\n`);
+ }
+}
+
+function make_rst_function(f, obj, depth = 0) {
+ let prefix = '';
+ if (obj.is_instance()) {
+ prefix = 'prototype.';
+ }
+
+ const indent = ' '.repeat(depth * 3);
+ const sig = make_rst_signature(obj);
+ f.write(indent);
+ f.write(`.. js:function:: ${prefix}${obj.name}(${sig})\n`);
+ f.write('\n');
+
+ f.write(indent_multiline(obj.description, (depth + 1) * 3));
+ f.write('\n\n');
+
+ obj.params.forEach((param) => {
+ make_rst_param(f, param, depth + 1);
+ });
+
+ if (obj.returns !== 'void') {
+ f.write(indent);
+ f.write(' :return:\n');
+ f.write(indent_multiline(obj.returns_desc, (depth + 2) * 3));
+ f.write('\n\n');
+ f.write(indent);
+ f.write(` :rtype: ${obj.returns}\n\n`);
+ }
+}
+
+function make_rst_object(f, obj) {
+ let brief = false;
+ // Our custom header flag.
+ if (obj.header !== null) {
+ f.write(make_header(obj.header, '-'));
+ f.write(`${obj.description}\n\n`);
+ brief = true;
+ }
+
+ // Format members table and descriptions
+ const data = [['type', 'name']].concat(obj.members.map((m) => [m.type, `:js:attr:\`${m.name}\``]));
+
+ f.write(make_header('Properties', '^'));
+ format_table(f, data, 0);
+
+ make_rst_attribute(f, obj, 0, brief);
+
+ if (!obj.members.length) {
+ return;
+ }
+
+ f.write(' **Property Descriptions**\n\n');
+
+ // Properties first
+ obj.members.filter((m) => !m.is_function()).forEach((m) => {
+ make_rst_attribute(f, m, 1);
+ });
+
+ // Callbacks last
+ obj.members.filter((m) => m.is_function()).forEach((m) => {
+ make_rst_function(f, m, 1);
+ });
+}
+
+function make_rst_class(f, obj) {
+ const header = obj.header ? obj.header : obj.name;
+ f.write(make_header(header, '-'));
+
+ if (obj.classdesc) {
+ f.write(`${obj.classdesc}\n\n`);
+ }
+
+ const funcs = obj.members.filter((m) => m.is_function());
+ function make_data(m) {
+ const base = m.is_static() ? obj.name : `${obj.name}.prototype`;
+ const params = make_rst_signature(m, true, true);
+ const sig = `:js:attr:\`${m.name} <${base}.${m.name}>\` **(**${params}**)**`;
+ return [m.returns, sig];
+ }
+ const sfuncs = funcs.filter((m) => m.is_static());
+ const ifuncs = funcs.filter((m) => !m.is_static());
+
+ f.write(make_header('Static Methods', '^'));
+ format_table(f, sfuncs.map((m) => make_data(m)));
+
+ f.write(make_header('Instance Methods', '^'));
+ format_table(f, ifuncs.map((m) => make_data(m)));
+
+ const sig = make_rst_signature(obj);
+ f.write(`.. js:class:: ${obj.name}(${sig})\n\n`);
+ f.write(indent_multiline(obj.description, 3));
+ f.write('\n\n');
+
+ obj.params.forEach((p) => {
+ make_rst_param(f, p, 1);
+ });
+
+ f.write(' **Static Methods**\n\n');
+ sfuncs.forEach((m) => {
+ make_rst_function(f, m, 1);
+ });
+
+ f.write(' **Instance Methods**\n\n');
+ ifuncs.forEach((m) => {
+ make_rst_function(f, m, 1);
+ });
+}
+
+function make_rst_module(f, obj) {
+ const header = obj.header !== null ? obj.header : obj.name;
+ f.write(make_header(header, '='));
+ f.write(obj.description);
+ f.write('\n\n');
+}
+
+function write_base_object(f, obj) {
+ if (obj.is_object()) {
+ make_rst_object(f, obj);
+ } else if (obj.is_function()) {
+ make_rst_function(f, obj);
+ } else if (obj.is_class()) {
+ make_rst_class(f, obj);
+ } else if (obj.is_module()) {
+ make_rst_module(f, obj);
+ }
+}
+
+function generate(f, docs) {
+ const globs = [];
+ const SYMBOLS = {};
+ docs.filter((d) => !d.ignore && d.kind !== 'package').forEach((d) => {
+ SYMBOLS[d.name] = d;
+ if (d.memberof) {
+ const up = SYMBOLS[d.memberof];
+ if (up === undefined) {
+ console.log(d); // eslint-disable-line no-console
+ console.log(`Undefined symbol! ${d.memberof}`); // eslint-disable-line no-console
+ throw new Error('Undefined symbol!');
+ }
+ SYMBOLS[d.memberof].add_member(d);
+ } else {
+ globs.push(d);
+ }
+ });
+
+ f.write('.. _doc_html5_shell_classref:\n\n');
+ globs.forEach((obj) => write_base_object(f, obj));
+}
+
+/**
+ * Generate documentation output.
+ *
+ * @param {TAFFY} data - A TaffyDB collection representing
+ * all the symbols documented in your code.
+ * @param {object} opts - An object with options information.
+ */
+exports.publish = function (data, opts) {
+ const docs = data().get().filter((doc) => !doc.undocumented && !doc.ignore).map((doc) => new JSDoclet(doc));
+ const dest = opts.destination;
+ if (dest === 'dry-run') {
+ process.stdout.write('Dry run... ');
+ generate({
+ write: function () { /* noop */ },
+ }, docs);
+ process.stdout.write('Okay!\n');
+ return;
+ }
+ if (dest !== '' && !dest.endsWith('.rst')) {
+ throw new Error('Destination file must be either a ".rst" file, or an empty string (for printing to stdout)');
+ }
+ if (dest !== '') {
+ const f = fs.createWriteStream(dest);
+ generate(f, docs);
+ } else {
+ generate(process.stdout, docs);
+ }
+};
diff --git a/platform/javascript/js/libs/library_godot_display.js b/platform/javascript/js/libs/library_godot_display.js
index fdb5cc0ec2..c72b6b3691 100644
--- a/platform/javascript/js/libs/library_godot_display.js
+++ b/platform/javascript/js/libs/library_godot_display.js
@@ -671,7 +671,7 @@ const GodotDisplay = {
document.head.appendChild(link);
}
const old_icon = GodotDisplay.window_icon;
- const png = new Blob([GodotRuntime.heapCopy(HEAPU8, p_ptr, p_len)], { type: 'image/png' });
+ const png = new Blob([GodotRuntime.heapSlice(HEAPU8, p_ptr, p_len)], { type: 'image/png' });
GodotDisplay.window_icon = URL.createObjectURL(png);
link.href = GodotDisplay.window_icon;
if (old_icon) {
@@ -711,7 +711,7 @@ const GodotDisplay = {
const shape = GodotRuntime.parseString(p_shape);
const old_shape = GodotDisplayCursor.cursors[shape];
if (p_len > 0) {
- const png = new Blob([GodotRuntime.heapCopy(HEAPU8, p_ptr, p_len)], { type: 'image/png' });
+ const png = new Blob([GodotRuntime.heapSlice(HEAPU8, p_ptr, p_len)], { type: 'image/png' });
const url = URL.createObjectURL(png);
GodotDisplayCursor.cursors[shape] = {
url: url,
diff --git a/platform/javascript/js/libs/library_godot_fetch.js b/platform/javascript/js/libs/library_godot_fetch.js
new file mode 100644
index 0000000000..4ae6a23593
--- /dev/null
+++ b/platform/javascript/js/libs/library_godot_fetch.js
@@ -0,0 +1,258 @@
+/*************************************************************************/
+/* library_godot_fetch.js */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 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. */
+/*************************************************************************/
+
+const GodotFetch = {
+ $GodotFetch__deps: ['$GodotRuntime'],
+ $GodotFetch: {
+
+ onread: function (id, result) {
+ const obj = IDHandler.get(id);
+ if (!obj) {
+ return;
+ }
+ if (result.value) {
+ obj.chunks.push(result.value);
+ }
+ obj.reading = false;
+ obj.done = result.done;
+ },
+
+ onresponse: function (id, response) {
+ const obj = IDHandler.get(id);
+ if (!obj) {
+ return;
+ }
+ let size = -1;
+ let compressed = false;
+ let chunked = false;
+ response.headers.forEach(function (value, header) {
+ const v = value.toLowerCase().trim();
+ const h = header.toLowerCase().trim();
+ if (h === 'content-encoding') {
+ compressed = true;
+ size = -1;
+ } else if (h === 'content-length') {
+ const len = Number.parseInt(value, 10);
+ if (!Number.isNaN(len) && !compressed) {
+ size = len;
+ }
+ } else if (h === 'transfer-encoding' && v === 'chunked') {
+ chunked = true;
+ }
+ });
+ obj.bodySize = size;
+ obj.status = response.status;
+ obj.response = response;
+ obj.reader = response.body.getReader();
+ obj.chunked = chunked;
+ },
+
+ onerror: function (id, err) {
+ GodotRuntime.error(err);
+ const obj = IDHandler.get(id);
+ if (!obj) {
+ return;
+ }
+ obj.error = err;
+ },
+
+ create: function (method, url, headers, body) {
+ const obj = {
+ request: null,
+ response: null,
+ reader: null,
+ error: null,
+ done: false,
+ reading: false,
+ status: 0,
+ chunks: [],
+ bodySize: -1,
+ };
+ const id = IDHandler.add(obj);
+ const init = {
+ method: method,
+ headers: headers,
+ body: body,
+ };
+ obj.request = fetch(url, init);
+ obj.request.then(GodotFetch.onresponse.bind(null, id)).catch(GodotFetch.onerror.bind(null, id));
+ return id;
+ },
+
+ free: function (id) {
+ const obj = IDHandler.get(id);
+ if (!obj) {
+ return;
+ }
+ IDHandler.remove(id);
+ if (!obj.request) {
+ return;
+ }
+ // Try to abort
+ obj.request.then(function (response) {
+ response.abort();
+ }).catch(function (e) { /* nothing to do */ });
+ },
+
+ read: function (id) {
+ const obj = IDHandler.get(id);
+ if (!obj) {
+ return;
+ }
+ if (obj.reader && !obj.reading) {
+ if (obj.done) {
+ obj.reader = null;
+ return;
+ }
+ obj.reading = true;
+ obj.reader.read().then(GodotFetch.onread.bind(null, id)).catch(GodotFetch.onerror.bind(null, id));
+ }
+ },
+ },
+
+ godot_js_fetch_create__sig: 'iii',
+ godot_js_fetch_create: function (p_method, p_url, p_headers, p_headers_size, p_body, p_body_size) {
+ const method = GodotRuntime.parseString(p_method);
+ const url = GodotRuntime.parseString(p_url);
+ const headers = GodotRuntime.parseStringArray(p_headers, p_headers_size);
+ const body = p_body_size ? GodotRuntime.heapSlice(HEAP8, p_body, p_body_size) : null;
+ return GodotFetch.create(method, url, headers.map(function (hv) {
+ const idx = hv.indexOf(':');
+ if (idx <= 0) {
+ return [];
+ }
+ return [
+ hv.slice(0, idx).trim(),
+ hv.slice(idx + 1).trim(),
+ ];
+ }).filter(function (v) {
+ return v.length === 2;
+ }), body);
+ },
+
+ godot_js_fetch_state_get__sig: 'ii',
+ godot_js_fetch_state_get: function (p_id) {
+ const obj = IDHandler.get(p_id);
+ if (!obj) {
+ return -1;
+ }
+ if (obj.error) {
+ return -1;
+ }
+ if (!obj.response) {
+ return 0;
+ }
+ if (obj.reader) {
+ return 1;
+ }
+ if (obj.done) {
+ return 2;
+ }
+ return -1;
+ },
+
+ godot_js_fetch_http_status_get__sig: 'ii',
+ godot_js_fetch_http_status_get: function (p_id) {
+ const obj = IDHandler.get(p_id);
+ if (!obj || !obj.response) {
+ return 0;
+ }
+ return obj.status;
+ },
+
+ godot_js_fetch_read_headers__sig: 'iii',
+ godot_js_fetch_read_headers: function (p_id, p_parse_cb, p_ref) {
+ const obj = IDHandler.get(p_id);
+ if (!obj || !obj.response) {
+ return 1;
+ }
+ const cb = GodotRuntime.get_func(p_parse_cb);
+ const arr = [];
+ obj.response.headers.forEach(function (v, h) {
+ arr.push(`${h}:${v}`);
+ });
+ const c_ptr = GodotRuntime.allocStringArray(arr);
+ cb(arr.length, c_ptr, p_ref);
+ GodotRuntime.freeStringArray(c_ptr, arr.length);
+ return 0;
+ },
+
+ godot_js_fetch_read_chunk__sig: 'ii',
+ godot_js_fetch_read_chunk: function (p_id, p_buf, p_buf_size) {
+ const obj = IDHandler.get(p_id);
+ if (!obj || !obj.response) {
+ return 0;
+ }
+ let to_read = p_buf_size;
+ const chunks = obj.chunks;
+ while (to_read && chunks.length) {
+ const chunk = obj.chunks[0];
+ if (chunk.length > to_read) {
+ GodotRuntime.heapCopy(HEAP8, chunk.slice(0, to_read), p_buf);
+ chunks[0] = chunk.slice(to_read);
+ to_read = 0;
+ } else {
+ GodotRuntime.heapCopy(HEAP8, chunk, p_buf);
+ to_read -= chunk.length;
+ chunks.pop();
+ }
+ }
+ if (!chunks.length) {
+ GodotFetch.read(p_id);
+ }
+ return p_buf_size - to_read;
+ },
+
+ godot_js_fetch_body_length_get__sig: 'ii',
+ godot_js_fetch_body_length_get: function (p_id) {
+ const obj = IDHandler.get(p_id);
+ if (!obj || !obj.response) {
+ return -1;
+ }
+ return obj.bodySize;
+ },
+
+ godot_js_fetch_is_chunked__sig: 'ii',
+ godot_js_fetch_is_chunked: function (p_id) {
+ const obj = IDHandler.get(p_id);
+ if (!obj || !obj.response) {
+ return -1;
+ }
+ return obj.chunked ? 1 : 0;
+ },
+
+ godot_js_fetch_free__sig: 'vi',
+ godot_js_fetch_free: function (id) {
+ GodotFetch.free(id);
+ },
+};
+
+autoAddDeps(GodotFetch, '$GodotFetch');
+mergeInto(LibraryManager.library, GodotFetch);
diff --git a/platform/javascript/js/libs/library_godot_http_request.js b/platform/javascript/js/libs/library_godot_http_request.js
deleted file mode 100644
index 7dc2a2df29..0000000000
--- a/platform/javascript/js/libs/library_godot_http_request.js
+++ /dev/null
@@ -1,142 +0,0 @@
-/*************************************************************************/
-/* http_request.js */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 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. */
-/*************************************************************************/
-
-const GodotHTTPRequest = {
- $GodotHTTPRequest__deps: ['$GodotRuntime'],
- $GodotHTTPRequest: {
- requests: [],
-
- getUnusedRequestId: function () {
- const idMax = GodotHTTPRequest.requests.length;
- for (let potentialId = 0; potentialId < idMax; ++potentialId) {
- if (GodotHTTPRequest.requests[potentialId] instanceof XMLHttpRequest) {
- continue;
- }
- return potentialId;
- }
- GodotHTTPRequest.requests.push(null);
- return idMax;
- },
-
- setupRequest: function (xhr) {
- xhr.responseType = 'arraybuffer';
- },
- },
-
- godot_xhr_new__sig: 'i',
- godot_xhr_new: function () {
- const newId = GodotHTTPRequest.getUnusedRequestId();
- GodotHTTPRequest.requests[newId] = new XMLHttpRequest();
- GodotHTTPRequest.setupRequest(GodotHTTPRequest.requests[newId]);
- return newId;
- },
-
- godot_xhr_reset__sig: 'vi',
- godot_xhr_reset: function (xhrId) {
- GodotHTTPRequest.requests[xhrId] = new XMLHttpRequest();
- GodotHTTPRequest.setupRequest(GodotHTTPRequest.requests[xhrId]);
- },
-
- godot_xhr_free__sig: 'vi',
- godot_xhr_free: function (xhrId) {
- GodotHTTPRequest.requests[xhrId].abort();
- GodotHTTPRequest.requests[xhrId] = null;
- },
-
- godot_xhr_open__sig: 'viiiii',
- godot_xhr_open: function (xhrId, method, url, p_user, p_password) {
- const user = p_user > 0 ? GodotRuntime.parseString(p_user) : null;
- const password = p_password > 0 ? GodotRuntime.parseString(p_password) : null;
- GodotHTTPRequest.requests[xhrId].open(GodotRuntime.parseString(method), GodotRuntime.parseString(url), true, user, password);
- },
-
- godot_xhr_set_request_header__sig: 'viii',
- godot_xhr_set_request_header: function (xhrId, header, value) {
- GodotHTTPRequest.requests[xhrId].setRequestHeader(GodotRuntime.parseString(header), GodotRuntime.parseString(value));
- },
-
- godot_xhr_send__sig: 'viii',
- godot_xhr_send: function (xhrId, p_ptr, p_len) {
- let data = null;
- if (p_ptr && p_len) {
- data = GodotRuntime.heapCopy(HEAP8, p_ptr, p_len);
- }
- GodotHTTPRequest.requests[xhrId].send(data);
- },
-
- godot_xhr_abort__sig: 'vi',
- godot_xhr_abort: function (xhrId) {
- GodotHTTPRequest.requests[xhrId].abort();
- },
-
- godot_xhr_get_status__sig: 'ii',
- godot_xhr_get_status: function (xhrId) {
- return GodotHTTPRequest.requests[xhrId].status;
- },
-
- godot_xhr_get_ready_state__sig: 'ii',
- godot_xhr_get_ready_state: function (xhrId) {
- return GodotHTTPRequest.requests[xhrId].readyState;
- },
-
- godot_xhr_get_response_headers_length__sig: 'ii',
- godot_xhr_get_response_headers_length: function (xhrId) {
- const headers = GodotHTTPRequest.requests[xhrId].getAllResponseHeaders();
- return headers === null ? 0 : GodotRuntime.strlen(headers);
- },
-
- godot_xhr_get_response_headers__sig: 'viii',
- godot_xhr_get_response_headers: function (xhrId, dst, len) {
- const str = GodotHTTPRequest.requests[xhrId].getAllResponseHeaders();
- if (str === null) {
- return;
- }
- GodotRuntime.stringToHeap(str, dst, len);
- },
-
- godot_xhr_get_response_length__sig: 'ii',
- godot_xhr_get_response_length: function (xhrId) {
- const body = GodotHTTPRequest.requests[xhrId].response;
- return body === null ? 0 : body.byteLength;
- },
-
- godot_xhr_get_response__sig: 'viii',
- godot_xhr_get_response: function (xhrId, dst, len) {
- let buf = GodotHTTPRequest.requests[xhrId].response;
- if (buf === null) {
- return;
- }
- buf = new Uint8Array(buf).subarray(0, len);
- HEAPU8.set(buf, dst);
- },
-};
-
-autoAddDeps(GodotHTTPRequest, '$GodotHTTPRequest');
-mergeInto(LibraryManager.library, GodotHTTPRequest);
diff --git a/platform/javascript/js/libs/library_godot_runtime.js b/platform/javascript/js/libs/library_godot_runtime.js
index 7e36ff8ab5..3da1ed8f06 100644
--- a/platform/javascript/js/libs/library_godot_runtime.js
+++ b/platform/javascript/js/libs/library_godot_runtime.js
@@ -72,11 +72,16 @@ const GodotRuntime = {
return p_heap.subarray(p_ptr / bytes, p_ptr / bytes + p_len);
},
- heapCopy: function (p_heap, p_ptr, p_len) {
+ heapSlice: function (p_heap, p_ptr, p_len) {
const bytes = p_heap.BYTES_PER_ELEMENT;
return p_heap.slice(p_ptr / bytes, p_ptr / bytes + p_len);
},
+ heapCopy: function (p_dst, p_src, p_ptr) {
+ const bytes = p_src.BYTES_PER_ELEMENT;
+ return p_dst.set(p_src, p_ptr / bytes);
+ },
+
/*
* Strings
*/
@@ -84,6 +89,15 @@ const GodotRuntime = {
return UTF8ToString(p_ptr); // eslint-disable-line no-undef
},
+ parseStringArray: function (p_ptr, p_size) {
+ const strings = [];
+ const ptrs = GodotRuntime.heapSub(HEAP32, p_ptr, p_size); // TODO wasm64
+ ptrs.forEach(function (ptr) {
+ strings.push(GodotRuntime.parseString(ptr));
+ });
+ return strings;
+ },
+
strlen: function (p_str) {
return lengthBytesUTF8(p_str); // eslint-disable-line no-undef
},
diff --git a/platform/javascript/package-lock.json b/platform/javascript/package-lock.json
index 8e298a495e..b8c434b3dd 100644
--- a/platform/javascript/package-lock.json
+++ b/platform/javascript/package-lock.json
@@ -43,6 +43,12 @@
}
}
},
+ "@babel/parser": {
+ "version": "7.13.4",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.13.4.tgz",
+ "integrity": "sha512-uvoOulWHhI+0+1f9L4BoozY7U5cIkZ9PgJqvb041d6vypgUmtVPG4vmGm4pSggjl8BELzvHyUeJSUyEMY6b+qA==",
+ "dev": true
+ },
"@eslint/eslintrc": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.1.3.tgz",
@@ -202,6 +208,12 @@
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
"dev": true
},
+ "bluebird": {
+ "version": "3.7.2",
+ "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
+ "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==",
+ "dev": true
+ },
"brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -218,6 +230,15 @@
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
"dev": true
},
+ "catharsis": {
+ "version": "0.8.11",
+ "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.8.11.tgz",
+ "integrity": "sha512-a+xUyMV7hD1BrDQA/3iPV7oc+6W26BgVJO05PGEoatMyIuPScQKsde6i3YorWX1qs+AZjnJ18NqdKoCtKiNh1g==",
+ "dev": true,
+ "requires": {
+ "lodash": "^4.17.14"
+ }
+ },
"chalk": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
@@ -362,6 +383,12 @@
"ansi-colors": "^4.1.1"
}
},
+ "entities": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.3.tgz",
+ "integrity": "sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ==",
+ "dev": true
+ },
"error-ex": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
@@ -925,6 +952,51 @@
"esprima": "^4.0.0"
}
},
+ "js2xmlparser": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-4.0.1.tgz",
+ "integrity": "sha512-KrPTolcw6RocpYjdC7pL7v62e55q7qOMHvLX1UCLc5AAS8qeJ6nukarEJAF2KL2PZxlbGueEbINqZR2bDe/gUw==",
+ "dev": true,
+ "requires": {
+ "xmlcreate": "^2.0.3"
+ }
+ },
+ "jsdoc": {
+ "version": "3.6.6",
+ "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.6.6.tgz",
+ "integrity": "sha512-znR99e1BHeyEkSvgDDpX0sTiTu+8aQyDl9DawrkOGZTTW8hv0deIFXx87114zJ7gRaDZKVQD/4tr1ifmJp9xhQ==",
+ "dev": true,
+ "requires": {
+ "@babel/parser": "^7.9.4",
+ "bluebird": "^3.7.2",
+ "catharsis": "^0.8.11",
+ "escape-string-regexp": "^2.0.0",
+ "js2xmlparser": "^4.0.1",
+ "klaw": "^3.0.0",
+ "markdown-it": "^10.0.0",
+ "markdown-it-anchor": "^5.2.7",
+ "marked": "^0.8.2",
+ "mkdirp": "^1.0.4",
+ "requizzle": "^0.2.3",
+ "strip-json-comments": "^3.1.0",
+ "taffydb": "2.6.2",
+ "underscore": "~1.10.2"
+ },
+ "dependencies": {
+ "escape-string-regexp": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz",
+ "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==",
+ "dev": true
+ },
+ "mkdirp": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
+ "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
+ "dev": true
+ }
+ }
+ },
"json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
@@ -946,6 +1018,15 @@
"minimist": "^1.2.0"
}
},
+ "klaw": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/klaw/-/klaw-3.0.0.tgz",
+ "integrity": "sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.9"
+ }
+ },
"levn": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
@@ -956,6 +1037,15 @@
"type-check": "~0.4.0"
}
},
+ "linkify-it": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-2.2.0.tgz",
+ "integrity": "sha512-GnAl/knGn+i1U/wjBz3akz2stz+HrHLsxMwHQGofCDfPvlf+gDKN58UtfmUquTY4/MXeE2x7k19KQmeoZi94Iw==",
+ "dev": true,
+ "requires": {
+ "uc.micro": "^1.0.1"
+ }
+ },
"load-json-file": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz",
@@ -984,6 +1074,37 @@
"integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
"dev": true
},
+ "markdown-it": {
+ "version": "10.0.0",
+ "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-10.0.0.tgz",
+ "integrity": "sha512-YWOP1j7UbDNz+TumYP1kpwnP0aEa711cJjrAQrzd0UXlbJfc5aAq0F/PZHjiioqDC1NKgvIMX+o+9Bk7yuM2dg==",
+ "dev": true,
+ "requires": {
+ "argparse": "^1.0.7",
+ "entities": "~2.0.0",
+ "linkify-it": "^2.0.0",
+ "mdurl": "^1.0.1",
+ "uc.micro": "^1.0.5"
+ }
+ },
+ "markdown-it-anchor": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-5.3.0.tgz",
+ "integrity": "sha512-/V1MnLL/rgJ3jkMWo84UR+K+jF1cxNG1a+KwqeXqTIJ+jtA8aWSHuigx8lTzauiIjBDbwF3NcWQMotd0Dm39jA==",
+ "dev": true
+ },
+ "marked": {
+ "version": "0.8.2",
+ "resolved": "https://registry.npmjs.org/marked/-/marked-0.8.2.tgz",
+ "integrity": "sha512-EGwzEeCcLniFX51DhTpmTom+dSA/MG/OBUDjnWtHbEnjAH180VzUeAw+oE4+Zv+CoYBWyRlYOTR0N8SO9R1PVw==",
+ "dev": true
+ },
+ "mdurl": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz",
+ "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=",
+ "dev": true
+ },
"minimatch": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
@@ -1287,6 +1408,15 @@
"integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==",
"dev": true
},
+ "requizzle": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.3.tgz",
+ "integrity": "sha512-YanoyJjykPxGHii0fZP0uUPEXpvqfBDxWV7s6GKAiiOsiqhX6vHNyW3Qzdmqp/iq/ExbhaGbVrjB4ruEVSM4GQ==",
+ "dev": true,
+ "requires": {
+ "lodash": "^4.17.14"
+ }
+ },
"resolve": {
"version": "1.17.0",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz",
@@ -1513,6 +1643,12 @@
"string-width": "^3.0.0"
}
},
+ "taffydb": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/taffydb/-/taffydb-2.6.2.tgz",
+ "integrity": "sha1-fLy2S1oUG2ou/CxdLGe04VCyomg=",
+ "dev": true
+ },
"text-table": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
@@ -1546,6 +1682,18 @@
"integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==",
"dev": true
},
+ "uc.micro": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz",
+ "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==",
+ "dev": true
+ },
+ "underscore": {
+ "version": "1.10.2",
+ "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.10.2.tgz",
+ "integrity": "sha512-N4P+Q/BuyuEKFJ43B9gYuOj4TQUHXX+j2FqguVOpjkssLUUrnJofCcBccJSCoeturDoZU6GorDTHSvUDlSQbTg==",
+ "dev": true
+ },
"uri-js": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz",
@@ -1600,6 +1748,12 @@
"requires": {
"mkdirp": "^0.5.1"
}
+ },
+ "xmlcreate": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.3.tgz",
+ "integrity": "sha512-HgS+X6zAztGa9zIK3Y3LXuJes33Lz9x+YyTxgrkIdabu2vqcGOWwdfCpf1hWLRrd553wd4QCDf6BBO6FfdsRiQ==",
+ "dev": true
}
}
}
diff --git a/platform/javascript/package.json b/platform/javascript/package.json
index 630b584f5b..d9d272923e 100644
--- a/platform/javascript/package.json
+++ b/platform/javascript/package.json
@@ -5,20 +5,24 @@
"description": "Linting setup for Godot's HTML5 platform code",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
- "lint": "npm run lint:engine && npm run lint:libs && npm run lint:modules",
+ "docs": "jsdoc --template js/jsdoc2rst/ js/engine/engine.js js/engine/config.js --destination ''",
+ "lint": "npm run lint:engine && npm run lint:libs && npm run lint:modules && npm run lint:tools",
"lint:engine": "eslint \"js/engine/*.js\" --no-eslintrc -c .eslintrc.engine.js",
"lint:libs": "eslint \"js/libs/*.js\" --no-eslintrc -c .eslintrc.libs.js",
"lint:modules": "eslint \"../../modules/**/*.js\" --no-eslintrc -c .eslintrc.libs.js",
- "format": "npm run format:engine && npm run format:libs && npm run format:modules",
+ "lint:tools": "eslint \"js/jsdoc2rst/**/*.js\" --no-eslintrc -c .eslintrc.engine.js",
+ "format": "npm run format:engine && npm run format:libs && npm run format:modules && npm run format:tools",
"format:engine": "npm run lint:engine -- --fix",
"format:libs": "npm run lint:libs -- --fix",
- "format:modules": "npm run lint:modules -- --fix"
+ "format:modules": "npm run lint:modules -- --fix",
+ "format:tools": "npm run lint:tools -- --fix"
},
"author": "Godot Engine contributors",
"license": "MIT",
"devDependencies": {
"eslint": "^7.9.0",
"eslint-config-airbnb-base": "^14.2.0",
- "eslint-plugin-import": "^2.22.0"
+ "eslint-plugin-import": "^2.22.0",
+ "jsdoc": "^3.6.6"
}
}
diff --git a/platform/linuxbsd/detect.py b/platform/linuxbsd/detect.py
index 8f1afe0e66..09d185ae2b 100644
--- a/platform/linuxbsd/detect.py
+++ b/platform/linuxbsd/detect.py
@@ -70,6 +70,7 @@ def get_opts():
BoolVariable("use_asan", "Use LLVM/GCC compiler address sanitizer (ASAN))", False),
BoolVariable("use_lsan", "Use LLVM/GCC compiler leak sanitizer (LSAN))", False),
BoolVariable("use_tsan", "Use LLVM/GCC compiler thread sanitizer (TSAN))", False),
+ BoolVariable("use_msan", "Use LLVM/GCC compiler memory sanitizer (MSAN))", False),
BoolVariable("pulseaudio", "Detect and use PulseAudio", True),
BoolVariable("udev", "Use udev for gamepad connection callbacks", True),
BoolVariable("debug_symbols", "Add debugging symbols to release/release_debug builds", True),
@@ -142,7 +143,7 @@ def configure(env):
env.Append(CCFLAGS=["-ftest-coverage", "-fprofile-arcs"])
env.Append(LINKFLAGS=["-ftest-coverage", "-fprofile-arcs"])
- if env["use_ubsan"] or env["use_asan"] or env["use_lsan"] or env["use_tsan"]:
+ if env["use_ubsan"] or env["use_asan"] or env["use_lsan"] or env["use_tsan"] or env["use_msan"]:
env.extra_suffix += "s"
if env["use_ubsan"]:
@@ -161,6 +162,10 @@ def configure(env):
env.Append(CCFLAGS=["-fsanitize=thread"])
env.Append(LINKFLAGS=["-fsanitize=thread"])
+ if env["use_msan"]:
+ env.Append(CCFLAGS=["-fsanitize=memory"])
+ env.Append(LINKFLAGS=["-fsanitize=memory"])
+
if env["use_lto"]:
if not env["use_llvm"] and env.GetOption("num_jobs") > 1:
env.Append(CCFLAGS=["-flto"])
diff --git a/platform/linuxbsd/display_server_x11.cpp b/platform/linuxbsd/display_server_x11.cpp
index fceeb82325..d7f7054acb 100644
--- a/platform/linuxbsd/display_server_x11.cpp
+++ b/platform/linuxbsd/display_server_x11.cpp
@@ -727,9 +727,9 @@ Point2i DisplayServerX11::screen_get_position(int p_screen) const {
int count;
XineramaScreenInfo *xsi = XineramaQueryScreens(x11_display, &count);
- if (p_screen >= count) {
- return Point2i(0, 0);
- }
+
+ // Check if screen is valid
+ ERR_FAIL_INDEX_V(p_screen, count, Point2i(0, 0));
Point2i position = Point2i(xsi[p_screen].x_org, xsi[p_screen].y_org);
@@ -758,9 +758,9 @@ Rect2i DisplayServerX11::screen_get_usable_rect(int p_screen) const {
int count;
XineramaScreenInfo *xsi = XineramaQueryScreens(x11_display, &count);
- if (p_screen >= count) {
- return Rect2i(0, 0, 0, 0);
- }
+
+ // Check if screen is valid
+ ERR_FAIL_INDEX_V(p_screen, count, Rect2i(0, 0, 0, 0));
Rect2i rect = Rect2i(xsi[p_screen].x_org, xsi[p_screen].y_org, xsi[p_screen].width, xsi[p_screen].height);
XFree(xsi);
@@ -1041,11 +1041,13 @@ void DisplayServerX11::window_set_current_screen(int p_screen, WindowID p_window
ERR_FAIL_COND(!windows.has(p_window));
WindowData &wd = windows[p_window];
- int count = get_screen_count();
- if (p_screen >= count) {
- return;
+ if (p_screen == SCREEN_OF_MAIN_WINDOW) {
+ p_screen = window_get_current_screen();
}
+ // Check if screen is valid
+ ERR_FAIL_INDEX(p_screen, get_screen_count());
+
if (window_get_mode(p_window) == WINDOW_MODE_FULLSCREEN) {
Point2i position = screen_get_position(p_screen);
Size2i size = screen_get_size(p_screen);
diff --git a/platform/osx/detect.py b/platform/osx/detect.py
index acea00c5ac..c39a4426be 100644
--- a/platform/osx/detect.py
+++ b/platform/osx/detect.py
@@ -35,6 +35,7 @@ def get_opts():
BoolVariable("separate_debug_symbols", "Create a separate file containing debugging symbols", False),
BoolVariable("use_ubsan", "Use LLVM/GCC compiler undefined behavior sanitizer (UBSAN)", False),
BoolVariable("use_asan", "Use LLVM/GCC compiler address sanitizer (ASAN))", False),
+ BoolVariable("use_lsan", "Use LLVM/GCC compiler leak sanitizer (LSAN))", False),
BoolVariable("use_tsan", "Use LLVM/GCC compiler thread sanitizer (TSAN))", False),
]
@@ -131,7 +132,7 @@ def configure(env):
env["AS"] = basecmd + "as"
env.Append(CPPDEFINES=["__MACPORTS__"]) # hack to fix libvpx MM256_BROADCASTSI128_SI256 define
- if env["use_ubsan"] or env["use_asan"] or env["use_tsan"]:
+ if env["use_ubsan"] or env["use_asan"] or env["use_lsan"] or env["use_tsan"]:
env.extra_suffix += "s"
if env["use_ubsan"]:
@@ -142,6 +143,10 @@ def configure(env):
env.Append(CCFLAGS=["-fsanitize=address"])
env.Append(LINKFLAGS=["-fsanitize=address"])
+ if env["use_lsan"]:
+ env.Append(CCFLAGS=["-fsanitize=leak"])
+ env.Append(LINKFLAGS=["-fsanitize=leak"])
+
if env["use_tsan"]:
env.Append(CCFLAGS=["-fsanitize=thread"])
env.Append(LINKFLAGS=["-fsanitize=thread"])
diff --git a/platform/osx/export/export.cpp b/platform/osx/export/export.cpp
index 337cfd6808..f31d8b9b81 100644
--- a/platform/osx/export/export.cpp
+++ b/platform/osx/export/export.cpp
@@ -56,7 +56,7 @@ class EditorExportPlatformOSX : public EditorExportPlatform {
void _make_icon(const Ref<Image> &p_icon, Vector<uint8_t> &p_data);
Error _notarize(const Ref<EditorExportPreset> &p_preset, const String &p_path);
- Error _code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path);
+ Error _code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path, const String &p_ent_path);
Error _create_dmg(const String &p_dmg_path, const String &p_pkg_name, const String &p_app_path_name);
void _zip_folder_recursive(zipFile &p_zip, const String &p_root_path, const String &p_folder, const String &p_pkg_name);
@@ -114,7 +114,7 @@ public:
virtual void get_platform_features(List<String> *r_features) override {
r_features->push_back("pc");
r_features->push_back("s3tc");
- r_features->push_back("OSX");
+ r_features->push_back("macOS");
}
virtual void resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, Set<String> &p_features) override {
@@ -159,7 +159,30 @@ void EditorExportPlatformOSX::get_export_options(List<ExportOption> *r_options)
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/identity", PROPERTY_HINT_PLACEHOLDER_TEXT, "Type: Name (ID)"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/timestamp"), true));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/hardened_runtime"), true));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/entitlements", PROPERTY_HINT_GLOBAL_FILE, "*.plist"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/entitlements/custom_file", PROPERTY_HINT_GLOBAL_FILE, "*.plist"), ""));
+
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/allow_jit_code_execution"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/allow_unsigned_executable_memory"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/allow_dyld_environment_variables"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/disable_library_validation"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/audio_input"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/camera"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/location"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/address_book"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/calendars"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/photos_library"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/apple_events"), false));
+
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/app_sandbox/enabled"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/app_sandbox/network_server"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/app_sandbox/network_client"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/app_sandbox/device_usb"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/app_sandbox/device_bluetooth"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/entitlements/app_sandbox/files_downloads", PROPERTY_HINT_ENUM, "No,Read-only,Read-write"), 0));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/entitlements/app_sandbox/files_pictures", PROPERTY_HINT_ENUM, "No,Read-only,Read-write"), 0));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/entitlements/app_sandbox/files_music", PROPERTY_HINT_ENUM, "No,Read-only,Read-write"), 0));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/entitlements/app_sandbox/files_movies", PROPERTY_HINT_ENUM, "No,Read-only,Read-write"), 0));
+
r_options->push_back(ExportOption(PropertyInfo(Variant::PACKED_STRING_ARRAY, "codesign/custom_options"), PackedStringArray()));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "notarization/enable"), false));
@@ -437,7 +460,7 @@ Error EditorExportPlatformOSX::_notarize(const Ref<EditorExportPreset> &p_preset
return OK;
}
-Error EditorExportPlatformOSX::_code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path) {
+Error EditorExportPlatformOSX::_code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path, const String &p_ent_path) {
#ifdef OSX_ENABLED
List<String> args;
@@ -449,9 +472,9 @@ Error EditorExportPlatformOSX::_code_sign(const Ref<EditorExportPreset> &p_prese
args.push_back("runtime");
}
- if ((p_preset->get("codesign/entitlements") != "") && (p_path.get_extension() != "dmg")) {
+ if (p_path.get_extension() != "dmg") {
args.push_back("--entitlements");
- args.push_back(p_preset->get("codesign/entitlements"));
+ args.push_back(p_ent_path);
}
PackedStringArray user_args = p_preset->get("codesign/custom_options");
@@ -607,6 +630,7 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
// Now process our template.
bool found_binary = false;
int total_size = 0;
+ Vector<String> dylibs_found;
while (ret == UNZ_OK && err == OK) {
bool is_execute = false;
@@ -678,14 +702,18 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
ret = unzGoToNextFile(src_pkg_zip);
continue; // skip
}
- file = file.replace("/data.mono.osx.64.release_debug/", "/data_" + pkg_name + "/");
+ file = file.replace("/data.mono.osx.64.release_debug/", "/GodotSharp/");
}
if (file.find("/data.mono.osx.64.release/") != -1) {
if (p_debug) {
ret = unzGoToNextFile(src_pkg_zip);
continue; // skip
}
- file = file.replace("/data.mono.osx.64.release/", "/data_" + pkg_name + "/");
+ file = file.replace("/data.mono.osx.64.release/", "/GodotSharp/");
+ }
+
+ if (file.ends_with(".dylib")) {
+ dylibs_found.push_back(file);
}
print_line("ADDING: " + file + " size: " + itos(data.size()));
@@ -735,22 +763,149 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
// See if we can code sign our new package.
bool sign_enabled = p_preset->get("codesign/enable");
+ String ent_path = p_preset->get("codesign/entitlements/custom_file");
+ if (sign_enabled && (ent_path == "")) {
+ ent_path = EditorSettings::get_singleton()->get_cache_dir().plus_file(pkg_name + ".entitlements");
+
+ FileAccess *ent_f = FileAccess::open(ent_path, FileAccess::WRITE);
+ if (ent_f) {
+ ent_f->store_line("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
+ ent_f->store_line("<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">");
+ ent_f->store_line("<plist version=\"1.0\">");
+ ent_f->store_line("<dict>");
+ if ((bool)p_preset->get("codesign/entitlements/allow_jit_code_execution")) {
+ ent_f->store_line("<key>com.apple.security.cs.allow-jit</key>");
+ ent_f->store_line("<true/>");
+ }
+ if ((bool)p_preset->get("codesign/entitlements/allow_unsigned_executable_memory")) {
+ ent_f->store_line("<key>com.apple.security.cs.allow-unsigned-executable-memory</key>");
+ ent_f->store_line("<true/>");
+ }
+ if ((bool)p_preset->get("codesign/entitlements/allow_dyld_environment_variables")) {
+ ent_f->store_line("<key>com.apple.security.cs.allow-dyld-environment-variables</key>");
+ ent_f->store_line("<true/>");
+ }
+ if ((bool)p_preset->get("codesign/entitlements/disable_library_validation")) {
+ ent_f->store_line("<key>com.apple.security.cs.disable-library-validation</key>");
+ ent_f->store_line("<true/>");
+ }
+ if ((bool)p_preset->get("codesign/entitlements/audio_input")) {
+ ent_f->store_line("<key>com.apple.security.device.audio-input</key>");
+ ent_f->store_line("<true/>");
+ }
+ if ((bool)p_preset->get("codesign/entitlements/camera")) {
+ ent_f->store_line("<key>com.apple.security.device.camera</key>");
+ ent_f->store_line("<true/>");
+ }
+ if ((bool)p_preset->get("codesign/entitlements/location")) {
+ ent_f->store_line("<key>com.apple.security.personal-information.location</key>");
+ ent_f->store_line("<true/>");
+ }
+ if ((bool)p_preset->get("codesign/entitlements/address_book")) {
+ ent_f->store_line("<key>com.apple.security.personal-information.addressbook</key>");
+ ent_f->store_line("<true/>");
+ }
+ if ((bool)p_preset->get("codesign/entitlements/calendars")) {
+ ent_f->store_line("<key>com.apple.security.personal-information.calendars</key>");
+ ent_f->store_line("<true/>");
+ }
+ if ((bool)p_preset->get("codesign/entitlements/photos_library")) {
+ ent_f->store_line("<key>com.apple.security.personal-information.photos-library</key>");
+ ent_f->store_line("<true/>");
+ }
+ if ((bool)p_preset->get("codesign/entitlements/apple_events")) {
+ ent_f->store_line("<key>com.apple.security.automation.apple-events</key>");
+ ent_f->store_line("<true/>");
+ }
+
+ if ((bool)p_preset->get("codesign/entitlements/app_sandbox/enabled")) {
+ ent_f->store_line("<key>com.apple.security.app-sandbox</key>");
+ ent_f->store_line("<true/>");
+
+ if ((bool)p_preset->get("codesign/entitlements/app_sandbox/network_server")) {
+ ent_f->store_line("<key>com.apple.security.network.server</key>");
+ ent_f->store_line("<true/>");
+ }
+ if ((bool)p_preset->get("codesign/entitlements/app_sandbox/network_client")) {
+ ent_f->store_line("<key>com.apple.security.network.client</key>");
+ ent_f->store_line("<true/>");
+ }
+ if ((bool)p_preset->get("codesign/entitlements/app_sandbox/device_usb")) {
+ ent_f->store_line("<key>com.apple.security.device.usb</key>");
+ ent_f->store_line("<true/>");
+ }
+ if ((bool)p_preset->get("codesign/entitlements/app_sandbox/device_bluetooth")) {
+ ent_f->store_line("<key>com.apple.security.device.bluetooth</key>");
+ ent_f->store_line("<true/>");
+ }
+ if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_downloads") == 1) {
+ ent_f->store_line("<key>com.apple.security.files.downloads.read-only</key>");
+ ent_f->store_line("<true/>");
+ }
+ if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_downloads") == 2) {
+ ent_f->store_line("<key>com.apple.security.files.downloads.read-write</key>");
+ ent_f->store_line("<true/>");
+ }
+ if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_pictures") == 1) {
+ ent_f->store_line("<key>com.apple.security.files.pictures.read-only</key>");
+ ent_f->store_line("<true/>");
+ }
+ if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_pictures") == 2) {
+ ent_f->store_line("<key>com.apple.security.files.pictures.read-write</key>");
+ ent_f->store_line("<true/>");
+ }
+ if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_music") == 1) {
+ ent_f->store_line("<key>com.apple.security.files.music.read-only</key>");
+ ent_f->store_line("<true/>");
+ }
+ if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_music") == 2) {
+ ent_f->store_line("<key>com.apple.security.files.music.read-write</key>");
+ ent_f->store_line("<true/>");
+ }
+ if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_movies") == 1) {
+ ent_f->store_line("<key>com.apple.security.files.movies.read-only</key>");
+ ent_f->store_line("<true/>");
+ }
+ if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_movies") == 2) {
+ ent_f->store_line("<key>com.apple.security.files.movies.read-write</key>");
+ ent_f->store_line("<true/>");
+ }
+ }
+
+ ent_f->store_line("</dict>");
+ ent_f->store_line("</plist>");
+
+ ent_f->close();
+ memdelete(ent_f);
+ } else {
+ err = ERR_CANT_CREATE;
+ }
+ }
+
if (err == OK) {
DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
for (int i = 0; i < shared_objects.size(); i++) {
err = da->copy(shared_objects[i].path, tmp_app_path_name + "/Contents/Frameworks/" + shared_objects[i].path.get_file());
if (err == OK && sign_enabled) {
- err = _code_sign(p_preset, tmp_app_path_name + "/Contents/Frameworks/" + shared_objects[i].path.get_file());
+ err = _code_sign(p_preset, tmp_app_path_name + "/Contents/Frameworks/" + shared_objects[i].path.get_file(), ent_path);
}
}
memdelete(da);
}
+ if (sign_enabled) {
+ for (int i = 0; i < dylibs_found.size(); i++) {
+ if (err == OK) {
+ err = _code_sign(p_preset, tmp_app_path_name + "/" + dylibs_found[i], ent_path);
+ }
+ }
+ }
+
if (err == OK && sign_enabled) {
if (ep.step("Code signing bundle", 2)) {
return ERR_SKIP;
}
- err = _code_sign(p_preset, tmp_app_path_name + "/Contents/MacOS/" + pkg_name);
+ err = _code_sign(p_preset, tmp_app_path_name + "/Contents/MacOS/" + pkg_name, ent_path);
}
if (export_format == "dmg") {
@@ -766,7 +921,7 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
if (ep.step("Code signing DMG", 3)) {
return ERR_SKIP;
}
- err = _code_sign(p_preset, p_path);
+ err = _code_sign(p_preset, p_path, ent_path);
}
} else {
// Create ZIP.
diff --git a/platform/server/detect.py b/platform/server/detect.py
index 5be7e81e7a..c799ce03e1 100644
--- a/platform/server/detect.py
+++ b/platform/server/detect.py
@@ -39,6 +39,7 @@ def get_opts():
BoolVariable("use_lsan", "Use LLVM/GCC compiler leak sanitizer (LSAN))", False),
BoolVariable("use_tsan", "Use LLVM/GCC compiler thread sanitizer (TSAN))", False),
BoolVariable("debug_symbols", "Add debugging symbols to release/release_debug builds", True),
+ BoolVariable("use_msan", "Use LLVM/GCC compiler memory sanitizer (MSAN))", False),
BoolVariable("separate_debug_symbols", "Create a separate file containing debugging symbols", False),
BoolVariable("execinfo", "Use libexecinfo on systems where glibc is not available", False),
]
@@ -99,7 +100,7 @@ def configure(env):
env.Append(CCFLAGS=["-ftest-coverage", "-fprofile-arcs"])
env.Append(LINKFLAGS=["-ftest-coverage", "-fprofile-arcs"])
- if env["use_ubsan"] or env["use_asan"] or env["use_lsan"] or env["use_tsan"]:
+ if env["use_ubsan"] or env["use_asan"] or env["use_lsan"] or env["use_tsan"] or env["use_msan"]:
env.extra_suffix += "s"
if env["use_ubsan"]:
@@ -118,6 +119,10 @@ def configure(env):
env.Append(CCFLAGS=["-fsanitize=thread"])
env.Append(LINKFLAGS=["-fsanitize=thread"])
+ if env["use_msan"]:
+ env.Append(CCFLAGS=["-fsanitize=memory"])
+ env.Append(LINKFLAGS=["-fsanitize=memory"])
+
if env["use_lto"]:
env.Append(CCFLAGS=["-flto"])
if not env["use_llvm"] and env.GetOption("num_jobs") > 1:
diff --git a/platform/windows/godot.natvis b/platform/windows/godot.natvis
index d85dfbc3d3..857c6a88f1 100644
--- a/platform/windows/godot.natvis
+++ b/platform/windows/godot.natvis
@@ -10,6 +10,16 @@
</Expand>
</Type>
+ <Type Name="LocalVector&lt;*&gt;">
+ <Expand>
+ <Item Name="[size]">count</Item>
+ <ArrayItems>
+ <Size>count</Size>
+ <ValuePointer>data</ValuePointer>
+ </ArrayItems>
+ </Expand>
+ </Type>
+
<Type Name="List&lt;*&gt;">
<Expand>
<Item Name="[size]">_data ? (_data->size_cache) : 0</Item>
diff --git a/scene/2d/area_2d.cpp b/scene/2d/area_2d.cpp
index 68d5b4b540..49d1654e3f 100644
--- a/scene/2d/area_2d.cpp
+++ b/scene/2d/area_2d.cpp
@@ -590,10 +590,10 @@ void Area2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("_body_inout"), &Area2D::_body_inout);
ClassDB::bind_method(D_METHOD("_area_inout"), &Area2D::_area_inout);
- ADD_SIGNAL(MethodInfo("body_shape_entered", PropertyInfo(Variant::INT, "body_id"), PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node"), PropertyInfo(Variant::INT, "body_shape"), PropertyInfo(Variant::INT, "local_shape")));
- ADD_SIGNAL(MethodInfo("body_shape_exited", PropertyInfo(Variant::INT, "body_id"), PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node"), PropertyInfo(Variant::INT, "body_shape"), PropertyInfo(Variant::INT, "local_shape")));
- ADD_SIGNAL(MethodInfo("body_entered", PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node")));
- ADD_SIGNAL(MethodInfo("body_exited", PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node")));
+ ADD_SIGNAL(MethodInfo("body_shape_entered", PropertyInfo(Variant::INT, "body_id"), PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node2D"), PropertyInfo(Variant::INT, "body_shape"), PropertyInfo(Variant::INT, "local_shape")));
+ ADD_SIGNAL(MethodInfo("body_shape_exited", PropertyInfo(Variant::INT, "body_id"), PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node2D"), PropertyInfo(Variant::INT, "body_shape"), PropertyInfo(Variant::INT, "local_shape")));
+ ADD_SIGNAL(MethodInfo("body_entered", PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node2D")));
+ ADD_SIGNAL(MethodInfo("body_exited", PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node2D")));
ADD_SIGNAL(MethodInfo("area_shape_entered", PropertyInfo(Variant::INT, "area_id"), PropertyInfo(Variant::OBJECT, "area", PROPERTY_HINT_RESOURCE_TYPE, "Area2D"), PropertyInfo(Variant::INT, "area_shape"), PropertyInfo(Variant::INT, "local_shape")));
ADD_SIGNAL(MethodInfo("area_shape_exited", PropertyInfo(Variant::INT, "area_id"), PropertyInfo(Variant::OBJECT, "area", PROPERTY_HINT_RESOURCE_TYPE, "Area2D"), PropertyInfo(Variant::INT, "area_shape"), PropertyInfo(Variant::INT, "local_shape")));
diff --git a/scene/2d/camera_2d.cpp b/scene/2d/camera_2d.cpp
index b4de12b113..c5ac4e1a05 100644
--- a/scene/2d/camera_2d.cpp
+++ b/scene/2d/camera_2d.cpp
@@ -263,6 +263,7 @@ void Camera2D::_notification(int p_what) {
viewport = nullptr;
} break;
+#ifdef TOOLS_ENABLED
case NOTIFICATION_DRAW: {
if (!is_inside_tree() || !Engine::get_singleton()->is_editor_hint()) {
break;
@@ -339,8 +340,8 @@ void Camera2D::_notification(int p_what) {
draw_line(inv_transform.xform(margin_endpoints[i]), inv_transform.xform(margin_endpoints[(i + 1) % 4]), margin_drawing_color, margin_drawing_width);
}
}
-
} break;
+#endif
}
}
@@ -610,7 +611,9 @@ Node *Camera2D::get_custom_viewport() const {
void Camera2D::set_screen_drawing_enabled(bool enable) {
screen_drawing_enabled = enable;
+#ifdef TOOLS_ENABLED
update();
+#endif
}
bool Camera2D::is_screen_drawing_enabled() const {
@@ -619,7 +622,9 @@ bool Camera2D::is_screen_drawing_enabled() const {
void Camera2D::set_limit_drawing_enabled(bool enable) {
limit_drawing_enabled = enable;
+#ifdef TOOLS_ENABLED
update();
+#endif
}
bool Camera2D::is_limit_drawing_enabled() const {
@@ -628,7 +633,9 @@ bool Camera2D::is_limit_drawing_enabled() const {
void Camera2D::set_margin_drawing_enabled(bool enable) {
margin_drawing_enabled = enable;
+#ifdef TOOLS_ENABLED
update();
+#endif
}
bool Camera2D::is_margin_drawing_enabled() const {
diff --git a/scene/2d/collision_polygon_2d.cpp b/scene/2d/collision_polygon_2d.cpp
index 39d7705226..38198c496e 100644
--- a/scene/2d/collision_polygon_2d.cpp
+++ b/scene/2d/collision_polygon_2d.cpp
@@ -41,13 +41,13 @@
void CollisionPolygon2D::_build_polygon() {
parent->shape_owner_clear_shapes(owner_id);
- if (polygon.size() == 0) {
- return;
- }
-
bool solids = build_mode == BUILD_SOLIDS;
if (solids) {
+ if (polygon.size() < 3) {
+ return;
+ }
+
//here comes the sun, lalalala
//decompose concave into multiple convex polygons and add them
Vector<Vector<Vector2>> decomp = _decompose_in_convex();
@@ -58,6 +58,10 @@ void CollisionPolygon2D::_build_polygon() {
}
} else {
+ if (polygon.size() < 2) {
+ return;
+ }
+
Ref<ConcavePolygonShape2D> concave = memnew(ConcavePolygonShape2D);
Vector<Vector2> segments;
@@ -132,25 +136,28 @@ void CollisionPolygon2D::_notification(int p_what) {
break;
}
- for (int i = 0; i < polygon.size(); i++) {
+ int polygon_count = polygon.size();
+ for (int i = 0; i < polygon_count; i++) {
Vector2 p = polygon[i];
- Vector2 n = polygon[(i + 1) % polygon.size()];
+ Vector2 n = polygon[(i + 1) % polygon_count];
// draw line with width <= 1, so it does not scale with zoom and break pixel exact editing
draw_line(p, n, Color(0.9, 0.2, 0.0, 0.8), 1);
}
+
+ if (polygon_count > 2) {
#define DEBUG_DECOMPOSE
#if defined(TOOLS_ENABLED) && defined(DEBUG_DECOMPOSE)
+ Vector<Vector<Vector2>> decomp = _decompose_in_convex();
- Vector<Vector<Vector2>> decomp = _decompose_in_convex();
-
- Color c(0.4, 0.9, 0.1);
- for (int i = 0; i < decomp.size(); i++) {
- c.set_hsv(Math::fmod(c.get_h() + 0.738, 1), c.get_s(), c.get_v(), 0.5);
- draw_colored_polygon(decomp[i], c);
- }
+ Color c(0.4, 0.9, 0.1);
+ for (int i = 0; i < decomp.size(); i++) {
+ c.set_hsv(Math::fmod(c.get_h() + 0.738, 1), c.get_s(), c.get_v(), 0.5);
+ draw_colored_polygon(decomp[i], c);
+ }
#else
- draw_colored_polygon(polygon, get_tree()->get_debug_collisions_color());
+ draw_colored_polygon(polygon, get_tree()->get_debug_collisions_color());
#endif
+ }
if (one_way_collision) {
Color dcol = get_tree()->get_debug_collisions_color(); //0.9,0.2,0.2,0.4);
@@ -211,6 +218,8 @@ void CollisionPolygon2D::set_build_mode(BuildMode p_mode) {
_build_polygon();
_update_in_shape_owner();
}
+ update();
+ update_configuration_warning();
}
CollisionPolygon2D::BuildMode CollisionPolygon2D::get_build_mode() const {
@@ -241,11 +250,27 @@ String CollisionPolygon2D::get_configuration_warning() const {
warning += TTR("CollisionPolygon2D only serves to provide a collision shape to a CollisionObject2D derived node. Please only use it as a child of Area2D, StaticBody2D, RigidBody2D, KinematicBody2D, etc. to give them a shape.");
}
- if (polygon.is_empty()) {
+ int polygon_count = polygon.size();
+ if (polygon_count == 0) {
if (!warning.is_empty()) {
warning += "\n\n";
}
warning += TTR("An empty CollisionPolygon2D has no effect on collision.");
+ } else {
+ bool solids = build_mode == BUILD_SOLIDS;
+ if (solids) {
+ if (polygon_count < 3) {
+ if (!warning.is_empty()) {
+ warning += "\n\n";
+ }
+ warning += TTR("Invalid polygon. At least 3 points are needed in 'Solids' build mode.");
+ }
+ } else if (polygon_count < 2) {
+ if (!warning.is_empty()) {
+ warning += "\n\n";
+ }
+ warning += TTR("Invalid polygon. At least 2 points are needed in 'Segments' build mode.");
+ }
}
return warning;
diff --git a/scene/2d/cpu_particles_2d.cpp b/scene/2d/cpu_particles_2d.cpp
index 0e51264171..48acee1bc4 100644
--- a/scene/2d/cpu_particles_2d.cpp
+++ b/scene/2d/cpu_particles_2d.cpp
@@ -1032,66 +1032,64 @@ void CPUParticles2D::_update_render_thread() {
}
void CPUParticles2D::_notification(int p_what) {
- if (p_what == NOTIFICATION_ENTER_TREE) {
- set_process_internal(emitting);
- }
-
- if (p_what == NOTIFICATION_EXIT_TREE) {
- _set_redraw(false);
- }
-
- if (p_what == NOTIFICATION_DRAW) {
- // first update before rendering to avoid one frame delay after emitting starts
- if (emitting && (time == 0)) {
- _update_internal();
- }
-
- if (!redraw) {
- return; // don't add to render list
- }
-
- RID texrid;
- if (texture.is_valid()) {
- texrid = texture->get_rid();
- }
-
- RS::get_singleton()->canvas_item_add_multimesh(get_canvas_item(), multimesh, texrid);
- }
-
- if (p_what == NOTIFICATION_INTERNAL_PROCESS) {
- _update_internal();
- }
-
- if (p_what == NOTIFICATION_TRANSFORM_CHANGED) {
- inv_emission_transform = get_global_transform().affine_inverse();
+ switch (p_what) {
+ case NOTIFICATION_ENTER_TREE: {
+ set_process_internal(emitting);
+ } break;
+ case NOTIFICATION_EXIT_TREE: {
+ _set_redraw(false);
+ } break;
+ case NOTIFICATION_DRAW: {
+ // first update before rendering to avoid one frame delay after emitting starts
+ if (emitting && (time == 0)) {
+ _update_internal();
+ }
- if (!local_coords) {
- int pc = particles.size();
+ if (!redraw) {
+ return; // don't add to render list
+ }
- float *w = particle_data.ptrw();
- const Particle *r = particles.ptr();
- float *ptr = w;
+ RID texrid;
+ if (texture.is_valid()) {
+ texrid = texture->get_rid();
+ }
- for (int i = 0; i < pc; i++) {
- Transform2D t = inv_emission_transform * r[i].transform;
+ RS::get_singleton()->canvas_item_add_multimesh(get_canvas_item(), multimesh, texrid);
+ } break;
+ case NOTIFICATION_INTERNAL_PROCESS: {
+ _update_internal();
+ } break;
+ case NOTIFICATION_TRANSFORM_CHANGED: {
+ inv_emission_transform = get_global_transform().affine_inverse();
- if (r[i].active) {
- ptr[0] = t.elements[0][0];
- ptr[1] = t.elements[1][0];
- ptr[2] = 0;
- ptr[3] = t.elements[2][0];
- ptr[4] = t.elements[0][1];
- ptr[5] = t.elements[1][1];
- ptr[6] = 0;
- ptr[7] = t.elements[2][1];
+ if (!local_coords) {
+ int pc = particles.size();
+
+ float *w = particle_data.ptrw();
+ const Particle *r = particles.ptr();
+ float *ptr = w;
+
+ for (int i = 0; i < pc; i++) {
+ Transform2D t = inv_emission_transform * r[i].transform;
+
+ if (r[i].active) {
+ ptr[0] = t.elements[0][0];
+ ptr[1] = t.elements[1][0];
+ ptr[2] = 0;
+ ptr[3] = t.elements[2][0];
+ ptr[4] = t.elements[0][1];
+ ptr[5] = t.elements[1][1];
+ ptr[6] = 0;
+ ptr[7] = t.elements[2][1];
+
+ } else {
+ zeromem(ptr, sizeof(float) * 8);
+ }
- } else {
- zeromem(ptr, sizeof(float) * 8);
+ ptr += 16;
}
-
- ptr += 16;
}
- }
+ } break;
}
}
diff --git a/scene/2d/joints_2d.cpp b/scene/2d/joints_2d.cpp
index f4f08674c9..7d9cdd52ac 100644
--- a/scene/2d/joints_2d.cpp
+++ b/scene/2d/joints_2d.cpp
@@ -49,19 +49,9 @@ void Joint2D::_disconnect_signals() {
}
}
-void Joint2D::_body_exit_tree(const ObjectID &p_body_id) {
+void Joint2D::_body_exit_tree() {
_disconnect_signals();
- Object *object = ObjectDB::get_instance(p_body_id);
- PhysicsBody2D *body = Object::cast_to<PhysicsBody2D>(object);
- ERR_FAIL_NULL(body);
- RID body_rid = body->get_rid();
- if (ba == body_rid) {
- a = NodePath();
- }
- if (bb == body_rid) {
- b = NodePath();
- }
- _update_joint();
+ _update_joint(true);
}
void Joint2D::_update_joint(bool p_only_free) {
@@ -142,8 +132,8 @@ void Joint2D::_update_joint(bool p_only_free) {
ba = body_a->get_rid();
bb = body_b->get_rid();
- body_a->connect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &Joint2D::_body_exit_tree), make_binds(body_a->get_instance_id()));
- body_b->connect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &Joint2D::_body_exit_tree), make_binds(body_b->get_instance_id()));
+ body_a->connect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &Joint2D::_body_exit_tree));
+ body_b->connect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &Joint2D::_body_exit_tree));
PhysicsServer2D::get_singleton()->joint_disable_collisions_between_bodies(joint, exclude_from_collision);
}
diff --git a/scene/2d/joints_2d.h b/scene/2d/joints_2d.h
index 3607a6c176..08e02ee29d 100644
--- a/scene/2d/joints_2d.h
+++ b/scene/2d/joints_2d.h
@@ -51,7 +51,7 @@ class Joint2D : public Node2D {
protected:
void _disconnect_signals();
- void _body_exit_tree(const ObjectID &p_body_id);
+ void _body_exit_tree();
void _update_joint(bool p_only_free = false);
void _notification(int p_what);
diff --git a/scene/2d/polygon_2d.cpp b/scene/2d/polygon_2d.cpp
index ecc05fb931..a60a32f1d2 100644
--- a/scene/2d/polygon_2d.cpp
+++ b/scene/2d/polygon_2d.cpp
@@ -88,6 +88,12 @@ bool Polygon2D::_edit_is_selected_on_click(const Point2 &p_point, double p_toler
}
return Geometry2D::is_point_in_polygon(p_point - get_offset(), polygon2d);
}
+
+void Polygon2D::_validate_property(PropertyInfo &property) const {
+ if (!invert && property.name == "invert_border") {
+ property.usage = PROPERTY_USAGE_NOEDITOR;
+ }
+}
#endif
void Polygon2D::_skeleton_bone_setup_changed() {
@@ -455,6 +461,7 @@ Size2 Polygon2D::get_texture_scale() const {
void Polygon2D::set_invert(bool p_invert) {
invert = p_invert;
update();
+ notify_property_list_changed();
}
bool Polygon2D::get_invert() const {
diff --git a/scene/2d/polygon_2d.h b/scene/2d/polygon_2d.h
index ab01a4ffd0..43a66aad13 100644
--- a/scene/2d/polygon_2d.h
+++ b/scene/2d/polygon_2d.h
@@ -72,6 +72,10 @@ class Polygon2D : public Node2D {
void _skeleton_bone_setup_changed();
+#ifdef TOOLS_ENABLED
+ void _validate_property(PropertyInfo &property) const override;
+#endif
+
protected:
void _notification(int p_what);
static void _bind_methods();
diff --git a/scene/2d/ray_cast_2d.cpp b/scene/2d/ray_cast_2d.cpp
index 2cc3a74270..50625a0f39 100644
--- a/scene/2d/ray_cast_2d.cpp
+++ b/scene/2d/ray_cast_2d.cpp
@@ -159,30 +159,7 @@ void RayCast2D::_notification(int p_what) {
if (!Engine::get_singleton()->is_editor_hint() && !get_tree()->is_debugging_collisions_hint()) {
break;
}
- Transform2D xf;
- xf.rotate(target_position.angle());
- xf.translate(Vector2(target_position.length(), 0));
-
- // Draw an arrow indicating where the RayCast is pointing to
- Color draw_col = get_tree()->get_debug_collisions_color();
- if (!enabled) {
- float g = draw_col.get_v();
- draw_col.r = g;
- draw_col.g = g;
- draw_col.b = g;
- }
- draw_line(Vector2(), target_position, draw_col, 2);
- Vector<Vector2> pts;
- float tsize = 8.0;
- pts.push_back(xf.xform(Vector2(tsize, 0)));
- pts.push_back(xf.xform(Vector2(0, Math_SQRT12 * tsize)));
- pts.push_back(xf.xform(Vector2(0, -Math_SQRT12 * tsize)));
- Vector<Color> cols;
- for (int i = 0; i < 3; i++) {
- cols.push_back(draw_col);
- }
-
- draw_primitive(pts, cols, Vector<Vector2>());
+ _draw_debug_shape();
} break;
@@ -212,7 +189,7 @@ void RayCast2D::_update_raycast_state() {
}
PhysicsDirectSpaceState2D::RayResult rr;
-
+ bool prev_collision_state = collided;
if (dss->intersect_ray(gt.get_origin(), gt.xform(to), rr, exclude, collision_mask, collide_with_bodies, collide_with_areas)) {
collided = true;
against = rr.collider_id;
@@ -224,6 +201,48 @@ void RayCast2D::_update_raycast_state() {
against = ObjectID();
against_shape = 0;
}
+
+ if (prev_collision_state != collided) {
+ update();
+ }
+}
+
+void RayCast2D::_draw_debug_shape() {
+ Color draw_col = collided ? Color(1.0, 0.01, 0) : get_tree()->get_debug_collisions_color();
+ if (!enabled) {
+ float g = draw_col.get_v();
+ draw_col.r = g;
+ draw_col.g = g;
+ draw_col.b = g;
+ }
+
+ // Draw an arrow indicating where the RayCast is pointing to
+ const float max_arrow_size = 6;
+ const float line_width = 1.4;
+ bool no_line = target_position.length() < line_width;
+ float arrow_size = CLAMP(target_position.length() * 2 / 3, line_width, max_arrow_size);
+
+ if (no_line) {
+ arrow_size = target_position.length();
+ } else {
+ draw_line(Vector2(), target_position - target_position.normalized() * arrow_size, draw_col, line_width);
+ }
+
+ Transform2D xf;
+ xf.rotate(target_position.angle());
+ xf.translate(Vector2(no_line ? 0 : target_position.length() - arrow_size, 0));
+
+ Vector<Vector2> pts;
+ pts.push_back(xf.xform(Vector2(arrow_size, 0)));
+ pts.push_back(xf.xform(Vector2(0, 0.5 * arrow_size)));
+ pts.push_back(xf.xform(Vector2(0, -0.5 * arrow_size)));
+
+ Vector<Color> cols;
+ for (int i = 0; i < 3; i++) {
+ cols.push_back(draw_col);
+ }
+
+ draw_primitive(pts, cols, Vector<Vector2>());
}
void RayCast2D::force_raycast_update() {
diff --git a/scene/2d/ray_cast_2d.h b/scene/2d/ray_cast_2d.h
index dab3302e25..984c6bda49 100644
--- a/scene/2d/ray_cast_2d.h
+++ b/scene/2d/ray_cast_2d.h
@@ -51,6 +51,8 @@ class RayCast2D : public Node2D {
bool collide_with_areas = false;
bool collide_with_bodies = true;
+ void _draw_debug_shape();
+
protected:
void _notification(int p_what);
void _update_raycast_state();
diff --git a/scene/2d/sprite_2d.cpp b/scene/2d/sprite_2d.cpp
index dde9790b44..31040020dd 100644
--- a/scene/2d/sprite_2d.cpp
+++ b/scene/2d/sprite_2d.cpp
@@ -206,6 +206,7 @@ void Sprite2D::set_region(bool p_region) {
region = p_region;
update();
+ notify_property_list_changed();
}
bool Sprite2D::is_region() const {
@@ -383,6 +384,10 @@ void Sprite2D::_validate_property(PropertyInfo &property) const {
if (property.name == "frame_coords") {
property.usage |= PROPERTY_USAGE_KEYING_INCREMENTS;
}
+
+ if (!region && (property.name == "region_rect" || property.name == "region_filter_clip")) {
+ property.usage = PROPERTY_USAGE_NOEDITOR;
+ }
}
void Sprite2D::_texture_changed() {
diff --git a/scene/3d/area_3d.cpp b/scene/3d/area_3d.cpp
index 99c5276636..23eda379be 100644
--- a/scene/3d/area_3d.cpp
+++ b/scene/3d/area_3d.cpp
@@ -640,10 +640,10 @@ void Area3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_reverb_uniformity", "amount"), &Area3D::set_reverb_uniformity);
ClassDB::bind_method(D_METHOD("get_reverb_uniformity"), &Area3D::get_reverb_uniformity);
- ADD_SIGNAL(MethodInfo("body_shape_entered", PropertyInfo(Variant::INT, "body_id"), PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node"), PropertyInfo(Variant::INT, "body_shape"), PropertyInfo(Variant::INT, "local_shape")));
- ADD_SIGNAL(MethodInfo("body_shape_exited", PropertyInfo(Variant::INT, "body_id"), PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node"), PropertyInfo(Variant::INT, "body_shape"), PropertyInfo(Variant::INT, "local_shape")));
- ADD_SIGNAL(MethodInfo("body_entered", PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node")));
- ADD_SIGNAL(MethodInfo("body_exited", PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node")));
+ ADD_SIGNAL(MethodInfo("body_shape_entered", PropertyInfo(Variant::INT, "body_id"), PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node3D"), PropertyInfo(Variant::INT, "body_shape"), PropertyInfo(Variant::INT, "local_shape")));
+ ADD_SIGNAL(MethodInfo("body_shape_exited", PropertyInfo(Variant::INT, "body_id"), PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node3D"), PropertyInfo(Variant::INT, "body_shape"), PropertyInfo(Variant::INT, "local_shape")));
+ ADD_SIGNAL(MethodInfo("body_entered", PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node3D")));
+ ADD_SIGNAL(MethodInfo("body_exited", PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node3D")));
ADD_SIGNAL(MethodInfo("area_shape_entered", PropertyInfo(Variant::INT, "area_id"), PropertyInfo(Variant::OBJECT, "area", PROPERTY_HINT_RESOURCE_TYPE, "Area3D"), PropertyInfo(Variant::INT, "area_shape"), PropertyInfo(Variant::INT, "local_shape")));
ADD_SIGNAL(MethodInfo("area_shape_exited", PropertyInfo(Variant::INT, "area_id"), PropertyInfo(Variant::OBJECT, "area", PROPERTY_HINT_RESOURCE_TYPE, "Area3D"), PropertyInfo(Variant::INT, "area_shape"), PropertyInfo(Variant::INT, "local_shape")));
diff --git a/scene/3d/baked_lightmap.cpp b/scene/3d/baked_lightmap.cpp
index 578ea823f0..402e2b8f40 100644
--- a/scene/3d/baked_lightmap.cpp
+++ b/scene/3d/baked_lightmap.cpp
@@ -78,6 +78,7 @@ void BakedLightmapData::clear_users() {
}
void BakedLightmapData::_set_user_data(const Array &p_data) {
+ ERR_FAIL_COND(p_data.size() <= 0);
ERR_FAIL_COND((p_data.size() % 4) != 0);
for (int i = 0; i < p_data.size(); i += 4) {
diff --git a/scene/3d/collision_object_3d.cpp b/scene/3d/collision_object_3d.cpp
index b7da4822e2..849ef7a2bf 100644
--- a/scene/3d/collision_object_3d.cpp
+++ b/scene/3d/collision_object_3d.cpp
@@ -30,6 +30,7 @@
#include "collision_object_3d.h"
+#include "mesh_instance_3d.h"
#include "scene/scene_string_names.h"
#include "servers/physics_server_3d.h"
@@ -110,6 +111,42 @@ void CollisionObject3D::_update_pickable() {
}
}
+void CollisionObject3D::_update_debug_shapes() {
+ for (Set<uint32_t>::Element *shapedata_idx = debug_shapes_to_update.front(); shapedata_idx; shapedata_idx = shapedata_idx->next()) {
+ if (shapes.has(shapedata_idx->get())) {
+ ShapeData &shapedata = shapes[shapedata_idx->get()];
+ for (int i = 0; i < shapedata.shapes.size(); i++) {
+ ShapeData::ShapeBase &s = shapedata.shapes.write[i];
+ if (s.debug_shape) {
+ s.debug_shape->queue_delete();
+ s.debug_shape = nullptr;
+ }
+ if (s.shape.is_null() || shapedata.disabled) {
+ continue;
+ }
+
+ Ref<Mesh> mesh = s.shape->get_debug_mesh();
+ MeshInstance3D *mi = memnew(MeshInstance3D);
+ mi->set_transform(shapedata.xform);
+ mi->set_mesh(mesh);
+ add_child(mi);
+ mi->force_update_transform();
+ s.debug_shape = mi;
+ }
+ }
+ }
+ debug_shapes_to_update.clear();
+}
+
+void CollisionObject3D::_update_shape_data(uint32_t p_owner) {
+ if (is_inside_tree() && get_tree()->is_debugging_collisions_hint()) {
+ if (debug_shapes_to_update.is_empty()) {
+ call_deferred("_update_debug_shapes");
+ }
+ debug_shapes_to_update.insert(p_owner);
+ }
+}
+
void CollisionObject3D::set_ray_pickable(bool p_ray_pickable) {
ray_pickable = p_ray_pickable;
_update_pickable();
@@ -141,6 +178,8 @@ void CollisionObject3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("shape_owner_clear_shapes", "owner_id"), &CollisionObject3D::shape_owner_clear_shapes);
ClassDB::bind_method(D_METHOD("shape_find_owner", "shape_index"), &CollisionObject3D::shape_find_owner);
+ ClassDB::bind_method(D_METHOD("_update_debug_shapes"), &CollisionObject3D::_update_debug_shapes);
+
BIND_VMETHOD(MethodInfo("_input_event", PropertyInfo(Variant::OBJECT, "camera"), PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent"), PropertyInfo(Variant::VECTOR3, "click_position"), PropertyInfo(Variant::VECTOR3, "click_normal"), PropertyInfo(Variant::INT, "shape_idx")));
ADD_SIGNAL(MethodInfo("input_event", PropertyInfo(Variant::OBJECT, "camera", PROPERTY_HINT_RESOURCE_TYPE, "Node"), PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent"), PropertyInfo(Variant::VECTOR3, "click_position"), PropertyInfo(Variant::VECTOR3, "click_normal"), PropertyInfo(Variant::INT, "shape_idx")));
@@ -188,6 +227,7 @@ void CollisionObject3D::shape_owner_set_disabled(uint32_t p_owner, bool p_disabl
PhysicsServer3D::get_singleton()->body_set_shape_disabled(rid, sd.shapes[i].index, p_disabled);
}
}
+ _update_shape_data(p_owner);
}
bool CollisionObject3D::is_shape_owner_disabled(uint32_t p_owner) const {
@@ -223,6 +263,8 @@ void CollisionObject3D::shape_owner_set_transform(uint32_t p_owner, const Transf
PhysicsServer3D::get_singleton()->body_set_shape_transform(rid, sd.shapes[i].index, p_transform);
}
}
+
+ _update_shape_data(p_owner);
}
Transform CollisionObject3D::shape_owner_get_transform(uint32_t p_owner) const {
@@ -245,6 +287,7 @@ void CollisionObject3D::shape_owner_add_shape(uint32_t p_owner, const Ref<Shape3
ShapeData::ShapeBase s;
s.index = total_subshapes;
s.shape = p_shape;
+
if (area) {
PhysicsServer3D::get_singleton()->area_add_shape(rid, p_shape->get_rid(), sd.xform, sd.disabled);
} else {
@@ -253,6 +296,8 @@ void CollisionObject3D::shape_owner_add_shape(uint32_t p_owner, const Ref<Shape3
sd.shapes.push_back(s);
total_subshapes++;
+
+ _update_shape_data(p_owner);
}
int CollisionObject3D::shape_owner_get_shape_count(uint32_t p_owner) const {
@@ -279,13 +324,19 @@ void CollisionObject3D::shape_owner_remove_shape(uint32_t p_owner, int p_shape)
ERR_FAIL_COND(!shapes.has(p_owner));
ERR_FAIL_INDEX(p_shape, shapes[p_owner].shapes.size());
- int index_to_remove = shapes[p_owner].shapes[p_shape].index;
+ const ShapeData::ShapeBase &s = shapes[p_owner].shapes[p_shape];
+ int index_to_remove = s.index;
+
if (area) {
PhysicsServer3D::get_singleton()->area_remove_shape(rid, index_to_remove);
} else {
PhysicsServer3D::get_singleton()->body_remove_shape(rid, index_to_remove);
}
+ if (s.debug_shape) {
+ s.debug_shape->queue_delete();
+ }
+
shapes[p_owner].shapes.remove(p_shape);
for (Map<uint32_t, ShapeData>::Element *E = shapes.front(); E; E = E->next()) {
diff --git a/scene/3d/collision_object_3d.h b/scene/3d/collision_object_3d.h
index b7473ca12a..fe20176984 100644
--- a/scene/3d/collision_object_3d.h
+++ b/scene/3d/collision_object_3d.h
@@ -45,6 +45,7 @@ class CollisionObject3D : public Node3D {
Object *owner = nullptr;
Transform xform;
struct ShapeBase {
+ Node *debug_shape = nullptr;
Ref<Shape3D> shape;
int index = 0;
};
@@ -60,8 +61,12 @@ class CollisionObject3D : public Node3D {
bool capture_input_on_drag = false;
bool ray_pickable = true;
+ Set<uint32_t> debug_shapes_to_update;
+
void _update_pickable();
+ void _update_shape_data(uint32_t p_owner);
+
protected:
CollisionObject3D(RID p_rid, bool p_area);
@@ -72,6 +77,8 @@ protected:
virtual void _mouse_enter();
virtual void _mouse_exit();
+ void _update_debug_shapes();
+
public:
uint32_t create_shape_owner(Object *p_owner);
void remove_shape_owner(uint32_t owner);
diff --git a/scene/3d/collision_shape_3d.cpp b/scene/3d/collision_shape_3d.cpp
index 914c8eab7a..242d82ab4c 100644
--- a/scene/3d/collision_shape_3d.cpp
+++ b/scene/3d/collision_shape_3d.cpp
@@ -100,9 +100,6 @@ void CollisionShape3D::_notification(int p_what) {
if (parent) {
_update_in_shape_owner();
}
- if (get_tree()->is_debugging_collisions_hint()) {
- _update_debug_shape();
- }
} break;
case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: {
if (parent) {
@@ -163,8 +160,6 @@ void CollisionShape3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("make_convex_from_siblings"), &CollisionShape3D::make_convex_from_siblings);
ClassDB::set_method_flags("CollisionShape3D", "make_convex_from_siblings", METHOD_FLAGS_DEFAULT | METHOD_FLAG_EDITOR);
- ClassDB::bind_method(D_METHOD("_update_debug_shape"), &CollisionShape3D::_update_debug_shape);
-
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shape", PROPERTY_HINT_RESOURCE_TYPE, "Shape3D"), "set_shape", "get_shape");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disabled"), "set_disabled", "is_disabled");
}
@@ -224,34 +219,9 @@ CollisionShape3D::~CollisionShape3D() {
//RenderingServer::get_singleton()->free(indicator);
}
-void CollisionShape3D::_update_debug_shape() {
- debug_shape_dirty = false;
-
- if (debug_shape) {
- debug_shape->queue_delete();
- debug_shape = nullptr;
- }
-
- Ref<Shape3D> s = get_shape();
- if (s.is_null()) {
- return;
- }
-
- Ref<Mesh> mesh = s->get_debug_mesh();
- MeshInstance3D *mi = memnew(MeshInstance3D);
- mi->set_mesh(mesh);
- add_child(mi);
- debug_shape = mi;
-}
-
void CollisionShape3D::_shape_changed() {
// If this is a heightfield shape our center may have changed
if (parent) {
_update_in_shape_owner(true);
}
-
- if (is_inside_tree() && get_tree()->is_debugging_collisions_hint() && !debug_shape_dirty) {
- debug_shape_dirty = true;
- call_deferred("_update_debug_shape");
- }
}
diff --git a/scene/3d/collision_shape_3d.h b/scene/3d/collision_shape_3d.h
index f55c09ffaa..5512417f75 100644
--- a/scene/3d/collision_shape_3d.h
+++ b/scene/3d/collision_shape_3d.h
@@ -43,14 +43,10 @@ class CollisionShape3D : public Node3D {
uint32_t owner_id = 0;
CollisionObject3D *parent = nullptr;
- Node *debug_shape = nullptr;
- bool debug_shape_dirty;
-
void resource_changed(RES res);
bool disabled = false;
protected:
- void _update_debug_shape();
void _shape_changed();
void _update_in_shape_owner(bool p_xform_only = false);
diff --git a/scene/3d/decal.cpp b/scene/3d/decal.cpp
index 0f10f2b85f..b5eab35605 100644
--- a/scene/3d/decal.cpp
+++ b/scene/3d/decal.cpp
@@ -109,6 +109,7 @@ Color Decal::get_modulate() const {
void Decal::set_enable_distance_fade(bool p_enable) {
distance_fade_enabled = p_enable;
RS::get_singleton()->decal_set_distance_fade(decal, distance_fade_enabled, distance_fade_begin, distance_fade_length);
+ notify_property_list_changed();
}
bool Decal::is_distance_fade_enabled() const {
@@ -153,6 +154,14 @@ Vector<Face3> Decal::get_faces(uint32_t p_usage_flags) const {
return Vector<Face3>();
}
+#ifdef TOOLS_ENABLED
+void Decal::_validate_property(PropertyInfo &property) const {
+ if (!distance_fade_enabled && (property.name == "distance_fade_begin" || property.name == "distance_fade_length")) {
+ property.usage = PROPERTY_USAGE_NOEDITOR;
+ }
+}
+#endif
+
void Decal::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_extents", "extents"), &Decal::set_extents);
ClassDB::bind_method(D_METHOD("get_extents"), &Decal::get_extents);
diff --git a/scene/3d/decal.h b/scene/3d/decal.h
index 095579d775..20d86ee16c 100644
--- a/scene/3d/decal.h
+++ b/scene/3d/decal.h
@@ -62,6 +62,10 @@ private:
float distance_fade_begin = 10.0;
float distance_fade_length = 1.0;
+#ifdef TOOLS_ENABLED
+ void _validate_property(PropertyInfo &property) const override;
+#endif
+
protected:
static void _bind_methods();
diff --git a/scene/3d/gi_probe.cpp b/scene/3d/gi_probe.cpp
index 8a8bfe50b9..43f820e5d4 100644
--- a/scene/3d/gi_probe.cpp
+++ b/scene/3d/gi_probe.cpp
@@ -415,13 +415,16 @@ Vector3i GIProbe::get_estimated_cell_size() const {
void GIProbe::bake(Node *p_from_node, bool p_create_visual_debug) {
static const int subdiv_value[SUBDIV_MAX] = { 6, 7, 8, 9 };
+ p_from_node = p_from_node ? p_from_node : get_parent();
+ ERR_FAIL_NULL(p_from_node);
+
Voxelizer baker;
baker.begin_bake(subdiv_value[subdiv], AABB(-extents, extents * 2.0));
List<PlotMesh> mesh_list;
- _find_meshes(p_from_node ? p_from_node : get_parent(), mesh_list);
+ _find_meshes(p_from_node, mesh_list);
if (bake_begin_function) {
bake_begin_function(mesh_list.size() + 1);
diff --git a/scene/3d/light_3d.cpp b/scene/3d/light_3d.cpp
index b0a10b5547..87f54022b3 100644
--- a/scene/3d/light_3d.cpp
+++ b/scene/3d/light_3d.cpp
@@ -65,6 +65,8 @@ void Light3D::set_shadow(bool p_enable) {
if (type == RenderingServer::LIGHT_SPOT || type == RenderingServer::LIGHT_OMNI) {
update_configuration_warning();
}
+
+ notify_property_list_changed();
}
bool Light3D::has_shadow() const {
@@ -202,6 +204,10 @@ bool Light3D::is_editor_only() const {
}
void Light3D::_validate_property(PropertyInfo &property) const {
+ if (!shadow && (property.name == "shadow_color" || property.name == "shadow_color" || property.name == "shadow_bias" || property.name == "shadow_normal_bias" || property.name == "shadow_reverse_cull_face" || property.name == "shadow_transmittance_bias" || property.name == "shadow_blur")) {
+ property.usage = PROPERTY_USAGE_NOEDITOR;
+ }
+
if (get_light_type() == RS::LIGHT_DIRECTIONAL && property.name == "light_size") {
property.usage = 0;
}
diff --git a/scene/3d/physics_joint_3d.cpp b/scene/3d/physics_joint_3d.cpp
index 0463bed9d6..de9c75621b 100644
--- a/scene/3d/physics_joint_3d.cpp
+++ b/scene/3d/physics_joint_3d.cpp
@@ -46,24 +46,15 @@ void Joint3D::_disconnect_signals() {
}
}
-void Joint3D::_body_exit_tree(const ObjectID &p_body_id) {
+void Joint3D::_body_exit_tree() {
_disconnect_signals();
- Object *object = ObjectDB::get_instance(p_body_id);
- PhysicsBody3D *body = Object::cast_to<PhysicsBody3D>(object);
- ERR_FAIL_NULL(body);
- RID body_rid = body->get_rid();
- if (ba == body_rid) {
- a = NodePath();
- }
- if (bb == body_rid) {
- b = NodePath();
- }
- _update_joint();
+ _update_joint(true);
}
void Joint3D::_update_joint(bool p_only_free) {
if (ba.is_valid() && bb.is_valid()) {
PhysicsServer3D::get_singleton()->body_remove_collision_exception(ba, bb);
+ PhysicsServer3D::get_singleton()->body_remove_collision_exception(bb, ba);
}
ba = RID();
@@ -133,12 +124,12 @@ void Joint3D::_update_joint(bool p_only_free) {
if (body_a) {
ba = body_a->get_rid();
- body_a->connect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &Joint3D::_body_exit_tree), make_binds(body_a->get_instance_id()));
+ body_a->connect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &Joint3D::_body_exit_tree));
}
if (body_b) {
bb = body_b->get_rid();
- body_b->connect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &Joint3D::_body_exit_tree), make_binds(body_b->get_instance_id()));
+ body_b->connect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &Joint3D::_body_exit_tree));
}
PhysicsServer3D::get_singleton()->joint_disable_collisions_between_bodies(joint, exclude_from_collision);
diff --git a/scene/3d/physics_joint_3d.h b/scene/3d/physics_joint_3d.h
index 8d0a16e432..f624ba602b 100644
--- a/scene/3d/physics_joint_3d.h
+++ b/scene/3d/physics_joint_3d.h
@@ -51,7 +51,7 @@ class Joint3D : public Node3D {
protected:
void _disconnect_signals();
- void _body_exit_tree(const ObjectID &p_body_id);
+ void _body_exit_tree();
void _update_joint(bool p_only_free = false);
void _notification(int p_what);
diff --git a/scene/3d/ray_cast_3d.cpp b/scene/3d/ray_cast_3d.cpp
index bfe79f15f5..465de2cb47 100644
--- a/scene/3d/ray_cast_3d.cpp
+++ b/scene/3d/ray_cast_3d.cpp
@@ -37,10 +37,13 @@
void RayCast3D::set_target_position(const Vector3 &p_point) {
target_position = p_point;
- if (is_inside_tree() && (Engine::get_singleton()->is_editor_hint() || get_tree()->is_debugging_collisions_hint())) {
- update_gizmo();
- }
- if (is_inside_tree() && get_tree()->is_debugging_collisions_hint()) {
+ update_gizmo();
+
+ if (Engine::get_singleton()->is_editor_hint()) {
+ if (is_inside_tree()) {
+ _update_debug_shape_vertices();
+ }
+ } else if (debug_shape) {
_update_debug_shape();
}
}
@@ -146,6 +149,9 @@ bool RayCast3D::get_exclude_parent_body() const {
void RayCast3D::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
+ if (Engine::get_singleton()->is_editor_hint()) {
+ _update_debug_shape_vertices();
+ }
if (enabled && !Engine::get_singleton()->is_editor_hint()) {
set_physics_process_internal(true);
} else {
@@ -183,10 +189,7 @@ void RayCast3D::_notification(int p_what) {
bool prev_collision_state = collided;
_update_raycast_state();
if (prev_collision_state != collided && get_tree()->is_debugging_collisions_hint()) {
- if (debug_material.is_valid()) {
- Ref<StandardMaterial3D> line_material = static_cast<Ref<StandardMaterial3D>>(debug_material);
- line_material->set_albedo(collided ? Color(1.0, 0, 0) : Color(1.0, 0.8, 0.6));
- }
+ _update_debug_shape_material(true);
}
} break;
@@ -310,6 +313,12 @@ void RayCast3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_collide_with_bodies", "enable"), &RayCast3D::set_collide_with_bodies);
ClassDB::bind_method(D_METHOD("is_collide_with_bodies_enabled"), &RayCast3D::is_collide_with_bodies_enabled);
+ ClassDB::bind_method(D_METHOD("set_debug_shape_custom_color", "debug_shape_custom_color"), &RayCast3D::set_debug_shape_custom_color);
+ ClassDB::bind_method(D_METHOD("get_debug_shape_custom_color"), &RayCast3D::get_debug_shape_custom_color);
+
+ ClassDB::bind_method(D_METHOD("set_debug_shape_thickness", "debug_shape_thickness"), &RayCast3D::set_debug_shape_thickness);
+ ClassDB::bind_method(D_METHOD("get_debug_shape_thickness"), &RayCast3D::get_debug_shape_thickness);
+
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "is_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "exclude_parent"), "set_exclude_parent_body", "get_exclude_parent_body");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "target_position"), "set_target_position", "get_target_position");
@@ -318,16 +327,80 @@ void RayCast3D::_bind_methods() {
ADD_GROUP("Collide With", "collide_with");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collide_with_areas", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collide_with_areas", "is_collide_with_areas_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collide_with_bodies", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collide_with_bodies", "is_collide_with_bodies_enabled");
+
+ ADD_GROUP("Debug Shape", "debug_shape");
+ ADD_PROPERTY(PropertyInfo(Variant::COLOR, "debug_shape_custom_color"), "set_debug_shape_custom_color", "get_debug_shape_custom_color");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "debug_shape_thickness", PROPERTY_HINT_RANGE, "1,5"), "set_debug_shape_thickness", "get_debug_shape_thickness");
}
-void RayCast3D::_create_debug_shape() {
- if (!debug_material.is_valid()) {
- debug_material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D));
+float RayCast3D::get_debug_shape_thickness() const {
+ return debug_shape_thickness;
+}
+
+void RayCast3D::_update_debug_shape_vertices() {
+ debug_shape_vertices.clear();
+ debug_line_vertices.clear();
+
+ if (target_position == Vector3()) {
+ return;
+ }
+
+ debug_line_vertices.push_back(Vector3());
+ debug_line_vertices.push_back(target_position);
+
+ if (debug_shape_thickness > 1) {
+ float scale_factor = 100.0;
+ Vector3 dir = Vector3(target_position).normalized();
+ // Draw truncated pyramid
+ Vector3 normal = (fabs(dir.x) + fabs(dir.y) > CMP_EPSILON) ? Vector3(-dir.y, dir.x, 0).normalized() : Vector3(0, -dir.z, dir.y).normalized();
+ normal *= debug_shape_thickness / scale_factor;
+ int vertices_strip_order[14] = { 4, 5, 0, 1, 2, 5, 6, 4, 7, 0, 3, 2, 7, 6 };
+ for (int v = 0; v < 14; v++) {
+ Vector3 vertex = vertices_strip_order[v] < 4 ? normal : normal / 3.0 + target_position;
+ debug_shape_vertices.push_back(vertex.rotated(dir, Math_PI * (0.5 * (vertices_strip_order[v] % 4) + 0.25)));
+ }
+ }
+}
+
+void RayCast3D::set_debug_shape_thickness(const float p_debug_shape_thickness) {
+ debug_shape_thickness = p_debug_shape_thickness;
+ update_gizmo();
+
+ if (Engine::get_singleton()->is_editor_hint()) {
+ if (is_inside_tree()) {
+ _update_debug_shape_vertices();
+ }
+ } else if (debug_shape) {
+ _update_debug_shape();
+ }
+}
+
+const Vector<Vector3> &RayCast3D::get_debug_shape_vertices() const {
+ return debug_shape_vertices;
+}
- Ref<StandardMaterial3D> line_material = static_cast<Ref<StandardMaterial3D>>(debug_material);
- line_material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
- line_material->set_albedo(Color(1.0, 0.8, 0.6));
+const Vector<Vector3> &RayCast3D::get_debug_line_vertices() const {
+ return debug_line_vertices;
+}
+
+void RayCast3D::set_debug_shape_custom_color(const Color &p_color) {
+ debug_shape_custom_color = p_color;
+ if (debug_material.is_valid()) {
+ _update_debug_shape_material();
}
+}
+
+Ref<StandardMaterial3D> RayCast3D::get_debug_material() {
+ _update_debug_shape_material();
+ return debug_material;
+}
+
+const Color &RayCast3D::get_debug_shape_custom_color() const {
+ return debug_shape_custom_color;
+}
+
+void RayCast3D::_create_debug_shape() {
+ _update_debug_shape_material();
Ref<ArrayMesh> mesh = memnew(ArrayMesh);
@@ -338,6 +411,35 @@ void RayCast3D::_create_debug_shape() {
debug_shape = mi;
}
+void RayCast3D::_update_debug_shape_material(bool p_check_collision) {
+ if (!debug_material.is_valid()) {
+ Ref<StandardMaterial3D> material = memnew(StandardMaterial3D);
+ debug_material = material;
+
+ material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
+ material->set_transparency(BaseMaterial3D::TRANSPARENCY_ALPHA);
+ }
+
+ Color color = debug_shape_custom_color;
+ if (color == Color(0.0, 0.0, 0.0)) {
+ // Use the default debug shape color defined in the Project Settings.
+ color = get_tree()->get_debug_collisions_color();
+ }
+
+ if (p_check_collision) {
+ if ((color.get_h() < 0.055 || color.get_h() > 0.945) && color.get_s() > 0.5 && color.get_v() > 0.5) {
+ // If base color is already quite reddish, hightlight collision with green color
+ color = Color(0.0, 1.0, 0.0, color.a);
+ } else {
+ // Else, hightlight collision with red color
+ color = Color(1.0, 0, 0, color.a);
+ }
+ }
+
+ Ref<StandardMaterial3D> material = static_cast<Ref<StandardMaterial3D>>(debug_material);
+ material->set_albedo(color);
+}
+
void RayCast3D::_update_debug_shape() {
if (!enabled) {
return;
@@ -353,26 +455,28 @@ void RayCast3D::_update_debug_shape() {
return;
}
- Vector<Vector3> verts;
- verts.push_back(Vector3());
- verts.push_back(target_position);
+ _update_debug_shape_vertices();
- if (mesh->get_surface_count() == 0) {
- Array a;
- a.resize(Mesh::ARRAY_MAX);
- a[Mesh::ARRAY_VERTEX] = verts;
+ mesh->clear_surfaces();
- uint32_t flags = Mesh::ARRAY_FLAG_USE_DYNAMIC_UPDATE;
+ Array a;
+ a.resize(Mesh::ARRAY_MAX);
+ uint32_t flags = 0;
+ int surface_count = 0;
+
+ if (!debug_line_vertices.is_empty()) {
+ a[Mesh::ARRAY_VERTEX] = debug_line_vertices;
mesh->add_surface_from_arrays(Mesh::PRIMITIVE_LINES, a, Array(), Dictionary(), flags);
- mesh->surface_set_material(0, debug_material);
- } else {
- Vector<uint8_t> byte_array;
- int array_size = sizeof(Vector3) * verts.size();
- byte_array.resize(array_size);
- copymem(byte_array.ptrw(), verts.ptr(), array_size);
+ mesh->surface_set_material(surface_count, debug_material);
+ ++surface_count;
+ }
- RS::get_singleton()->mesh_surface_update_region(mesh->get_rid(), 0, 0, byte_array);
+ if (!debug_shape_vertices.is_empty()) {
+ a[Mesh::ARRAY_VERTEX] = debug_shape_vertices;
+ mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLE_STRIP, a, Array(), Dictionary(), flags);
+ mesh->surface_set_material(surface_count, debug_material);
+ ++surface_count;
}
}
diff --git a/scene/3d/ray_cast_3d.h b/scene/3d/ray_cast_3d.h
index ae92189527..968cede9f2 100644
--- a/scene/3d/ray_cast_3d.h
+++ b/scene/3d/ray_cast_3d.h
@@ -51,9 +51,15 @@ class RayCast3D : public Node3D {
Node *debug_shape = nullptr;
Ref<Material> debug_material;
+ Color debug_shape_custom_color = Color(0.0, 0.0, 0.0);
+ int debug_shape_thickness = 2;
+ Vector<Vector3> debug_shape_vertices;
+ Vector<Vector3> debug_line_vertices;
void _create_debug_shape();
void _update_debug_shape();
+ void _update_debug_shape_material(bool p_check_collision = false);
+ void _update_debug_shape_vertices();
void _clear_debug_shape();
bool collide_with_areas = false;
@@ -86,6 +92,17 @@ public:
void set_exclude_parent_body(bool p_exclude_parent_body);
bool get_exclude_parent_body() const;
+ const Color &get_debug_shape_custom_color() const;
+ void set_debug_shape_custom_color(const Color &p_color);
+
+ const Vector<Vector3> &get_debug_shape_vertices() const;
+ const Vector<Vector3> &get_debug_line_vertices() const;
+
+ Ref<StandardMaterial3D> get_debug_material();
+
+ float get_debug_shape_thickness() const;
+ void set_debug_shape_thickness(const float p_debug_thickness);
+
void force_raycast_update();
bool is_colliding() const;
Object *get_collider() const;
diff --git a/scene/3d/sprite_3d.cpp b/scene/3d/sprite_3d.cpp
index cb2df9130f..f881181ccd 100644
--- a/scene/3d/sprite_3d.cpp
+++ b/scene/3d/sprite_3d.cpp
@@ -518,6 +518,7 @@ void Sprite3D::set_region(bool p_region) {
region = p_region;
_queue_update();
+ notify_property_list_changed();
}
bool Sprite3D::is_region() const {
@@ -623,6 +624,12 @@ void Sprite3D::_validate_property(PropertyInfo &property) const {
if (property.name == "frame_coords") {
property.usage |= PROPERTY_USAGE_KEYING_INCREMENTS;
}
+
+#ifdef TOOLS_ENABLED
+ if (!region && property.name == "region_rect") {
+ property.usage = PROPERTY_USAGE_NOEDITOR;
+ }
+#endif
}
void Sprite3D::_bind_methods() {
diff --git a/scene/animation/tween.cpp b/scene/animation/tween.cpp
index 62d03ea80c..eb35979a47 100644
--- a/scene/animation/tween.cpp
+++ b/scene/animation/tween.cpp
@@ -869,8 +869,21 @@ void Tween::start() {
return;
}
+ pending_update++;
+ for (List<InterpolateData>::Element *E = interpolates.front(); E; E = E->next()) {
+ InterpolateData &data = E->get();
+ data.active = true;
+ }
+ pending_update--;
+
// We want to be activated
set_active(true);
+
+ // Don't resume from current position if stop_all() function has been used
+ if (was_stopped) {
+ seek(0);
+ }
+ was_stopped = false;
}
void Tween::reset(Object *p_object, StringName p_key) {
@@ -939,7 +952,7 @@ void Tween::stop(Object *p_object, StringName p_key) {
void Tween::stop_all() {
// We no longer need to be active since all tweens have been stopped
set_active(false);
-
+ was_stopped = true;
// For each interpolation...
pending_update++;
for (List<InterpolateData>::Element *E = interpolates.front(); E; E = E->next()) {
@@ -1350,6 +1363,9 @@ void Tween::interpolate_property(Object *p_object, NodePath p_property, Variant
return;
}
+ // Check that the target object is valid
+ ERR_FAIL_COND_MSG(p_object == nullptr, vformat("The Tween \"%s\"'s target node is `null`. Is the node reference correct?", get_name()));
+
// Get the property from the node path
p_property = p_property.get_as_property_path();
@@ -1378,6 +1394,9 @@ void Tween::interpolate_method(Object *p_object, StringName p_method, Variant p_
return;
}
+ // Check that the target object is valid
+ ERR_FAIL_COND_MSG(p_object == nullptr, vformat("The Tween \"%s\"'s target node is `null`. Is the node reference correct?", get_name()));
+
// Convert any integers into REALs as they are better for interpolation
if (p_initial_val.get_type() == Variant::INT) {
p_initial_val = p_initial_val.operator real_t();
diff --git a/scene/animation/tween.h b/scene/animation/tween.h
index 88c02be0bc..142c0c65e0 100644
--- a/scene/animation/tween.h
+++ b/scene/animation/tween.h
@@ -107,6 +107,7 @@ private:
float speed_scale = 1.0;
mutable int pending_update = 0;
int uid = 0;
+ bool was_stopped = false;
List<InterpolateData> interpolates;
diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp
index 0c104bf318..bff3024e38 100644
--- a/scene/gui/control.cpp
+++ b/scene/gui/control.cpp
@@ -93,7 +93,7 @@ void Control::_edit_set_state(const Dictionary &p_state) {
void Control::_edit_set_position(const Point2 &p_position) {
#ifdef TOOLS_ENABLED
- set_position(p_position, CanvasItemEditor::get_singleton()->is_anchors_mode_enabled());
+ set_position(p_position, CanvasItemEditor::get_singleton()->is_anchors_mode_enabled() && Object::cast_to<Control>(data.parent));
#else
// Unlikely to happen. TODO: enclose all _edit_ functions into TOOLS_ENABLED
set_position(p_position);
diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp
index 70015bcf88..331f0380c5 100644
--- a/scene/gui/graph_edit.cpp
+++ b/scene/gui/graph_edit.cpp
@@ -530,14 +530,14 @@ bool GraphEdit::_filter_input(const Point2 &p_point) {
for (int j = 0; j < gn->get_connection_output_count(); j++) {
Vector2 pos = gn->get_connection_output_position(j) + gn->get_position();
- if (is_in_hot_zone(pos, p_point)) {
+ if (is_in_hot_zone(pos / zoom, p_point / zoom)) {
return true;
}
}
for (int j = 0; j < gn->get_connection_input_count(); j++) {
Vector2 pos = gn->get_connection_input_position(j) + gn->get_position();
- if (is_in_hot_zone(pos, p_point)) {
+ if (is_in_hot_zone(pos / zoom, p_point / zoom)) {
return true;
}
}
@@ -551,7 +551,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
if (mb.is_valid() && mb->get_button_index() == BUTTON_LEFT && mb->is_pressed()) {
connecting_valid = false;
Ref<Texture2D> port = get_theme_icon("port", "GraphNode");
- click_pos = mb->get_position();
+ click_pos = mb->get_position() / zoom;
for (int i = get_child_count() - 1; i >= 0; i--) {
GraphNode *gn = Object::cast_to<GraphNode>(get_child(i));
if (!gn) {
@@ -560,7 +560,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
for (int j = 0; j < gn->get_connection_output_count(); j++) {
Vector2 pos = gn->get_connection_output_position(j) + gn->get_position();
- if (is_in_hot_zone(pos, click_pos)) {
+ if (is_in_hot_zone(pos / zoom, click_pos)) {
if (valid_left_disconnect_types.has(gn->get_connection_output_type(j))) {
//check disconnect
for (List<Connection>::Element *E = connections.front(); E; E = E->next()) {
@@ -602,7 +602,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
for (int j = 0; j < gn->get_connection_input_count(); j++) {
Vector2 pos = gn->get_connection_input_position(j) + gn->get_position();
- if (is_in_hot_zone(pos, click_pos)) {
+ if (is_in_hot_zone(pos / zoom, click_pos)) {
if (right_disconnects || valid_right_disconnect_types.has(gn->get_connection_input_type(j))) {
//check disconnect
for (List<Connection>::Element *E = connections.front(); E; E = E->next()) {
@@ -651,11 +651,11 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
connecting_target = false;
top_layer->update();
minimap->update();
- connecting_valid = just_disconnected || click_pos.distance_to(connecting_to) > 20.0 * zoom;
+ connecting_valid = just_disconnected || click_pos.distance_to(connecting_to / zoom) > 20.0 * zoom;
if (connecting_valid) {
Ref<Texture2D> port = get_theme_icon("port", "GraphNode");
- Vector2 mpos = mm->get_position();
+ Vector2 mpos = mm->get_position() / zoom;
for (int i = get_child_count() - 1; i >= 0; i--) {
GraphNode *gn = Object::cast_to<GraphNode>(get_child(i));
if (!gn) {
@@ -666,7 +666,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
for (int j = 0; j < gn->get_connection_output_count(); j++) {
Vector2 pos = gn->get_connection_output_position(j) + gn->get_position();
int type = gn->get_connection_output_type(j);
- if ((type == connecting_type || valid_connection_types.has(ConnType(type, connecting_type))) && is_in_hot_zone(pos, mpos)) {
+ if ((type == connecting_type || valid_connection_types.has(ConnType(type, connecting_type))) && is_in_hot_zone(pos / zoom, mpos)) {
connecting_target = true;
connecting_to = pos;
connecting_target_to = gn->get_name();
@@ -678,7 +678,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
for (int j = 0; j < gn->get_connection_input_count(); j++) {
Vector2 pos = gn->get_connection_input_position(j) + gn->get_position();
int type = gn->get_connection_input_type(j);
- if ((type == connecting_type || valid_connection_types.has(ConnType(type, connecting_type))) && is_in_hot_zone(pos, mpos)) {
+ if ((type == connecting_type || valid_connection_types.has(ConnType(type, connecting_type))) && is_in_hot_zone(pos / zoom, mpos)) {
connecting_target = true;
connecting_to = pos;
connecting_target_to = gn->get_name();
@@ -760,6 +760,11 @@ bool GraphEdit::is_in_hot_zone(const Vector2 &pos, const Vector2 &p_mouse_pos) {
continue;
}
Rect2 rect = child->get_rect();
+
+ // To prevent intersections with other nodes.
+ rect.position *= zoom;
+ rect.size *= zoom;
+
if (rect.has_point(p_mouse_pos)) {
//check sub-controls
Vector2 subpos = p_mouse_pos - rect.position;
@@ -1209,7 +1214,7 @@ void GraphEdit::_gui_input(const Ref<InputEvent> &p_ev) {
continue;
}
- if (gn_selected->has_point(b->get_position() - gn_selected->get_position())) {
+ if (gn_selected->has_point((b->get_position() - gn_selected->get_position()) / zoom)) {
gn = gn_selected;
break;
}
@@ -1553,7 +1558,12 @@ bool GraphEdit::is_minimap_enabled() const {
}
void GraphEdit::_minimap_toggled() {
- minimap->update();
+ if (is_minimap_enabled()) {
+ minimap->set_visible(true);
+ minimap->update();
+ } else {
+ minimap->set_visible(false);
+ }
}
void GraphEdit::set_connection_lines_thickness(float p_thickness) {
diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp
index ba08aae8e3..3e8ebd2429 100644
--- a/scene/gui/line_edit.cpp
+++ b/scene/gui/line_edit.cpp
@@ -255,11 +255,30 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) {
selection.creating = true;
} else {
- if (b->is_doubleclick() && selecting_enabled) {
- selection.enabled = true;
- selection.begin = 0;
- selection.end = text.length();
- selection.doubleclick = true;
+ if (selecting_enabled) {
+ if (!b->is_doubleclick() && (OS::get_singleton()->get_ticks_msec() - selection.last_dblclk) < 600) {
+ // Triple-click select all.
+ selection.enabled = true;
+ selection.begin = 0;
+ selection.end = text.length();
+ selection.doubleclick = true;
+ selection.last_dblclk = 0;
+ cursor_pos = selection.begin;
+ } else if (b->is_doubleclick()) {
+ // Double-click select word.
+ Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text_rid);
+ for (int i = 0; i < words.size(); i++) {
+ if (words[i].x < cursor_pos && words[i].y > cursor_pos) {
+ selection.enabled = true;
+ selection.begin = words[i].x;
+ selection.end = words[i].y;
+ selection.doubleclick = true;
+ selection.last_dblclk = OS::get_singleton()->get_ticks_msec();
+ cursor_pos = selection.end;
+ break;
+ }
+ }
+ }
}
selection.drag_attempt = false;
diff --git a/scene/gui/line_edit.h b/scene/gui/line_edit.h
index cbadf818cd..f1d9de255a 100644
--- a/scene/gui/line_edit.h
+++ b/scene/gui/line_edit.h
@@ -136,6 +136,7 @@ private:
bool creating = false;
bool doubleclick = false;
bool drag_attempt = false;
+ uint64_t last_dblclk = 0;
} selection;
struct TextOperation {
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index a79c633502..682584d73f 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -1578,11 +1578,11 @@ void RichTextLabel::_gui_input(Ref<InputEvent> p_event) {
if (k->is_pressed()) {
bool handled = false;
- if (k->is_action("ui_pageup") && vscroll->is_visible_in_tree()) {
+ if (k->is_action("ui_page_up") && vscroll->is_visible_in_tree()) {
vscroll->set_value(vscroll->get_value() - vscroll->get_page());
handled = true;
}
- if (k->is_action("ui_pagedown") && vscroll->is_visible_in_tree()) {
+ if (k->is_action("ui_page_down") && vscroll->is_visible_in_tree()) {
vscroll->set_value(vscroll->get_value() + vscroll->get_page());
handled = true;
}
diff --git a/scene/gui/slider.cpp b/scene/gui/slider.cpp
index 8b07299e30..2239226c78 100644
--- a/scene/gui/slider.cpp
+++ b/scene/gui/slider.cpp
@@ -73,8 +73,10 @@ void Slider::_gui_input(Ref<InputEvent> p_event) {
}
} else if (scrollable) {
if (mb->is_pressed() && mb->get_button_index() == BUTTON_WHEEL_UP) {
+ grab_focus();
set_value(get_value() + get_step());
} else if (mb->is_pressed() && mb->get_button_index() == BUTTON_WHEEL_DOWN) {
+ grab_focus();
set_value(get_value() - get_step());
}
}
diff --git a/scene/gui/spin_box.cpp b/scene/gui/spin_box.cpp
index 2c9720e4b6..d82cc98e01 100644
--- a/scene/gui/spin_box.cpp
+++ b/scene/gui/spin_box.cpp
@@ -91,6 +91,14 @@ void SpinBox::_range_click_timeout() {
}
}
+void SpinBox::_release_mouse() {
+ if (drag.enabled) {
+ drag.enabled = false;
+ Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_VISIBLE);
+ warp_mouse(drag.capture_pos);
+ }
+}
+
void SpinBox::_gui_input(const Ref<InputEvent> &p_event) {
if (!is_editable()) {
return;
@@ -136,12 +144,7 @@ void SpinBox::_gui_input(const Ref<InputEvent> &p_event) {
if (mb.is_valid() && !mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) {
//set_default_cursor_shape(CURSOR_ARROW);
range_click_timer->stop();
-
- if (drag.enabled) {
- drag.enabled = false;
- Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_VISIBLE);
- warp_mouse(drag.capture_pos);
- }
+ _release_mouse();
drag.allowed = false;
}
@@ -199,6 +202,8 @@ void SpinBox::_notification(int p_what) {
} else if (p_what == NOTIFICATION_ENTER_TREE) {
_adjust_width_for_icon(get_theme_icon("updown"));
_value_changed(0);
+ } else if (p_what == NOTIFICATION_EXIT_TREE) {
+ _release_mouse();
} else if (p_what == NOTIFICATION_TRANSLATION_CHANGED) {
_value_changed(0);
} else if (p_what == NOTIFICATION_THEME_CHANGED) {
diff --git a/scene/gui/spin_box.h b/scene/gui/spin_box.h
index 4c3adf30e8..e116adb64c 100644
--- a/scene/gui/spin_box.h
+++ b/scene/gui/spin_box.h
@@ -43,6 +43,7 @@ class SpinBox : public Range {
Timer *range_click_timer;
void _range_click_timeout();
+ void _release_mouse();
void _text_entered(const String &p_string);
virtual void _value_changed(double) override;
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index 36aa18417d..5a7901c11b 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -412,25 +412,16 @@ void TextEdit::_update_selection_mode_word() {
_get_mouse_pos(Point2i(mp.x, mp.y), row, col);
String line = text[row];
- int beg = CLAMP(col, 0, line.length());
- // If its the first selection and on whitespace make sure we grab the word instead.
- if (!selection.active) {
- while (beg > 0 && line[beg] <= 32) {
- beg--;
- }
- }
+ int cursor_pos = CLAMP(col, 0, line.length());
+ int beg = cursor_pos;
int end = beg;
- bool symbol = beg < line.length() && _is_symbol(line[beg]);
-
- // Get the word end and begin points.
- while (beg > 0 && line[beg - 1] > 32 && (symbol == _is_symbol(line[beg - 1]))) {
- beg--;
- }
- while (end < line.length() && line[end + 1] > 32 && (symbol == _is_symbol(line[end + 1]))) {
- end++;
- }
- if (end < line.length()) {
- end += 1;
+ Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text.get_line_data(row)->get_rid());
+ for (int i = 0; i < words.size(); i++) {
+ if (words[i].x < cursor_pos && words[i].y > cursor_pos) {
+ beg = words[i].x;
+ end = words[i].y;
+ break;
+ }
}
// Initial selection.
diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp
index 17fe001b5d..6ac4d7fd2f 100644
--- a/scene/gui/tree.cpp
+++ b/scene/gui/tree.cpp
@@ -3354,10 +3354,10 @@ void Tree::item_selected(int p_column, TreeItem *p_item) {
void Tree::item_deselected(int p_column, TreeItem *p_item) {
if (selected_item == p_item) {
selected_item = nullptr;
- }
- if (selected_col == p_column) {
- selected_col = -1;
+ if (selected_col == p_column) {
+ selected_col = -1;
+ }
}
if (select_mode == SELECT_MULTI || select_mode == SELECT_SINGLE) {
diff --git a/scene/main/canvas_layer.cpp b/scene/main/canvas_layer.cpp
index 91daa08ff8..d9b29daf26 100644
--- a/scene/main/canvas_layer.cpp
+++ b/scene/main/canvas_layer.cpp
@@ -231,6 +231,7 @@ void CanvasLayer::set_follow_viewport(bool p_enable) {
follow_viewport = p_enable;
_update_follow_viewport();
+ notify_property_list_changed();
}
bool CanvasLayer::is_following_viewport() const {
@@ -257,6 +258,14 @@ void CanvasLayer::_update_follow_viewport(bool p_force_exit) {
}
}
+#ifdef TOOLS_ENABLED
+void CanvasLayer::_validate_property(PropertyInfo &property) const {
+ if (!follow_viewport && property.name == "follow_viewport_scale") {
+ property.usage = PROPERTY_USAGE_NOEDITOR;
+ }
+}
+#endif
+
void CanvasLayer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_layer", "layer"), &CanvasLayer::set_layer);
ClassDB::bind_method(D_METHOD("get_layer"), &CanvasLayer::get_layer);
diff --git a/scene/main/canvas_layer.h b/scene/main/canvas_layer.h
index 181d1dd659..b20b291367 100644
--- a/scene/main/canvas_layer.h
+++ b/scene/main/canvas_layer.h
@@ -61,6 +61,10 @@ class CanvasLayer : public Node {
void _update_locrotscale();
void _update_follow_viewport(bool p_force_exit = false);
+#ifdef TOOLS_ENABLED
+ void _validate_property(PropertyInfo &property) const override;
+#endif
+
protected:
void _notification(int p_what);
static void _bind_methods();
diff --git a/scene/main/http_request.cpp b/scene/main/http_request.cpp
index ce7d6ef13c..71c372aec2 100644
--- a/scene/main/http_request.cpp
+++ b/scene/main/http_request.cpp
@@ -387,6 +387,9 @@ bool HTTPRequest::_update_connection() {
}
client->poll();
+ if (client->get_status() != HTTPClient::STATUS_BODY) {
+ return false;
+ }
PackedByteArray chunk = client->read_response_body_chunk();
downloaded.add(chunk.size());
@@ -415,6 +418,7 @@ bool HTTPRequest::_update_connection() {
} else if (client->get_status() == HTTPClient::STATUS_DISCONNECTED) {
// We read till EOF, with no errors. Request is done.
call_deferred("_request_done", RESULT_SUCCESS, response_code, response_headers, body);
+ return true;
}
return false;
diff --git a/scene/main/node.cpp b/scene/main/node.cpp
index 893621fbc4..9d8c7981e6 100644
--- a/scene/main/node.cpp
+++ b/scene/main/node.cpp
@@ -1976,7 +1976,7 @@ bool Node::is_editable_instance(const Node *p_node) const {
Node *Node::get_deepest_editable_node(Node *p_start_node) const {
ERR_FAIL_NULL_V(p_start_node, nullptr);
- ERR_FAIL_COND_V(!is_a_parent_of(p_start_node), nullptr);
+ ERR_FAIL_COND_V(!is_a_parent_of(p_start_node), p_start_node);
Node const *iterated_item = p_start_node;
Node *node = p_start_node;
@@ -2052,6 +2052,7 @@ Node *Node::_duplicate(int p_flags, Map<const Node *, Node *> *r_duplimap) const
if (get_filename() != "") { //an instance
node->set_filename(get_filename());
+ node->data.editable_instance = data.editable_instance;
}
StringName script_property_name = CoreStringNames::get_singleton()->_script;
@@ -2267,74 +2268,6 @@ void Node::remap_nested_resources(RES p_resource, const Map<RES, RES> &p_resourc
}
#endif
-void Node::_duplicate_and_reown(Node *p_new_parent, const Map<Node *, Node *> &p_reown_map) const {
- if (get_owner() != get_parent()->get_owner()) {
- return;
- }
-
- Node *node = nullptr;
-
- if (get_filename() != "") {
- Ref<PackedScene> res = ResourceLoader::load(get_filename());
- ERR_FAIL_COND_MSG(res.is_null(), "Cannot load scene: " + get_filename());
- node = res->instance();
- ERR_FAIL_COND(!node);
- } else {
- Object *obj = ClassDB::instance(get_class());
- ERR_FAIL_COND_MSG(!obj, "Node: Could not duplicate: " + String(get_class()) + ".");
- node = Object::cast_to<Node>(obj);
- if (!node) {
- memdelete(obj);
- ERR_FAIL_MSG("Node: Could not duplicate: " + String(get_class()) + ".");
- }
- }
-
- List<PropertyInfo> plist;
-
- get_property_list(&plist);
-
- for (List<PropertyInfo>::Element *E = plist.front(); E; E = E->next()) {
- if (!(E->get().usage & PROPERTY_USAGE_STORAGE)) {
- continue;
- }
- String name = E->get().name;
-
- Variant value = get(name).duplicate(true);
-
- node->set(name, value);
- }
-
- List<GroupInfo> groups;
- get_groups(&groups);
-
- for (List<GroupInfo>::Element *E = groups.front(); E; E = E->next()) {
- node->add_to_group(E->get().name, E->get().persistent);
- }
-
- node->set_name(get_name());
- p_new_parent->add_child(node);
-
- Node *owner = get_owner();
-
- if (p_reown_map.has(owner)) {
- owner = p_reown_map[owner];
- }
-
- if (owner) {
- NodePath p = get_path_to(owner);
- if (owner != this) {
- Node *new_owner = node->get_node(p);
- if (new_owner) {
- node->set_owner(new_owner);
- }
- }
- }
-
- for (int i = 0; i < get_child_count(); i++) {
- get_child(i)->_duplicate_and_reown(node, p_reown_map);
- }
-}
-
// Duplication of signals must happen after all the node descendants have been copied,
// because re-targeting of connections from some descendant to another is not possible
// if the emitter node comes later in tree order than the receiver
@@ -2389,49 +2322,6 @@ void Node::_duplicate_signals(const Node *p_original, Node *p_copy) const {
}
}
-Node *Node::duplicate_and_reown(const Map<Node *, Node *> &p_reown_map) const {
- ERR_FAIL_COND_V(get_filename() != "", nullptr);
-
- Object *obj = ClassDB::instance(get_class());
- ERR_FAIL_COND_V_MSG(!obj, nullptr, "Node: Could not duplicate: " + String(get_class()) + ".");
-
- Node *node = Object::cast_to<Node>(obj);
- if (!node) {
- memdelete(obj);
- ERR_FAIL_V_MSG(nullptr, "Node: Could not duplicate: " + String(get_class()) + ".");
- }
- node->set_name(get_name());
-
- List<PropertyInfo> plist;
-
- get_property_list(&plist);
-
- for (List<PropertyInfo>::Element *E = plist.front(); E; E = E->next()) {
- if (!(E->get().usage & PROPERTY_USAGE_STORAGE)) {
- continue;
- }
- String name = E->get().name;
- node->set(name, get(name));
- }
-
- List<GroupInfo> groups;
- get_groups(&groups);
-
- for (List<GroupInfo>::Element *E = groups.front(); E; E = E->next()) {
- node->add_to_group(E->get().name, E->get().persistent);
- }
-
- for (int i = 0; i < get_child_count(); i++) {
- get_child(i)->_duplicate_and_reown(node, p_reown_map);
- }
-
- // Duplication of signals must happen after all the node descendants have been copied,
- // because re-targeting of connections from some descendant to another is not possible
- // if the emitter node comes later in tree order than the receiver
- _duplicate_signals(this, node);
- return node;
-}
-
static void find_owned_by(Node *p_by, Node *p_node, List<Node *> *p_owned) {
if (p_node->get_owner() == p_by) {
p_owned->push_back(p_node);
diff --git a/scene/main/node.h b/scene/main/node.h
index 249a0ff86e..d47d271a10 100644
--- a/scene/main/node.h
+++ b/scene/main/node.h
@@ -173,7 +173,6 @@ private:
Array _get_node_and_resource(const NodePath &p_path);
void _duplicate_signals(const Node *p_original, Node *p_copy) const;
- void _duplicate_and_reown(Node *p_new_parent, const Map<Node *, Node *> &p_reown_map) const;
Node *_duplicate(int p_flags, Map<const Node *, Node *> *r_duplimap = nullptr) const;
TypedArray<Node> _get_children() const;
@@ -366,7 +365,6 @@ public:
bool is_processing_unhandled_key_input() const;
Node *duplicate(int p_flags = DUPLICATE_GROUPS | DUPLICATE_SIGNALS | DUPLICATE_SCRIPTS) const;
- Node *duplicate_and_reown(const Map<Node *, Node *> &p_reown_map) const;
#ifdef TOOLS_ENABLED
Node *duplicate_from_editor(Map<const Node *, Node *> &r_duplimap) const;
Node *duplicate_from_editor(Map<const Node *, Node *> &r_duplimap, const Map<RES, RES> &p_resource_remap) const;
diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp
index b10e23ac07..9aaddfd373 100644
--- a/scene/main/scene_tree.cpp
+++ b/scene/main/scene_tree.cpp
@@ -54,6 +54,7 @@
#include "window.h"
#include <stdio.h>
+#include <stdlib.h>
void SceneTreeTimer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_time_left", "time"), &SceneTreeTimer::set_time_left);
@@ -534,12 +535,7 @@ void SceneTree::finalize() {
}
void SceneTree::quit(int p_exit_code) {
- if (p_exit_code >= 0) {
- // Override the exit code if a positive argument is given (the default is `-1`).
- // This is a shorthand for calling `set_exit_code()` on the OS singleton then quitting.
- OS::get_singleton()->set_exit_code(p_exit_code);
- }
-
+ OS::get_singleton()->set_exit_code(p_exit_code);
_quit = true;
}
@@ -1205,7 +1201,7 @@ void SceneTree::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_node_count"), &SceneTree::get_node_count);
ClassDB::bind_method(D_METHOD("get_frame"), &SceneTree::get_frame);
- ClassDB::bind_method(D_METHOD("quit", "exit_code"), &SceneTree::quit, DEFVAL(-1));
+ ClassDB::bind_method(D_METHOD("quit", "exit_code"), &SceneTree::quit, DEFVAL(EXIT_SUCCESS));
ClassDB::bind_method(D_METHOD("queue_delete", "obj"), &SceneTree::queue_delete);
@@ -1350,6 +1346,8 @@ SceneTree::SceneTree() {
collision_debug_contacts = GLOBAL_DEF("debug/shapes/collision/max_contacts_displayed", 10000);
ProjectSettings::get_singleton()->set_custom_property_info("debug/shapes/collision/max_contacts_displayed", PropertyInfo(Variant::INT, "debug/shapes/collision/max_contacts_displayed", PROPERTY_HINT_RANGE, "0,20000,1")); // No negative
+ GLOBAL_DEF("debug/shapes/collision/draw_2d_outlines", true);
+
// Create with mainloop.
root = memnew(Window);
diff --git a/scene/main/scene_tree.h b/scene/main/scene_tree.h
index c2280c747b..a2f2adb8f8 100644
--- a/scene/main/scene_tree.h
+++ b/scene/main/scene_tree.h
@@ -245,7 +245,7 @@ public:
void set_auto_accept_quit(bool p_enable);
void set_quit_on_go_back(bool p_enable);
- void quit(int p_exit_code = -1);
+ void quit(int p_exit_code = EXIT_SUCCESS);
_FORCE_INLINE_ float get_physics_process_time() const { return physics_process_time; }
_FORCE_INLINE_ float get_process_time() const { return process_time; }
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index 54b670df6c..40b85e6d7b 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -1770,19 +1770,22 @@ Control *Viewport::_gui_find_control_at_pos(CanvasItem *p_node, const Point2 &p_
}
}
- if (!c) {
+ if (!c || c->data.mouse_filter == Control::MOUSE_FILTER_IGNORE) {
return nullptr;
}
matrix.affine_invert();
+ if (!c->has_point(matrix.xform(p_global))) {
+ return nullptr;
+ }
- //conditions for considering this as a valid control for return
- if (c->data.mouse_filter != Control::MOUSE_FILTER_IGNORE && c->has_point(matrix.xform(p_global)) && (!gui.drag_preview || (c != gui.drag_preview && !gui.drag_preview->is_a_parent_of(c)))) {
+ Control *drag_preview = _gui_get_drag_preview();
+ if (!drag_preview || (c != drag_preview && !drag_preview->is_a_parent_of(c))) {
r_inv_xform = matrix;
return c;
- } else {
- return nullptr;
}
+
+ return nullptr;
}
bool Viewport::_gui_drop(Control *p_at_control, Point2 p_at_pos, bool p_just_check) {
@@ -1920,9 +1923,10 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
gui.drag_data = Variant();
gui.dragging = false;
- if (gui.drag_preview) {
- memdelete(gui.drag_preview);
- gui.drag_preview = nullptr;
+ Control *drag_preview = _gui_get_drag_preview();
+ if (drag_preview) {
+ memdelete(drag_preview);
+ gui.drag_preview_id = ObjectID();
}
_propagate_viewport_notification(this, NOTIFICATION_DRAG_END);
//change mouse accordingly
@@ -1935,9 +1939,10 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
_gui_drop(gui.drag_mouse_over, gui.drag_mouse_over_pos, false);
}
- if (gui.drag_preview && mb->get_button_index() == BUTTON_LEFT) {
- memdelete(gui.drag_preview);
- gui.drag_preview = nullptr;
+ Control *drag_preview = _gui_get_drag_preview();
+ if (drag_preview) {
+ memdelete(drag_preview);
+ gui.drag_preview_id = ObjectID();
}
gui.drag_data = Variant();
@@ -2034,10 +2039,11 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
gui.mouse_focus_mask = 0;
break;
} else {
- if (gui.drag_preview != nullptr) {
+ Control *drag_preview = _gui_get_drag_preview();
+ if (drag_preview) {
ERR_PRINT("Don't set a drag preview and return null data. Preview was deleted and drag request ignored.");
- memdelete(gui.drag_preview);
- gui.drag_preview = nullptr;
+ memdelete(drag_preview);
+ gui.drag_preview_id = ObjectID();
}
gui.dragging = false;
}
@@ -2177,8 +2183,9 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
if (gui.drag_data.get_type() != Variant::NIL) {
//handle dragandrop
- if (gui.drag_preview) {
- gui.drag_preview->set_position(mpos);
+ Control *drag_preview = _gui_get_drag_preview();
+ if (drag_preview) {
+ drag_preview->set_position(mpos);
}
gui.drag_mouse_over = over;
@@ -2453,15 +2460,29 @@ void Viewport::_gui_set_drag_preview(Control *p_base, Control *p_control) {
ERR_FAIL_COND(p_control->is_inside_tree());
ERR_FAIL_COND(p_control->get_parent() != nullptr);
- if (gui.drag_preview) {
- memdelete(gui.drag_preview);
+ Control *drag_preview = _gui_get_drag_preview();
+ if (drag_preview) {
+ memdelete(drag_preview);
}
p_control->set_as_top_level(true);
p_control->set_position(gui.last_mouse_pos);
p_base->get_root_parent_control()->add_child(p_control); //add as child of viewport
p_control->raise();
- gui.drag_preview = p_control;
+ gui.drag_preview_id = p_control->get_instance_id();
+}
+
+Control *Viewport::_gui_get_drag_preview() {
+ if (gui.drag_preview_id.is_null()) {
+ return nullptr;
+ } else {
+ Control *drag_preview = Object::cast_to<Control>(ObjectDB::get_instance(gui.drag_preview_id));
+ if (!drag_preview) {
+ ERR_PRINT("Don't free the control set as drag preview.");
+ gui.drag_preview_id = ObjectID();
+ }
+ return drag_preview;
+ }
}
void Viewport::_gui_remove_root_control(List<Control *>::Element *RI) {
diff --git a/scene/main/viewport.h b/scene/main/viewport.h
index 2a0026a561..0f11e6fb19 100644
--- a/scene/main/viewport.h
+++ b/scene/main/viewport.h
@@ -357,7 +357,7 @@ private:
Point2 drag_accum;
bool drag_attempted = false;
Variant drag_data;
- Control *drag_preview = nullptr;
+ ObjectID drag_preview_id;
float tooltip_timer = -1.0;
float tooltip_delay = 0.0;
Transform2D focus_inv_xform;
@@ -415,6 +415,7 @@ private:
void _gui_force_drag(Control *p_base, const Variant &p_data, Control *p_control);
void _gui_set_drag_preview(Control *p_base, Control *p_control);
+ Control *_gui_get_drag_preview();
void _gui_remove_focus_for_window(Node *p_window);
void _gui_remove_focus();
diff --git a/scene/main/window.cpp b/scene/main/window.cpp
index 8198fa41c5..d697a1a5dd 100644
--- a/scene/main/window.cpp
+++ b/scene/main/window.cpp
@@ -826,6 +826,9 @@ bool Window::is_using_font_oversampling() const {
}
DisplayServer::WindowID Window::get_window_id() const {
+ if (embedder) {
+ return parent->get_window_id();
+ }
return window_id;
}
@@ -1035,6 +1038,9 @@ void Window::popup_centered_ratio(float p_ratio) {
void Window::popup(const Rect2i &p_screen_rect) {
emit_signal("about_to_popup");
+ // Update window size to calculate the actual window size based on contents minimum size and minimum size.
+ _update_window_size();
+
if (p_screen_rect != Rect2i()) {
set_position(p_screen_rect.position);
set_size(p_screen_rect.size);
diff --git a/scene/resources/animation.h b/scene/resources/animation.h
index fd22cc445c..66bc71c834 100644
--- a/scene/resources/animation.h
+++ b/scene/resources/animation.h
@@ -70,7 +70,7 @@ private:
bool loop_wrap = true;
NodePath path; // path to something
bool imported = false;
- bool enabled = false;
+ bool enabled = true;
Track() {}
virtual ~Track() {}
};
diff --git a/scene/resources/camera_effects.cpp b/scene/resources/camera_effects.cpp
index 4038338e1e..00312fc7b2 100644
--- a/scene/resources/camera_effects.cpp
+++ b/scene/resources/camera_effects.cpp
@@ -120,6 +120,7 @@ void CameraEffects::_update_dof_blur() {
void CameraEffects::set_override_exposure_enabled(bool p_enabled) {
override_exposure_enabled = p_enabled;
_update_override_exposure();
+ notify_property_list_changed();
}
bool CameraEffects::is_override_exposure_enabled() const {
@@ -144,6 +145,16 @@ void CameraEffects::_update_override_exposure() {
// Private methods, constructor and destructor
+#ifdef TOOLS_ENABLED
+void CameraEffects::_validate_property(PropertyInfo &property) const {
+ if ((!dof_blur_far_enabled && (property.name == "dof_blur_far_distance" || property.name == "dof_blur_far_transition")) ||
+ (!dof_blur_near_enabled && (property.name == "dof_blur_near_distance" || property.name == "dof_blur_near_transition")) ||
+ (!override_exposure_enabled && property.name == "override_exposure")) {
+ property.usage = PROPERTY_USAGE_NOEDITOR;
+ }
+}
+#endif
+
void CameraEffects::_bind_methods() {
// DOF blur
diff --git a/scene/resources/camera_effects.h b/scene/resources/camera_effects.h
index 28aa6b8660..51fb2b6cf7 100644
--- a/scene/resources/camera_effects.h
+++ b/scene/resources/camera_effects.h
@@ -57,6 +57,10 @@ private:
float override_exposure = 1.0;
void _update_override_exposure();
+#ifdef TOOLS_ENABLED
+ void _validate_property(PropertyInfo &property) const override;
+#endif
+
protected:
static void _bind_methods();
diff --git a/scene/resources/capsule_shape_2d.cpp b/scene/resources/capsule_shape_2d.cpp
index acf7319339..e5edba8a67 100644
--- a/scene/resources/capsule_shape_2d.cpp
+++ b/scene/resources/capsule_shape_2d.cpp
@@ -85,9 +85,11 @@ void CapsuleShape2D::draw(const RID &p_to_rid, const Color &p_color) {
Vector<Color> col;
col.push_back(p_color);
RenderingServer::get_singleton()->canvas_item_add_polygon(p_to_rid, points, col);
- RenderingServer::get_singleton()->canvas_item_add_polyline(p_to_rid, points, col);
- // Draw the last segment as it's not drawn by `canvas_item_add_polyline()`.
- RenderingServer::get_singleton()->canvas_item_add_line(p_to_rid, points[points.size() - 1], points[0], p_color);
+ if (is_collision_outline_enabled()) {
+ RenderingServer::get_singleton()->canvas_item_add_polyline(p_to_rid, points, col);
+ // Draw the last segment as it's not drawn by `canvas_item_add_polyline()`.
+ RenderingServer::get_singleton()->canvas_item_add_line(p_to_rid, points[points.size() - 1], points[0], p_color);
+ }
}
Rect2 CapsuleShape2D::get_rect() const {
diff --git a/scene/resources/circle_shape_2d.cpp b/scene/resources/circle_shape_2d.cpp
index a8a9c42fbd..f06bc4248d 100644
--- a/scene/resources/circle_shape_2d.cpp
+++ b/scene/resources/circle_shape_2d.cpp
@@ -79,9 +79,11 @@ void CircleShape2D::draw(const RID &p_to_rid, const Color &p_color) {
Vector<Color> col;
col.push_back(p_color);
RenderingServer::get_singleton()->canvas_item_add_polygon(p_to_rid, points, col);
- RenderingServer::get_singleton()->canvas_item_add_polyline(p_to_rid, points, col);
- // Draw the last segment as it's not drawn by `canvas_item_add_polyline()`.
- RenderingServer::get_singleton()->canvas_item_add_line(p_to_rid, points[points.size() - 1], points[0], p_color);
+ if (is_collision_outline_enabled()) {
+ RenderingServer::get_singleton()->canvas_item_add_polyline(p_to_rid, points, col);
+ // Draw the last segment as it's not drawn by `canvas_item_add_polyline()`.
+ RenderingServer::get_singleton()->canvas_item_add_line(p_to_rid, points[points.size() - 1], points[0], p_color);
+ }
}
CircleShape2D::CircleShape2D() :
diff --git a/scene/resources/convex_polygon_shape_2d.cpp b/scene/resources/convex_polygon_shape_2d.cpp
index 7271614995..6e56f6e7fc 100644
--- a/scene/resources/convex_polygon_shape_2d.cpp
+++ b/scene/resources/convex_polygon_shape_2d.cpp
@@ -72,12 +72,18 @@ void ConvexPolygonShape2D::_bind_methods() {
}
void ConvexPolygonShape2D::draw(const RID &p_to_rid, const Color &p_color) {
+ if (points.size() < 3) {
+ return;
+ }
+
Vector<Color> col;
col.push_back(p_color);
RenderingServer::get_singleton()->canvas_item_add_polygon(p_to_rid, points, col);
- RenderingServer::get_singleton()->canvas_item_add_polyline(p_to_rid, points, col);
- // Draw the last segment as it's not drawn by `canvas_item_add_polyline()`.
- RenderingServer::get_singleton()->canvas_item_add_line(p_to_rid, points[points.size() - 1], points[0], p_color);
+ if (is_collision_outline_enabled()) {
+ RenderingServer::get_singleton()->canvas_item_add_polyline(p_to_rid, points, col);
+ // Draw the last segment as it's not drawn by `canvas_item_add_polyline()`.
+ RenderingServer::get_singleton()->canvas_item_add_line(p_to_rid, points[points.size() - 1], points[0], p_color);
+ }
}
Rect2 ConvexPolygonShape2D::get_rect() const {
diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp
index 943176537b..a94209c75f 100644
--- a/scene/resources/default_theme/default_theme.cpp
+++ b/scene/resources/default_theme/default_theme.cpp
@@ -128,6 +128,38 @@ static Ref<Texture2D> flip_icon(Ref<Texture2D> p_texture, bool p_flip_y = false,
return texture;
}
+static Ref<FontData> make_font(int p_height, int p_ascent, int p_charcount, const int *p_char_rects, int p_kerning_count, const int *p_kernings, int p_w, int p_h, const unsigned char *p_img) {
+ Ref<FontData> font(memnew(FontData));
+ font->new_bitmap(p_height, p_ascent, p_height);
+
+ Ref<Image> image = memnew(Image(p_img));
+ Ref<ImageTexture> tex = memnew(ImageTexture);
+ tex->create_from_image(image);
+
+ font->bitmap_add_texture(tex);
+
+ for (int i = 0; i < p_charcount; i++) {
+ const int *c = &p_char_rects[i * 8];
+
+ int chr = c[0];
+ Rect2 frect;
+ frect.position.x = c[1];
+ frect.position.y = c[2];
+ frect.size.x = c[3];
+ frect.size.y = c[4];
+ Point2 align(c[6], c[5]);
+ int advance = c[7];
+
+ font->bitmap_add_char(chr, 0, frect, align, advance);
+ }
+
+ for (int i = 0; i < p_kerning_count; i++) {
+ font->bitmap_add_kerning_pair(p_kernings[i * 3 + 0], p_kernings[i * 3 + 1], p_kernings[i * 3 + 2]);
+ }
+
+ return font;
+}
+
static Ref<StyleBox> make_empty_stylebox(float p_margin_left = -1, float p_margin_top = -1, float p_margin_right = -1, float p_margin_bottom = -1) {
Ref<StyleBox> style(memnew(StyleBoxEmpty));
@@ -989,41 +1021,11 @@ void make_default_theme(bool p_hidpi, Ref<Font> p_font) {
if (p_font.is_valid()) {
default_font = p_font;
} else if (p_hidpi) {
- TextServer::BitmapFontData data;
- data.height = _hidpi_font_height;
- data.ascent = _hidpi_font_ascent;
- data.charcount = _hidpi_font_charcount;
- data.char_rects = &_hidpi_font_charrects[0][0];
- data.kerning_count = _hidpi_font_kerning_pair_count;
- data.kernings = &_hidpi_font_kerning_pairs[0][0];
- data.w = _hidpi_font_img_width;
- data.h = _hidpi_font_img_height;
- data.img = _hidpi_font_img_data;
-
- Ref<FontData> font_data;
- font_data.instance();
- font_data->load_memory((const uint8_t *)&data, sizeof(data), "fnt");
- default_font_size = font_data->get_base_size();
-
+ Ref<FontData> font_data = make_font(_hidpi_font_height, _hidpi_font_ascent, _hidpi_font_charcount, &_hidpi_font_charrects[0][0], _hidpi_font_kerning_pair_count, &_hidpi_font_kerning_pairs[0][0], _hidpi_font_img_width, _hidpi_font_img_height, _hidpi_font_img_data);
default_font.instance();
default_font->add_data(font_data);
} else {
- TextServer::BitmapFontData data;
- data.height = _lodpi_font_height;
- data.ascent = _lodpi_font_ascent;
- data.charcount = _lodpi_font_charcount;
- data.char_rects = &_lodpi_font_charrects[0][0];
- data.kerning_count = _lodpi_font_kerning_pair_count;
- data.kernings = &_lodpi_font_kerning_pairs[0][0];
- data.w = _lodpi_font_img_width;
- data.h = _lodpi_font_img_height;
- data.img = _lodpi_font_img_data;
-
- Ref<FontData> font_data;
- font_data.instance();
- font_data->load_memory((const uint8_t *)&data, sizeof(data), "fnt");
- default_font_size = font_data->get_base_size();
-
+ Ref<FontData> font_data = make_font(_lodpi_font_height, _lodpi_font_ascent, _lodpi_font_charcount, &_lodpi_font_charrects[0][0], _lodpi_font_kerning_pair_count, &_lodpi_font_kerning_pairs[0][0], _lodpi_font_img_width, _lodpi_font_img_height, _lodpi_font_img_data);
default_font.instance();
default_font->add_data(font_data);
}
diff --git a/scene/resources/environment.cpp b/scene/resources/environment.cpp
index 6f6af93848..c04b271d81 100644
--- a/scene/resources/environment.cpp
+++ b/scene/resources/environment.cpp
@@ -441,6 +441,7 @@ void Environment::_update_ssao() {
void Environment::set_sdfgi_enabled(bool p_enabled) {
sdfgi_enabled = p_enabled;
_update_sdfgi();
+ notify_property_list_changed();
}
bool Environment::is_sdfgi_enabled() const {
@@ -983,6 +984,7 @@ void Environment::_validate_property(PropertyInfo &property) const {
"auto_exposure_",
"ss_reflections_",
"ssao_",
+ "sdfgi_",
"glow_",
"adjustment_",
nullptr
diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp
index 702f2ed1c8..6f87c524d8 100644
--- a/scene/resources/font.cpp
+++ b/scene/resources/font.cpp
@@ -39,6 +39,11 @@
void FontData::_bind_methods() {
ClassDB::bind_method(D_METHOD("load_resource", "filename", "base_size"), &FontData::load_resource, DEFVAL(16));
ClassDB::bind_method(D_METHOD("load_memory", "data", "type", "base_size"), &FontData::_load_memory, DEFVAL(16));
+ ClassDB::bind_method(D_METHOD("new_bitmap", "height", "ascent", "base_size"), &FontData::new_bitmap);
+
+ ClassDB::bind_method(D_METHOD("bitmap_add_texture", "texture"), &FontData::bitmap_add_texture);
+ ClassDB::bind_method(D_METHOD("bitmap_add_char", "char", "texture_idx", "rect", "align", "advance"), &FontData::bitmap_add_char);
+ ClassDB::bind_method(D_METHOD("bitmap_add_kerning_pair", "A", "B", "kerning"), &FontData::bitmap_add_kerning_pair);
ClassDB::bind_method(D_METHOD("set_data_path", "path"), &FontData::set_data_path);
ClassDB::bind_method(D_METHOD("get_data_path"), &FontData::get_data_path);
@@ -229,6 +234,34 @@ void FontData::load_memory(const uint8_t *p_data, size_t p_size, const String &p
emit_changed();
}
+void FontData::new_bitmap(float p_height, float p_ascent, int p_base_size) {
+ if (rid != RID()) {
+ TS->free(rid);
+ }
+ rid = TS->create_font_bitmap(p_height, p_ascent, p_base_size);
+ path = TTR("(Bitmap: " + String::num_int64(rid.get_id(), 16, true) + ")");
+ base_size = TS->font_get_base_size(rid);
+ emit_changed();
+}
+
+void FontData::bitmap_add_texture(const Ref<Texture> &p_texture) {
+ if (rid != RID()) {
+ TS->font_bitmap_add_texture(rid, p_texture);
+ }
+}
+
+void FontData::bitmap_add_char(char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance) {
+ if (rid != RID()) {
+ TS->font_bitmap_add_char(rid, p_char, p_texture_idx, p_rect, p_align, p_advance);
+ }
+}
+
+void FontData::bitmap_add_kerning_pair(char32_t p_A, char32_t p_B, int p_kerning) {
+ if (rid != RID()) {
+ TS->font_bitmap_add_kerning_pair(rid, p_A, p_B, p_kerning);
+ }
+}
+
void FontData::set_data_path(const String &p_path) {
load_resource(p_path, base_size);
}
diff --git a/scene/resources/font.h b/scene/resources/font.h
index 56b5acde1a..200373aa8c 100644
--- a/scene/resources/font.h
+++ b/scene/resources/font.h
@@ -69,6 +69,12 @@ public:
void load_memory(const uint8_t *p_data, size_t p_size, const String &p_type, int p_base_size = 16);
void _load_memory(const PackedByteArray &p_data, const String &p_type, int p_base_size = 16);
+ void new_bitmap(float p_height, float p_ascent, int p_base_size = 16);
+
+ void bitmap_add_texture(const Ref<Texture> &p_texture);
+ void bitmap_add_char(char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance);
+ void bitmap_add_kerning_pair(char32_t p_A, char32_t p_B, int p_kerning);
+
void set_data_path(const String &p_path);
String get_data_path() const;
diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp
index 062d921855..9931757cc4 100644
--- a/scene/resources/material.cpp
+++ b/scene/resources/material.cpp
@@ -1584,7 +1584,7 @@ void BaseMaterial3D::set_flag(Flags p_flag, bool p_enabled) {
}
flags[p_flag] = p_enabled;
- if (p_flag == FLAG_USE_SHADOW_TO_OPACITY || p_flag == FLAG_USE_TEXTURE_REPEAT || p_flag == FLAG_SUBSURFACE_MODE_SKIN) {
+ if (p_flag == FLAG_USE_SHADOW_TO_OPACITY || p_flag == FLAG_USE_TEXTURE_REPEAT || p_flag == FLAG_SUBSURFACE_MODE_SKIN || p_flag == FLAG_USE_POINT_SIZE) {
notify_property_list_changed();
}
_queue_shader_change();
@@ -1650,7 +1650,7 @@ BaseMaterial3D::TextureFilter BaseMaterial3D::get_texture_filter() const {
void BaseMaterial3D::_validate_feature(const String &text, Feature feature, PropertyInfo &property) const {
if (property.name.begins_with(text) && property.name != text + "_enabled" && !features[feature]) {
- property.usage = 0;
+ property.usage = PROPERTY_USAGE_NOEDITOR;
}
}
@@ -1683,16 +1683,24 @@ void BaseMaterial3D::_validate_property(PropertyInfo &property) const {
property.usage = 0;
}
- if (property.name == "params_grow_amount" && !grow_enabled) {
- property.usage = 0;
+ if (property.name == "billboard_keep_scale" && billboard_mode == BILLBOARD_DISABLED) {
+ property.usage = PROPERTY_USAGE_NOEDITOR;
+ }
+
+ if (property.name == "grow_amount" && !grow_enabled) {
+ property.usage = PROPERTY_USAGE_NOEDITOR;
+ }
+
+ if (property.name == "point_size" && !flags[FLAG_USE_POINT_SIZE]) {
+ property.usage = PROPERTY_USAGE_NOEDITOR;
}
if (property.name == "proximity_fade_distance" && !proximity_fade_enabled) {
- property.usage = 0;
+ property.usage = PROPERTY_USAGE_NOEDITOR;
}
if ((property.name == "distance_fade_max_distance" || property.name == "distance_fade_min_distance") && distance_fade == DISTANCE_FADE_DISABLED) {
- property.usage = 0;
+ property.usage = PROPERTY_USAGE_NOEDITOR;
}
// you can only enable anti-aliasing (in mataerials) on alpha scissor and alpha hash
diff --git a/scene/resources/mesh_data_tool.cpp b/scene/resources/mesh_data_tool.cpp
index 1b82aca386..3fb4f8f211 100644
--- a/scene/resources/mesh_data_tool.cpp
+++ b/scene/resources/mesh_data_tool.cpp
@@ -50,6 +50,28 @@ Error MeshDataTool::create_from_surface(const Ref<ArrayMesh> &p_mesh, int p_surf
int vcount = varray.size();
ERR_FAIL_COND_V(vcount == 0, ERR_INVALID_PARAMETER);
+ Vector<int> indices;
+
+ if (arrays[Mesh::ARRAY_INDEX].get_type() != Variant::NIL) {
+ indices = arrays[Mesh::ARRAY_INDEX];
+ } else {
+ //make code simpler
+ indices.resize(vcount);
+ int *iw = indices.ptrw();
+ for (int i = 0; i < vcount; i++) {
+ iw[i] = i;
+ }
+ }
+
+ int icount = indices.size();
+ const int *r = indices.ptr();
+
+ ERR_FAIL_COND_V(icount == 0, ERR_INVALID_PARAMETER);
+ ERR_FAIL_COND_V(icount % 3, ERR_INVALID_PARAMETER);
+ for (int i = 0; i < icount; i++) {
+ ERR_FAIL_INDEX_V(r[i], vcount, ERR_INVALID_PARAMETER);
+ }
+
clear();
format = p_mesh->surface_get_format(p_surface);
material = p_mesh->surface_get_material(p_surface);
@@ -128,22 +150,6 @@ Error MeshDataTool::create_from_surface(const Ref<ArrayMesh> &p_mesh, int p_surf
vertices.write[i] = v;
}
- Vector<int> indices;
-
- if (arrays[Mesh::ARRAY_INDEX].get_type() != Variant::NIL) {
- indices = arrays[Mesh::ARRAY_INDEX];
- } else {
- //make code simpler
- indices.resize(vcount);
- int *iw = indices.ptrw();
- for (int i = 0; i < vcount; i++) {
- iw[i] = i;
- }
- }
-
- int icount = indices.size();
- const int *r = indices.ptr();
-
Map<Point2i, int> edge_indices;
for (int i = 0; i < icount; i += 3) {
diff --git a/scene/resources/navigation_mesh.cpp b/scene/resources/navigation_mesh.cpp
index 84f3c23f77..8c12f59a00 100644
--- a/scene/resources/navigation_mesh.cpp
+++ b/scene/resources/navigation_mesh.cpp
@@ -31,6 +31,8 @@
#include "navigation_mesh.h"
void NavigationMesh::create_from_mesh(const Ref<Mesh> &p_mesh) {
+ ERR_FAIL_COND(p_mesh.is_null());
+
vertices = Vector<Vector3>();
clear_polygons();
diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp
index 5d351f51f7..ab8a4b7934 100644
--- a/scene/resources/packed_scene.cpp
+++ b/scene/resources/packed_scene.cpp
@@ -147,15 +147,20 @@ Node *SceneState::instance(GenEditState p_edit_state) const {
}
#endif
}
- } else if (ClassDB::is_class_enabled(snames[n.type])) {
- //node belongs to this scene and must be created
- Object *obj = ClassDB::instance(snames[n.type]);
+ } else {
+ Object *obj = nullptr;
+
+ if (ClassDB::is_class_enabled(snames[n.type])) {
+ //node belongs to this scene and must be created
+ obj = ClassDB::instance(snames[n.type]);
+ }
+
if (!Object::cast_to<Node>(obj)) {
if (obj) {
memdelete(obj);
obj = nullptr;
}
- WARN_PRINT(String("Warning node of type " + snames[n.type].operator String() + " does not exist.").ascii().get_data());
+ WARN_PRINT(vformat("Node %s of type %s cannot be created. A placeholder will be created instead.", snames[n.name], snames[n.type]).ascii().get_data());
if (n.parent >= 0 && n.parent < nc && ret_nodes[n.parent]) {
if (Object::cast_to<Node3D>(ret_nodes[n.parent])) {
obj = memnew(Node3D);
@@ -172,10 +177,6 @@ Node *SceneState::instance(GenEditState p_edit_state) const {
}
node = Object::cast_to<Node>(obj);
-
- } else {
- //print_line("Class is disabled for: " + itos(n.type));
- //print_line("name: " + String(snames[n.type]));
}
if (node) {
diff --git a/scene/resources/rectangle_shape_2d.cpp b/scene/resources/rectangle_shape_2d.cpp
index 0fd65d8c72..dc4c6dc2d7 100644
--- a/scene/resources/rectangle_shape_2d.cpp
+++ b/scene/resources/rectangle_shape_2d.cpp
@@ -47,23 +47,25 @@ Vector2 RectangleShape2D::get_size() const {
}
void RectangleShape2D::draw(const RID &p_to_rid, const Color &p_color) {
- // Draw an outlined rectangle to make individual shapes easier to distinguish.
- Vector<Vector2> stroke_points;
- stroke_points.resize(5);
- stroke_points.write[0] = -size * 0.5;
- stroke_points.write[1] = Vector2(size.x, -size.y) * 0.5;
- stroke_points.write[2] = size * 0.5;
- stroke_points.write[3] = Vector2(-size.x, size.y) * 0.5;
- stroke_points.write[4] = -size * 0.5;
+ RenderingServer::get_singleton()->canvas_item_add_rect(p_to_rid, Rect2(-size * 0.5, size), p_color);
+ if (is_collision_outline_enabled()) {
+ // Draw an outlined rectangle to make individual shapes easier to distinguish.
+ Vector<Vector2> stroke_points;
+ stroke_points.resize(5);
+ stroke_points.write[0] = -size * 0.5;
+ stroke_points.write[1] = Vector2(size.x, -size.y) * 0.5;
+ stroke_points.write[2] = size * 0.5;
+ stroke_points.write[3] = Vector2(-size.x, size.y) * 0.5;
+ stroke_points.write[4] = -size * 0.5;
- Vector<Color> stroke_colors;
- stroke_colors.resize(5);
- for (int i = 0; i < 5; i++) {
- stroke_colors.write[i] = (p_color);
- }
+ Vector<Color> stroke_colors;
+ stroke_colors.resize(5);
+ for (int i = 0; i < 5; i++) {
+ stroke_colors.write[i] = (p_color);
+ }
- RenderingServer::get_singleton()->canvas_item_add_rect(p_to_rid, Rect2(-size * 0.5, size), p_color);
- RenderingServer::get_singleton()->canvas_item_add_polyline(p_to_rid, stroke_points, stroke_colors);
+ RenderingServer::get_singleton()->canvas_item_add_polyline(p_to_rid, stroke_points, stroke_colors);
+ }
}
Rect2 RectangleShape2D::get_rect() const {
diff --git a/scene/resources/shape_2d.cpp b/scene/resources/shape_2d.cpp
index f8a5855d33..013b1ef1a9 100644
--- a/scene/resources/shape_2d.cpp
+++ b/scene/resources/shape_2d.cpp
@@ -29,7 +29,11 @@
/*************************************************************************/
#include "shape_2d.h"
+
+#include "core/config/engine.h"
+#include "core/config/project_settings.h"
#include "servers/physics_server_2d.h"
+
RID Shape2D::get_rid() const {
return shape;
}
@@ -105,6 +109,15 @@ void Shape2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "custom_solver_bias", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_custom_solver_bias", "get_custom_solver_bias");
}
+bool Shape2D::is_collision_outline_enabled() {
+#ifdef TOOLS_ENABLED
+ if (Engine::get_singleton()->is_editor_hint()) {
+ return true;
+ }
+#endif
+ return GLOBAL_DEF("debug/shapes/collision/draw_2d_outlines", true);
+}
+
Shape2D::Shape2D(const RID &p_rid) {
shape = p_rid;
}
diff --git a/scene/resources/shape_2d.h b/scene/resources/shape_2d.h
index 7b00e7e426..14bdd60e4b 100644
--- a/scene/resources/shape_2d.h
+++ b/scene/resources/shape_2d.h
@@ -61,6 +61,9 @@ public:
/// Returns the radius of a circle that fully enclose this shape
virtual real_t get_enclosing_radius() const = 0;
virtual RID get_rid() const override;
+
+ static bool is_collision_outline_enabled();
+
Shape2D();
~Shape2D();
};
diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp
index 8cccf81659..d2bb1338d8 100644
--- a/scene/resources/texture.cpp
+++ b/scene/resources/texture.cpp
@@ -151,7 +151,7 @@ void ImageTexture::_reload_hook(const RID &p_hook) {
}
void ImageTexture::create_from_image(const Ref<Image> &p_image) {
- ERR_FAIL_COND_MSG(p_image.is_null(), "Invalid image");
+ ERR_FAIL_COND_MSG(p_image.is_null() || p_image->is_empty(), "Invalid image");
w = p_image->get_width();
h = p_image->get_height();
format = p_image->get_format();
diff --git a/scene/resources/visual_shader_nodes.cpp b/scene/resources/visual_shader_nodes.cpp
index d6200059f6..a99c09e89c 100644
--- a/scene/resources/visual_shader_nodes.cpp
+++ b/scene/resources/visual_shader_nodes.cpp
@@ -2742,8 +2742,6 @@ int VisualShaderNodeClamp::get_input_port_count() const {
VisualShaderNodeClamp::PortType VisualShaderNodeClamp::get_input_port_type(int p_port) const {
switch (op_type) {
- case OP_TYPE_FLOAT:
- return PORT_TYPE_SCALAR;
case OP_TYPE_INT:
return PORT_TYPE_SCALAR_INT;
case OP_TYPE_VECTOR:
@@ -2771,8 +2769,6 @@ int VisualShaderNodeClamp::get_output_port_count() const {
VisualShaderNodeClamp::PortType VisualShaderNodeClamp::get_output_port_type(int p_port) const {
switch (op_type) {
- case OP_TYPE_FLOAT:
- return PORT_TYPE_SCALAR;
case OP_TYPE_INT:
return PORT_TYPE_SCALAR_INT;
case OP_TYPE_VECTOR:
@@ -2954,18 +2950,11 @@ int VisualShaderNodeStep::get_input_port_count() const {
VisualShaderNodeStep::PortType VisualShaderNodeStep::get_input_port_type(int p_port) const {
switch (op_type) {
- case OP_TYPE_SCALAR:
- return PORT_TYPE_SCALAR;
case OP_TYPE_VECTOR:
return PORT_TYPE_VECTOR;
case OP_TYPE_VECTOR_SCALAR:
- switch (p_port) {
- case 0:
- return PORT_TYPE_SCALAR;
- case 1:
- return PORT_TYPE_VECTOR;
- default:
- break;
+ if (p_port == 1) {
+ return PORT_TYPE_VECTOR;
}
break;
default:
@@ -2989,8 +2978,6 @@ int VisualShaderNodeStep::get_output_port_count() const {
VisualShaderNodeStep::PortType VisualShaderNodeStep::get_output_port_type(int p_port) const {
switch (op_type) {
- case OP_TYPE_SCALAR:
- return PORT_TYPE_SCALAR;
case OP_TYPE_VECTOR:
return PORT_TYPE_VECTOR;
case OP_TYPE_VECTOR_SCALAR:
@@ -3032,7 +3019,7 @@ void VisualShaderNodeStep::set_op_type(OpType p_op_type) {
set_input_port_default_value(0, 0.0); // edge
}
if (op_type == OP_TYPE_SCALAR) {
- set_input_port_default_value(1, 0.0); // x
+ set_input_port_default_value(1, Vector3(0.0, 0.0, 0.0)); // x
}
break;
default:
@@ -3085,20 +3072,11 @@ int VisualShaderNodeSmoothStep::get_input_port_count() const {
VisualShaderNodeSmoothStep::PortType VisualShaderNodeSmoothStep::get_input_port_type(int p_port) const {
switch (op_type) {
- case OP_TYPE_SCALAR:
- return PORT_TYPE_SCALAR;
case OP_TYPE_VECTOR:
return PORT_TYPE_VECTOR;
case OP_TYPE_VECTOR_SCALAR:
- switch (p_port) {
- case 0:
- return PORT_TYPE_SCALAR; // edge0
- case 1:
- return PORT_TYPE_SCALAR; // edge1
- case 2:
- return PORT_TYPE_VECTOR; // x
- default:
- break;
+ if (p_port == 2) {
+ return PORT_TYPE_VECTOR; // x
}
break;
default:
@@ -3124,8 +3102,6 @@ int VisualShaderNodeSmoothStep::get_output_port_count() const {
VisualShaderNodeSmoothStep::PortType VisualShaderNodeSmoothStep::get_output_port_type(int p_port) const {
switch (op_type) {
- case OP_TYPE_SCALAR:
- return PORT_TYPE_SCALAR;
case OP_TYPE_VECTOR:
return PORT_TYPE_VECTOR;
case OP_TYPE_VECTOR_SCALAR:
@@ -3319,16 +3295,11 @@ int VisualShaderNodeMix::get_input_port_count() const {
VisualShaderNodeMix::PortType VisualShaderNodeMix::get_input_port_type(int p_port) const {
switch (op_type) {
- case OP_TYPE_SCALAR:
- return PORT_TYPE_SCALAR;
case OP_TYPE_VECTOR:
- if (p_port == 2) {
- return PORT_TYPE_VECTOR;
- }
return PORT_TYPE_VECTOR;
case OP_TYPE_VECTOR_SCALAR:
if (p_port == 2) {
- return PORT_TYPE_SCALAR;
+ break;
}
return PORT_TYPE_VECTOR;
default:
@@ -3353,8 +3324,6 @@ int VisualShaderNodeMix::get_output_port_count() const {
VisualShaderNodeMix::PortType VisualShaderNodeMix::get_output_port_type(int p_port) const {
switch (op_type) {
- case OP_TYPE_SCALAR:
- return PORT_TYPE_SCALAR;
case OP_TYPE_VECTOR:
return PORT_TYPE_VECTOR;
case OP_TYPE_VECTOR_SCALAR:
@@ -4540,9 +4509,7 @@ int VisualShaderNodeTextureUniformTriplanar::get_input_port_count() const {
}
VisualShaderNodeTextureUniform::PortType VisualShaderNodeTextureUniformTriplanar::get_input_port_type(int p_port) const {
- if (p_port == 0) {
- return PORT_TYPE_VECTOR;
- } else if (p_port == 1) {
+ if (p_port == 0 || p_port == 1) {
return PORT_TYPE_VECTOR;
}
return PORT_TYPE_SCALAR;
@@ -4923,8 +4890,6 @@ VisualShaderNodeSwitch::PortType VisualShaderNodeSwitch::get_input_port_type(int
}
if (p_port == 1 || p_port == 2) {
switch (op_type) {
- case OP_TYPE_FLOAT:
- return PORT_TYPE_SCALAR;
case OP_TYPE_INT:
return PORT_TYPE_SCALAR_INT;
case OP_TYPE_VECTOR:
@@ -4959,8 +4924,6 @@ int VisualShaderNodeSwitch::get_output_port_count() const {
VisualShaderNodeSwitch::PortType VisualShaderNodeSwitch::get_output_port_type(int p_port) const {
switch (op_type) {
- case OP_TYPE_FLOAT:
- return PORT_TYPE_SCALAR;
case OP_TYPE_INT:
return PORT_TYPE_SCALAR_INT;
case OP_TYPE_VECTOR:
@@ -5238,9 +5201,6 @@ int VisualShaderNodeCompare::get_input_port_count() const {
}
VisualShaderNodeCompare::PortType VisualShaderNodeCompare::get_input_port_type(int p_port) const {
- if (p_port == 2) {
- return PORT_TYPE_SCALAR;
- }
switch (ctype) {
case CTYPE_SCALAR:
return PORT_TYPE_SCALAR;
@@ -5252,8 +5212,9 @@ VisualShaderNodeCompare::PortType VisualShaderNodeCompare::get_input_port_type(i
return PORT_TYPE_BOOLEAN;
case CTYPE_TRANSFORM:
return PORT_TYPE_TRANSFORM;
+ default:
+ return PORT_TYPE_SCALAR;
}
- return PORT_TYPE_VECTOR;
}
String VisualShaderNodeCompare::get_input_port_name(int p_port) const {
@@ -5472,10 +5433,10 @@ int VisualShaderNodeMultiplyAdd::get_input_port_count() const {
}
VisualShaderNodeMultiplyAdd::PortType VisualShaderNodeMultiplyAdd::get_input_port_type(int p_port) const {
- if (op_type == OP_TYPE_SCALAR) {
- return PORT_TYPE_SCALAR;
+ if (op_type == OP_TYPE_VECTOR) {
+ return PORT_TYPE_VECTOR;
}
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_SCALAR;
}
String VisualShaderNodeMultiplyAdd::get_input_port_name(int p_port) const {
diff --git a/servers/audio/effects/audio_effect_chorus.cpp b/servers/audio/effects/audio_effect_chorus.cpp
index 76a995eb37..eb2268aa2e 100644
--- a/servers/audio/effects/audio_effect_chorus.cpp
+++ b/servers/audio/effects/audio_effect_chorus.cpp
@@ -309,7 +309,7 @@ void AudioEffectChorus::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_dry", "amount"), &AudioEffectChorus::set_dry);
ClassDB::bind_method(D_METHOD("get_dry"), &AudioEffectChorus::get_dry);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "voice_count", PROPERTY_HINT_RANGE, "1,4,1"), "set_voice_count", "get_voice_count");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "voice_count", PROPERTY_HINT_RANGE, "1,4,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_voice_count", "get_voice_count");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dry", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_dry", "get_dry");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "wet", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_wet", "get_wet");
diff --git a/servers/physics_2d/joints_2d_sw.cpp b/servers/physics_2d/joints_2d_sw.cpp
index f503868ba5..c7b556deba 100644
--- a/servers/physics_2d/joints_2d_sw.cpp
+++ b/servers/physics_2d/joints_2d_sw.cpp
@@ -205,15 +205,6 @@ PinJoint2DSW::PinJoint2DSW(const Vector2 &p_pos, Body2DSW *p_body_a, Body2DSW *p
}
}
-PinJoint2DSW::~PinJoint2DSW() {
- if (A) {
- A->remove_constraint(this, 0);
- }
- if (B) {
- B->remove_constraint(this, 1);
- }
-}
-
//////////////////////////////////////////////
//////////////////////////////////////////////
//////////////////////////////////////////////
@@ -346,11 +337,6 @@ GrooveJoint2DSW::GrooveJoint2DSW(const Vector2 &p_a_groove1, const Vector2 &p_a_
B->add_constraint(this, 1);
}
-GrooveJoint2DSW::~GrooveJoint2DSW() {
- A->remove_constraint(this, 0);
- B->remove_constraint(this, 1);
-}
-
//////////////////////////////////////////////
//////////////////////////////////////////////
//////////////////////////////////////////////
@@ -442,8 +428,3 @@ DampedSpringJoint2DSW::DampedSpringJoint2DSW(const Vector2 &p_anchor_a, const Ve
A->add_constraint(this, 0);
B->add_constraint(this, 1);
}
-
-DampedSpringJoint2DSW::~DampedSpringJoint2DSW() {
- A->remove_constraint(this, 0);
- B->remove_constraint(this, 1);
-}
diff --git a/servers/physics_2d/joints_2d_sw.h b/servers/physics_2d/joints_2d_sw.h
index 6050dc2775..628de972ae 100644
--- a/servers/physics_2d/joints_2d_sw.h
+++ b/servers/physics_2d/joints_2d_sw.h
@@ -60,6 +60,15 @@ public:
bias = 0;
max_force = max_bias = 3.40282e+38;
};
+
+ virtual ~Joint2DSW() {
+ for (int i = 0; i < get_body_count(); i++) {
+ Body2DSW *body = get_body_ptr()[i];
+ if (body) {
+ body->remove_constraint(this, i);
+ }
+ }
+ };
};
class PinJoint2DSW : public Joint2DSW {
@@ -90,7 +99,6 @@ public:
real_t get_param(PhysicsServer2D::PinJointParam p_param) const;
PinJoint2DSW(const Vector2 &p_pos, Body2DSW *p_body_a, Body2DSW *p_body_b = nullptr);
- ~PinJoint2DSW();
};
class GrooveJoint2DSW : public Joint2DSW {
@@ -124,7 +132,6 @@ public:
virtual void solve(real_t p_step);
GrooveJoint2DSW(const Vector2 &p_a_groove1, const Vector2 &p_a_groove2, const Vector2 &p_b_anchor, Body2DSW *p_body_a, Body2DSW *p_body_b);
- ~GrooveJoint2DSW();
};
class DampedSpringJoint2DSW : public Joint2DSW {
@@ -160,7 +167,6 @@ public:
real_t get_param(PhysicsServer2D::DampedSpringParam p_param) const;
DampedSpringJoint2DSW(const Vector2 &p_anchor_a, const Vector2 &p_anchor_b, Body2DSW *p_body_a, Body2DSW *p_body_b);
- ~DampedSpringJoint2DSW();
};
#endif // JOINTS_2D_SW_H
diff --git a/servers/physics_2d/space_2d_sw.cpp b/servers/physics_2d/space_2d_sw.cpp
index 9cbc01d1d3..4f12248c3e 100644
--- a/servers/physics_2d/space_2d_sw.cpp
+++ b/servers/physics_2d/space_2d_sw.cpp
@@ -84,6 +84,10 @@ int PhysicsDirectSpaceState2DSW::_intersect_point_impl(const Vector2 &p_point, S
int shape_idx = space->intersection_query_subindex_results[i];
+ if (col_obj->is_shape_set_as_disabled(shape_idx)) {
+ continue;
+ }
+
Shape2DSW *shape = col_obj->get_shape(shape_idx);
Vector2 local_point = (col_obj->get_transform() * col_obj->get_shape_transform(shape_idx)).affine_inverse().xform(p_point);
@@ -229,6 +233,10 @@ int PhysicsDirectSpaceState2DSW::intersect_shape(const RID &p_shape, const Trans
const CollisionObject2DSW *col_obj = space->intersection_query_results[i];
int shape_idx = space->intersection_query_subindex_results[i];
+ if (col_obj->is_shape_set_as_disabled(shape_idx)) {
+ continue;
+ }
+
if (!CollisionSolver2DSW::solve(shape, p_xform, p_motion, col_obj->get_shape(shape_idx), col_obj->get_transform() * col_obj->get_shape_transform(shape_idx), Vector2(), nullptr, nullptr, nullptr, p_margin)) {
continue;
}
@@ -272,6 +280,10 @@ bool PhysicsDirectSpaceState2DSW::cast_motion(const RID &p_shape, const Transfor
const CollisionObject2DSW *col_obj = space->intersection_query_results[i];
int shape_idx = space->intersection_query_subindex_results[i];
+ if (col_obj->is_shape_set_as_disabled(shape_idx)) {
+ continue;
+ }
+
Transform2D col_obj_xform = col_obj->get_transform() * col_obj->get_shape_transform(shape_idx);
//test initial overlap, does it collide if going all the way?
if (!CollisionSolver2DSW::solve(shape, p_xform, p_motion, col_obj->get_shape(shape_idx), col_obj_xform, Vector2(), nullptr, nullptr, nullptr, p_margin)) {
@@ -346,12 +358,17 @@ bool PhysicsDirectSpaceState2DSW::collide_shape(RID p_shape, const Transform2D &
}
const CollisionObject2DSW *col_obj = space->intersection_query_results[i];
- int shape_idx = space->intersection_query_subindex_results[i];
if (p_exclude.has(col_obj->get_self())) {
continue;
}
+ int shape_idx = space->intersection_query_subindex_results[i];
+
+ if (col_obj->is_shape_set_as_disabled(shape_idx)) {
+ continue;
+ }
+
cbk.valid_dir = Vector2();
cbk.valid_depth = 0;
@@ -436,12 +453,17 @@ bool PhysicsDirectSpaceState2DSW::rest_info(RID p_shape, const Transform2D &p_sh
}
const CollisionObject2DSW *col_obj = space->intersection_query_results[i];
- int shape_idx = space->intersection_query_subindex_results[i];
if (p_exclude.has(col_obj->get_self())) {
continue;
}
+ int shape_idx = space->intersection_query_subindex_results[i];
+
+ if (col_obj->is_shape_set_as_disabled(shape_idx)) {
+ continue;
+ }
+
rcd.valid_dir = Vector2();
rcd.object = col_obj;
rcd.shape = shape_idx;
diff --git a/servers/physics_3d/collision_solver_3d_sat.cpp b/servers/physics_3d/collision_solver_3d_sat.cpp
index f507cacdc3..651961433c 100644
--- a/servers/physics_3d/collision_solver_3d_sat.cpp
+++ b/servers/physics_3d/collision_solver_3d_sat.cpp
@@ -845,7 +845,7 @@ static void _collision_sphere_capsule(const Shape3DSW *p_a, const Transform &p_t
//capsule sphere 1, sphere
- Vector3 capsule_axis = p_transform_b.basis.get_axis(2) * (capsule_B->get_height() * 0.5);
+ Vector3 capsule_axis = p_transform_b.basis.get_axis(1) * (capsule_B->get_height() * 0.5);
Vector3 capsule_ball_1 = p_transform_b.origin + capsule_axis;
@@ -1148,7 +1148,7 @@ static void _collision_box_capsule(const Shape3DSW *p_a, const Transform &p_tran
}
}
- Vector3 cyl_axis = p_transform_b.basis.get_axis(2).normalized();
+ Vector3 cyl_axis = p_transform_b.basis.get_axis(1).normalized();
// edges of A, capsule cylinder
@@ -1193,7 +1193,7 @@ static void _collision_box_capsule(const Shape3DSW *p_a, const Transform &p_tran
// capsule balls, edges of A
for (int i = 0; i < 2; i++) {
- Vector3 capsule_axis = p_transform_b.basis.get_axis(2) * (capsule_B->get_height() * 0.5);
+ Vector3 capsule_axis = p_transform_b.basis.get_axis(1) * (capsule_B->get_height() * 0.5);
Vector3 sphere_pos = p_transform_b.origin + ((i == 0) ? capsule_axis : -capsule_axis);
@@ -1569,8 +1569,8 @@ static void _collision_capsule_capsule(const Shape3DSW *p_a, const Transform &p_
// some values
- Vector3 capsule_A_axis = p_transform_a.basis.get_axis(2) * (capsule_A->get_height() * 0.5);
- Vector3 capsule_B_axis = p_transform_b.basis.get_axis(2) * (capsule_B->get_height() * 0.5);
+ Vector3 capsule_A_axis = p_transform_a.basis.get_axis(1) * (capsule_A->get_height() * 0.5);
+ Vector3 capsule_B_axis = p_transform_b.basis.get_axis(1) * (capsule_B->get_height() * 0.5);
Vector3 capsule_A_ball_1 = p_transform_a.origin + capsule_A_axis;
Vector3 capsule_A_ball_2 = p_transform_a.origin - capsule_A_axis;
@@ -1670,7 +1670,7 @@ static void _collision_capsule_convex_polygon(const Shape3DSW *p_a, const Transf
for (int i = 0; i < edge_count; i++) {
// cylinder
Vector3 edge_axis = p_transform_b.basis.xform(vertices[edges[i].a]) - p_transform_b.basis.xform(vertices[edges[i].b]);
- Vector3 axis = edge_axis.cross(p_transform_a.basis.get_axis(2)).normalized();
+ Vector3 axis = edge_axis.cross(p_transform_a.basis.get_axis(1)).normalized();
if (!separator.test_axis(axis)) {
return;
@@ -1682,7 +1682,7 @@ static void _collision_capsule_convex_polygon(const Shape3DSW *p_a, const Transf
for (int i = 0; i < 2; i++) {
// edges of B, capsule cylinder
- Vector3 capsule_axis = p_transform_a.basis.get_axis(2) * (capsule_A->get_height() * 0.5);
+ Vector3 capsule_axis = p_transform_a.basis.get_axis(1) * (capsule_A->get_height() * 0.5);
Vector3 sphere_pos = p_transform_a.origin + ((i == 0) ? capsule_axis : -capsule_axis);
@@ -1720,7 +1720,7 @@ static void _collision_capsule_face(const Shape3DSW *p_a, const Transform &p_tra
// edges of B, capsule cylinder
- Vector3 capsule_axis = p_transform_a.basis.get_axis(2) * (capsule_A->get_height() * 0.5);
+ Vector3 capsule_axis = p_transform_a.basis.get_axis(1) * (capsule_A->get_height() * 0.5);
for (int i = 0; i < 3; i++) {
// edge-cylinder
diff --git a/servers/physics_3d/joints_3d_sw.h b/servers/physics_3d/joints_3d_sw.h
index 1fe573c69e..225a71aca9 100644
--- a/servers/physics_3d/joints_3d_sw.h
+++ b/servers/physics_3d/joints_3d_sw.h
@@ -49,6 +49,15 @@ public:
_FORCE_INLINE_ Joint3DSW(Body3DSW **p_body_ptr = nullptr, int p_body_count = 0) :
Constraint3DSW(p_body_ptr, p_body_count) {
}
+
+ virtual ~Joint3DSW() {
+ for (int i = 0; i < get_body_count(); i++) {
+ Body3DSW *body = get_body_ptr()[i];
+ if (body) {
+ body->remove_constraint(this);
+ }
+ }
+ }
};
#endif // JOINTS_SW_H
diff --git a/servers/physics_3d/physics_server_3d_sw.cpp b/servers/physics_3d/physics_server_3d_sw.cpp
index 735e9094d2..6bbef09907 100644
--- a/servers/physics_3d/physics_server_3d_sw.cpp
+++ b/servers/physics_3d/physics_server_3d_sw.cpp
@@ -1312,9 +1312,6 @@ void PhysicsServer3DSW::free(RID p_rid) {
} else if (joint_owner.owns(p_rid)) {
Joint3DSW *joint = joint_owner.getornull(p_rid);
- for (int i = 0; i < joint->get_body_count(); i++) {
- joint->get_body_ptr()[i]->remove_constraint(joint);
- }
joint_owner.free(p_rid);
memdelete(joint);
diff --git a/servers/physics_3d/shape_3d_sw.cpp b/servers/physics_3d/shape_3d_sw.cpp
index 5bac4f19b9..02d0c66215 100644
--- a/servers/physics_3d/shape_3d_sw.cpp
+++ b/servers/physics_3d/shape_3d_sw.cpp
@@ -491,10 +491,10 @@ BoxShape3DSW::BoxShape3DSW() {
void CapsuleShape3DSW::project_range(const Vector3 &p_normal, const Transform &p_transform, real_t &r_min, real_t &r_max) const {
Vector3 n = p_transform.basis.xform_inv(p_normal).normalized();
- real_t h = (n.z > 0) ? height : -height;
+ real_t h = (n.y > 0) ? height : -height;
n *= radius;
- n.z += h * 0.5;
+ n.y += h * 0.5;
r_max = p_normal.dot(p_transform.xform(n));
r_min = p_normal.dot(p_transform.xform(-n));
@@ -503,36 +503,36 @@ void CapsuleShape3DSW::project_range(const Vector3 &p_normal, const Transform &p
Vector3 CapsuleShape3DSW::get_support(const Vector3 &p_normal) const {
Vector3 n = p_normal;
- real_t h = (n.z > 0) ? height : -height;
+ real_t h = (n.y > 0) ? height : -height;
n *= radius;
- n.z += h * 0.5;
+ n.y += h * 0.5;
return n;
}
void CapsuleShape3DSW::get_supports(const Vector3 &p_normal, int p_max, Vector3 *r_supports, int &r_amount, FeatureType &r_type) const {
Vector3 n = p_normal;
- real_t d = n.z;
+ real_t d = n.y;
if (Math::abs(d) < _EDGE_IS_VALID_SUPPORT_THRESHOLD) {
// make it flat
- n.z = 0.0;
+ n.y = 0.0;
n.normalize();
n *= radius;
r_amount = 2;
r_type = FEATURE_EDGE;
r_supports[0] = n;
- r_supports[0].z += height * 0.5;
+ r_supports[0].y += height * 0.5;
r_supports[1] = n;
- r_supports[1].z -= height * 0.5;
+ r_supports[1].y -= height * 0.5;
} else {
real_t h = (d > 0) ? height : -height;
n *= radius;
- n.z += h * 0.5;
+ n.y += h * 0.5;
r_amount = 1;
r_type = FEATURE_POINT;
*r_supports = n;
@@ -551,7 +551,7 @@ bool CapsuleShape3DSW::intersect_segment(const Vector3 &p_begin, const Vector3 &
// test against cylinder and spheres :-|
- collided = Geometry3D::segment_intersects_cylinder(p_begin, p_end, height, radius, &auxres, &auxn);
+ collided = Geometry3D::segment_intersects_cylinder(p_begin, p_end, height, radius, &auxres, &auxn, 1);
if (collided) {
real_t d = norm.dot(auxres);
@@ -563,7 +563,7 @@ bool CapsuleShape3DSW::intersect_segment(const Vector3 &p_begin, const Vector3 &
}
}
- collided = Geometry3D::segment_intersects_sphere(p_begin, p_end, Vector3(0, 0, height * 0.5), radius, &auxres, &auxn);
+ collided = Geometry3D::segment_intersects_sphere(p_begin, p_end, Vector3(0, height * 0.5, 0), radius, &auxres, &auxn);
if (collided) {
real_t d = norm.dot(auxres);
@@ -575,7 +575,7 @@ bool CapsuleShape3DSW::intersect_segment(const Vector3 &p_begin, const Vector3 &
}
}
- collided = Geometry3D::segment_intersects_sphere(p_begin, p_end, Vector3(0, 0, height * -0.5), radius, &auxres, &auxn);
+ collided = Geometry3D::segment_intersects_sphere(p_begin, p_end, Vector3(0, height * -0.5, 0), radius, &auxres, &auxn);
if (collided) {
real_t d = norm.dot(auxres);
@@ -596,19 +596,19 @@ bool CapsuleShape3DSW::intersect_segment(const Vector3 &p_begin, const Vector3 &
}
bool CapsuleShape3DSW::intersect_point(const Vector3 &p_point) const {
- if (Math::abs(p_point.z) < height * 0.5) {
- return Vector3(p_point.x, p_point.y, 0).length() < radius;
+ if (Math::abs(p_point.y) < height * 0.5) {
+ return Vector3(p_point.x, 0, p_point.z).length() < radius;
} else {
Vector3 p = p_point;
- p.z = Math::abs(p.z) - height * 0.5;
+ p.y = Math::abs(p.y) - height * 0.5;
return p.length() < radius;
}
}
Vector3 CapsuleShape3DSW::get_closest_point_to(const Vector3 &p_point) const {
Vector3 s[2] = {
- Vector3(0, 0, -height * 0.5),
- Vector3(0, 0, height * 0.5),
+ Vector3(0, -height * 0.5, 0),
+ Vector3(0, height * 0.5, 0),
};
Vector3 p = Geometry3D::get_closest_point_to_segment(p_point, s);
@@ -633,7 +633,7 @@ Vector3 CapsuleShape3DSW::get_moment_of_inertia(real_t p_mass) const {
void CapsuleShape3DSW::_setup(real_t p_height, real_t p_radius) {
height = p_height;
radius = p_radius;
- configure(AABB(Vector3(-radius, -radius, -height * 0.5 - radius), Vector3(radius * 2, radius * 2, height + radius * 2.0)));
+ configure(AABB(Vector3(-radius, -height * 0.5 - radius, -radius), Vector3(radius * 2, height + radius * 2.0, radius * 2)));
}
void CapsuleShape3DSW::set_data(const Variant &p_data) {
diff --git a/servers/physics_3d/space_3d_sw.cpp b/servers/physics_3d/space_3d_sw.cpp
index dd5754b9ac..c8741dc930 100644
--- a/servers/physics_3d/space_3d_sw.cpp
+++ b/servers/physics_3d/space_3d_sw.cpp
@@ -210,6 +210,10 @@ int PhysicsDirectSpaceState3DSW::intersect_shape(const RID &p_shape, const Trans
const CollisionObject3DSW *col_obj = space->intersection_query_results[i];
int shape_idx = space->intersection_query_subindex_results[i];
+ if (col_obj->is_shape_set_as_disabled(shape_idx)) {
+ continue;
+ }
+
if (!CollisionSolver3DSW::solve_static(shape, p_xform, col_obj->get_shape(shape_idx), col_obj->get_transform() * col_obj->get_shape_transform(shape_idx), nullptr, nullptr, nullptr, p_margin, 0)) {
continue;
}
@@ -265,6 +269,10 @@ bool PhysicsDirectSpaceState3DSW::cast_motion(const RID &p_shape, const Transfor
const CollisionObject3DSW *col_obj = space->intersection_query_results[i];
int shape_idx = space->intersection_query_subindex_results[i];
+ if (col_obj->is_shape_set_as_disabled(shape_idx)) {
+ continue;
+ }
+
Vector3 point_A, point_B;
Vector3 sep_axis = p_motion.normalized();
@@ -365,12 +373,17 @@ bool PhysicsDirectSpaceState3DSW::collide_shape(RID p_shape, const Transform &p_
}
const CollisionObject3DSW *col_obj = space->intersection_query_results[i];
- int shape_idx = space->intersection_query_subindex_results[i];
if (p_exclude.has(col_obj->get_self())) {
continue;
}
+ int shape_idx = space->intersection_query_subindex_results[i];
+
+ if (col_obj->is_shape_set_as_disabled(shape_idx)) {
+ continue;
+ }
+
if (CollisionSolver3DSW::solve_static(shape, p_shape_xform, col_obj->get_shape(shape_idx), col_obj->get_transform() * col_obj->get_shape_transform(shape_idx), cbkres, cbkptr, nullptr, p_margin)) {
collided = true;
}
@@ -435,12 +448,17 @@ bool PhysicsDirectSpaceState3DSW::rest_info(RID p_shape, const Transform &p_shap
}
const CollisionObject3DSW *col_obj = space->intersection_query_results[i];
- int shape_idx = space->intersection_query_subindex_results[i];
if (p_exclude.has(col_obj->get_self())) {
continue;
}
+ int shape_idx = space->intersection_query_subindex_results[i];
+
+ if (col_obj->is_shape_set_as_disabled(shape_idx)) {
+ continue;
+ }
+
rcd.object = col_obj;
rcd.shape = shape_idx;
bool sc = CollisionSolver3DSW::solve_static(shape, p_shape_xform, col_obj->get_shape(shape_idx), col_obj->get_transform() * col_obj->get_shape_transform(shape_idx), _rest_cbk_result, &rcd, nullptr, p_margin);
diff --git a/servers/rendering/renderer_canvas_cull.cpp b/servers/rendering/renderer_canvas_cull.cpp
index e34bc9acb8..7f3fc2f8f4 100644
--- a/servers/rendering/renderer_canvas_cull.cpp
+++ b/servers/rendering/renderer_canvas_cull.cpp
@@ -148,6 +148,8 @@ void RendererCanvasCull::_cull_canvas_item(Item *p_canvas_item, const Transform2
} else {
ci->final_clip_rect = global_rect;
}
+ ci->final_clip_rect.position = ci->final_clip_rect.position.round();
+ ci->final_clip_rect.size = ci->final_clip_rect.size.round();
ci->final_clip_owner = ci;
} else {
@@ -525,11 +527,11 @@ void RendererCanvasCull::canvas_item_add_line(RID p_item, const Point2 &p_from,
Item::CommandPrimitive *line = canvas_item->alloc_command<Item::CommandPrimitive>();
ERR_FAIL_COND(!line);
if (p_width > 1.001) {
- Vector2 t = (p_from - p_to).orthogonal().normalized();
- line->points[0] = p_from + t * p_width;
- line->points[1] = p_from - t * p_width;
- line->points[2] = p_to - t * p_width;
- line->points[3] = p_to + t * p_width;
+ Vector2 t = (p_from - p_to).orthogonal().normalized() * p_width * 0.5;
+ line->points[0] = p_from + t;
+ line->points[1] = p_from - t;
+ line->points[2] = p_to - t;
+ line->points[3] = p_to + t;
line->point_count = 4;
} else {
line->point_count = 2;
diff --git a/servers/rendering/renderer_rd/renderer_scene_environment_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_environment_rd.cpp
new file mode 100644
index 0000000000..d631cb4bac
--- /dev/null
+++ b/servers/rendering/renderer_rd/renderer_scene_environment_rd.cpp
@@ -0,0 +1,126 @@
+/*************************************************************************/
+/* renderer_scene_environment_rd.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 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 "servers/rendering/renderer_rd/renderer_scene_environment_rd.h"
+
+uint64_t RendererSceneEnvironmentRD::auto_exposure_counter = 2;
+
+void RendererSceneEnvironmentRD::set_ambient_light(const Color &p_color, RS::EnvironmentAmbientSource p_ambient, float p_energy, float p_sky_contribution, RS::EnvironmentReflectionSource p_reflection_source, const Color &p_ao_color) {
+ ambient_light = p_color;
+ ambient_source = p_ambient;
+ ambient_light_energy = p_energy;
+ ambient_sky_contribution = p_sky_contribution;
+ reflection_source = p_reflection_source;
+ ao_color = p_ao_color;
+}
+
+void RendererSceneEnvironmentRD::set_tonemap(RS::EnvironmentToneMapper p_tone_mapper, float p_exposure, float p_white, bool p_auto_exposure, float p_min_luminance, float p_max_luminance, float p_auto_exp_speed, float p_auto_exp_scale) {
+ exposure = p_exposure;
+ tone_mapper = p_tone_mapper;
+ if (!auto_exposure && p_auto_exposure) {
+ auto_exposure_version = ++auto_exposure_counter;
+ }
+ auto_exposure = p_auto_exposure;
+ white = p_white;
+ min_luminance = p_min_luminance;
+ max_luminance = p_max_luminance;
+ auto_exp_speed = p_auto_exp_speed;
+ auto_exp_scale = p_auto_exp_scale;
+}
+
+void RendererSceneEnvironmentRD::set_glow(bool p_enable, Vector<float> p_levels, float p_intensity, float p_strength, float p_mix, float p_bloom_threshold, RS::EnvironmentGlowBlendMode p_blend_mode, float p_hdr_bleed_threshold, float p_hdr_bleed_scale, float p_hdr_luminance_cap) {
+ ERR_FAIL_COND_MSG(p_levels.size() != 7, "Size of array of glow levels must be 7");
+ glow_enabled = p_enable;
+ glow_levels = p_levels;
+ glow_intensity = p_intensity;
+ glow_strength = p_strength;
+ glow_mix = p_mix;
+ glow_bloom = p_bloom_threshold;
+ glow_blend_mode = p_blend_mode;
+ glow_hdr_bleed_threshold = p_hdr_bleed_threshold;
+ glow_hdr_bleed_scale = p_hdr_bleed_scale;
+ glow_hdr_luminance_cap = p_hdr_luminance_cap;
+}
+
+void RendererSceneEnvironmentRD::set_sdfgi(bool p_enable, RS::EnvironmentSDFGICascades p_cascades, float p_min_cell_size, RS::EnvironmentSDFGIYScale p_y_scale, bool p_use_occlusion, float p_bounce_feedback, bool p_read_sky, float p_energy, float p_normal_bias, float p_probe_bias) {
+ sdfgi_enabled = p_enable;
+ sdfgi_cascades = p_cascades;
+ sdfgi_min_cell_size = p_min_cell_size;
+ sdfgi_use_occlusion = p_use_occlusion;
+ sdfgi_bounce_feedback = p_bounce_feedback;
+ sdfgi_read_sky_light = p_read_sky;
+ sdfgi_energy = p_energy;
+ sdfgi_normal_bias = p_normal_bias;
+ sdfgi_probe_bias = p_probe_bias;
+ sdfgi_y_scale = p_y_scale;
+}
+
+void RendererSceneEnvironmentRD::set_fog(bool p_enable, const Color &p_light_color, float p_light_energy, float p_sun_scatter, float p_density, float p_height, float p_height_density, float p_fog_aerial_perspective) {
+ fog_enabled = p_enable;
+ fog_light_color = p_light_color;
+ fog_light_energy = p_light_energy;
+ fog_sun_scatter = p_sun_scatter;
+ fog_density = p_density;
+ fog_height = p_height;
+ fog_height_density = p_height_density;
+ fog_aerial_perspective = p_fog_aerial_perspective;
+}
+
+void RendererSceneEnvironmentRD::set_volumetric_fog(bool p_enable, float p_density, const Color &p_light, float p_light_energy, float p_length, float p_detail_spread, float p_gi_inject, bool p_temporal_reprojection, float p_temporal_reprojection_amount) {
+ volumetric_fog_enabled = p_enable;
+ volumetric_fog_density = p_density;
+ volumetric_fog_light = p_light;
+ volumetric_fog_light_energy = p_light_energy;
+ volumetric_fog_length = p_length;
+ volumetric_fog_detail_spread = p_detail_spread;
+ volumetric_fog_gi_inject = p_gi_inject;
+ volumetric_fog_temporal_reprojection = p_temporal_reprojection;
+ volumetric_fog_temporal_reprojection_amount = p_temporal_reprojection_amount;
+}
+
+void RendererSceneEnvironmentRD::set_ssr(bool p_enable, int p_max_steps, float p_fade_int, float p_fade_out, float p_depth_tolerance) {
+ ssr_enabled = p_enable;
+ ssr_max_steps = p_max_steps;
+ ssr_fade_in = p_fade_int;
+ ssr_fade_out = p_fade_out;
+ ssr_depth_tolerance = p_depth_tolerance;
+}
+
+void RendererSceneEnvironmentRD::set_ssao(bool p_enable, float p_radius, float p_intensity, float p_power, float p_detail, float p_horizon, float p_sharpness, float p_light_affect, float p_ao_channel_affect) {
+ ssao_enabled = p_enable;
+ ssao_radius = p_radius;
+ ssao_intensity = p_intensity;
+ ssao_power = p_power;
+ ssao_detail = p_detail;
+ ssao_horizon = p_horizon;
+ ssao_sharpness = p_sharpness;
+ ssao_direct_light_affect = p_light_affect;
+ ssao_ao_channel_affect = p_ao_channel_affect;
+}
diff --git a/servers/rendering/renderer_rd/renderer_scene_environment_rd.h b/servers/rendering/renderer_rd/renderer_scene_environment_rd.h
new file mode 100644
index 0000000000..992c4bf471
--- /dev/null
+++ b/servers/rendering/renderer_rd/renderer_scene_environment_rd.h
@@ -0,0 +1,155 @@
+/*************************************************************************/
+/* renderer_scene_environment_rd.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 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 RENDERING_SERVER_SCENE_ENVIRONMENT_RD_H
+#define RENDERING_SERVER_SCENE_ENVIRONMENT_RD_H
+
+#include "servers/rendering/renderer_scene_render.h"
+#include "servers/rendering/rendering_device.h"
+
+class RendererSceneEnvironmentRD {
+private:
+ static uint64_t auto_exposure_counter;
+
+public:
+ // BG
+ RS::EnvironmentBG background = RS::ENV_BG_CLEAR_COLOR;
+ RID sky;
+ float sky_custom_fov = 0.0;
+ Basis sky_orientation;
+ Color bg_color;
+ float bg_energy = 1.0;
+ int canvas_max_layer = 0;
+ RS::EnvironmentAmbientSource ambient_source = RS::ENV_AMBIENT_SOURCE_BG;
+ Color ambient_light;
+ float ambient_light_energy = 1.0;
+ float ambient_sky_contribution = 1.0;
+ RS::EnvironmentReflectionSource reflection_source = RS::ENV_REFLECTION_SOURCE_BG;
+ Color ao_color;
+
+ /// Tonemap
+
+ RS::EnvironmentToneMapper tone_mapper;
+ float exposure = 1.0;
+ float white = 1.0;
+ bool auto_exposure = false;
+ float min_luminance = 0.2;
+ float max_luminance = 8.0;
+ float auto_exp_speed = 0.2;
+ float auto_exp_scale = 0.5;
+ uint64_t auto_exposure_version = 0;
+
+ // Fog
+ bool fog_enabled = false;
+ Color fog_light_color = Color(0.5, 0.6, 0.7);
+ float fog_light_energy = 1.0;
+ float fog_sun_scatter = 0.0;
+ float fog_density = 0.001;
+ float fog_height = 0.0;
+ float fog_height_density = 0.0; //can be negative to invert effect
+ float fog_aerial_perspective = 0.0;
+
+ /// Volumetric Fog
+ ///
+ bool volumetric_fog_enabled = false;
+ float volumetric_fog_density = 0.01;
+ Color volumetric_fog_light = Color(0, 0, 0);
+ float volumetric_fog_light_energy = 0.0;
+ float volumetric_fog_length = 64.0;
+ float volumetric_fog_detail_spread = 2.0;
+ float volumetric_fog_gi_inject = 0.0;
+ bool volumetric_fog_temporal_reprojection = true;
+ float volumetric_fog_temporal_reprojection_amount = 0.9;
+
+ /// Glow
+
+ bool glow_enabled = false;
+ Vector<float> glow_levels;
+ float glow_intensity = 0.8;
+ float glow_strength = 1.0;
+ float glow_bloom = 0.0;
+ float glow_mix = 0.01;
+ RS::EnvironmentGlowBlendMode glow_blend_mode = RS::ENV_GLOW_BLEND_MODE_SOFTLIGHT;
+ float glow_hdr_bleed_threshold = 1.0;
+ float glow_hdr_luminance_cap = 12.0;
+ float glow_hdr_bleed_scale = 2.0;
+
+ /// SSAO
+
+ bool ssao_enabled = false;
+ float ssao_radius = 1.0;
+ float ssao_intensity = 2.0;
+ float ssao_power = 1.5;
+ float ssao_detail = 0.5;
+ float ssao_horizon = 0.06;
+ float ssao_sharpness = 0.98;
+ float ssao_direct_light_affect = 0.0;
+ float ssao_ao_channel_affect = 0.0;
+
+ /// SSR
+ ///
+ bool ssr_enabled = false;
+ int ssr_max_steps = 64;
+ float ssr_fade_in = 0.15;
+ float ssr_fade_out = 2.0;
+ float ssr_depth_tolerance = 0.2;
+
+ /// SDFGI
+ bool sdfgi_enabled = false;
+ RS::EnvironmentSDFGICascades sdfgi_cascades;
+ float sdfgi_min_cell_size = 0.2;
+ bool sdfgi_use_occlusion = false;
+ float sdfgi_bounce_feedback = 0.0;
+ bool sdfgi_read_sky_light = false;
+ float sdfgi_energy = 1.0;
+ float sdfgi_normal_bias = 1.1;
+ float sdfgi_probe_bias = 1.1;
+ RS::EnvironmentSDFGIYScale sdfgi_y_scale = RS::ENV_SDFGI_Y_SCALE_DISABLED;
+
+ /// Adjustments
+
+ bool adjustments_enabled = false;
+ float adjustments_brightness = 1.0f;
+ float adjustments_contrast = 1.0f;
+ float adjustments_saturation = 1.0f;
+ bool use_1d_color_correction = false;
+ RID color_correction = RID();
+
+ void set_ambient_light(const Color &p_color, RS::EnvironmentAmbientSource p_ambient, float p_energy, float p_sky_contribution, RS::EnvironmentReflectionSource p_reflection_source, const Color &p_ao_color);
+ void set_tonemap(RS::EnvironmentToneMapper p_tone_mapper, float p_exposure, float p_white, bool p_auto_exposure, float p_min_luminance, float p_max_luminance, float p_auto_exp_speed, float p_auto_exp_scale);
+ void set_glow(bool p_enable, Vector<float> p_levels, float p_intensity, float p_strength, float p_mix, float p_bloom_threshold, RS::EnvironmentGlowBlendMode p_blend_mode, float p_hdr_bleed_threshold, float p_hdr_bleed_scale, float p_hdr_luminance_cap);
+ void set_sdfgi(bool p_enable, RS::EnvironmentSDFGICascades p_cascades, float p_min_cell_size, RS::EnvironmentSDFGIYScale p_y_scale, bool p_use_occlusion, float p_bounce_feedback, bool p_read_sky, float p_energy, float p_normal_bias, float p_probe_bias);
+ void set_fog(bool p_enable, const Color &p_light_color, float p_light_energy, float p_sun_scatter, float p_density, float p_height, float p_height_density, float p_fog_aerial_perspective);
+ void set_volumetric_fog(bool p_enable, float p_density, const Color &p_light, float p_light_energy, float p_length, float p_detail_spread, float p_gi_inject, bool p_temporal_reprojection, float p_temporal_reprojection_amount);
+ void set_ssr(bool p_enable, int p_max_steps, float p_fade_int, float p_fade_out, float p_depth_tolerance);
+ void set_ssao(bool p_enable, float p_radius, float p_intensity, float p_power, float p_detail, float p_horizon, float p_sharpness, float p_light_affect, float p_ao_channel_affect);
+};
+
+#endif /* !RENDERING_SERVER_SCENE_ENVIRONMENT_RD_H */
diff --git a/servers/rendering/renderer_rd/renderer_scene_gi_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_gi_rd.cpp
new file mode 100644
index 0000000000..62589cc97c
--- /dev/null
+++ b/servers/rendering/renderer_rd/renderer_scene_gi_rd.cpp
@@ -0,0 +1,3383 @@
+/*************************************************************************/
+/* renderer_scene_gi_rd.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 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 "renderer_scene_gi_rd.h"
+
+#include "core/config/project_settings.h"
+#include "renderer_compositor_rd.h"
+#include "servers/rendering/renderer_rd/renderer_scene_render_rd.h"
+#include "servers/rendering/rendering_server_default.h"
+
+const Vector3i RendererSceneGIRD::SDFGI::Cascade::DIRTY_ALL = Vector3i(0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF);
+
+////////////////////////////////////////////////////////////////////////////////
+// SDFGI
+
+void RendererSceneGIRD::SDFGI::create(RendererSceneEnvironmentRD *p_env, const Vector3 &p_world_position, uint32_t p_requested_history_size, RendererSceneGIRD *p_gi) {
+ storage = p_gi->storage;
+ gi = p_gi;
+ cascade_mode = p_env->sdfgi_cascades;
+ min_cell_size = p_env->sdfgi_min_cell_size;
+ uses_occlusion = p_env->sdfgi_use_occlusion;
+ y_scale_mode = p_env->sdfgi_y_scale;
+ static const float y_scale[3] = { 1.0, 1.5, 2.0 };
+ y_mult = y_scale[y_scale_mode];
+ static const int cascasde_size[3] = { 4, 6, 8 };
+ cascades.resize(cascasde_size[cascade_mode]);
+ probe_axis_count = SDFGI::PROBE_DIVISOR + 1;
+ solid_cell_ratio = gi->sdfgi_solid_cell_ratio;
+ solid_cell_count = uint32_t(float(cascade_size * cascade_size * cascade_size) * solid_cell_ratio);
+
+ float base_cell_size = min_cell_size;
+
+ RD::TextureFormat tf_sdf;
+ tf_sdf.format = RD::DATA_FORMAT_R8_UNORM;
+ tf_sdf.width = cascade_size; // Always 64x64
+ tf_sdf.height = cascade_size;
+ tf_sdf.depth = cascade_size;
+ tf_sdf.texture_type = RD::TEXTURE_TYPE_3D;
+ tf_sdf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_CAN_COPY_TO_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT;
+
+ {
+ RD::TextureFormat tf_render = tf_sdf;
+ tf_render.format = RD::DATA_FORMAT_R16_UINT;
+ render_albedo = RD::get_singleton()->texture_create(tf_render, RD::TextureView());
+ tf_render.format = RD::DATA_FORMAT_R32_UINT;
+ render_emission = RD::get_singleton()->texture_create(tf_render, RD::TextureView());
+ render_emission_aniso = RD::get_singleton()->texture_create(tf_render, RD::TextureView());
+
+ tf_render.format = RD::DATA_FORMAT_R8_UNORM; //at least its easy to visualize
+
+ for (int i = 0; i < 8; i++) {
+ render_occlusion[i] = RD::get_singleton()->texture_create(tf_render, RD::TextureView());
+ }
+
+ tf_render.format = RD::DATA_FORMAT_R32_UINT;
+ render_geom_facing = RD::get_singleton()->texture_create(tf_render, RD::TextureView());
+
+ tf_render.format = RD::DATA_FORMAT_R8G8B8A8_UINT;
+ render_sdf[0] = RD::get_singleton()->texture_create(tf_render, RD::TextureView());
+ render_sdf[1] = RD::get_singleton()->texture_create(tf_render, RD::TextureView());
+
+ tf_render.width /= 2;
+ tf_render.height /= 2;
+ tf_render.depth /= 2;
+
+ render_sdf_half[0] = RD::get_singleton()->texture_create(tf_render, RD::TextureView());
+ render_sdf_half[1] = RD::get_singleton()->texture_create(tf_render, RD::TextureView());
+ }
+
+ RD::TextureFormat tf_occlusion = tf_sdf;
+ tf_occlusion.format = RD::DATA_FORMAT_R16_UINT;
+ tf_occlusion.shareable_formats.push_back(RD::DATA_FORMAT_R16_UINT);
+ tf_occlusion.shareable_formats.push_back(RD::DATA_FORMAT_R4G4B4A4_UNORM_PACK16);
+ tf_occlusion.depth *= cascades.size(); //use depth for occlusion slices
+ tf_occlusion.width *= 2; //use width for the other half
+
+ RD::TextureFormat tf_light = tf_sdf;
+ tf_light.format = RD::DATA_FORMAT_R32_UINT;
+ tf_light.shareable_formats.push_back(RD::DATA_FORMAT_R32_UINT);
+ tf_light.shareable_formats.push_back(RD::DATA_FORMAT_E5B9G9R9_UFLOAT_PACK32);
+
+ RD::TextureFormat tf_aniso0 = tf_sdf;
+ tf_aniso0.format = RD::DATA_FORMAT_R8G8B8A8_UNORM;
+ RD::TextureFormat tf_aniso1 = tf_sdf;
+ tf_aniso1.format = RD::DATA_FORMAT_R8G8_UNORM;
+
+ int passes = nearest_shift(cascade_size) - 1;
+
+ //store lightprobe SH
+ RD::TextureFormat tf_probes;
+ tf_probes.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT;
+ tf_probes.width = probe_axis_count * probe_axis_count;
+ tf_probes.height = probe_axis_count * SDFGI::SH_SIZE;
+ tf_probes.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_CAN_COPY_TO_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT;
+ tf_probes.texture_type = RD::TEXTURE_TYPE_2D_ARRAY;
+
+ history_size = p_requested_history_size;
+
+ RD::TextureFormat tf_probe_history = tf_probes;
+ tf_probe_history.format = RD::DATA_FORMAT_R16G16B16A16_SINT; //signed integer because SH are signed
+ tf_probe_history.array_layers = history_size;
+
+ RD::TextureFormat tf_probe_average = tf_probes;
+ tf_probe_average.format = RD::DATA_FORMAT_R32G32B32A32_SINT; //signed integer because SH are signed
+ tf_probe_average.texture_type = RD::TEXTURE_TYPE_2D;
+
+ lightprobe_history_scroll = RD::get_singleton()->texture_create(tf_probe_history, RD::TextureView());
+ lightprobe_average_scroll = RD::get_singleton()->texture_create(tf_probe_average, RD::TextureView());
+
+ {
+ //octahedral lightprobes
+ RD::TextureFormat tf_octprobes = tf_probes;
+ tf_octprobes.array_layers = cascades.size() * 2;
+ tf_octprobes.format = RD::DATA_FORMAT_R32_UINT; //pack well with RGBE
+ tf_octprobes.width = probe_axis_count * probe_axis_count * (SDFGI::LIGHTPROBE_OCT_SIZE + 2);
+ tf_octprobes.height = probe_axis_count * (SDFGI::LIGHTPROBE_OCT_SIZE + 2);
+ tf_octprobes.shareable_formats.push_back(RD::DATA_FORMAT_R32_UINT);
+ tf_octprobes.shareable_formats.push_back(RD::DATA_FORMAT_E5B9G9R9_UFLOAT_PACK32);
+ //lightprobe texture is an octahedral texture
+
+ lightprobe_data = RD::get_singleton()->texture_create(tf_octprobes, RD::TextureView());
+ RD::TextureView tv;
+ tv.format_override = RD::DATA_FORMAT_E5B9G9R9_UFLOAT_PACK32;
+ lightprobe_texture = RD::get_singleton()->texture_create_shared(tv, lightprobe_data);
+
+ //texture handling ambient data, to integrate with volumetric foc
+ RD::TextureFormat tf_ambient = tf_probes;
+ tf_ambient.array_layers = cascades.size();
+ tf_ambient.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT; //pack well with RGBE
+ tf_ambient.width = probe_axis_count * probe_axis_count;
+ tf_ambient.height = probe_axis_count;
+ tf_ambient.texture_type = RD::TEXTURE_TYPE_2D_ARRAY;
+ //lightprobe texture is an octahedral texture
+ ambient_texture = RD::get_singleton()->texture_create(tf_ambient, RD::TextureView());
+ }
+
+ cascades_ubo = RD::get_singleton()->uniform_buffer_create(sizeof(SDFGI::Cascade::UBO) * SDFGI::MAX_CASCADES);
+
+ occlusion_data = RD::get_singleton()->texture_create(tf_occlusion, RD::TextureView());
+ {
+ RD::TextureView tv;
+ tv.format_override = RD::DATA_FORMAT_R4G4B4A4_UNORM_PACK16;
+ occlusion_texture = RD::get_singleton()->texture_create_shared(tv, occlusion_data);
+ }
+
+ for (uint32_t i = 0; i < cascades.size(); i++) {
+ SDFGI::Cascade &cascade = cascades[i];
+
+ /* 3D Textures */
+
+ cascade.sdf_tex = RD::get_singleton()->texture_create(tf_sdf, RD::TextureView());
+
+ cascade.light_data = RD::get_singleton()->texture_create(tf_light, RD::TextureView());
+
+ cascade.light_aniso_0_tex = RD::get_singleton()->texture_create(tf_aniso0, RD::TextureView());
+ cascade.light_aniso_1_tex = RD::get_singleton()->texture_create(tf_aniso1, RD::TextureView());
+
+ {
+ RD::TextureView tv;
+ tv.format_override = RD::DATA_FORMAT_E5B9G9R9_UFLOAT_PACK32;
+ cascade.light_tex = RD::get_singleton()->texture_create_shared(tv, cascade.light_data);
+
+ RD::get_singleton()->texture_clear(cascade.light_tex, Color(0, 0, 0, 0), 0, 1, 0, 1);
+ RD::get_singleton()->texture_clear(cascade.light_aniso_0_tex, Color(0, 0, 0, 0), 0, 1, 0, 1);
+ RD::get_singleton()->texture_clear(cascade.light_aniso_1_tex, Color(0, 0, 0, 0), 0, 1, 0, 1);
+ }
+
+ cascade.cell_size = base_cell_size;
+ Vector3 world_position = p_world_position;
+ world_position.y *= y_mult;
+ int32_t probe_cells = cascade_size / SDFGI::PROBE_DIVISOR;
+ Vector3 probe_size = Vector3(1, 1, 1) * cascade.cell_size * probe_cells;
+ Vector3i probe_pos = Vector3i((world_position / probe_size + Vector3(0.5, 0.5, 0.5)).floor());
+ cascade.position = probe_pos * probe_cells;
+
+ cascade.dirty_regions = SDFGI::Cascade::DIRTY_ALL;
+
+ base_cell_size *= 2.0;
+
+ /* Probe History */
+
+ cascade.lightprobe_history_tex = RD::get_singleton()->texture_create(tf_probe_history, RD::TextureView());
+ RD::get_singleton()->texture_clear(cascade.lightprobe_history_tex, Color(0, 0, 0, 0), 0, 1, 0, tf_probe_history.array_layers); //needs to be cleared for average to work
+
+ cascade.lightprobe_average_tex = RD::get_singleton()->texture_create(tf_probe_average, RD::TextureView());
+ RD::get_singleton()->texture_clear(cascade.lightprobe_average_tex, Color(0, 0, 0, 0), 0, 1, 0, 1); //needs to be cleared for average to work
+
+ /* Buffers */
+
+ cascade.solid_cell_buffer = RD::get_singleton()->storage_buffer_create(sizeof(SDFGI::Cascade::SolidCell) * solid_cell_count);
+ cascade.solid_cell_dispatch_buffer = RD::get_singleton()->storage_buffer_create(sizeof(uint32_t) * 4, Vector<uint8_t>(), RD::STORAGE_BUFFER_USAGE_DISPATCH_INDIRECT);
+ cascade.lights_buffer = RD::get_singleton()->storage_buffer_create(sizeof(SDGIShader::Light) * MAX(SDFGI::MAX_STATIC_LIGHTS, SDFGI::MAX_DYNAMIC_LIGHTS));
+ {
+ Vector<RD::Uniform> uniforms;
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
+ u.binding = 1;
+ u.ids.push_back(render_sdf[(passes & 1) ? 1 : 0]); //if passes are even, we read from buffer 0, else we read from buffer 1
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
+ u.binding = 2;
+ u.ids.push_back(render_albedo);
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
+ u.binding = 3;
+ for (int j = 0; j < 8; j++) {
+ u.ids.push_back(render_occlusion[j]);
+ }
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
+ u.binding = 4;
+ u.ids.push_back(render_emission);
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
+ u.binding = 5;
+ u.ids.push_back(render_emission_aniso);
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
+ u.binding = 6;
+ u.ids.push_back(render_geom_facing);
+ uniforms.push_back(u);
+ }
+
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
+ u.binding = 7;
+ u.ids.push_back(cascade.sdf_tex);
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
+ u.binding = 8;
+ u.ids.push_back(occlusion_data);
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
+ u.binding = 10;
+ u.ids.push_back(cascade.solid_cell_dispatch_buffer);
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
+ u.binding = 11;
+ u.ids.push_back(cascade.solid_cell_buffer);
+ uniforms.push_back(u);
+ }
+
+ cascade.sdf_store_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, gi->sdfgi_shader.preprocess.version_get_shader(gi->sdfgi_shader.preprocess_shader, SDGIShader::PRE_PROCESS_STORE), 0);
+ }
+
+ {
+ Vector<RD::Uniform> uniforms;
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
+ u.binding = 1;
+ u.ids.push_back(render_albedo);
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
+ u.binding = 2;
+ u.ids.push_back(render_geom_facing);
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
+ u.binding = 3;
+ u.ids.push_back(render_emission);
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
+ u.binding = 4;
+ u.ids.push_back(render_emission_aniso);
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
+ u.binding = 5;
+ u.ids.push_back(cascade.solid_cell_dispatch_buffer);
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
+ u.binding = 6;
+ u.ids.push_back(cascade.solid_cell_buffer);
+ uniforms.push_back(u);
+ }
+
+ cascade.scroll_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, gi->sdfgi_shader.preprocess.version_get_shader(gi->sdfgi_shader.preprocess_shader, SDGIShader::PRE_PROCESS_SCROLL), 0);
+ }
+ {
+ Vector<RD::Uniform> uniforms;
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
+ u.binding = 1;
+ for (int j = 0; j < 8; j++) {
+ u.ids.push_back(render_occlusion[j]);
+ }
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
+ u.binding = 2;
+ u.ids.push_back(occlusion_data);
+ uniforms.push_back(u);
+ }
+
+ cascade.scroll_occlusion_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, gi->sdfgi_shader.preprocess.version_get_shader(gi->sdfgi_shader.preprocess_shader, SDGIShader::PRE_PROCESS_SCROLL_OCCLUSION), 0);
+ }
+ }
+
+ //direct light
+ for (uint32_t i = 0; i < cascades.size(); i++) {
+ SDFGI::Cascade &cascade = cascades[i];
+
+ Vector<RD::Uniform> uniforms;
+ {
+ RD::Uniform u;
+ u.binding = 1;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
+ for (uint32_t j = 0; j < SDFGI::MAX_CASCADES; j++) {
+ if (j < cascades.size()) {
+ u.ids.push_back(cascades[j].sdf_tex);
+ } else {
+ u.ids.push_back(storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_3D_WHITE));
+ }
+ }
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.binding = 2;
+ u.uniform_type = RD::UNIFORM_TYPE_SAMPLER;
+ u.ids.push_back(storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED));
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.binding = 3;
+ u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
+ u.ids.push_back(cascade.solid_cell_dispatch_buffer);
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.binding = 4;
+ u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
+ u.ids.push_back(cascade.solid_cell_buffer);
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.binding = 5;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
+ u.ids.push_back(cascade.light_data);
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.binding = 6;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
+ u.ids.push_back(cascade.light_aniso_0_tex);
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.binding = 7;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
+ u.ids.push_back(cascade.light_aniso_1_tex);
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.binding = 8;
+ u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER;
+ u.ids.push_back(cascades_ubo);
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.binding = 9;
+ u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
+ u.ids.push_back(cascade.lights_buffer);
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.binding = 10;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
+ u.ids.push_back(lightprobe_texture);
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.binding = 11;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
+ u.ids.push_back(occlusion_texture);
+ uniforms.push_back(u);
+ }
+
+ cascade.sdf_direct_light_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, gi->sdfgi_shader.direct_light.version_get_shader(gi->sdfgi_shader.direct_light_shader, 0), 0);
+ }
+
+ //preprocess initialize uniform set
+ {
+ Vector<RD::Uniform> uniforms;
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
+ u.binding = 1;
+ u.ids.push_back(render_albedo);
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
+ u.binding = 2;
+ u.ids.push_back(render_sdf[0]);
+ uniforms.push_back(u);
+ }
+
+ sdf_initialize_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, gi->sdfgi_shader.preprocess.version_get_shader(gi->sdfgi_shader.preprocess_shader, SDGIShader::PRE_PROCESS_JUMP_FLOOD_INITIALIZE), 0);
+ }
+
+ {
+ Vector<RD::Uniform> uniforms;
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
+ u.binding = 1;
+ u.ids.push_back(render_albedo);
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
+ u.binding = 2;
+ u.ids.push_back(render_sdf_half[0]);
+ uniforms.push_back(u);
+ }
+
+ sdf_initialize_half_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, gi->sdfgi_shader.preprocess.version_get_shader(gi->sdfgi_shader.preprocess_shader, SDGIShader::PRE_PROCESS_JUMP_FLOOD_INITIALIZE_HALF), 0);
+ }
+
+ //jump flood uniform set
+ {
+ Vector<RD::Uniform> uniforms;
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
+ u.binding = 1;
+ u.ids.push_back(render_sdf[0]);
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
+ u.binding = 2;
+ u.ids.push_back(render_sdf[1]);
+ uniforms.push_back(u);
+ }
+
+ jump_flood_uniform_set[0] = RD::get_singleton()->uniform_set_create(uniforms, gi->sdfgi_shader.preprocess.version_get_shader(gi->sdfgi_shader.preprocess_shader, SDGIShader::PRE_PROCESS_JUMP_FLOOD), 0);
+ SWAP(uniforms.write[0].ids.write[0], uniforms.write[1].ids.write[0]);
+ jump_flood_uniform_set[1] = RD::get_singleton()->uniform_set_create(uniforms, gi->sdfgi_shader.preprocess.version_get_shader(gi->sdfgi_shader.preprocess_shader, SDGIShader::PRE_PROCESS_JUMP_FLOOD), 0);
+ }
+ //jump flood half uniform set
+ {
+ Vector<RD::Uniform> uniforms;
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
+ u.binding = 1;
+ u.ids.push_back(render_sdf_half[0]);
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
+ u.binding = 2;
+ u.ids.push_back(render_sdf_half[1]);
+ uniforms.push_back(u);
+ }
+
+ jump_flood_half_uniform_set[0] = RD::get_singleton()->uniform_set_create(uniforms, gi->sdfgi_shader.preprocess.version_get_shader(gi->sdfgi_shader.preprocess_shader, SDGIShader::PRE_PROCESS_JUMP_FLOOD), 0);
+ SWAP(uniforms.write[0].ids.write[0], uniforms.write[1].ids.write[0]);
+ jump_flood_half_uniform_set[1] = RD::get_singleton()->uniform_set_create(uniforms, gi->sdfgi_shader.preprocess.version_get_shader(gi->sdfgi_shader.preprocess_shader, SDGIShader::PRE_PROCESS_JUMP_FLOOD), 0);
+ }
+
+ //upscale half size sdf
+ {
+ Vector<RD::Uniform> uniforms;
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
+ u.binding = 1;
+ u.ids.push_back(render_albedo);
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
+ u.binding = 2;
+ u.ids.push_back(render_sdf_half[(passes & 1) ? 0 : 1]); //reverse pass order because half size
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
+ u.binding = 3;
+ u.ids.push_back(render_sdf[(passes & 1) ? 0 : 1]); //reverse pass order because it needs an extra JFA pass
+ uniforms.push_back(u);
+ }
+
+ upscale_jfa_uniform_set_index = (passes & 1) ? 0 : 1;
+ sdf_upscale_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, gi->sdfgi_shader.preprocess.version_get_shader(gi->sdfgi_shader.preprocess_shader, SDGIShader::PRE_PROCESS_JUMP_FLOOD_UPSCALE), 0);
+ }
+
+ //occlusion uniform set
+ {
+ Vector<RD::Uniform> uniforms;
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
+ u.binding = 1;
+ u.ids.push_back(render_albedo);
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
+ u.binding = 2;
+ for (int i = 0; i < 8; i++) {
+ u.ids.push_back(render_occlusion[i]);
+ }
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
+ u.binding = 3;
+ u.ids.push_back(render_geom_facing);
+ uniforms.push_back(u);
+ }
+
+ occlusion_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, gi->sdfgi_shader.preprocess.version_get_shader(gi->sdfgi_shader.preprocess_shader, SDGIShader::PRE_PROCESS_OCCLUSION), 0);
+ }
+
+ for (uint32_t i = 0; i < cascades.size(); i++) {
+ //integrate uniform
+
+ Vector<RD::Uniform> uniforms;
+
+ {
+ RD::Uniform u;
+ u.binding = 1;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
+ for (uint32_t j = 0; j < SDFGI::MAX_CASCADES; j++) {
+ if (j < cascades.size()) {
+ u.ids.push_back(cascades[j].sdf_tex);
+ } else {
+ u.ids.push_back(storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_3D_WHITE));
+ }
+ }
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.binding = 2;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
+ for (uint32_t j = 0; j < SDFGI::MAX_CASCADES; j++) {
+ if (j < cascades.size()) {
+ u.ids.push_back(cascades[j].light_tex);
+ } else {
+ u.ids.push_back(storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_3D_WHITE));
+ }
+ }
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.binding = 3;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
+ for (uint32_t j = 0; j < SDFGI::MAX_CASCADES; j++) {
+ if (j < cascades.size()) {
+ u.ids.push_back(cascades[j].light_aniso_0_tex);
+ } else {
+ u.ids.push_back(storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_3D_WHITE));
+ }
+ }
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.binding = 4;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
+ for (uint32_t j = 0; j < SDFGI::MAX_CASCADES; j++) {
+ if (j < cascades.size()) {
+ u.ids.push_back(cascades[j].light_aniso_1_tex);
+ } else {
+ u.ids.push_back(storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_3D_WHITE));
+ }
+ }
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_SAMPLER;
+ u.binding = 6;
+ u.ids.push_back(storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED));
+ uniforms.push_back(u);
+ }
+
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER;
+ u.binding = 7;
+ u.ids.push_back(cascades_ubo);
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
+ u.binding = 8;
+ u.ids.push_back(lightprobe_data);
+ uniforms.push_back(u);
+ }
+
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
+ u.binding = 9;
+ u.ids.push_back(cascades[i].lightprobe_history_tex);
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
+ u.binding = 10;
+ u.ids.push_back(cascades[i].lightprobe_average_tex);
+ uniforms.push_back(u);
+ }
+
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
+ u.binding = 11;
+ u.ids.push_back(lightprobe_history_scroll);
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
+ u.binding = 12;
+ u.ids.push_back(lightprobe_average_scroll);
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
+ u.binding = 13;
+ RID parent_average;
+ if (i < cascades.size() - 1) {
+ parent_average = cascades[i + 1].lightprobe_average_tex;
+ } else {
+ parent_average = cascades[i - 1].lightprobe_average_tex; //to use something, but it won't be used
+ }
+ u.ids.push_back(parent_average);
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
+ u.binding = 14;
+ u.ids.push_back(ambient_texture);
+ uniforms.push_back(u);
+ }
+
+ cascades[i].integrate_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, gi->sdfgi_shader.integrate.version_get_shader(gi->sdfgi_shader.integrate_shader, 0), 0);
+ }
+
+ bounce_feedback = p_env->sdfgi_bounce_feedback;
+ energy = p_env->sdfgi_energy;
+ normal_bias = p_env->sdfgi_normal_bias;
+ probe_bias = p_env->sdfgi_probe_bias;
+ reads_sky = p_env->sdfgi_read_sky_light;
+}
+
+void RendererSceneGIRD::SDFGI::erase() {
+ for (uint32_t i = 0; i < cascades.size(); i++) {
+ const SDFGI::Cascade &c = cascades[i];
+ RD::get_singleton()->free(c.light_data);
+ RD::get_singleton()->free(c.light_aniso_0_tex);
+ RD::get_singleton()->free(c.light_aniso_1_tex);
+ RD::get_singleton()->free(c.sdf_tex);
+ RD::get_singleton()->free(c.solid_cell_dispatch_buffer);
+ RD::get_singleton()->free(c.solid_cell_buffer);
+ RD::get_singleton()->free(c.lightprobe_history_tex);
+ RD::get_singleton()->free(c.lightprobe_average_tex);
+ RD::get_singleton()->free(c.lights_buffer);
+ }
+
+ RD::get_singleton()->free(render_albedo);
+ RD::get_singleton()->free(render_emission);
+ RD::get_singleton()->free(render_emission_aniso);
+
+ RD::get_singleton()->free(render_sdf[0]);
+ RD::get_singleton()->free(render_sdf[1]);
+
+ RD::get_singleton()->free(render_sdf_half[0]);
+ RD::get_singleton()->free(render_sdf_half[1]);
+
+ for (int i = 0; i < 8; i++) {
+ RD::get_singleton()->free(render_occlusion[i]);
+ }
+
+ RD::get_singleton()->free(render_geom_facing);
+
+ RD::get_singleton()->free(lightprobe_data);
+ RD::get_singleton()->free(lightprobe_history_scroll);
+ RD::get_singleton()->free(occlusion_data);
+ RD::get_singleton()->free(ambient_texture);
+
+ RD::get_singleton()->free(cascades_ubo);
+}
+
+void RendererSceneGIRD::SDFGI::update(RendererSceneEnvironmentRD *p_env, const Vector3 &p_world_position) {
+ bounce_feedback = p_env->sdfgi_bounce_feedback;
+ energy = p_env->sdfgi_energy;
+ normal_bias = p_env->sdfgi_normal_bias;
+ probe_bias = p_env->sdfgi_probe_bias;
+ reads_sky = p_env->sdfgi_read_sky_light;
+
+ int32_t drag_margin = (cascade_size / SDFGI::PROBE_DIVISOR) / 2;
+
+ for (uint32_t i = 0; i < cascades.size(); i++) {
+ SDFGI::Cascade &cascade = cascades[i];
+ cascade.dirty_regions = Vector3i();
+
+ Vector3 probe_half_size = Vector3(1, 1, 1) * cascade.cell_size * float(cascade_size / SDFGI::PROBE_DIVISOR) * 0.5;
+ probe_half_size = Vector3(0, 0, 0);
+
+ Vector3 world_position = p_world_position;
+ world_position.y *= y_mult;
+ Vector3i pos_in_cascade = Vector3i((world_position + probe_half_size) / cascade.cell_size);
+
+ for (int j = 0; j < 3; j++) {
+ if (pos_in_cascade[j] < cascade.position[j]) {
+ while (pos_in_cascade[j] < (cascade.position[j] - drag_margin)) {
+ cascade.position[j] -= drag_margin * 2;
+ cascade.dirty_regions[j] += drag_margin * 2;
+ }
+ } else if (pos_in_cascade[j] > cascade.position[j]) {
+ while (pos_in_cascade[j] > (cascade.position[j] + drag_margin)) {
+ cascade.position[j] += drag_margin * 2;
+ cascade.dirty_regions[j] -= drag_margin * 2;
+ }
+ }
+
+ if (cascade.dirty_regions[j] == 0) {
+ continue; // not dirty
+ } else if (uint32_t(ABS(cascade.dirty_regions[j])) >= cascade_size) {
+ //moved too much, just redraw everything (make all dirty)
+ cascade.dirty_regions = SDFGI::Cascade::DIRTY_ALL;
+ break;
+ }
+ }
+
+ if (cascade.dirty_regions != Vector3i() && cascade.dirty_regions != SDFGI::Cascade::DIRTY_ALL) {
+ //see how much the total dirty volume represents from the total volume
+ uint32_t total_volume = cascade_size * cascade_size * cascade_size;
+ uint32_t safe_volume = 1;
+ for (int j = 0; j < 3; j++) {
+ safe_volume *= cascade_size - ABS(cascade.dirty_regions[j]);
+ }
+ uint32_t dirty_volume = total_volume - safe_volume;
+ if (dirty_volume > (safe_volume / 2)) {
+ //more than half the volume is dirty, make all dirty so its only rendered once
+ cascade.dirty_regions = SDFGI::Cascade::DIRTY_ALL;
+ }
+ }
+ }
+}
+
+void RendererSceneGIRD::SDFGI::update_light() {
+ RD::get_singleton()->draw_command_begin_label("SDFGI Update dynamic Light");
+
+ /* Update dynamic light */
+
+ RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, gi->sdfgi_shader.direct_light_pipeline[SDGIShader::DIRECT_LIGHT_MODE_DYNAMIC]);
+
+ SDGIShader::DirectLightPushConstant push_constant;
+
+ push_constant.grid_size[0] = cascade_size;
+ push_constant.grid_size[1] = cascade_size;
+ push_constant.grid_size[2] = cascade_size;
+ push_constant.max_cascades = cascades.size();
+ push_constant.probe_axis_size = probe_axis_count;
+ push_constant.bounce_feedback = bounce_feedback;
+ push_constant.y_mult = y_mult;
+ push_constant.use_occlusion = uses_occlusion;
+
+ for (uint32_t i = 0; i < cascades.size(); i++) {
+ SDFGI::Cascade &cascade = cascades[i];
+ push_constant.light_count = cascade_dynamic_light_count[i];
+ push_constant.cascade = i;
+
+ if (cascades[i].all_dynamic_lights_dirty || gi->sdfgi_frames_to_update_light == RS::ENV_SDFGI_UPDATE_LIGHT_IN_1_FRAME) {
+ push_constant.process_offset = 0;
+ push_constant.process_increment = 1;
+ } else {
+ static uint32_t frames_to_update_table[RS::ENV_SDFGI_UPDATE_LIGHT_MAX] = {
+ 1, 2, 4, 8, 16
+ };
+
+ uint32_t frames_to_update = frames_to_update_table[gi->sdfgi_frames_to_update_light];
+
+ push_constant.process_offset = RSG::rasterizer->get_frame_number() % frames_to_update;
+ push_constant.process_increment = frames_to_update;
+ }
+ cascades[i].all_dynamic_lights_dirty = false;
+
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, cascade.sdf_direct_light_uniform_set, 0);
+ RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(SDGIShader::DirectLightPushConstant));
+ RD::get_singleton()->compute_list_dispatch_indirect(compute_list, cascade.solid_cell_dispatch_buffer, 0);
+ }
+ RD::get_singleton()->compute_list_end(RD::BARRIER_MASK_COMPUTE);
+ RD::get_singleton()->draw_command_end_label();
+}
+
+void RendererSceneGIRD::SDFGI::update_probes(RendererSceneEnvironmentRD *p_env, RendererSceneSkyRD::Sky *p_sky) {
+ RD::get_singleton()->draw_command_begin_label("SDFGI Update Probes");
+
+ SDGIShader::IntegratePushConstant push_constant;
+ push_constant.grid_size[1] = cascade_size;
+ push_constant.grid_size[2] = cascade_size;
+ push_constant.grid_size[0] = cascade_size;
+ push_constant.max_cascades = cascades.size();
+ push_constant.probe_axis_size = probe_axis_count;
+ push_constant.history_index = render_pass % history_size;
+ push_constant.history_size = history_size;
+ static const uint32_t ray_count[RS::ENV_SDFGI_RAY_COUNT_MAX] = { 4, 8, 16, 32, 64, 96, 128 };
+ push_constant.ray_count = ray_count[gi->sdfgi_ray_count];
+ push_constant.ray_bias = probe_bias;
+ push_constant.image_size[0] = probe_axis_count * probe_axis_count;
+ push_constant.image_size[1] = probe_axis_count;
+ push_constant.store_ambient_texture = p_env->volumetric_fog_enabled;
+
+ RID sky_uniform_set = gi->sdfgi_shader.integrate_default_sky_uniform_set;
+ push_constant.sky_mode = SDGIShader::IntegratePushConstant::SKY_MODE_DISABLED;
+ push_constant.y_mult = y_mult;
+
+ if (reads_sky && p_env) {
+ push_constant.sky_energy = p_env->bg_energy;
+
+ if (p_env->background == RS::ENV_BG_CLEAR_COLOR) {
+ push_constant.sky_mode = SDGIShader::IntegratePushConstant::SKY_MODE_COLOR;
+ Color c = storage->get_default_clear_color().to_linear();
+ push_constant.sky_color[0] = c.r;
+ push_constant.sky_color[1] = c.g;
+ push_constant.sky_color[2] = c.b;
+ } else if (p_env->background == RS::ENV_BG_COLOR) {
+ push_constant.sky_mode = SDGIShader::IntegratePushConstant::SKY_MODE_COLOR;
+ Color c = p_env->bg_color;
+ push_constant.sky_color[0] = c.r;
+ push_constant.sky_color[1] = c.g;
+ push_constant.sky_color[2] = c.b;
+
+ } else if (p_env->background == RS::ENV_BG_SKY) {
+ if (p_sky && p_sky->radiance.is_valid()) {
+ if (integrate_sky_uniform_set.is_null() || !RD::get_singleton()->uniform_set_is_valid(integrate_sky_uniform_set)) {
+ Vector<RD::Uniform> uniforms;
+
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
+ u.binding = 0;
+ u.ids.push_back(p_sky->radiance);
+ uniforms.push_back(u);
+ }
+
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_SAMPLER;
+ u.binding = 1;
+ u.ids.push_back(storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED));
+ uniforms.push_back(u);
+ }
+
+ integrate_sky_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, gi->sdfgi_shader.integrate.version_get_shader(gi->sdfgi_shader.integrate_shader, 0), 1);
+ }
+ sky_uniform_set = integrate_sky_uniform_set;
+ push_constant.sky_mode = SDGIShader::IntegratePushConstant::SKY_MODE_SKY;
+ }
+ }
+ }
+
+ render_pass++;
+
+ RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(true);
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, gi->sdfgi_shader.integrate_pipeline[SDGIShader::INTEGRATE_MODE_PROCESS]);
+
+ int32_t probe_divisor = cascade_size / SDFGI::PROBE_DIVISOR;
+ for (uint32_t i = 0; i < cascades.size(); i++) {
+ push_constant.cascade = i;
+ push_constant.world_offset[0] = cascades[i].position.x / probe_divisor;
+ push_constant.world_offset[1] = cascades[i].position.y / probe_divisor;
+ push_constant.world_offset[2] = cascades[i].position.z / probe_divisor;
+
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, cascades[i].integrate_uniform_set, 0);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, sky_uniform_set, 1);
+
+ RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(SDGIShader::IntegratePushConstant));
+ RD::get_singleton()->compute_list_dispatch_threads(compute_list, probe_axis_count * probe_axis_count, probe_axis_count, 1);
+ }
+
+ //end later after raster to avoid barriering on layout changes
+ //RD::get_singleton()->compute_list_end(RD::BARRIER_MASK_NO_BARRIER);
+
+ RD::get_singleton()->draw_command_end_label();
+}
+
+void RendererSceneGIRD::SDFGI::store_probes() {
+ RD::get_singleton()->barrier(RD::BARRIER_MASK_COMPUTE, RD::BARRIER_MASK_COMPUTE);
+ RD::get_singleton()->draw_command_begin_label("SDFGI Store Probes");
+
+ SDGIShader::IntegratePushConstant push_constant;
+ push_constant.grid_size[1] = cascade_size;
+ push_constant.grid_size[2] = cascade_size;
+ push_constant.grid_size[0] = cascade_size;
+ push_constant.max_cascades = cascades.size();
+ push_constant.probe_axis_size = probe_axis_count;
+ push_constant.history_index = render_pass % history_size;
+ push_constant.history_size = history_size;
+ static const uint32_t ray_count[RS::ENV_SDFGI_RAY_COUNT_MAX] = { 4, 8, 16, 32, 64, 96, 128 };
+ push_constant.ray_count = ray_count[gi->sdfgi_ray_count];
+ push_constant.ray_bias = probe_bias;
+ push_constant.image_size[0] = probe_axis_count * probe_axis_count;
+ push_constant.image_size[1] = probe_axis_count;
+ push_constant.store_ambient_texture = false;
+
+ push_constant.sky_mode = 0;
+ push_constant.y_mult = y_mult;
+
+ // Then store values into the lightprobe texture. Separating these steps has a small performance hit, but it allows for multiple bounces
+ RENDER_TIMESTAMP("Average Probes");
+
+ RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, gi->sdfgi_shader.integrate_pipeline[SDGIShader::INTEGRATE_MODE_STORE]);
+
+ //convert to octahedral to store
+ push_constant.image_size[0] *= SDFGI::LIGHTPROBE_OCT_SIZE;
+ push_constant.image_size[1] *= SDFGI::LIGHTPROBE_OCT_SIZE;
+
+ for (uint32_t i = 0; i < cascades.size(); i++) {
+ push_constant.cascade = i;
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, cascades[i].integrate_uniform_set, 0);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, gi->sdfgi_shader.integrate_default_sky_uniform_set, 1);
+ RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(SDGIShader::IntegratePushConstant));
+ RD::get_singleton()->compute_list_dispatch_threads(compute_list, probe_axis_count * probe_axis_count * SDFGI::LIGHTPROBE_OCT_SIZE, probe_axis_count * SDFGI::LIGHTPROBE_OCT_SIZE, 1);
+ }
+
+ RD::get_singleton()->compute_list_end(RD::BARRIER_MASK_COMPUTE);
+
+ RD::get_singleton()->draw_command_end_label();
+}
+
+int RendererSceneGIRD::SDFGI::get_pending_region_data(int p_region, Vector3i &r_local_offset, Vector3i &r_local_size, AABB &r_bounds) const {
+ int dirty_count = 0;
+ for (uint32_t i = 0; i < cascades.size(); i++) {
+ const SDFGI::Cascade &c = cascades[i];
+
+ if (c.dirty_regions == SDFGI::Cascade::DIRTY_ALL) {
+ if (dirty_count == p_region) {
+ r_local_offset = Vector3i();
+ r_local_size = Vector3i(1, 1, 1) * cascade_size;
+
+ r_bounds.position = Vector3((Vector3i(1, 1, 1) * -int32_t(cascade_size >> 1) + c.position)) * c.cell_size * Vector3(1, 1.0 / y_mult, 1);
+ r_bounds.size = Vector3(r_local_size) * c.cell_size * Vector3(1, 1.0 / y_mult, 1);
+ return i;
+ }
+ dirty_count++;
+ } else {
+ for (int j = 0; j < 3; j++) {
+ if (c.dirty_regions[j] != 0) {
+ if (dirty_count == p_region) {
+ Vector3i from = Vector3i(0, 0, 0);
+ Vector3i to = Vector3i(1, 1, 1) * cascade_size;
+
+ if (c.dirty_regions[j] > 0) {
+ //fill from the beginning
+ to[j] = c.dirty_regions[j];
+ } else {
+ //fill from the end
+ from[j] = to[j] + c.dirty_regions[j];
+ }
+
+ for (int k = 0; k < j; k++) {
+ // "chip" away previous regions to avoid re-voxelizing the same thing
+ if (c.dirty_regions[k] > 0) {
+ from[k] += c.dirty_regions[k];
+ } else if (c.dirty_regions[k] < 0) {
+ to[k] += c.dirty_regions[k];
+ }
+ }
+
+ r_local_offset = from;
+ r_local_size = to - from;
+
+ r_bounds.position = Vector3(from + Vector3i(1, 1, 1) * -int32_t(cascade_size >> 1) + c.position) * c.cell_size * Vector3(1, 1.0 / y_mult, 1);
+ r_bounds.size = Vector3(r_local_size) * c.cell_size * Vector3(1, 1.0 / y_mult, 1);
+
+ return i;
+ }
+
+ dirty_count++;
+ }
+ }
+ }
+ }
+ return -1;
+}
+
+void RendererSceneGIRD::SDFGI::update_cascades() {
+ //update cascades
+ SDFGI::Cascade::UBO cascade_data[SDFGI::MAX_CASCADES];
+ int32_t probe_divisor = cascade_size / SDFGI::PROBE_DIVISOR;
+
+ for (uint32_t i = 0; i < cascades.size(); i++) {
+ Vector3 pos = Vector3((Vector3i(1, 1, 1) * -int32_t(cascade_size >> 1) + cascades[i].position)) * cascades[i].cell_size;
+
+ cascade_data[i].offset[0] = pos.x;
+ cascade_data[i].offset[1] = pos.y;
+ cascade_data[i].offset[2] = pos.z;
+ cascade_data[i].to_cell = 1.0 / cascades[i].cell_size;
+ cascade_data[i].probe_offset[0] = cascades[i].position.x / probe_divisor;
+ cascade_data[i].probe_offset[1] = cascades[i].position.y / probe_divisor;
+ cascade_data[i].probe_offset[2] = cascades[i].position.z / probe_divisor;
+ cascade_data[i].pad = 0;
+ }
+
+ RD::get_singleton()->buffer_update(cascades_ubo, 0, sizeof(SDFGI::Cascade::UBO) * SDFGI::MAX_CASCADES, cascade_data, RD::BARRIER_MASK_COMPUTE);
+}
+
+void RendererSceneGIRD::SDFGI::debug_draw(const CameraMatrix &p_projection, const Transform &p_transform, int p_width, int p_height, RID p_render_target, RID p_texture) {
+ // !BAS! Need to find a nicer way then adding width/height/renderbuffer/texture as parameters
+
+ if (!debug_uniform_set.is_valid() || !RD::get_singleton()->uniform_set_is_valid(debug_uniform_set)) {
+ Vector<RD::Uniform> uniforms;
+ {
+ RD::Uniform u;
+ u.binding = 1;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
+ for (uint32_t i = 0; i < SDFGI::MAX_CASCADES; i++) {
+ if (i < cascades.size()) {
+ u.ids.push_back(cascades[i].sdf_tex);
+ } else {
+ u.ids.push_back(storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_3D_WHITE));
+ }
+ }
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.binding = 2;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
+ for (uint32_t i = 0; i < SDFGI::MAX_CASCADES; i++) {
+ if (i < cascades.size()) {
+ u.ids.push_back(cascades[i].light_tex);
+ } else {
+ u.ids.push_back(storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_3D_WHITE));
+ }
+ }
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.binding = 3;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
+ for (uint32_t i = 0; i < SDFGI::MAX_CASCADES; i++) {
+ if (i < cascades.size()) {
+ u.ids.push_back(cascades[i].light_aniso_0_tex);
+ } else {
+ u.ids.push_back(storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_3D_WHITE));
+ }
+ }
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.binding = 4;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
+ for (uint32_t i = 0; i < SDFGI::MAX_CASCADES; i++) {
+ if (i < cascades.size()) {
+ u.ids.push_back(cascades[i].light_aniso_1_tex);
+ } else {
+ u.ids.push_back(storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_3D_WHITE));
+ }
+ }
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.binding = 5;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
+ u.ids.push_back(occlusion_texture);
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.binding = 8;
+ u.uniform_type = RD::UNIFORM_TYPE_SAMPLER;
+ u.ids.push_back(storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED));
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.binding = 9;
+ u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER;
+ u.ids.push_back(cascades_ubo);
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.binding = 10;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
+ u.ids.push_back(p_texture);
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.binding = 11;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
+ u.ids.push_back(lightprobe_texture);
+ uniforms.push_back(u);
+ }
+ debug_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, gi->sdfgi_shader.debug_shader_version, 0);
+ }
+
+ RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, gi->sdfgi_shader.debug_pipeline);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, debug_uniform_set, 0);
+
+ SDGIShader::DebugPushConstant push_constant;
+ push_constant.grid_size[0] = cascade_size;
+ push_constant.grid_size[1] = cascade_size;
+ push_constant.grid_size[2] = cascade_size;
+ push_constant.max_cascades = cascades.size();
+ push_constant.screen_size[0] = p_width;
+ push_constant.screen_size[1] = p_height;
+ push_constant.probe_axis_size = probe_axis_count;
+ push_constant.use_occlusion = uses_occlusion;
+ push_constant.y_mult = y_mult;
+
+ Vector2 vp_half = p_projection.get_viewport_half_extents();
+ push_constant.cam_extent[0] = vp_half.x;
+ push_constant.cam_extent[1] = vp_half.y;
+ push_constant.cam_extent[2] = -p_projection.get_z_near();
+
+ push_constant.cam_transform[0] = p_transform.basis.elements[0][0];
+ push_constant.cam_transform[1] = p_transform.basis.elements[1][0];
+ push_constant.cam_transform[2] = p_transform.basis.elements[2][0];
+ push_constant.cam_transform[3] = 0;
+ push_constant.cam_transform[4] = p_transform.basis.elements[0][1];
+ push_constant.cam_transform[5] = p_transform.basis.elements[1][1];
+ push_constant.cam_transform[6] = p_transform.basis.elements[2][1];
+ push_constant.cam_transform[7] = 0;
+ push_constant.cam_transform[8] = p_transform.basis.elements[0][2];
+ push_constant.cam_transform[9] = p_transform.basis.elements[1][2];
+ push_constant.cam_transform[10] = p_transform.basis.elements[2][2];
+ push_constant.cam_transform[11] = 0;
+ push_constant.cam_transform[12] = p_transform.origin.x;
+ push_constant.cam_transform[13] = p_transform.origin.y;
+ push_constant.cam_transform[14] = p_transform.origin.z;
+ push_constant.cam_transform[15] = 1;
+
+ RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(SDGIShader::DebugPushConstant));
+
+ RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_width, p_height, 1);
+ RD::get_singleton()->compute_list_end();
+
+ Size2 rtsize = storage->render_target_get_size(p_render_target);
+ storage->get_effects()->copy_to_fb_rect(p_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) {
+ SDGIShader::DebugProbesPushConstant push_constant;
+
+ for (int i = 0; i < 4; i++) {
+ for (int j = 0; j < 4; j++) {
+ push_constant.projection[i * 4 + j] = p_camera_with_transform.matrix[i][j];
+ }
+ }
+
+ //gen spheres from strips
+ uint32_t band_points = 16;
+ push_constant.band_power = 4;
+ push_constant.sections_in_band = ((band_points / 2) - 1);
+ push_constant.band_mask = band_points - 2;
+ push_constant.section_arc = Math_TAU / float(push_constant.sections_in_band);
+ push_constant.y_mult = y_mult;
+
+ uint32_t total_points = push_constant.sections_in_band * band_points;
+ uint32_t total_probes = probe_axis_count * probe_axis_count * probe_axis_count;
+
+ push_constant.grid_size[0] = cascade_size;
+ push_constant.grid_size[1] = cascade_size;
+ push_constant.grid_size[2] = cascade_size;
+ push_constant.cascade = 0;
+
+ push_constant.probe_axis_size = probe_axis_count;
+
+ if (!debug_probes_uniform_set.is_valid() || !RD::get_singleton()->uniform_set_is_valid(debug_probes_uniform_set)) {
+ Vector<RD::Uniform> uniforms;
+ {
+ RD::Uniform u;
+ u.binding = 1;
+ u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER;
+ u.ids.push_back(cascades_ubo);
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.binding = 2;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
+ u.ids.push_back(lightprobe_texture);
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.binding = 3;
+ u.uniform_type = RD::UNIFORM_TYPE_SAMPLER;
+ u.ids.push_back(storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED));
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.binding = 4;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
+ u.ids.push_back(occlusion_texture);
+ uniforms.push_back(u);
+ }
+
+ debug_probes_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, gi->sdfgi_shader.debug_probes.version_get_shader(gi->sdfgi_shader.debug_probes_shader, 0), 0);
+ }
+
+ RD::get_singleton()->draw_list_bind_render_pipeline(p_draw_list, gi->sdfgi_shader.debug_probes_pipeline[SDGIShader::PROBE_DEBUG_PROBES].get_render_pipeline(RD::INVALID_FORMAT_ID, RD::get_singleton()->framebuffer_get_format(p_framebuffer)));
+ RD::get_singleton()->draw_list_bind_uniform_set(p_draw_list, debug_probes_uniform_set, 0);
+ RD::get_singleton()->draw_list_set_push_constant(p_draw_list, &push_constant, sizeof(SDGIShader::DebugProbesPushConstant));
+ RD::get_singleton()->draw_list_draw(p_draw_list, false, total_probes, total_points);
+
+ if (gi->sdfgi_debug_probe_dir != Vector3()) {
+ print_line("CLICK DEBUG ME?");
+ uint32_t cascade = 0;
+ Vector3 offset = Vector3((Vector3i(1, 1, 1) * -int32_t(cascade_size >> 1) + cascades[cascade].position)) * cascades[cascade].cell_size * Vector3(1.0, 1.0 / y_mult, 1.0);
+ Vector3 probe_size = cascades[cascade].cell_size * (cascade_size / SDFGI::PROBE_DIVISOR) * Vector3(1.0, 1.0 / y_mult, 1.0);
+ Vector3 ray_from = gi->sdfgi_debug_probe_pos;
+ Vector3 ray_to = gi->sdfgi_debug_probe_pos + gi->sdfgi_debug_probe_dir * cascades[cascade].cell_size * Math::sqrt(3.0) * cascade_size;
+ float sphere_radius = 0.2;
+ float closest_dist = 1e20;
+ gi->sdfgi_debug_probe_enabled = false;
+
+ Vector3i probe_from = cascades[cascade].position / (cascade_size / SDFGI::PROBE_DIVISOR);
+ for (int i = 0; i < (SDFGI::PROBE_DIVISOR + 1); i++) {
+ for (int j = 0; j < (SDFGI::PROBE_DIVISOR + 1); j++) {
+ for (int k = 0; k < (SDFGI::PROBE_DIVISOR + 1); k++) {
+ Vector3 pos = offset + probe_size * Vector3(i, j, k);
+ Vector3 res;
+ if (Geometry3D::segment_intersects_sphere(ray_from, ray_to, pos, sphere_radius, &res)) {
+ float d = ray_from.distance_to(res);
+ if (d < closest_dist) {
+ closest_dist = d;
+ gi->sdfgi_debug_probe_enabled = true;
+ gi->sdfgi_debug_probe_index = probe_from + Vector3i(i, j, k);
+ }
+ }
+ }
+ }
+ }
+
+ if (gi->sdfgi_debug_probe_enabled) {
+ print_line("found: " + gi->sdfgi_debug_probe_index);
+ } else {
+ print_line("no found");
+ }
+ gi->sdfgi_debug_probe_dir = Vector3();
+ }
+
+ if (gi->sdfgi_debug_probe_enabled) {
+ uint32_t cascade = 0;
+ uint32_t probe_cells = (cascade_size / SDFGI::PROBE_DIVISOR);
+ Vector3i probe_from = cascades[cascade].position / probe_cells;
+ Vector3i ofs = gi->sdfgi_debug_probe_index - probe_from;
+ if (ofs.x < 0 || ofs.y < 0 || ofs.z < 0) {
+ return;
+ }
+ if (ofs.x > SDFGI::PROBE_DIVISOR || ofs.y > SDFGI::PROBE_DIVISOR || ofs.z > SDFGI::PROBE_DIVISOR) {
+ return;
+ }
+
+ uint32_t mult = (SDFGI::PROBE_DIVISOR + 1);
+ uint32_t index = ofs.z * mult * mult + ofs.y * mult + ofs.x;
+
+ push_constant.probe_debug_index = index;
+
+ uint32_t cell_count = probe_cells * 2 * probe_cells * 2 * probe_cells * 2;
+
+ RD::get_singleton()->draw_list_bind_render_pipeline(p_draw_list, gi->sdfgi_shader.debug_probes_pipeline[SDGIShader::PROBE_DEBUG_VISIBILITY].get_render_pipeline(RD::INVALID_FORMAT_ID, RD::get_singleton()->framebuffer_get_format(p_framebuffer)));
+ RD::get_singleton()->draw_list_bind_uniform_set(p_draw_list, debug_probes_uniform_set, 0);
+ RD::get_singleton()->draw_list_set_push_constant(p_draw_list, &push_constant, sizeof(SDGIShader::DebugProbesPushConstant));
+ RD::get_singleton()->draw_list_draw(p_draw_list, false, cell_count, total_points);
+ }
+}
+
+void RendererSceneGIRD::SDFGI::pre_process_gi(const Transform &p_transform, RendererSceneRenderRD *p_scene_render) {
+ /* Update general SDFGI Buffer */
+
+ SDFGIData sdfgi_data;
+
+ sdfgi_data.grid_size[0] = cascade_size;
+ sdfgi_data.grid_size[1] = cascade_size;
+ sdfgi_data.grid_size[2] = cascade_size;
+
+ sdfgi_data.max_cascades = cascades.size();
+ sdfgi_data.probe_axis_size = probe_axis_count;
+ sdfgi_data.cascade_probe_size[0] = sdfgi_data.probe_axis_size - 1; //float version for performance
+ sdfgi_data.cascade_probe_size[1] = sdfgi_data.probe_axis_size - 1;
+ sdfgi_data.cascade_probe_size[2] = sdfgi_data.probe_axis_size - 1;
+
+ float csize = cascade_size;
+ sdfgi_data.probe_to_uvw = 1.0 / float(sdfgi_data.cascade_probe_size[0]);
+ sdfgi_data.use_occlusion = uses_occlusion;
+ //sdfgi_data.energy = energy;
+
+ sdfgi_data.y_mult = y_mult;
+
+ float cascade_voxel_size = (csize / sdfgi_data.cascade_probe_size[0]);
+ float occlusion_clamp = (cascade_voxel_size - 0.5) / cascade_voxel_size;
+ sdfgi_data.occlusion_clamp[0] = occlusion_clamp;
+ sdfgi_data.occlusion_clamp[1] = occlusion_clamp;
+ sdfgi_data.occlusion_clamp[2] = occlusion_clamp;
+ sdfgi_data.normal_bias = (normal_bias / csize) * sdfgi_data.cascade_probe_size[0];
+
+ //vec2 tex_pixel_size = 1.0 / vec2(ivec2( (OCT_SIZE+2) * params.probe_axis_size * params.probe_axis_size, (OCT_SIZE+2) * params.probe_axis_size ) );
+ //vec3 probe_uv_offset = (ivec3(OCT_SIZE+2,OCT_SIZE+2,(OCT_SIZE+2) * params.probe_axis_size)) * tex_pixel_size.xyx;
+
+ uint32_t oct_size = SDFGI::LIGHTPROBE_OCT_SIZE;
+
+ sdfgi_data.lightprobe_tex_pixel_size[0] = 1.0 / ((oct_size + 2) * sdfgi_data.probe_axis_size * sdfgi_data.probe_axis_size);
+ sdfgi_data.lightprobe_tex_pixel_size[1] = 1.0 / ((oct_size + 2) * sdfgi_data.probe_axis_size);
+ sdfgi_data.lightprobe_tex_pixel_size[2] = 1.0;
+
+ sdfgi_data.energy = energy;
+
+ sdfgi_data.lightprobe_uv_offset[0] = float(oct_size + 2) * sdfgi_data.lightprobe_tex_pixel_size[0];
+ sdfgi_data.lightprobe_uv_offset[1] = float(oct_size + 2) * sdfgi_data.lightprobe_tex_pixel_size[1];
+ sdfgi_data.lightprobe_uv_offset[2] = float((oct_size + 2) * sdfgi_data.probe_axis_size) * sdfgi_data.lightprobe_tex_pixel_size[0];
+
+ sdfgi_data.occlusion_renormalize[0] = 0.5;
+ sdfgi_data.occlusion_renormalize[1] = 1.0;
+ sdfgi_data.occlusion_renormalize[2] = 1.0 / float(sdfgi_data.max_cascades);
+
+ int32_t probe_divisor = cascade_size / SDFGI::PROBE_DIVISOR;
+
+ for (uint32_t i = 0; i < sdfgi_data.max_cascades; i++) {
+ SDFGIData::ProbeCascadeData &c = sdfgi_data.cascades[i];
+ Vector3 pos = Vector3((Vector3i(1, 1, 1) * -int32_t(cascade_size >> 1) + cascades[i].position)) * cascades[i].cell_size;
+ Vector3 cam_origin = p_transform.origin;
+ cam_origin.y *= y_mult;
+ pos -= cam_origin; //make pos local to camera, to reduce numerical error
+ c.position[0] = pos.x;
+ c.position[1] = pos.y;
+ c.position[2] = pos.z;
+ c.to_probe = 1.0 / (float(cascade_size) * cascades[i].cell_size / float(probe_axis_count - 1));
+
+ Vector3i probe_ofs = cascades[i].position / probe_divisor;
+ c.probe_world_offset[0] = probe_ofs.x;
+ c.probe_world_offset[1] = probe_ofs.y;
+ c.probe_world_offset[2] = probe_ofs.z;
+
+ c.to_cell = 1.0 / cascades[i].cell_size;
+ }
+
+ RD::get_singleton()->buffer_update(gi->sdfgi_ubo, 0, sizeof(SDFGIData), &sdfgi_data, RD::BARRIER_MASK_COMPUTE);
+
+ /* Update dynamic lights in SDFGI cascades */
+
+ for (uint32_t i = 0; i < cascades.size(); i++) {
+ SDFGI::Cascade &cascade = cascades[i];
+
+ SDGIShader::Light lights[SDFGI::MAX_DYNAMIC_LIGHTS];
+ uint32_t idx = 0;
+ for (uint32_t j = 0; j < (uint32_t)p_scene_render->render_state.sdfgi_update_data->directional_lights->size(); j++) {
+ if (idx == SDFGI::MAX_DYNAMIC_LIGHTS) {
+ break;
+ }
+
+ RendererSceneRenderRD::LightInstance *li = p_scene_render->light_instance_owner.getornull(p_scene_render->render_state.sdfgi_update_data->directional_lights->get(j));
+ ERR_CONTINUE(!li);
+
+ if (storage->light_directional_is_sky_only(li->light)) {
+ continue;
+ }
+
+ Vector3 dir = -li->transform.basis.get_axis(Vector3::AXIS_Z);
+ dir.y *= y_mult;
+ dir.normalize();
+ lights[idx].direction[0] = dir.x;
+ lights[idx].direction[1] = dir.y;
+ lights[idx].direction[2] = dir.z;
+ Color color = storage->light_get_color(li->light);
+ color = color.to_linear();
+ lights[idx].color[0] = color.r;
+ lights[idx].color[1] = color.g;
+ lights[idx].color[2] = color.b;
+ lights[idx].type = RS::LIGHT_DIRECTIONAL;
+ lights[idx].energy = storage->light_get_param(li->light, RS::LIGHT_PARAM_ENERGY);
+ lights[idx].has_shadow = storage->light_has_shadow(li->light);
+
+ idx++;
+ }
+
+ AABB cascade_aabb;
+ cascade_aabb.position = Vector3((Vector3i(1, 1, 1) * -int32_t(cascade_size >> 1) + cascade.position)) * cascade.cell_size;
+ cascade_aabb.size = Vector3(1, 1, 1) * cascade_size * cascade.cell_size;
+
+ for (uint32_t j = 0; j < p_scene_render->render_state.sdfgi_update_data->positional_light_count; j++) {
+ if (idx == SDFGI::MAX_DYNAMIC_LIGHTS) {
+ break;
+ }
+
+ RendererSceneRenderRD::LightInstance *li = p_scene_render->light_instance_owner.getornull(p_scene_render->render_state.sdfgi_update_data->positional_light_instances[j]);
+ ERR_CONTINUE(!li);
+
+ uint32_t max_sdfgi_cascade = storage->light_get_max_sdfgi_cascade(li->light);
+ if (i > max_sdfgi_cascade) {
+ continue;
+ }
+
+ if (!cascade_aabb.intersects(li->aabb)) {
+ continue;
+ }
+
+ Vector3 dir = -li->transform.basis.get_axis(Vector3::AXIS_Z);
+ //faster to not do this here
+ //dir.y *= y_mult;
+ //dir.normalize();
+ lights[idx].direction[0] = dir.x;
+ lights[idx].direction[1] = dir.y;
+ lights[idx].direction[2] = dir.z;
+ Vector3 pos = li->transform.origin;
+ pos.y *= y_mult;
+ lights[idx].position[0] = pos.x;
+ lights[idx].position[1] = pos.y;
+ lights[idx].position[2] = pos.z;
+ Color color = storage->light_get_color(li->light);
+ color = color.to_linear();
+ lights[idx].color[0] = color.r;
+ lights[idx].color[1] = color.g;
+ lights[idx].color[2] = color.b;
+ lights[idx].type = storage->light_get_type(li->light);
+ lights[idx].energy = storage->light_get_param(li->light, RS::LIGHT_PARAM_ENERGY);
+ lights[idx].has_shadow = storage->light_has_shadow(li->light);
+ lights[idx].attenuation = storage->light_get_param(li->light, RS::LIGHT_PARAM_ATTENUATION);
+ lights[idx].radius = storage->light_get_param(li->light, RS::LIGHT_PARAM_RANGE);
+ lights[idx].cos_spot_angle = Math::cos(Math::deg2rad(storage->light_get_param(li->light, RS::LIGHT_PARAM_SPOT_ANGLE)));
+ lights[idx].inv_spot_attenuation = 1.0f / storage->light_get_param(li->light, RS::LIGHT_PARAM_SPOT_ATTENUATION);
+
+ idx++;
+ }
+
+ if (idx > 0) {
+ RD::get_singleton()->buffer_update(cascade.lights_buffer, 0, idx * sizeof(SDGIShader::Light), lights, RD::BARRIER_MASK_COMPUTE);
+ }
+
+ cascade_dynamic_light_count[i] = idx;
+ }
+}
+
+void RendererSceneGIRD::SDFGI::render_region(RID p_render_buffers, int p_region, const PagedArray<RendererSceneRender::GeometryInstance *> &p_instances, RendererSceneRenderRD *p_scene_render) {
+ //print_line("rendering region " + itos(p_region));
+ RendererSceneRenderRD::RenderBuffers *rb = p_scene_render->render_buffers_owner.getornull(p_render_buffers);
+ ERR_FAIL_COND(!rb); // we wouldn't be here if this failed but...
+ AABB bounds;
+ Vector3i from;
+ Vector3i size;
+
+ int cascade_prev = get_pending_region_data(p_region - 1, from, size, bounds);
+ int cascade_next = get_pending_region_data(p_region + 1, from, size, bounds);
+ int cascade = get_pending_region_data(p_region, from, size, bounds);
+ ERR_FAIL_COND(cascade < 0);
+
+ if (cascade_prev != cascade) {
+ //initialize render
+ RD::get_singleton()->texture_clear(render_albedo, Color(0, 0, 0, 0), 0, 1, 0, 1);
+ RD::get_singleton()->texture_clear(render_emission, Color(0, 0, 0, 0), 0, 1, 0, 1);
+ RD::get_singleton()->texture_clear(render_emission_aniso, Color(0, 0, 0, 0), 0, 1, 0, 1);
+ RD::get_singleton()->texture_clear(render_geom_facing, Color(0, 0, 0, 0), 0, 1, 0, 1);
+ }
+
+ //print_line("rendering cascade " + itos(p_region) + " objects: " + itos(p_cull_count) + " bounds: " + bounds + " from: " + from + " size: " + size + " cell size: " + rtos(cascades[cascade].cell_size));
+ p_scene_render->_render_sdfgi(p_render_buffers, from, size, bounds, p_instances, render_albedo, render_emission, render_emission_aniso, render_geom_facing);
+
+ if (cascade_next != cascade) {
+ RD::get_singleton()->draw_command_begin_label("SDFGI Pre-Process Cascade");
+
+ RENDER_TIMESTAMP(">SDFGI Update SDF");
+ //done rendering! must update SDF
+ //clear dispatch indirect data
+
+ SDGIShader::PreprocessPushConstant push_constant;
+ zeromem(&push_constant, sizeof(SDGIShader::PreprocessPushConstant));
+
+ RENDER_TIMESTAMP("Scroll SDF");
+
+ //scroll
+ if (cascades[cascade].dirty_regions != SDFGI::Cascade::DIRTY_ALL) {
+ //for scroll
+ Vector3i dirty = cascades[cascade].dirty_regions;
+ push_constant.scroll[0] = dirty.x;
+ push_constant.scroll[1] = dirty.y;
+ push_constant.scroll[2] = dirty.z;
+ } else {
+ //for no scroll
+ push_constant.scroll[0] = 0;
+ push_constant.scroll[1] = 0;
+ push_constant.scroll[2] = 0;
+ }
+
+ cascades[cascade].all_dynamic_lights_dirty = true;
+
+ push_constant.grid_size = cascade_size;
+ push_constant.cascade = cascade;
+
+ if (cascades[cascade].dirty_regions != SDFGI::Cascade::DIRTY_ALL) {
+ RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
+
+ //must pre scroll existing data because not all is dirty
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, gi->sdfgi_shader.preprocess_pipeline[SDGIShader::PRE_PROCESS_SCROLL]);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, cascades[cascade].scroll_uniform_set, 0);
+
+ RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(SDGIShader::PreprocessPushConstant));
+ RD::get_singleton()->compute_list_dispatch_indirect(compute_list, cascades[cascade].solid_cell_dispatch_buffer, 0);
+ // no barrier do all together
+
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, gi->sdfgi_shader.preprocess_pipeline[SDGIShader::PRE_PROCESS_SCROLL_OCCLUSION]);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, cascades[cascade].scroll_occlusion_uniform_set, 0);
+
+ Vector3i dirty = cascades[cascade].dirty_regions;
+ Vector3i groups;
+ groups.x = cascade_size - ABS(dirty.x);
+ groups.y = cascade_size - ABS(dirty.y);
+ groups.z = cascade_size - ABS(dirty.z);
+
+ RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(SDGIShader::PreprocessPushConstant));
+ RD::get_singleton()->compute_list_dispatch_threads(compute_list, groups.x, groups.y, groups.z);
+
+ //no barrier, continue together
+
+ {
+ //scroll probes and their history also
+
+ SDGIShader::IntegratePushConstant ipush_constant;
+ ipush_constant.grid_size[1] = cascade_size;
+ ipush_constant.grid_size[2] = cascade_size;
+ ipush_constant.grid_size[0] = cascade_size;
+ ipush_constant.max_cascades = cascades.size();
+ ipush_constant.probe_axis_size = probe_axis_count;
+ ipush_constant.history_index = 0;
+ ipush_constant.history_size = history_size;
+ ipush_constant.ray_count = 0;
+ ipush_constant.ray_bias = 0;
+ ipush_constant.sky_mode = 0;
+ ipush_constant.sky_energy = 0;
+ ipush_constant.sky_color[0] = 0;
+ ipush_constant.sky_color[1] = 0;
+ ipush_constant.sky_color[2] = 0;
+ ipush_constant.y_mult = y_mult;
+ ipush_constant.store_ambient_texture = false;
+
+ ipush_constant.image_size[0] = probe_axis_count * probe_axis_count;
+ ipush_constant.image_size[1] = probe_axis_count;
+
+ int32_t probe_divisor = cascade_size / SDFGI::PROBE_DIVISOR;
+ ipush_constant.cascade = cascade;
+ ipush_constant.world_offset[0] = cascades[cascade].position.x / probe_divisor;
+ ipush_constant.world_offset[1] = cascades[cascade].position.y / probe_divisor;
+ ipush_constant.world_offset[2] = cascades[cascade].position.z / probe_divisor;
+
+ ipush_constant.scroll[0] = dirty.x / probe_divisor;
+ ipush_constant.scroll[1] = dirty.y / probe_divisor;
+ ipush_constant.scroll[2] = dirty.z / probe_divisor;
+
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, gi->sdfgi_shader.integrate_pipeline[SDGIShader::INTEGRATE_MODE_SCROLL]);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, cascades[cascade].integrate_uniform_set, 0);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, gi->sdfgi_shader.integrate_default_sky_uniform_set, 1);
+ RD::get_singleton()->compute_list_set_push_constant(compute_list, &ipush_constant, sizeof(SDGIShader::IntegratePushConstant));
+ RD::get_singleton()->compute_list_dispatch_threads(compute_list, probe_axis_count * probe_axis_count, probe_axis_count, 1);
+
+ RD::get_singleton()->compute_list_add_barrier(compute_list);
+
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, gi->sdfgi_shader.integrate_pipeline[SDGIShader::INTEGRATE_MODE_SCROLL_STORE]);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, cascades[cascade].integrate_uniform_set, 0);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, gi->sdfgi_shader.integrate_default_sky_uniform_set, 1);
+ RD::get_singleton()->compute_list_set_push_constant(compute_list, &ipush_constant, sizeof(SDGIShader::IntegratePushConstant));
+ RD::get_singleton()->compute_list_dispatch_threads(compute_list, probe_axis_count * probe_axis_count, probe_axis_count, 1);
+
+ RD::get_singleton()->compute_list_add_barrier(compute_list);
+
+ if (bounce_feedback > 0.0) {
+ //multibounce requires this to be stored so direct light can read from it
+
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, gi->sdfgi_shader.integrate_pipeline[SDGIShader::INTEGRATE_MODE_STORE]);
+
+ //convert to octahedral to store
+ ipush_constant.image_size[0] *= SDFGI::LIGHTPROBE_OCT_SIZE;
+ ipush_constant.image_size[1] *= SDFGI::LIGHTPROBE_OCT_SIZE;
+
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, cascades[cascade].integrate_uniform_set, 0);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, gi->sdfgi_shader.integrate_default_sky_uniform_set, 1);
+ RD::get_singleton()->compute_list_set_push_constant(compute_list, &ipush_constant, sizeof(SDGIShader::IntegratePushConstant));
+ RD::get_singleton()->compute_list_dispatch_threads(compute_list, probe_axis_count * probe_axis_count * SDFGI::LIGHTPROBE_OCT_SIZE, probe_axis_count * SDFGI::LIGHTPROBE_OCT_SIZE, 1);
+ }
+ }
+
+ //ok finally barrier
+ RD::get_singleton()->compute_list_end();
+ }
+
+ //clear dispatch indirect data
+ uint32_t dispatch_indirct_data[4] = { 0, 0, 0, 0 };
+ RD::get_singleton()->buffer_update(cascades[cascade].solid_cell_dispatch_buffer, 0, sizeof(uint32_t) * 4, dispatch_indirct_data);
+
+ RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
+
+ bool half_size = true; //much faster, very little difference
+ static const int optimized_jf_group_size = 8;
+
+ if (half_size) {
+ push_constant.grid_size >>= 1;
+
+ uint32_t cascade_half_size = cascade_size >> 1;
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, gi->sdfgi_shader.preprocess_pipeline[SDGIShader::PRE_PROCESS_JUMP_FLOOD_INITIALIZE_HALF]);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, sdf_initialize_half_uniform_set, 0);
+ RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(SDGIShader::PreprocessPushConstant));
+ RD::get_singleton()->compute_list_dispatch_threads(compute_list, cascade_half_size, cascade_half_size, cascade_half_size);
+ RD::get_singleton()->compute_list_add_barrier(compute_list);
+
+ //must start with regular jumpflood
+
+ push_constant.half_size = true;
+ {
+ RENDER_TIMESTAMP("SDFGI Jump Flood (Half Size)");
+
+ uint32_t s = cascade_half_size;
+
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, gi->sdfgi_shader.preprocess_pipeline[SDGIShader::PRE_PROCESS_JUMP_FLOOD]);
+
+ int jf_us = 0;
+ //start with regular jump flood for very coarse reads, as this is impossible to optimize
+ while (s > 1) {
+ s /= 2;
+ push_constant.step_size = s;
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, jump_flood_half_uniform_set[jf_us], 0);
+ RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(SDGIShader::PreprocessPushConstant));
+ RD::get_singleton()->compute_list_dispatch_threads(compute_list, cascade_half_size, cascade_half_size, cascade_half_size);
+ RD::get_singleton()->compute_list_add_barrier(compute_list);
+ jf_us = jf_us == 0 ? 1 : 0;
+
+ if (cascade_half_size / (s / 2) >= optimized_jf_group_size) {
+ break;
+ }
+ }
+
+ RENDER_TIMESTAMP("SDFGI Jump Flood Optimized (Half Size)");
+
+ //continue with optimized jump flood for smaller reads
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, gi->sdfgi_shader.preprocess_pipeline[SDGIShader::PRE_PROCESS_JUMP_FLOOD_OPTIMIZED]);
+ while (s > 1) {
+ s /= 2;
+ push_constant.step_size = s;
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, jump_flood_half_uniform_set[jf_us], 0);
+ RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(SDGIShader::PreprocessPushConstant));
+ RD::get_singleton()->compute_list_dispatch_threads(compute_list, cascade_half_size, cascade_half_size, cascade_half_size);
+ RD::get_singleton()->compute_list_add_barrier(compute_list);
+ jf_us = jf_us == 0 ? 1 : 0;
+ }
+ }
+
+ // restore grid size for last passes
+ push_constant.grid_size = cascade_size;
+
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, gi->sdfgi_shader.preprocess_pipeline[SDGIShader::PRE_PROCESS_JUMP_FLOOD_UPSCALE]);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, sdf_upscale_uniform_set, 0);
+ RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(SDGIShader::PreprocessPushConstant));
+ RD::get_singleton()->compute_list_dispatch_threads(compute_list, cascade_size, cascade_size, cascade_size);
+ RD::get_singleton()->compute_list_add_barrier(compute_list);
+
+ //run one pass of fullsize jumpflood to fix up half size arctifacts
+
+ push_constant.half_size = false;
+ push_constant.step_size = 1;
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, gi->sdfgi_shader.preprocess_pipeline[SDGIShader::PRE_PROCESS_JUMP_FLOOD_OPTIMIZED]);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, jump_flood_uniform_set[upscale_jfa_uniform_set_index], 0);
+ RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(SDGIShader::PreprocessPushConstant));
+ RD::get_singleton()->compute_list_dispatch_threads(compute_list, cascade_size, cascade_size, cascade_size);
+ RD::get_singleton()->compute_list_add_barrier(compute_list);
+
+ } else {
+ //full size jumpflood
+ RENDER_TIMESTAMP("SDFGI Jump Flood");
+
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, gi->sdfgi_shader.preprocess_pipeline[SDGIShader::PRE_PROCESS_JUMP_FLOOD_INITIALIZE]);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, sdf_initialize_uniform_set, 0);
+ RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(SDGIShader::PreprocessPushConstant));
+ RD::get_singleton()->compute_list_dispatch_threads(compute_list, cascade_size, cascade_size, cascade_size);
+
+ RD::get_singleton()->compute_list_add_barrier(compute_list);
+
+ push_constant.half_size = false;
+ {
+ uint32_t s = cascade_size;
+
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, gi->sdfgi_shader.preprocess_pipeline[SDGIShader::PRE_PROCESS_JUMP_FLOOD]);
+
+ int jf_us = 0;
+ //start with regular jump flood for very coarse reads, as this is impossible to optimize
+ while (s > 1) {
+ s /= 2;
+ push_constant.step_size = s;
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, jump_flood_uniform_set[jf_us], 0);
+ RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(SDGIShader::PreprocessPushConstant));
+ RD::get_singleton()->compute_list_dispatch_threads(compute_list, cascade_size, cascade_size, cascade_size);
+ RD::get_singleton()->compute_list_add_barrier(compute_list);
+ jf_us = jf_us == 0 ? 1 : 0;
+
+ if (cascade_size / (s / 2) >= optimized_jf_group_size) {
+ break;
+ }
+ }
+
+ RENDER_TIMESTAMP("SDFGI Jump Flood Optimized");
+
+ //continue with optimized jump flood for smaller reads
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, gi->sdfgi_shader.preprocess_pipeline[SDGIShader::PRE_PROCESS_JUMP_FLOOD_OPTIMIZED]);
+ while (s > 1) {
+ s /= 2;
+ push_constant.step_size = s;
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, jump_flood_uniform_set[jf_us], 0);
+ RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(SDGIShader::PreprocessPushConstant));
+ RD::get_singleton()->compute_list_dispatch_threads(compute_list, cascade_size, cascade_size, cascade_size);
+ RD::get_singleton()->compute_list_add_barrier(compute_list);
+ jf_us = jf_us == 0 ? 1 : 0;
+ }
+ }
+ }
+
+ RENDER_TIMESTAMP("SDFGI Occlusion");
+
+ // occlusion
+ {
+ uint32_t probe_size = cascade_size / SDFGI::PROBE_DIVISOR;
+ Vector3i probe_global_pos = cascades[cascade].position / probe_size;
+
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, gi->sdfgi_shader.preprocess_pipeline[SDGIShader::PRE_PROCESS_OCCLUSION]);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, occlusion_uniform_set, 0);
+ for (int i = 0; i < 8; i++) {
+ //dispatch all at once for performance
+ Vector3i offset(i & 1, (i >> 1) & 1, (i >> 2) & 1);
+
+ if ((probe_global_pos.x & 1) != 0) {
+ offset.x = (offset.x + 1) & 1;
+ }
+ if ((probe_global_pos.y & 1) != 0) {
+ offset.y = (offset.y + 1) & 1;
+ }
+ if ((probe_global_pos.z & 1) != 0) {
+ offset.z = (offset.z + 1) & 1;
+ }
+ push_constant.probe_offset[0] = offset.x;
+ push_constant.probe_offset[1] = offset.y;
+ push_constant.probe_offset[2] = offset.z;
+ push_constant.occlusion_index = i;
+ RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(SDGIShader::PreprocessPushConstant));
+
+ Vector3i groups = Vector3i(probe_size + 1, probe_size + 1, probe_size + 1) - offset; //if offset, it's one less probe per axis to compute
+ RD::get_singleton()->compute_list_dispatch(compute_list, groups.x, groups.y, groups.z);
+ }
+ RD::get_singleton()->compute_list_add_barrier(compute_list);
+ }
+
+ RENDER_TIMESTAMP("SDFGI Store");
+
+ // store
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, gi->sdfgi_shader.preprocess_pipeline[SDGIShader::PRE_PROCESS_STORE]);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, cascades[cascade].sdf_store_uniform_set, 0);
+ RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(SDGIShader::PreprocessPushConstant));
+ RD::get_singleton()->compute_list_dispatch_threads(compute_list, cascade_size, cascade_size, cascade_size);
+
+ RD::get_singleton()->compute_list_end();
+
+ //clear these textures, as they will have previous garbage on next draw
+ RD::get_singleton()->texture_clear(cascades[cascade].light_tex, Color(0, 0, 0, 0), 0, 1, 0, 1);
+ RD::get_singleton()->texture_clear(cascades[cascade].light_aniso_0_tex, Color(0, 0, 0, 0), 0, 1, 0, 1);
+ RD::get_singleton()->texture_clear(cascades[cascade].light_aniso_1_tex, Color(0, 0, 0, 0), 0, 1, 0, 1);
+
+#if 0
+ Vector<uint8_t> data = RD::get_singleton()->texture_get_data(cascades[cascade].sdf, 0);
+ Ref<Image> img;
+ img.instance();
+ for (uint32_t i = 0; i < cascade_size; i++) {
+ Vector<uint8_t> subarr = data.subarray(128 * 128 * i, 128 * 128 * (i + 1) - 1);
+ img->create(cascade_size, cascade_size, false, Image::FORMAT_L8, subarr);
+ img->save_png("res://cascade_sdf_" + itos(cascade) + "_" + itos(i) + ".png");
+ }
+
+ //finalize render and update sdf
+#endif
+
+#if 0
+ Vector<uint8_t> data = RD::get_singleton()->texture_get_data(render_albedo, 0);
+ Ref<Image> img;
+ img.instance();
+ for (uint32_t i = 0; i < cascade_size; i++) {
+ Vector<uint8_t> subarr = data.subarray(128 * 128 * i * 2, 128 * 128 * (i + 1) * 2 - 1);
+ img->createcascade_size, cascade_size, false, Image::FORMAT_RGB565, subarr);
+ img->convert(Image::FORMAT_RGBA8);
+ img->save_png("res://cascade_" + itos(cascade) + "_" + itos(i) + ".png");
+ }
+
+ //finalize render and update sdf
+#endif
+
+ RENDER_TIMESTAMP("<SDFGI Update SDF");
+ RD::get_singleton()->draw_command_end_label();
+ }
+}
+
+void RendererSceneGIRD::SDFGI::render_static_lights(RID p_render_buffers, uint32_t p_cascade_count, const uint32_t *p_cascade_indices, const PagedArray<RID> *p_positional_light_cull_result, RendererSceneRenderRD *p_scene_render) {
+ RendererSceneRenderRD::RenderBuffers *rb = p_scene_render->render_buffers_owner.getornull(p_render_buffers);
+ ERR_FAIL_COND(!rb); // we wouldn't be here if this failed but...
+
+ RD::get_singleton()->draw_command_begin_label("SDFGI Render Static Lighs");
+
+ update_cascades();
+ ; //need cascades updated for this
+
+ SDGIShader::Light lights[SDFGI::MAX_STATIC_LIGHTS];
+ uint32_t light_count[SDFGI::MAX_STATIC_LIGHTS];
+
+ for (uint32_t i = 0; i < p_cascade_count; i++) {
+ ERR_CONTINUE(p_cascade_indices[i] >= cascades.size());
+
+ SDFGI::Cascade &cc = cascades[p_cascade_indices[i]];
+
+ { //fill light buffer
+
+ AABB cascade_aabb;
+ cascade_aabb.position = Vector3((Vector3i(1, 1, 1) * -int32_t(cascade_size >> 1) + cc.position)) * cc.cell_size;
+ cascade_aabb.size = Vector3(1, 1, 1) * cascade_size * cc.cell_size;
+
+ int idx = 0;
+
+ for (uint32_t j = 0; j < (uint32_t)p_positional_light_cull_result[i].size(); j++) {
+ if (idx == SDFGI::MAX_STATIC_LIGHTS) {
+ break;
+ }
+
+ RendererSceneRenderRD::LightInstance *li = p_scene_render->light_instance_owner.getornull(p_positional_light_cull_result[i][j]);
+ ERR_CONTINUE(!li);
+
+ uint32_t max_sdfgi_cascade = storage->light_get_max_sdfgi_cascade(li->light);
+ if (p_cascade_indices[i] > max_sdfgi_cascade) {
+ continue;
+ }
+
+ if (!cascade_aabb.intersects(li->aabb)) {
+ continue;
+ }
+
+ lights[idx].type = storage->light_get_type(li->light);
+
+ Vector3 dir = -li->transform.basis.get_axis(Vector3::AXIS_Z);
+ if (lights[idx].type == RS::LIGHT_DIRECTIONAL) {
+ dir.y *= y_mult; //only makes sense for directional
+ dir.normalize();
+ }
+ lights[idx].direction[0] = dir.x;
+ lights[idx].direction[1] = dir.y;
+ lights[idx].direction[2] = dir.z;
+ Vector3 pos = li->transform.origin;
+ pos.y *= y_mult;
+ lights[idx].position[0] = pos.x;
+ lights[idx].position[1] = pos.y;
+ lights[idx].position[2] = pos.z;
+ Color color = storage->light_get_color(li->light);
+ color = color.to_linear();
+ lights[idx].color[0] = color.r;
+ lights[idx].color[1] = color.g;
+ lights[idx].color[2] = color.b;
+ lights[idx].energy = storage->light_get_param(li->light, RS::LIGHT_PARAM_ENERGY);
+ lights[idx].has_shadow = storage->light_has_shadow(li->light);
+ lights[idx].attenuation = storage->light_get_param(li->light, RS::LIGHT_PARAM_ATTENUATION);
+ lights[idx].radius = storage->light_get_param(li->light, RS::LIGHT_PARAM_RANGE);
+ lights[idx].cos_spot_angle = Math::cos(Math::deg2rad(storage->light_get_param(li->light, RS::LIGHT_PARAM_SPOT_ANGLE)));
+ lights[idx].inv_spot_attenuation = 1.0f / storage->light_get_param(li->light, RS::LIGHT_PARAM_SPOT_ATTENUATION);
+
+ idx++;
+ }
+
+ if (idx > 0) {
+ RD::get_singleton()->buffer_update(cc.lights_buffer, 0, idx * sizeof(SDGIShader::Light), lights);
+ }
+
+ light_count[i] = idx;
+ }
+ }
+
+ /* Static Lights */
+ RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
+
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, gi->sdfgi_shader.direct_light_pipeline[SDGIShader::DIRECT_LIGHT_MODE_STATIC]);
+
+ SDGIShader::DirectLightPushConstant dl_push_constant;
+
+ dl_push_constant.grid_size[0] = cascade_size;
+ dl_push_constant.grid_size[1] = cascade_size;
+ dl_push_constant.grid_size[2] = cascade_size;
+ dl_push_constant.max_cascades = cascades.size();
+ dl_push_constant.probe_axis_size = probe_axis_count;
+ dl_push_constant.bounce_feedback = 0.0; // this is static light, do not multibounce yet
+ dl_push_constant.y_mult = y_mult;
+ dl_push_constant.use_occlusion = uses_occlusion;
+
+ //all must be processed
+ dl_push_constant.process_offset = 0;
+ dl_push_constant.process_increment = 1;
+
+ for (uint32_t i = 0; i < p_cascade_count; i++) {
+ ERR_CONTINUE(p_cascade_indices[i] >= cascades.size());
+
+ SDFGI::Cascade &cc = cascades[p_cascade_indices[i]];
+
+ dl_push_constant.light_count = light_count[i];
+ dl_push_constant.cascade = p_cascade_indices[i];
+
+ if (dl_push_constant.light_count > 0) {
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, cc.sdf_direct_light_uniform_set, 0);
+ RD::get_singleton()->compute_list_set_push_constant(compute_list, &dl_push_constant, sizeof(SDGIShader::DirectLightPushConstant));
+ RD::get_singleton()->compute_list_dispatch_indirect(compute_list, cc.solid_cell_dispatch_buffer, 0);
+ }
+ }
+
+ RD::get_singleton()->compute_list_end();
+
+ RD::get_singleton()->draw_command_end_label();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// GIProbeInstance
+
+void RendererSceneGIRD::GIProbeInstance::update(bool p_update_light_instances, const Vector<RID> &p_light_instances, const PagedArray<RendererSceneRender::GeometryInstance *> &p_dynamic_objects, RendererSceneRenderRD *p_scene_render) {
+ uint32_t data_version = storage->gi_probe_get_data_version(probe);
+
+ // (RE)CREATE IF NEEDED
+
+ if (last_probe_data_version != data_version) {
+ //need to re-create everything
+ if (texture.is_valid()) {
+ RD::get_singleton()->free(texture);
+ RD::get_singleton()->free(write_buffer);
+ mipmaps.clear();
+ }
+
+ for (int i = 0; i < dynamic_maps.size(); i++) {
+ RD::get_singleton()->free(dynamic_maps[i].texture);
+ RD::get_singleton()->free(dynamic_maps[i].depth);
+ }
+
+ dynamic_maps.clear();
+
+ Vector3i octree_size = storage->gi_probe_get_octree_size(probe);
+
+ if (octree_size != Vector3i()) {
+ //can create a 3D texture
+ Vector<int> levels = storage->gi_probe_get_level_counts(probe);
+
+ RD::TextureFormat tf;
+ tf.format = RD::DATA_FORMAT_R8G8B8A8_UNORM;
+ tf.width = octree_size.x;
+ tf.height = octree_size.y;
+ tf.depth = octree_size.z;
+ tf.texture_type = RD::TEXTURE_TYPE_3D;
+ tf.mipmaps = levels.size();
+
+ tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_CAN_COPY_TO_BIT;
+
+ texture = RD::get_singleton()->texture_create(tf, RD::TextureView());
+
+ RD::get_singleton()->texture_clear(texture, Color(0, 0, 0, 0), 0, levels.size(), 0, 1);
+
+ {
+ int total_elements = 0;
+ for (int i = 0; i < levels.size(); i++) {
+ total_elements += levels[i];
+ }
+
+ write_buffer = RD::get_singleton()->storage_buffer_create(total_elements * 16);
+ }
+
+ for (int i = 0; i < levels.size(); i++) {
+ GIProbeInstance::Mipmap mipmap;
+ mipmap.texture = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), texture, 0, i, RD::TEXTURE_SLICE_3D);
+ mipmap.level = levels.size() - i - 1;
+ mipmap.cell_offset = 0;
+ for (uint32_t j = 0; j < mipmap.level; j++) {
+ mipmap.cell_offset += levels[j];
+ }
+ mipmap.cell_count = levels[mipmap.level];
+
+ Vector<RD::Uniform> uniforms;
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
+ u.binding = 1;
+ u.ids.push_back(storage->gi_probe_get_octree_buffer(probe));
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
+ u.binding = 2;
+ u.ids.push_back(storage->gi_probe_get_data_buffer(probe));
+ uniforms.push_back(u);
+ }
+
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
+ u.binding = 4;
+ u.ids.push_back(write_buffer);
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
+ u.binding = 9;
+ u.ids.push_back(storage->gi_probe_get_sdf_texture(probe));
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_SAMPLER;
+ u.binding = 10;
+ u.ids.push_back(storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED));
+ uniforms.push_back(u);
+ }
+
+ {
+ Vector<RD::Uniform> copy_uniforms = uniforms;
+ if (i == 0) {
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER;
+ u.binding = 3;
+ u.ids.push_back(gi->gi_probe_lights_uniform);
+ copy_uniforms.push_back(u);
+ }
+
+ mipmap.uniform_set = RD::get_singleton()->uniform_set_create(copy_uniforms, gi->giprobe_lighting_shader_version_shaders[GI_PROBE_SHADER_VERSION_COMPUTE_LIGHT], 0);
+
+ copy_uniforms = uniforms; //restore
+
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
+ u.binding = 5;
+ u.ids.push_back(texture);
+ copy_uniforms.push_back(u);
+ }
+ mipmap.second_bounce_uniform_set = RD::get_singleton()->uniform_set_create(copy_uniforms, gi->giprobe_lighting_shader_version_shaders[GI_PROBE_SHADER_VERSION_COMPUTE_SECOND_BOUNCE], 0);
+ } else {
+ mipmap.uniform_set = RD::get_singleton()->uniform_set_create(copy_uniforms, gi->giprobe_lighting_shader_version_shaders[GI_PROBE_SHADER_VERSION_COMPUTE_MIPMAP], 0);
+ }
+ }
+
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
+ u.binding = 5;
+ u.ids.push_back(mipmap.texture);
+ uniforms.push_back(u);
+ }
+
+ mipmap.write_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, gi->giprobe_lighting_shader_version_shaders[GI_PROBE_SHADER_VERSION_WRITE_TEXTURE], 0);
+
+ mipmaps.push_back(mipmap);
+ }
+
+ {
+ uint32_t dynamic_map_size = MAX(MAX(octree_size.x, octree_size.y), octree_size.z);
+ uint32_t oversample = nearest_power_of_2_templated(4);
+ int mipmap_index = 0;
+
+ while (mipmap_index < mipmaps.size()) {
+ GIProbeInstance::DynamicMap dmap;
+
+ if (oversample > 0) {
+ dmap.size = dynamic_map_size * (1 << oversample);
+ dmap.mipmap = -1;
+ oversample--;
+ } else {
+ dmap.size = dynamic_map_size >> mipmap_index;
+ dmap.mipmap = mipmap_index;
+ mipmap_index++;
+ }
+
+ RD::TextureFormat dtf;
+ dtf.width = dmap.size;
+ dtf.height = dmap.size;
+ dtf.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT;
+ dtf.usage_bits = RD::TEXTURE_USAGE_STORAGE_BIT;
+
+ if (dynamic_maps.size() == 0) {
+ dtf.usage_bits |= RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
+ }
+ dmap.texture = RD::get_singleton()->texture_create(dtf, RD::TextureView());
+
+ if (dynamic_maps.size() == 0) {
+ //render depth for first one
+ dtf.format = RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_D32_SFLOAT, RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) ? RD::DATA_FORMAT_D32_SFLOAT : RD::DATA_FORMAT_X8_D24_UNORM_PACK32;
+ dtf.usage_bits = RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
+ dmap.fb_depth = RD::get_singleton()->texture_create(dtf, RD::TextureView());
+ }
+
+ //just use depth as-is
+ dtf.format = RD::DATA_FORMAT_R32_SFLOAT;
+ dtf.usage_bits = RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
+
+ dmap.depth = RD::get_singleton()->texture_create(dtf, RD::TextureView());
+
+ if (dynamic_maps.size() == 0) {
+ dtf.format = RD::DATA_FORMAT_R8G8B8A8_UNORM;
+ dtf.usage_bits = RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
+ dmap.albedo = RD::get_singleton()->texture_create(dtf, RD::TextureView());
+ dmap.normal = RD::get_singleton()->texture_create(dtf, RD::TextureView());
+ dmap.orm = RD::get_singleton()->texture_create(dtf, RD::TextureView());
+
+ Vector<RID> fb;
+ fb.push_back(dmap.albedo);
+ fb.push_back(dmap.normal);
+ fb.push_back(dmap.orm);
+ fb.push_back(dmap.texture); //emission
+ fb.push_back(dmap.depth);
+ fb.push_back(dmap.fb_depth);
+
+ dmap.fb = RD::get_singleton()->framebuffer_create(fb);
+
+ {
+ Vector<RD::Uniform> uniforms;
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER;
+ u.binding = 3;
+ u.ids.push_back(gi->gi_probe_lights_uniform);
+ uniforms.push_back(u);
+ }
+
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
+ u.binding = 5;
+ u.ids.push_back(dmap.albedo);
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
+ u.binding = 6;
+ u.ids.push_back(dmap.normal);
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
+ u.binding = 7;
+ u.ids.push_back(dmap.orm);
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
+ u.binding = 8;
+ u.ids.push_back(dmap.fb_depth);
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
+ u.binding = 9;
+ u.ids.push_back(storage->gi_probe_get_sdf_texture(probe));
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_SAMPLER;
+ u.binding = 10;
+ u.ids.push_back(storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED));
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
+ u.binding = 11;
+ u.ids.push_back(dmap.texture);
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
+ u.binding = 12;
+ u.ids.push_back(dmap.depth);
+ uniforms.push_back(u);
+ }
+
+ dmap.uniform_set = RD::get_singleton()->uniform_set_create(uniforms, gi->giprobe_lighting_shader_version_shaders[GI_PROBE_SHADER_VERSION_DYNAMIC_OBJECT_LIGHTING], 0);
+ }
+ } else {
+ bool plot = dmap.mipmap >= 0;
+ bool write = dmap.mipmap < (mipmaps.size() - 1);
+
+ Vector<RD::Uniform> uniforms;
+
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
+ u.binding = 5;
+ u.ids.push_back(dynamic_maps[dynamic_maps.size() - 1].texture);
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
+ u.binding = 6;
+ u.ids.push_back(dynamic_maps[dynamic_maps.size() - 1].depth);
+ uniforms.push_back(u);
+ }
+
+ if (write) {
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
+ u.binding = 7;
+ u.ids.push_back(dmap.texture);
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
+ u.binding = 8;
+ u.ids.push_back(dmap.depth);
+ uniforms.push_back(u);
+ }
+ }
+
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
+ u.binding = 9;
+ u.ids.push_back(storage->gi_probe_get_sdf_texture(probe));
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_SAMPLER;
+ u.binding = 10;
+ u.ids.push_back(storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED));
+ uniforms.push_back(u);
+ }
+
+ if (plot) {
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
+ u.binding = 11;
+ u.ids.push_back(mipmaps[dmap.mipmap].texture);
+ uniforms.push_back(u);
+ }
+ }
+
+ dmap.uniform_set = RD::get_singleton()->uniform_set_create(
+ uniforms,
+ gi->giprobe_lighting_shader_version_shaders[(write && plot) ? GI_PROBE_SHADER_VERSION_DYNAMIC_SHRINK_WRITE_PLOT : (write ? GI_PROBE_SHADER_VERSION_DYNAMIC_SHRINK_WRITE : GI_PROBE_SHADER_VERSION_DYNAMIC_SHRINK_PLOT)],
+ 0);
+ }
+
+ dynamic_maps.push_back(dmap);
+ }
+ }
+ }
+
+ last_probe_data_version = data_version;
+ p_update_light_instances = true; //just in case
+
+ p_scene_render->_base_uniforms_changed();
+ }
+
+ // UDPDATE TIME
+
+ if (has_dynamic_object_data) {
+ //if it has dynamic object data, it needs to be cleared
+ RD::get_singleton()->texture_clear(texture, Color(0, 0, 0, 0), 0, mipmaps.size(), 0, 1);
+ }
+
+ uint32_t light_count = 0;
+
+ if (p_update_light_instances || p_dynamic_objects.size() > 0) {
+ light_count = MIN(gi->gi_probe_max_lights, (uint32_t)p_light_instances.size());
+
+ {
+ Transform to_cell = storage->gi_probe_get_to_cell_xform(probe);
+ Transform to_probe_xform = (transform * to_cell.affine_inverse()).affine_inverse();
+ //update lights
+
+ for (uint32_t i = 0; i < light_count; i++) {
+ GIProbeLight &l = gi->gi_probe_lights[i];
+ RID light_instance = p_light_instances[i];
+ RID light = p_scene_render->light_instance_get_base_light(light_instance);
+
+ l.type = storage->light_get_type(light);
+ if (l.type == RS::LIGHT_DIRECTIONAL && storage->light_directional_is_sky_only(light)) {
+ light_count--;
+ continue;
+ }
+
+ l.attenuation = storage->light_get_param(light, RS::LIGHT_PARAM_ATTENUATION);
+ l.energy = storage->light_get_param(light, RS::LIGHT_PARAM_ENERGY) * storage->light_get_param(light, RS::LIGHT_PARAM_INDIRECT_ENERGY);
+ l.radius = to_cell.basis.xform(Vector3(storage->light_get_param(light, RS::LIGHT_PARAM_RANGE), 0, 0)).length();
+ Color color = storage->light_get_color(light).to_linear();
+ l.color[0] = color.r;
+ l.color[1] = color.g;
+ l.color[2] = color.b;
+
+ l.cos_spot_angle = Math::cos(Math::deg2rad(storage->light_get_param(light, RS::LIGHT_PARAM_SPOT_ANGLE)));
+ l.inv_spot_attenuation = 1.0f / storage->light_get_param(light, RS::LIGHT_PARAM_SPOT_ATTENUATION);
+
+ Transform xform = p_scene_render->light_instance_get_base_transform(light_instance);
+
+ Vector3 pos = to_probe_xform.xform(xform.origin);
+ Vector3 dir = to_probe_xform.basis.xform(-xform.basis.get_axis(2)).normalized();
+
+ l.position[0] = pos.x;
+ l.position[1] = pos.y;
+ l.position[2] = pos.z;
+
+ l.direction[0] = dir.x;
+ l.direction[1] = dir.y;
+ l.direction[2] = dir.z;
+
+ l.has_shadow = storage->light_has_shadow(light);
+ }
+
+ RD::get_singleton()->buffer_update(gi->gi_probe_lights_uniform, 0, sizeof(GIProbeLight) * light_count, gi->gi_probe_lights);
+ }
+ }
+
+ if (has_dynamic_object_data || p_update_light_instances || p_dynamic_objects.size()) {
+ // PROCESS MIPMAPS
+ if (mipmaps.size()) {
+ //can update mipmaps
+
+ Vector3i probe_size = storage->gi_probe_get_octree_size(probe);
+
+ GIProbePushConstant push_constant;
+
+ push_constant.limits[0] = probe_size.x;
+ push_constant.limits[1] = probe_size.y;
+ push_constant.limits[2] = probe_size.z;
+ push_constant.stack_size = mipmaps.size();
+ push_constant.emission_scale = 1.0;
+ push_constant.propagation = storage->gi_probe_get_propagation(probe);
+ push_constant.dynamic_range = storage->gi_probe_get_dynamic_range(probe);
+ push_constant.light_count = light_count;
+ push_constant.aniso_strength = 0;
+
+ /* print_line("probe update to version " + itos(last_probe_version));
+ print_line("propagation " + rtos(push_constant.propagation));
+ print_line("dynrange " + rtos(push_constant.dynamic_range));
+ */
+ RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
+
+ int passes;
+ if (p_update_light_instances) {
+ passes = storage->gi_probe_is_using_two_bounces(probe) ? 2 : 1;
+ } else {
+ passes = 1; //only re-blitting is necessary
+ }
+ int wg_size = 64;
+ int wg_limit_x = RD::get_singleton()->limit_get(RD::LIMIT_MAX_COMPUTE_WORKGROUP_COUNT_X);
+
+ for (int pass = 0; pass < passes; pass++) {
+ if (p_update_light_instances) {
+ for (int i = 0; i < mipmaps.size(); i++) {
+ if (i == 0) {
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, gi->giprobe_lighting_shader_version_pipelines[pass == 0 ? GI_PROBE_SHADER_VERSION_COMPUTE_LIGHT : GI_PROBE_SHADER_VERSION_COMPUTE_SECOND_BOUNCE]);
+ } else if (i == 1) {
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, gi->giprobe_lighting_shader_version_pipelines[GI_PROBE_SHADER_VERSION_COMPUTE_MIPMAP]);
+ }
+
+ if (pass == 1 || i > 0) {
+ RD::get_singleton()->compute_list_add_barrier(compute_list); //wait til previous step is done
+ }
+ if (pass == 0 || i > 0) {
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, mipmaps[i].uniform_set, 0);
+ } else {
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, mipmaps[i].second_bounce_uniform_set, 0);
+ }
+
+ push_constant.cell_offset = mipmaps[i].cell_offset;
+ push_constant.cell_count = mipmaps[i].cell_count;
+
+ int wg_todo = (mipmaps[i].cell_count - 1) / wg_size + 1;
+ while (wg_todo) {
+ int wg_count = MIN(wg_todo, wg_limit_x);
+ RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(GIProbePushConstant));
+ RD::get_singleton()->compute_list_dispatch(compute_list, wg_count, 1, 1);
+ wg_todo -= wg_count;
+ push_constant.cell_offset += wg_count * wg_size;
+ }
+ }
+
+ RD::get_singleton()->compute_list_add_barrier(compute_list); //wait til previous step is done
+ }
+
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, gi->giprobe_lighting_shader_version_pipelines[GI_PROBE_SHADER_VERSION_WRITE_TEXTURE]);
+
+ for (int i = 0; i < mipmaps.size(); i++) {
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, mipmaps[i].write_uniform_set, 0);
+
+ push_constant.cell_offset = mipmaps[i].cell_offset;
+ push_constant.cell_count = mipmaps[i].cell_count;
+
+ int wg_todo = (mipmaps[i].cell_count - 1) / wg_size + 1;
+ while (wg_todo) {
+ int wg_count = MIN(wg_todo, wg_limit_x);
+ RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(GIProbePushConstant));
+ RD::get_singleton()->compute_list_dispatch(compute_list, wg_count, 1, 1);
+ wg_todo -= wg_count;
+ push_constant.cell_offset += wg_count * wg_size;
+ }
+ }
+ }
+
+ RD::get_singleton()->compute_list_end();
+ }
+ }
+
+ has_dynamic_object_data = false; //clear until dynamic object data is used again
+
+ if (p_dynamic_objects.size() && dynamic_maps.size()) {
+ Vector3i octree_size = storage->gi_probe_get_octree_size(probe);
+ int multiplier = dynamic_maps[0].size / MAX(MAX(octree_size.x, octree_size.y), octree_size.z);
+
+ Transform oversample_scale;
+ oversample_scale.basis.scale(Vector3(multiplier, multiplier, multiplier));
+
+ Transform to_cell = oversample_scale * storage->gi_probe_get_to_cell_xform(probe);
+ Transform to_world_xform = transform * to_cell.affine_inverse();
+ Transform to_probe_xform = to_world_xform.affine_inverse();
+
+ AABB probe_aabb(Vector3(), octree_size);
+
+ //this could probably be better parallelized in compute..
+ for (int i = 0; i < (int)p_dynamic_objects.size(); i++) {
+ RendererSceneRender::GeometryInstance *instance = p_dynamic_objects[i];
+
+ //transform aabb to giprobe
+ AABB aabb = (to_probe_xform * p_scene_render->geometry_instance_get_transform(instance)).xform(p_scene_render->geometry_instance_get_aabb(instance));
+
+ //this needs to wrap to grid resolution to avoid jitter
+ //also extend margin a bit just in case
+ Vector3i begin = aabb.position - Vector3i(1, 1, 1);
+ Vector3i end = aabb.position + aabb.size + Vector3i(1, 1, 1);
+
+ for (int j = 0; j < 3; j++) {
+ if ((end[j] - begin[j]) & 1) {
+ end[j]++; //for half extents split, it needs to be even
+ }
+ begin[j] = MAX(begin[j], 0);
+ end[j] = MIN(end[j], octree_size[j] * multiplier);
+ }
+
+ //aabb = aabb.intersection(probe_aabb); //intersect
+ aabb.position = begin;
+ aabb.size = end - begin;
+
+ //print_line("aabb: " + aabb);
+
+ for (int j = 0; j < 6; j++) {
+ //if (j != 0 && j != 3) {
+ // continue;
+ //}
+ static const Vector3 render_z[6] = {
+ Vector3(1, 0, 0),
+ Vector3(0, 1, 0),
+ Vector3(0, 0, 1),
+ Vector3(-1, 0, 0),
+ Vector3(0, -1, 0),
+ Vector3(0, 0, -1),
+ };
+ static const Vector3 render_up[6] = {
+ Vector3(0, 1, 0),
+ Vector3(0, 0, 1),
+ Vector3(0, 1, 0),
+ Vector3(0, 1, 0),
+ Vector3(0, 0, 1),
+ Vector3(0, 1, 0),
+ };
+
+ Vector3 render_dir = render_z[j];
+ Vector3 up_dir = render_up[j];
+
+ Vector3 center = aabb.position + aabb.size * 0.5;
+ Transform xform;
+ xform.set_look_at(center - aabb.size * 0.5 * render_dir, center, up_dir);
+
+ Vector3 x_dir = xform.basis.get_axis(0).abs();
+ int x_axis = int(Vector3(0, 1, 2).dot(x_dir));
+ Vector3 y_dir = xform.basis.get_axis(1).abs();
+ int y_axis = int(Vector3(0, 1, 2).dot(y_dir));
+ Vector3 z_dir = -xform.basis.get_axis(2);
+ int z_axis = int(Vector3(0, 1, 2).dot(z_dir.abs()));
+
+ Rect2i rect(aabb.position[x_axis], aabb.position[y_axis], aabb.size[x_axis], aabb.size[y_axis]);
+ bool x_flip = bool(Vector3(1, 1, 1).dot(xform.basis.get_axis(0)) < 0);
+ bool y_flip = bool(Vector3(1, 1, 1).dot(xform.basis.get_axis(1)) < 0);
+ bool z_flip = bool(Vector3(1, 1, 1).dot(xform.basis.get_axis(2)) > 0);
+
+ CameraMatrix cm;
+ cm.set_orthogonal(-rect.size.width / 2, rect.size.width / 2, -rect.size.height / 2, rect.size.height / 2, 0.0001, aabb.size[z_axis]);
+
+ if (p_scene_render->cull_argument.size() == 0) {
+ p_scene_render->cull_argument.push_back(nullptr);
+ }
+ p_scene_render->cull_argument[0] = instance;
+
+ p_scene_render->_render_material(to_world_xform * xform, cm, true, p_scene_render->cull_argument, dynamic_maps[0].fb, Rect2i(Vector2i(), rect.size));
+
+ GIProbeDynamicPushConstant push_constant;
+ zeromem(&push_constant, sizeof(GIProbeDynamicPushConstant));
+ push_constant.limits[0] = octree_size.x;
+ push_constant.limits[1] = octree_size.y;
+ push_constant.limits[2] = octree_size.z;
+ push_constant.light_count = p_light_instances.size();
+ push_constant.x_dir[0] = x_dir[0];
+ push_constant.x_dir[1] = x_dir[1];
+ push_constant.x_dir[2] = x_dir[2];
+ push_constant.y_dir[0] = y_dir[0];
+ push_constant.y_dir[1] = y_dir[1];
+ push_constant.y_dir[2] = y_dir[2];
+ push_constant.z_dir[0] = z_dir[0];
+ push_constant.z_dir[1] = z_dir[1];
+ push_constant.z_dir[2] = z_dir[2];
+ push_constant.z_base = xform.origin[z_axis];
+ push_constant.z_sign = (z_flip ? -1.0 : 1.0);
+ push_constant.pos_multiplier = float(1.0) / multiplier;
+ push_constant.dynamic_range = storage->gi_probe_get_dynamic_range(probe);
+ push_constant.flip_x = x_flip;
+ push_constant.flip_y = y_flip;
+ push_constant.rect_pos[0] = rect.position[0];
+ push_constant.rect_pos[1] = rect.position[1];
+ push_constant.rect_size[0] = rect.size[0];
+ push_constant.rect_size[1] = rect.size[1];
+ push_constant.prev_rect_ofs[0] = 0;
+ push_constant.prev_rect_ofs[1] = 0;
+ push_constant.prev_rect_size[0] = 0;
+ push_constant.prev_rect_size[1] = 0;
+ push_constant.on_mipmap = false;
+ push_constant.propagation = storage->gi_probe_get_propagation(probe);
+ push_constant.pad[0] = 0;
+ push_constant.pad[1] = 0;
+ push_constant.pad[2] = 0;
+
+ //process lighting
+ RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, gi->giprobe_lighting_shader_version_pipelines[GI_PROBE_SHADER_VERSION_DYNAMIC_OBJECT_LIGHTING]);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, dynamic_maps[0].uniform_set, 0);
+ RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(GIProbeDynamicPushConstant));
+ RD::get_singleton()->compute_list_dispatch(compute_list, (rect.size.x - 1) / 8 + 1, (rect.size.y - 1) / 8 + 1, 1);
+ //print_line("rect: " + itos(i) + ": " + rect);
+
+ for (int k = 1; k < dynamic_maps.size(); k++) {
+ // enlarge the rect if needed so all pixels fit when downscaled,
+ // this ensures downsampling is smooth and optimal because no pixels are left behind
+
+ //x
+ if (rect.position.x & 1) {
+ rect.size.x++;
+ push_constant.prev_rect_ofs[0] = 1; //this is used to ensure reading is also optimal
+ } else {
+ push_constant.prev_rect_ofs[0] = 0;
+ }
+ if (rect.size.x & 1) {
+ rect.size.x++;
+ }
+
+ rect.position.x >>= 1;
+ rect.size.x = MAX(1, rect.size.x >> 1);
+
+ //y
+ if (rect.position.y & 1) {
+ rect.size.y++;
+ push_constant.prev_rect_ofs[1] = 1;
+ } else {
+ push_constant.prev_rect_ofs[1] = 0;
+ }
+ if (rect.size.y & 1) {
+ rect.size.y++;
+ }
+
+ rect.position.y >>= 1;
+ rect.size.y = MAX(1, rect.size.y >> 1);
+
+ //shrink limits to ensure plot does not go outside map
+ if (dynamic_maps[k].mipmap > 0) {
+ for (int l = 0; l < 3; l++) {
+ push_constant.limits[l] = MAX(1, push_constant.limits[l] >> 1);
+ }
+ }
+
+ //print_line("rect: " + itos(i) + ": " + rect);
+ push_constant.rect_pos[0] = rect.position[0];
+ push_constant.rect_pos[1] = rect.position[1];
+ push_constant.prev_rect_size[0] = push_constant.rect_size[0];
+ push_constant.prev_rect_size[1] = push_constant.rect_size[1];
+ push_constant.rect_size[0] = rect.size[0];
+ push_constant.rect_size[1] = rect.size[1];
+ push_constant.on_mipmap = dynamic_maps[k].mipmap > 0;
+
+ RD::get_singleton()->compute_list_add_barrier(compute_list);
+
+ if (dynamic_maps[k].mipmap < 0) {
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, gi->giprobe_lighting_shader_version_pipelines[GI_PROBE_SHADER_VERSION_DYNAMIC_SHRINK_WRITE]);
+ } else if (k < dynamic_maps.size() - 1) {
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, gi->giprobe_lighting_shader_version_pipelines[GI_PROBE_SHADER_VERSION_DYNAMIC_SHRINK_WRITE_PLOT]);
+ } else {
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, gi->giprobe_lighting_shader_version_pipelines[GI_PROBE_SHADER_VERSION_DYNAMIC_SHRINK_PLOT]);
+ }
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, dynamic_maps[k].uniform_set, 0);
+ RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(GIProbeDynamicPushConstant));
+ RD::get_singleton()->compute_list_dispatch(compute_list, (rect.size.x - 1) / 8 + 1, (rect.size.y - 1) / 8 + 1, 1);
+ }
+
+ RD::get_singleton()->compute_list_end();
+ }
+ }
+
+ has_dynamic_object_data = true; //clear until dynamic object data is used again
+ }
+
+ last_probe_version = storage->gi_probe_get_version(probe);
+}
+
+void RendererSceneGIRD::GIProbeInstance::debug(RD::DrawListID p_draw_list, RID p_framebuffer, const CameraMatrix &p_camera_with_transform, bool p_lighting, bool p_emission, float p_alpha) {
+ if (mipmaps.size() == 0) {
+ return;
+ }
+
+ CameraMatrix cam_transform = (p_camera_with_transform * CameraMatrix(transform)) * CameraMatrix(storage->gi_probe_get_to_cell_xform(probe).affine_inverse());
+
+ int level = 0;
+ Vector3i octree_size = storage->gi_probe_get_octree_size(probe);
+
+ GIProbeDebugPushConstant push_constant;
+ push_constant.alpha = p_alpha;
+ push_constant.dynamic_range = storage->gi_probe_get_dynamic_range(probe);
+ push_constant.cell_offset = mipmaps[level].cell_offset;
+ push_constant.level = level;
+
+ push_constant.bounds[0] = octree_size.x >> level;
+ push_constant.bounds[1] = octree_size.y >> level;
+ push_constant.bounds[2] = octree_size.z >> level;
+ push_constant.pad = 0;
+
+ for (int i = 0; i < 4; i++) {
+ for (int j = 0; j < 4; j++) {
+ push_constant.projection[i * 4 + j] = cam_transform.matrix[i][j];
+ }
+ }
+
+ if (gi->giprobe_debug_uniform_set.is_valid()) {
+ RD::get_singleton()->free(gi->giprobe_debug_uniform_set);
+ }
+ Vector<RD::Uniform> uniforms;
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
+ u.binding = 1;
+ u.ids.push_back(storage->gi_probe_get_data_buffer(probe));
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
+ u.binding = 2;
+ u.ids.push_back(texture);
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_SAMPLER;
+ u.binding = 3;
+ u.ids.push_back(storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED));
+ uniforms.push_back(u);
+ }
+
+ int cell_count;
+ if (!p_emission && p_lighting && has_dynamic_object_data) {
+ cell_count = push_constant.bounds[0] * push_constant.bounds[1] * push_constant.bounds[2];
+ } else {
+ cell_count = mipmaps[level].cell_count;
+ }
+
+ gi->giprobe_debug_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, gi->giprobe_debug_shader_version_shaders[0], 0);
+
+ int giprobe_debug_pipeline = GI_PROBE_DEBUG_COLOR;
+ if (p_emission) {
+ giprobe_debug_pipeline = GI_PROBE_DEBUG_EMISSION;
+ } else if (p_lighting) {
+ giprobe_debug_pipeline = has_dynamic_object_data ? GI_PROBE_DEBUG_LIGHT_FULL : GI_PROBE_DEBUG_LIGHT;
+ }
+ RD::get_singleton()->draw_list_bind_render_pipeline(
+ p_draw_list,
+ gi->giprobe_debug_shader_version_pipelines[giprobe_debug_pipeline].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_framebuffer)));
+ RD::get_singleton()->draw_list_bind_uniform_set(p_draw_list, gi->giprobe_debug_uniform_set, 0);
+ RD::get_singleton()->draw_list_set_push_constant(p_draw_list, &push_constant, sizeof(GIProbeDebugPushConstant));
+ RD::get_singleton()->draw_list_draw(p_draw_list, false, cell_count, 36);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// GIRD
+
+RendererSceneGIRD::RendererSceneGIRD() {
+ sdfgi_ray_count = RS::EnvironmentSDFGIRayCount(CLAMP(int32_t(GLOBAL_GET("rendering/global_illumination/sdfgi/probe_ray_count")), 0, int32_t(RS::ENV_SDFGI_RAY_COUNT_MAX - 1)));
+ sdfgi_frames_to_converge = RS::EnvironmentSDFGIFramesToConverge(CLAMP(int32_t(GLOBAL_GET("rendering/global_illumination/sdfgi/frames_to_converge")), 0, int32_t(RS::ENV_SDFGI_CONVERGE_MAX - 1)));
+ sdfgi_frames_to_update_light = RS::EnvironmentSDFGIFramesToUpdateLight(CLAMP(int32_t(GLOBAL_GET("rendering/global_illumination/sdfgi/frames_to_update_lights")), 0, int32_t(RS::ENV_SDFGI_UPDATE_LIGHT_MAX - 1)));
+}
+
+RendererSceneGIRD::~RendererSceneGIRD() {
+}
+
+void RendererSceneGIRD::init_gi(RendererStorageRD *p_storage) {
+ storage = p_storage;
+
+ {
+ //kinda complicated to compute the amount of slots, we try to use as many as we can
+
+ gi_probe_max_lights = 32;
+
+ gi_probe_lights = memnew_arr(GIProbeLight, gi_probe_max_lights);
+ gi_probe_lights_uniform = RD::get_singleton()->uniform_buffer_create(gi_probe_max_lights * sizeof(GIProbeLight));
+ gi_probe_quality = RS::GIProbeQuality(CLAMP(int(GLOBAL_GET("rendering/global_illumination/gi_probes/quality")), 0, 1));
+
+ String defines = "\n#define MAX_LIGHTS " + itos(gi_probe_max_lights) + "\n";
+
+ Vector<String> versions;
+ versions.push_back("\n#define MODE_COMPUTE_LIGHT\n");
+ versions.push_back("\n#define MODE_SECOND_BOUNCE\n");
+ versions.push_back("\n#define MODE_UPDATE_MIPMAPS\n");
+ versions.push_back("\n#define MODE_WRITE_TEXTURE\n");
+ versions.push_back("\n#define MODE_DYNAMIC\n#define MODE_DYNAMIC_LIGHTING\n");
+ versions.push_back("\n#define MODE_DYNAMIC\n#define MODE_DYNAMIC_SHRINK\n#define MODE_DYNAMIC_SHRINK_WRITE\n");
+ versions.push_back("\n#define MODE_DYNAMIC\n#define MODE_DYNAMIC_SHRINK\n#define MODE_DYNAMIC_SHRINK_PLOT\n");
+ versions.push_back("\n#define MODE_DYNAMIC\n#define MODE_DYNAMIC_SHRINK\n#define MODE_DYNAMIC_SHRINK_PLOT\n#define MODE_DYNAMIC_SHRINK_WRITE\n");
+
+ giprobe_shader.initialize(versions, defines);
+ giprobe_lighting_shader_version = giprobe_shader.version_create();
+ for (int i = 0; i < GI_PROBE_SHADER_VERSION_MAX; i++) {
+ giprobe_lighting_shader_version_shaders[i] = giprobe_shader.version_get_shader(giprobe_lighting_shader_version, i);
+ giprobe_lighting_shader_version_pipelines[i] = RD::get_singleton()->compute_pipeline_create(giprobe_lighting_shader_version_shaders[i]);
+ }
+ }
+
+ {
+ String defines;
+ Vector<String> versions;
+ versions.push_back("\n#define MODE_DEBUG_COLOR\n");
+ versions.push_back("\n#define MODE_DEBUG_LIGHT\n");
+ versions.push_back("\n#define MODE_DEBUG_EMISSION\n");
+ versions.push_back("\n#define MODE_DEBUG_LIGHT\n#define MODE_DEBUG_LIGHT_FULL\n");
+
+ giprobe_debug_shader.initialize(versions, defines);
+ giprobe_debug_shader_version = giprobe_debug_shader.version_create();
+ for (int i = 0; i < GI_PROBE_DEBUG_MAX; i++) {
+ giprobe_debug_shader_version_shaders[i] = giprobe_debug_shader.version_get_shader(giprobe_debug_shader_version, i);
+
+ RD::PipelineRasterizationState rs;
+ rs.cull_mode = RD::POLYGON_CULL_FRONT;
+ RD::PipelineDepthStencilState ds;
+ ds.enable_depth_test = true;
+ ds.enable_depth_write = true;
+ ds.depth_compare_operator = RD::COMPARE_OP_LESS_OR_EQUAL;
+
+ giprobe_debug_shader_version_pipelines[i].setup(giprobe_debug_shader_version_shaders[i], RD::RENDER_PRIMITIVE_TRIANGLES, rs, RD::PipelineMultisampleState(), ds, RD::PipelineColorBlendState::create_disabled(), 0);
+ }
+ }
+}
+
+void RendererSceneGIRD::init_sdfgi(RendererSceneSkyRD *p_sky) {
+ {
+ Vector<String> preprocess_modes;
+ preprocess_modes.push_back("\n#define MODE_SCROLL\n");
+ preprocess_modes.push_back("\n#define MODE_SCROLL_OCCLUSION\n");
+ preprocess_modes.push_back("\n#define MODE_INITIALIZE_JUMP_FLOOD\n");
+ preprocess_modes.push_back("\n#define MODE_INITIALIZE_JUMP_FLOOD_HALF\n");
+ preprocess_modes.push_back("\n#define MODE_JUMPFLOOD\n");
+ preprocess_modes.push_back("\n#define MODE_JUMPFLOOD_OPTIMIZED\n");
+ preprocess_modes.push_back("\n#define MODE_UPSCALE_JUMP_FLOOD\n");
+ preprocess_modes.push_back("\n#define MODE_OCCLUSION\n");
+ preprocess_modes.push_back("\n#define MODE_STORE\n");
+ String defines = "\n#define OCCLUSION_SIZE " + itos(SDFGI::CASCADE_SIZE / SDFGI::PROBE_DIVISOR) + "\n";
+ sdfgi_shader.preprocess.initialize(preprocess_modes, defines);
+ sdfgi_shader.preprocess_shader = sdfgi_shader.preprocess.version_create();
+ for (int i = 0; i < SDGIShader::PRE_PROCESS_MAX; i++) {
+ sdfgi_shader.preprocess_pipeline[i] = RD::get_singleton()->compute_pipeline_create(sdfgi_shader.preprocess.version_get_shader(sdfgi_shader.preprocess_shader, i));
+ }
+ }
+
+ {
+ //calculate tables
+ String defines = "\n#define OCT_SIZE " + itos(SDFGI::LIGHTPROBE_OCT_SIZE) + "\n";
+
+ Vector<String> direct_light_modes;
+ direct_light_modes.push_back("\n#define MODE_PROCESS_STATIC\n");
+ direct_light_modes.push_back("\n#define MODE_PROCESS_DYNAMIC\n");
+ sdfgi_shader.direct_light.initialize(direct_light_modes, defines);
+ sdfgi_shader.direct_light_shader = sdfgi_shader.direct_light.version_create();
+ for (int i = 0; i < SDGIShader::DIRECT_LIGHT_MODE_MAX; i++) {
+ sdfgi_shader.direct_light_pipeline[i] = RD::get_singleton()->compute_pipeline_create(sdfgi_shader.direct_light.version_get_shader(sdfgi_shader.direct_light_shader, i));
+ }
+ }
+
+ {
+ //calculate tables
+ String defines = "\n#define OCT_SIZE " + itos(SDFGI::LIGHTPROBE_OCT_SIZE) + "\n";
+ defines += "\n#define SH_SIZE " + itos(SDFGI::SH_SIZE) + "\n";
+ if (p_sky->sky_use_cubemap_array) {
+ defines += "\n#define USE_CUBEMAP_ARRAY\n";
+ }
+
+ Vector<String> integrate_modes;
+ integrate_modes.push_back("\n#define MODE_PROCESS\n");
+ integrate_modes.push_back("\n#define MODE_STORE\n");
+ integrate_modes.push_back("\n#define MODE_SCROLL\n");
+ integrate_modes.push_back("\n#define MODE_SCROLL_STORE\n");
+ sdfgi_shader.integrate.initialize(integrate_modes, defines);
+ sdfgi_shader.integrate_shader = sdfgi_shader.integrate.version_create();
+
+ for (int i = 0; i < SDGIShader::INTEGRATE_MODE_MAX; i++) {
+ sdfgi_shader.integrate_pipeline[i] = RD::get_singleton()->compute_pipeline_create(sdfgi_shader.integrate.version_get_shader(sdfgi_shader.integrate_shader, i));
+ }
+
+ {
+ Vector<RD::Uniform> uniforms;
+
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
+ u.binding = 0;
+ u.ids.push_back(storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_CUBEMAP_WHITE));
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_SAMPLER;
+ u.binding = 1;
+ u.ids.push_back(storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED));
+ uniforms.push_back(u);
+ }
+
+ sdfgi_shader.integrate_default_sky_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, sdfgi_shader.integrate.version_get_shader(sdfgi_shader.integrate_shader, 0), 1);
+ }
+ }
+
+ //GK
+ {
+ //calculate tables
+ String defines = "\n#define SDFGI_OCT_SIZE " + itos(SDFGI::LIGHTPROBE_OCT_SIZE) + "\n";
+ Vector<String> gi_modes;
+ gi_modes.push_back("\n#define USE_GIPROBES\n");
+ gi_modes.push_back("\n#define USE_SDFGI\n");
+ gi_modes.push_back("\n#define USE_SDFGI\n\n#define USE_GIPROBES\n");
+ gi_modes.push_back("\n#define MODE_HALF_RES\n#define USE_GIPROBES\n");
+ gi_modes.push_back("\n#define MODE_HALF_RES\n#define USE_SDFGI\n");
+ gi_modes.push_back("\n#define MODE_HALF_RES\n#define USE_SDFGI\n\n#define USE_GIPROBES\n");
+
+ shader.initialize(gi_modes, defines);
+ shader_version = shader.version_create();
+ for (int i = 0; i < MODE_MAX; i++) {
+ pipelines[i] = RD::get_singleton()->compute_pipeline_create(shader.version_get_shader(shader_version, i));
+ }
+
+ sdfgi_ubo = RD::get_singleton()->uniform_buffer_create(sizeof(SDFGIData));
+ }
+ {
+ String defines = "\n#define OCT_SIZE " + itos(SDFGI::LIGHTPROBE_OCT_SIZE) + "\n";
+ Vector<String> debug_modes;
+ debug_modes.push_back("");
+ sdfgi_shader.debug.initialize(debug_modes, defines);
+ sdfgi_shader.debug_shader = sdfgi_shader.debug.version_create();
+ sdfgi_shader.debug_shader_version = sdfgi_shader.debug.version_get_shader(sdfgi_shader.debug_shader, 0);
+ sdfgi_shader.debug_pipeline = RD::get_singleton()->compute_pipeline_create(sdfgi_shader.debug_shader_version);
+ }
+ {
+ String defines = "\n#define OCT_SIZE " + itos(SDFGI::LIGHTPROBE_OCT_SIZE) + "\n";
+
+ Vector<String> versions;
+ versions.push_back("\n#define MODE_PROBES\n");
+ versions.push_back("\n#define MODE_VISIBILITY\n");
+
+ sdfgi_shader.debug_probes.initialize(versions, defines);
+ sdfgi_shader.debug_probes_shader = sdfgi_shader.debug_probes.version_create();
+
+ {
+ RD::PipelineRasterizationState rs;
+ rs.cull_mode = RD::POLYGON_CULL_DISABLED;
+ RD::PipelineDepthStencilState ds;
+ ds.enable_depth_test = true;
+ ds.enable_depth_write = true;
+ ds.depth_compare_operator = RD::COMPARE_OP_LESS_OR_EQUAL;
+ for (int i = 0; i < SDGIShader::PROBE_DEBUG_MAX; i++) {
+ RID debug_probes_shader_version = sdfgi_shader.debug_probes.version_get_shader(sdfgi_shader.debug_probes_shader, i);
+ sdfgi_shader.debug_probes_pipeline[i].setup(debug_probes_shader_version, RD::RENDER_PRIMITIVE_TRIANGLE_STRIPS, rs, RD::PipelineMultisampleState(), ds, RD::PipelineColorBlendState::create_disabled(), 0);
+ }
+ }
+ }
+ default_giprobe_buffer = RD::get_singleton()->uniform_buffer_create(sizeof(GIProbeData) * MAX_GIPROBES);
+}
+
+void RendererSceneGIRD::free() {
+ RD::get_singleton()->free(default_giprobe_buffer);
+ RD::get_singleton()->free(gi_probe_lights_uniform);
+ RD::get_singleton()->free(sdfgi_ubo);
+
+ giprobe_debug_shader.version_free(giprobe_debug_shader_version);
+ giprobe_shader.version_free(giprobe_lighting_shader_version);
+ shader.version_free(shader_version);
+ sdfgi_shader.debug_probes.version_free(sdfgi_shader.debug_probes_shader);
+ sdfgi_shader.debug.version_free(sdfgi_shader.debug_shader);
+ sdfgi_shader.direct_light.version_free(sdfgi_shader.direct_light_shader);
+ sdfgi_shader.integrate.version_free(sdfgi_shader.integrate_shader);
+ sdfgi_shader.preprocess.version_free(sdfgi_shader.preprocess_shader);
+
+ memdelete_arr(gi_probe_lights);
+}
+
+RendererSceneGIRD::SDFGI *RendererSceneGIRD::create_sdfgi(RendererSceneEnvironmentRD *p_env, const Vector3 &p_world_position, uint32_t p_requested_history_size) {
+ SDFGI *sdfgi = memnew(SDFGI);
+
+ sdfgi->create(p_env, p_world_position, p_requested_history_size, this);
+
+ return sdfgi;
+}
+
+void RendererSceneGIRD::setup_giprobes(RID p_render_buffers, const Transform &p_transform, const PagedArray<RID> &p_gi_probes, uint32_t &r_gi_probes_used, RendererSceneRenderRD *p_scene_render) {
+ r_gi_probes_used = 0;
+
+ // feels a little dirty to use our container this way but....
+ RendererSceneRenderRD::RenderBuffers *rb = p_scene_render->render_buffers_owner.getornull(p_render_buffers);
+ ERR_FAIL_COND(rb == nullptr);
+
+ RID gi_probe_buffer = p_scene_render->render_buffers_get_gi_probe_buffer(p_render_buffers);
+
+ RD::get_singleton()->draw_command_begin_label("GIProbes Setup");
+
+ GIProbeData gi_probe_data[MAX_GIPROBES];
+
+ bool giprobes_changed = false;
+
+ Transform to_camera;
+ to_camera.origin = p_transform.origin; //only translation, make local
+
+ for (int i = 0; i < MAX_GIPROBES; i++) {
+ RID texture;
+ if (i < (int)p_gi_probes.size()) {
+ GIProbeInstance *gipi = gi_probe_instance_owner.getornull(p_gi_probes[i]);
+
+ if (gipi) {
+ texture = gipi->texture;
+ GIProbeData &gipd = gi_probe_data[i];
+
+ RID base_probe = gipi->probe;
+
+ Transform to_cell = storage->gi_probe_get_to_cell_xform(gipi->probe) * gipi->transform.affine_inverse() * to_camera;
+
+ gipd.xform[0] = to_cell.basis.elements[0][0];
+ gipd.xform[1] = to_cell.basis.elements[1][0];
+ gipd.xform[2] = to_cell.basis.elements[2][0];
+ gipd.xform[3] = 0;
+ gipd.xform[4] = to_cell.basis.elements[0][1];
+ gipd.xform[5] = to_cell.basis.elements[1][1];
+ gipd.xform[6] = to_cell.basis.elements[2][1];
+ gipd.xform[7] = 0;
+ gipd.xform[8] = to_cell.basis.elements[0][2];
+ gipd.xform[9] = to_cell.basis.elements[1][2];
+ gipd.xform[10] = to_cell.basis.elements[2][2];
+ gipd.xform[11] = 0;
+ gipd.xform[12] = to_cell.origin.x;
+ gipd.xform[13] = to_cell.origin.y;
+ gipd.xform[14] = to_cell.origin.z;
+ gipd.xform[15] = 1;
+
+ Vector3 bounds = storage->gi_probe_get_octree_size(base_probe);
+
+ gipd.bounds[0] = bounds.x;
+ gipd.bounds[1] = bounds.y;
+ gipd.bounds[2] = bounds.z;
+
+ gipd.dynamic_range = storage->gi_probe_get_dynamic_range(base_probe) * storage->gi_probe_get_energy(base_probe);
+ gipd.bias = storage->gi_probe_get_bias(base_probe);
+ gipd.normal_bias = storage->gi_probe_get_normal_bias(base_probe);
+ gipd.blend_ambient = !storage->gi_probe_is_interior(base_probe);
+ gipd.anisotropy_strength = 0;
+ gipd.ao = storage->gi_probe_get_ao(base_probe);
+ gipd.ao_size = Math::pow(storage->gi_probe_get_ao_size(base_probe), 4.0f);
+ gipd.mipmaps = gipi->mipmaps.size();
+ }
+
+ r_gi_probes_used++;
+ }
+
+ if (texture == RID()) {
+ texture = storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_3D_WHITE);
+ }
+
+ if (texture != rb->gi.giprobe_textures[i]) {
+ giprobes_changed = true;
+ rb->gi.giprobe_textures[i] = texture;
+ }
+ }
+
+ if (giprobes_changed) {
+ if (RD::get_singleton()->uniform_set_is_valid(rb->gi_uniform_set)) {
+ RD::get_singleton()->free(rb->gi_uniform_set);
+ }
+ rb->gi_uniform_set = RID();
+ if (rb->volumetric_fog) {
+ if (RD::get_singleton()->uniform_set_is_valid(rb->volumetric_fog->uniform_set)) {
+ RD::get_singleton()->free(rb->volumetric_fog->uniform_set);
+ RD::get_singleton()->free(rb->volumetric_fog->uniform_set2);
+ }
+ rb->volumetric_fog->uniform_set = RID();
+ rb->volumetric_fog->uniform_set2 = RID();
+ }
+ }
+
+ if (p_gi_probes.size() > 0) {
+ RD::get_singleton()->buffer_update(gi_probe_buffer, 0, sizeof(GIProbeData) * MIN((uint64_t)MAX_GIPROBES, p_gi_probes.size()), gi_probe_data, RD::BARRIER_MASK_COMPUTE);
+ }
+
+ RD::get_singleton()->draw_command_end_label();
+}
+
+void RendererSceneGIRD::process_gi(RID p_render_buffers, RID p_normal_roughness_buffer, RID p_gi_probe_buffer, RID p_environment, const CameraMatrix &p_projection, const Transform &p_transform, const PagedArray<RID> &p_gi_probes, RendererSceneRenderRD *p_scene_render) {
+ RD::get_singleton()->draw_command_begin_label("GI Render");
+
+ RendererSceneRenderRD::RenderBuffers *rb = p_scene_render->render_buffers_owner.getornull(p_render_buffers);
+ ERR_FAIL_COND(rb == nullptr);
+ RendererSceneEnvironmentRD *env = p_scene_render->environment_owner.getornull(p_environment);
+
+ if (rb->ambient_buffer.is_null() || rb->using_half_size_gi != half_resolution) {
+ if (rb->ambient_buffer.is_valid()) {
+ RD::get_singleton()->free(rb->ambient_buffer);
+ RD::get_singleton()->free(rb->reflection_buffer);
+ }
+
+ RD::TextureFormat tf;
+ tf.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT;
+ tf.width = rb->width;
+ tf.height = rb->height;
+ if (half_resolution) {
+ tf.width >>= 1;
+ tf.height >>= 1;
+ }
+ tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT;
+ rb->reflection_buffer = RD::get_singleton()->texture_create(tf, RD::TextureView());
+ rb->ambient_buffer = RD::get_singleton()->texture_create(tf, RD::TextureView());
+ rb->using_half_size_gi = half_resolution;
+
+ p_scene_render->_render_buffers_uniform_set_changed(p_render_buffers);
+ }
+
+ PushConstant push_constant;
+
+ push_constant.screen_size[0] = rb->width;
+ push_constant.screen_size[1] = rb->height;
+ push_constant.z_near = p_projection.get_z_near();
+ push_constant.z_far = p_projection.get_z_far();
+ push_constant.orthogonal = p_projection.is_orthogonal();
+ push_constant.proj_info[0] = -2.0f / (rb->width * p_projection.matrix[0][0]);
+ push_constant.proj_info[1] = -2.0f / (rb->height * p_projection.matrix[1][1]);
+ push_constant.proj_info[2] = (1.0f - p_projection.matrix[0][2]) / p_projection.matrix[0][0];
+ push_constant.proj_info[3] = (1.0f + p_projection.matrix[1][2]) / p_projection.matrix[1][1];
+ push_constant.max_giprobes = MIN((uint64_t)MAX_GIPROBES, p_gi_probes.size());
+ push_constant.high_quality_vct = gi_probe_quality == RS::GI_PROBE_QUALITY_HIGH;
+
+ bool use_sdfgi = rb->sdfgi != nullptr;
+ bool use_giprobes = push_constant.max_giprobes > 0;
+
+ if (env) {
+ push_constant.ao_color[0] = env->ao_color.r;
+ push_constant.ao_color[1] = env->ao_color.g;
+ push_constant.ao_color[2] = env->ao_color.b;
+ } else {
+ push_constant.ao_color[0] = 0;
+ push_constant.ao_color[1] = 0;
+ push_constant.ao_color[2] = 0;
+ }
+
+ push_constant.cam_rotation[0] = p_transform.basis[0][0];
+ push_constant.cam_rotation[1] = p_transform.basis[1][0];
+ push_constant.cam_rotation[2] = p_transform.basis[2][0];
+ push_constant.cam_rotation[3] = 0;
+ push_constant.cam_rotation[4] = p_transform.basis[0][1];
+ push_constant.cam_rotation[5] = p_transform.basis[1][1];
+ push_constant.cam_rotation[6] = p_transform.basis[2][1];
+ push_constant.cam_rotation[7] = 0;
+ push_constant.cam_rotation[8] = p_transform.basis[0][2];
+ push_constant.cam_rotation[9] = p_transform.basis[1][2];
+ push_constant.cam_rotation[10] = p_transform.basis[2][2];
+ push_constant.cam_rotation[11] = 0;
+
+ if (rb->gi_uniform_set.is_null() || !RD::get_singleton()->uniform_set_is_valid(rb->gi_uniform_set)) {
+ Vector<RD::Uniform> uniforms;
+ {
+ RD::Uniform u;
+ u.binding = 1;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
+ for (uint32_t j = 0; j < SDFGI::MAX_CASCADES; j++) {
+ if (rb->sdfgi && j < rb->sdfgi->cascades.size()) {
+ u.ids.push_back(rb->sdfgi->cascades[j].sdf_tex);
+ } else {
+ u.ids.push_back(storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_3D_WHITE));
+ }
+ }
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.binding = 2;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
+ for (uint32_t j = 0; j < SDFGI::MAX_CASCADES; j++) {
+ if (rb->sdfgi && j < rb->sdfgi->cascades.size()) {
+ u.ids.push_back(rb->sdfgi->cascades[j].light_tex);
+ } else {
+ u.ids.push_back(storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_3D_WHITE));
+ }
+ }
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.binding = 3;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
+ for (uint32_t j = 0; j < SDFGI::MAX_CASCADES; j++) {
+ if (rb->sdfgi && j < rb->sdfgi->cascades.size()) {
+ u.ids.push_back(rb->sdfgi->cascades[j].light_aniso_0_tex);
+ } else {
+ u.ids.push_back(storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_3D_WHITE));
+ }
+ }
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.binding = 4;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
+ for (uint32_t j = 0; j < SDFGI::MAX_CASCADES; j++) {
+ if (rb->sdfgi && j < rb->sdfgi->cascades.size()) {
+ u.ids.push_back(rb->sdfgi->cascades[j].light_aniso_1_tex);
+ } else {
+ u.ids.push_back(storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_3D_WHITE));
+ }
+ }
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
+ u.binding = 5;
+ if (rb->sdfgi) {
+ u.ids.push_back(rb->sdfgi->occlusion_texture);
+ } else {
+ u.ids.push_back(storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_3D_WHITE));
+ }
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_SAMPLER;
+ u.binding = 6;
+ u.ids.push_back(storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED));
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_SAMPLER;
+ u.binding = 7;
+ u.ids.push_back(storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED));
+ uniforms.push_back(u);
+ }
+
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
+ u.binding = 9;
+ u.ids.push_back(rb->ambient_buffer);
+ uniforms.push_back(u);
+ }
+
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
+ u.binding = 10;
+ u.ids.push_back(rb->reflection_buffer);
+ uniforms.push_back(u);
+ }
+
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
+ u.binding = 11;
+ if (rb->sdfgi) {
+ u.ids.push_back(rb->sdfgi->lightprobe_texture);
+ } else {
+ u.ids.push_back(storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_2D_ARRAY_WHITE));
+ }
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
+ u.binding = 12;
+ u.ids.push_back(rb->depth_texture);
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
+ u.binding = 13;
+ u.ids.push_back(p_normal_roughness_buffer);
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
+ u.binding = 14;
+ RID buffer = p_gi_probe_buffer.is_valid() ? p_gi_probe_buffer : storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_BLACK);
+ u.ids.push_back(buffer);
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER;
+ u.binding = 15;
+ u.ids.push_back(sdfgi_ubo);
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER;
+ u.binding = 16;
+ u.ids.push_back(rb->gi.giprobe_buffer);
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
+ u.binding = 17;
+ for (int i = 0; i < MAX_GIPROBES; i++) {
+ u.ids.push_back(rb->gi.giprobe_textures[i]);
+ }
+ uniforms.push_back(u);
+ }
+
+ rb->gi_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, shader.version_get_shader(shader_version, 0), 0);
+ }
+
+ Mode mode;
+
+ if (rb->using_half_size_gi) {
+ mode = (use_sdfgi && use_giprobes) ? MODE_HALF_RES_COMBINED : (use_sdfgi ? MODE_HALF_RES_SDFGI : MODE_HALF_RES_GIPROBE);
+ } else {
+ mode = (use_sdfgi && use_giprobes) ? MODE_COMBINED : (use_sdfgi ? MODE_SDFGI : MODE_GIPROBE);
+ }
+ RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(true);
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, pipelines[mode]);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, rb->gi_uniform_set, 0);
+ RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(PushConstant));
+
+ if (rb->using_half_size_gi) {
+ RD::get_singleton()->compute_list_dispatch_threads(compute_list, rb->width >> 1, rb->height >> 1, 1);
+ } else {
+ RD::get_singleton()->compute_list_dispatch_threads(compute_list, rb->width, rb->height, 1);
+ }
+ //do barrier later to allow oeverlap
+ //RD::get_singleton()->compute_list_end(RD::BARRIER_MASK_NO_BARRIER); //no barriers, let other compute, raster and transfer happen at the same time
+ RD::get_singleton()->draw_command_end_label();
+}
+
+RID RendererSceneGIRD::gi_probe_instance_create(RID p_base) {
+ GIProbeInstance gi_probe;
+ gi_probe.gi = this;
+ gi_probe.storage = storage;
+ gi_probe.probe = p_base;
+ RID rid = gi_probe_instance_owner.make_rid(gi_probe);
+ return rid;
+}
+
+void RendererSceneGIRD::debug_giprobe(RID p_gi_probe, RD::DrawListID p_draw_list, RID p_framebuffer, const CameraMatrix &p_camera_with_transform, bool p_lighting, bool p_emission, float p_alpha) {
+ GIProbeInstance *gi_probe = gi_probe_instance_owner.getornull(p_gi_probe);
+ ERR_FAIL_COND(!gi_probe);
+
+ gi_probe->debug(p_draw_list, p_framebuffer, p_camera_with_transform, p_lighting, p_emission, p_alpha);
+}
diff --git a/servers/rendering/renderer_rd/renderer_scene_gi_rd.h b/servers/rendering/renderer_rd/renderer_scene_gi_rd.h
new file mode 100644
index 0000000000..6cff9b7837
--- /dev/null
+++ b/servers/rendering/renderer_rd/renderer_scene_gi_rd.h
@@ -0,0 +1,653 @@
+/*************************************************************************/
+/* renderer_scene_gi_rd.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 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 RENDERING_SERVER_SCENE_GI_RD_H
+#define RENDERING_SERVER_SCENE_GI_RD_H
+
+#include "core/templates/local_vector.h"
+#include "core/templates/rid_owner.h"
+#include "servers/rendering/renderer_rd/renderer_scene_environment_rd.h"
+#include "servers/rendering/renderer_rd/renderer_scene_sky_rd.h"
+#include "servers/rendering/renderer_rd/shaders/gi.glsl.gen.h"
+#include "servers/rendering/renderer_rd/shaders/giprobe.glsl.gen.h"
+#include "servers/rendering/renderer_rd/shaders/giprobe_debug.glsl.gen.h"
+#include "servers/rendering/renderer_rd/shaders/sdfgi_debug.glsl.gen.h"
+#include "servers/rendering/renderer_rd/shaders/sdfgi_debug_probes.glsl.gen.h"
+#include "servers/rendering/renderer_rd/shaders/sdfgi_direct_light.glsl.gen.h"
+#include "servers/rendering/renderer_rd/shaders/sdfgi_integrate.glsl.gen.h"
+#include "servers/rendering/renderer_rd/shaders/sdfgi_preprocess.glsl.gen.h"
+#include "servers/rendering/renderer_scene_render.h"
+#include "servers/rendering/rendering_device.h"
+
+// Forward declare RendererSceneRenderRD so we can pass it into some of our methods, these classes are pretty tightly bound
+class RendererSceneRenderRD;
+
+class RendererSceneGIRD {
+private:
+ // !BAS! need to see which things become internal..
+
+ RendererStorageRD *storage;
+
+public:
+ /* GIPROBE INSTANCE */
+
+ struct GIProbeLight {
+ uint32_t type;
+ float energy;
+ float radius;
+ float attenuation;
+
+ float color[3];
+ float cos_spot_angle;
+
+ float position[3];
+ float inv_spot_attenuation;
+
+ float direction[3];
+ uint32_t has_shadow;
+ };
+
+ struct GIProbePushConstant {
+ int32_t limits[3];
+ uint32_t stack_size;
+
+ float emission_scale;
+ float propagation;
+ float dynamic_range;
+ uint32_t light_count;
+
+ uint32_t cell_offset;
+ uint32_t cell_count;
+ float aniso_strength;
+ uint32_t pad;
+ };
+
+ struct GIProbeDynamicPushConstant {
+ int32_t limits[3];
+ uint32_t light_count;
+ int32_t x_dir[3];
+ float z_base;
+ int32_t y_dir[3];
+ float z_sign;
+ int32_t z_dir[3];
+ float pos_multiplier;
+ uint32_t rect_pos[2];
+ uint32_t rect_size[2];
+ uint32_t prev_rect_ofs[2];
+ uint32_t prev_rect_size[2];
+ uint32_t flip_x;
+ uint32_t flip_y;
+ float dynamic_range;
+ uint32_t on_mipmap;
+ float propagation;
+ float pad[3];
+ };
+
+ struct GIProbeInstance {
+ // access to our containers
+ RendererStorageRD *storage;
+ RendererSceneGIRD *gi;
+
+ RID probe;
+ RID texture;
+ RID write_buffer;
+
+ struct Mipmap {
+ RID texture;
+ RID uniform_set;
+ RID second_bounce_uniform_set;
+ RID write_uniform_set;
+ uint32_t level;
+ uint32_t cell_offset;
+ uint32_t cell_count;
+ };
+ Vector<Mipmap> mipmaps;
+
+ struct DynamicMap {
+ RID texture; //color normally, or emission on first pass
+ RID fb_depth; //actual depth buffer for the first pass, float depth for later passes
+ RID depth; //actual depth buffer for the first pass, float depth for later passes
+ RID normal; //normal buffer for the first pass
+ RID albedo; //emission buffer for the first pass
+ RID orm; //orm buffer for the first pass
+ RID fb; //used for rendering, only valid on first map
+ RID uniform_set;
+ uint32_t size;
+ int mipmap; // mipmap to write to, -1 if no mipmap assigned
+ };
+
+ Vector<DynamicMap> dynamic_maps;
+
+ int slot = -1;
+ uint32_t last_probe_version = 0;
+ uint32_t last_probe_data_version = 0;
+
+ //uint64_t last_pass = 0;
+ uint32_t render_index = 0;
+
+ bool has_dynamic_object_data = false;
+
+ Transform transform;
+
+ void update(bool p_update_light_instances, const Vector<RID> &p_light_instances, const PagedArray<RendererSceneRender::GeometryInstance *> &p_dynamic_objects, RendererSceneRenderRD *p_scene_render);
+ void debug(RD::DrawListID p_draw_list, RID p_framebuffer, const CameraMatrix &p_camera_with_transform, bool p_lighting, bool p_emission, float p_alpha);
+ };
+
+ GIProbeLight *gi_probe_lights;
+ uint32_t gi_probe_max_lights;
+ RID gi_probe_lights_uniform;
+
+ enum {
+ GI_PROBE_SHADER_VERSION_COMPUTE_LIGHT,
+ GI_PROBE_SHADER_VERSION_COMPUTE_SECOND_BOUNCE,
+ GI_PROBE_SHADER_VERSION_COMPUTE_MIPMAP,
+ GI_PROBE_SHADER_VERSION_WRITE_TEXTURE,
+ GI_PROBE_SHADER_VERSION_DYNAMIC_OBJECT_LIGHTING,
+ GI_PROBE_SHADER_VERSION_DYNAMIC_SHRINK_WRITE,
+ GI_PROBE_SHADER_VERSION_DYNAMIC_SHRINK_PLOT,
+ GI_PROBE_SHADER_VERSION_DYNAMIC_SHRINK_WRITE_PLOT,
+ GI_PROBE_SHADER_VERSION_MAX
+ };
+
+ GiprobeShaderRD giprobe_shader;
+ RID giprobe_lighting_shader_version;
+ RID giprobe_lighting_shader_version_shaders[GI_PROBE_SHADER_VERSION_MAX];
+ RID giprobe_lighting_shader_version_pipelines[GI_PROBE_SHADER_VERSION_MAX];
+
+ mutable RID_Owner<GIProbeInstance> gi_probe_instance_owner;
+
+ RS::GIProbeQuality gi_probe_quality = RS::GI_PROBE_QUALITY_HIGH;
+
+ enum {
+ GI_PROBE_DEBUG_COLOR,
+ GI_PROBE_DEBUG_LIGHT,
+ GI_PROBE_DEBUG_EMISSION,
+ GI_PROBE_DEBUG_LIGHT_FULL,
+ GI_PROBE_DEBUG_MAX
+ };
+
+ struct GIProbeDebugPushConstant {
+ float projection[16];
+ uint32_t cell_offset;
+ float dynamic_range;
+ float alpha;
+ uint32_t level;
+ int32_t bounds[3];
+ uint32_t pad;
+ };
+
+ GiprobeDebugShaderRD giprobe_debug_shader;
+ RID giprobe_debug_shader_version;
+ RID giprobe_debug_shader_version_shaders[GI_PROBE_DEBUG_MAX];
+ PipelineCacheRD giprobe_debug_shader_version_pipelines[GI_PROBE_DEBUG_MAX];
+ RID giprobe_debug_uniform_set;
+
+ /* SDFGI */
+
+ struct SDFGI {
+ enum {
+ MAX_CASCADES = 8,
+ CASCADE_SIZE = 128,
+ PROBE_DIVISOR = 16,
+ ANISOTROPY_SIZE = 6,
+ MAX_DYNAMIC_LIGHTS = 128,
+ MAX_STATIC_LIGHTS = 1024,
+ LIGHTPROBE_OCT_SIZE = 6,
+ SH_SIZE = 16
+ };
+
+ struct Cascade {
+ struct UBO {
+ float offset[3];
+ float to_cell;
+ int32_t probe_offset[3];
+ uint32_t pad;
+ };
+
+ //cascade blocks are full-size for volume (128^3), half size for albedo/emission
+ RID sdf_tex;
+ RID light_tex;
+ RID light_aniso_0_tex;
+ RID light_aniso_1_tex;
+
+ RID light_data;
+ RID light_aniso_0_data;
+ RID light_aniso_1_data;
+
+ struct SolidCell { // this struct is unused, but remains as reference for size
+ uint32_t position;
+ uint32_t albedo;
+ uint32_t static_light;
+ uint32_t static_light_aniso;
+ };
+
+ RID solid_cell_dispatch_buffer; //buffer for indirect compute dispatch
+ RID solid_cell_buffer;
+
+ RID lightprobe_history_tex;
+ RID lightprobe_average_tex;
+
+ float cell_size;
+ Vector3i position;
+
+ static const Vector3i DIRTY_ALL;
+ Vector3i dirty_regions; //(0,0,0 is not dirty, negative is refresh from the end, DIRTY_ALL is refresh all.
+
+ RID sdf_store_uniform_set;
+ RID sdf_direct_light_uniform_set;
+ RID scroll_uniform_set;
+ RID scroll_occlusion_uniform_set;
+ RID integrate_uniform_set;
+ RID lights_buffer;
+
+ bool all_dynamic_lights_dirty = true;
+ };
+
+ // access to our containers
+ RendererStorageRD *storage;
+ RendererSceneGIRD *gi;
+
+ // used for rendering (voxelization)
+ RID render_albedo;
+ RID render_emission;
+ RID render_emission_aniso;
+ RID render_occlusion[8];
+ RID render_geom_facing;
+
+ RID render_sdf[2];
+ RID render_sdf_half[2];
+
+ // used for ping pong processing in cascades
+ RID sdf_initialize_uniform_set;
+ RID sdf_initialize_half_uniform_set;
+ RID jump_flood_uniform_set[2];
+ RID jump_flood_half_uniform_set[2];
+ RID sdf_upscale_uniform_set;
+ int upscale_jfa_uniform_set_index;
+ RID occlusion_uniform_set;
+
+ uint32_t cascade_size = 128;
+
+ LocalVector<Cascade> cascades;
+
+ RID lightprobe_texture;
+ RID lightprobe_data;
+ RID occlusion_texture;
+ RID occlusion_data;
+ RID ambient_texture; //integrates with volumetric fog
+
+ RID lightprobe_history_scroll; //used for scrolling lightprobes
+ RID lightprobe_average_scroll; //used for scrolling lightprobes
+
+ uint32_t history_size = 0;
+ float solid_cell_ratio = 0;
+ uint32_t solid_cell_count = 0;
+
+ RS::EnvironmentSDFGICascades cascade_mode;
+ float min_cell_size = 0;
+ uint32_t probe_axis_count = 0; //amount of probes per axis, this is an odd number because it encloses endpoints
+
+ RID debug_uniform_set;
+ RID debug_probes_uniform_set;
+ RID cascades_ubo;
+
+ bool uses_occlusion = false;
+ float bounce_feedback = 0.0;
+ bool reads_sky = false;
+ float energy = 1.0;
+ float normal_bias = 1.1;
+ float probe_bias = 1.1;
+ RS::EnvironmentSDFGIYScale y_scale_mode = RS::ENV_SDFGI_Y_SCALE_DISABLED;
+
+ float y_mult = 1.0;
+
+ uint32_t render_pass = 0;
+
+ int32_t cascade_dynamic_light_count[SDFGI::MAX_CASCADES]; //used dynamically
+ RID integrate_sky_uniform_set;
+
+ void create(RendererSceneEnvironmentRD *p_env, const Vector3 &p_world_position, uint32_t p_requested_history_size, RendererSceneGIRD *p_gi);
+ void erase();
+ void update(RendererSceneEnvironmentRD *p_env, const Vector3 &p_world_position);
+ void update_light();
+ void update_probes(RendererSceneEnvironmentRD *p_env, RendererSceneSkyRD::Sky *p_sky);
+ void store_probes();
+ int get_pending_region_data(int p_region, Vector3i &r_local_offset, Vector3i &r_local_size, AABB &r_bounds) const;
+ void update_cascades();
+
+ void debug_draw(const CameraMatrix &p_projection, const Transform &p_transform, int p_width, int p_height, RID p_render_target, RID p_texture);
+ void debug_probes(RD::DrawListID p_draw_list, RID p_framebuffer, const CameraMatrix &p_camera_with_transform);
+
+ void pre_process_gi(const Transform &p_transform, RendererSceneRenderRD *p_scene_render);
+ void render_region(RID p_render_buffers, int p_region, const PagedArray<RendererSceneRender::GeometryInstance *> &p_instances, RendererSceneRenderRD *p_scene_render);
+ void render_static_lights(RID p_render_buffers, uint32_t p_cascade_count, const uint32_t *p_cascade_indices, const PagedArray<RID> *p_positional_light_cull_result, RendererSceneRenderRD *p_scene_render);
+ };
+
+ RS::EnvironmentSDFGIRayCount sdfgi_ray_count = RS::ENV_SDFGI_RAY_COUNT_16;
+ RS::EnvironmentSDFGIFramesToConverge sdfgi_frames_to_converge = RS::ENV_SDFGI_CONVERGE_IN_10_FRAMES;
+ RS::EnvironmentSDFGIFramesToUpdateLight sdfgi_frames_to_update_light = RS::ENV_SDFGI_UPDATE_LIGHT_IN_4_FRAMES;
+
+ float sdfgi_solid_cell_ratio = 0.25;
+ Vector3 sdfgi_debug_probe_pos;
+ Vector3 sdfgi_debug_probe_dir;
+ bool sdfgi_debug_probe_enabled = false;
+ Vector3i sdfgi_debug_probe_index;
+
+ struct SDGIShader {
+ enum SDFGIPreprocessShaderVersion {
+ PRE_PROCESS_SCROLL,
+ PRE_PROCESS_SCROLL_OCCLUSION,
+ PRE_PROCESS_JUMP_FLOOD_INITIALIZE,
+ PRE_PROCESS_JUMP_FLOOD_INITIALIZE_HALF,
+ PRE_PROCESS_JUMP_FLOOD,
+ PRE_PROCESS_JUMP_FLOOD_OPTIMIZED,
+ PRE_PROCESS_JUMP_FLOOD_UPSCALE,
+ PRE_PROCESS_OCCLUSION,
+ PRE_PROCESS_STORE,
+ PRE_PROCESS_MAX
+ };
+
+ struct PreprocessPushConstant {
+ int32_t scroll[3];
+ int32_t grid_size;
+
+ int32_t probe_offset[3];
+ int32_t step_size;
+
+ int32_t half_size;
+ uint32_t occlusion_index;
+ int32_t cascade;
+ uint32_t pad;
+ };
+
+ SdfgiPreprocessShaderRD preprocess;
+ RID preprocess_shader;
+ RID preprocess_pipeline[PRE_PROCESS_MAX];
+
+ struct DebugPushConstant {
+ float grid_size[3];
+ uint32_t max_cascades;
+
+ int32_t screen_size[2];
+ uint32_t use_occlusion;
+ float y_mult;
+
+ float cam_extent[3];
+ uint32_t probe_axis_size;
+
+ float cam_transform[16];
+ };
+
+ SdfgiDebugShaderRD debug;
+ RID debug_shader;
+ RID debug_shader_version;
+ RID debug_pipeline;
+
+ enum ProbeDebugMode {
+ PROBE_DEBUG_PROBES,
+ PROBE_DEBUG_VISIBILITY,
+ PROBE_DEBUG_MAX
+ };
+
+ struct DebugProbesPushConstant {
+ float projection[16];
+
+ uint32_t band_power;
+ uint32_t sections_in_band;
+ uint32_t band_mask;
+ float section_arc;
+
+ float grid_size[3];
+ uint32_t cascade;
+
+ uint32_t pad;
+ float y_mult;
+ int32_t probe_debug_index;
+ int32_t probe_axis_size;
+ };
+
+ SdfgiDebugProbesShaderRD debug_probes;
+ RID debug_probes_shader;
+ RID debug_probes_shader_version;
+
+ PipelineCacheRD debug_probes_pipeline[PROBE_DEBUG_MAX];
+
+ struct Light {
+ float color[3];
+ float energy;
+
+ float direction[3];
+ uint32_t has_shadow;
+
+ float position[3];
+ float attenuation;
+
+ uint32_t type;
+ float cos_spot_angle;
+ float inv_spot_attenuation;
+ float radius;
+
+ float shadow_color[4];
+ };
+
+ struct DirectLightPushConstant {
+ float grid_size[3];
+ uint32_t max_cascades;
+
+ uint32_t cascade;
+ uint32_t light_count;
+ uint32_t process_offset;
+ uint32_t process_increment;
+
+ int32_t probe_axis_size;
+ float bounce_feedback;
+ float y_mult;
+ uint32_t use_occlusion;
+ };
+
+ enum {
+ DIRECT_LIGHT_MODE_STATIC,
+ DIRECT_LIGHT_MODE_DYNAMIC,
+ DIRECT_LIGHT_MODE_MAX
+ };
+ SdfgiDirectLightShaderRD direct_light;
+ RID direct_light_shader;
+ RID direct_light_pipeline[DIRECT_LIGHT_MODE_MAX];
+
+ enum {
+ INTEGRATE_MODE_PROCESS,
+ INTEGRATE_MODE_STORE,
+ INTEGRATE_MODE_SCROLL,
+ INTEGRATE_MODE_SCROLL_STORE,
+ INTEGRATE_MODE_MAX
+ };
+ struct IntegratePushConstant {
+ enum {
+ SKY_MODE_DISABLED,
+ SKY_MODE_COLOR,
+ SKY_MODE_SKY,
+ };
+
+ float grid_size[3];
+ uint32_t max_cascades;
+
+ uint32_t probe_axis_size;
+ uint32_t cascade;
+ uint32_t history_index;
+ uint32_t history_size;
+
+ uint32_t ray_count;
+ float ray_bias;
+ int32_t image_size[2];
+
+ int32_t world_offset[3];
+ uint32_t sky_mode;
+
+ int32_t scroll[3];
+ float sky_energy;
+
+ float sky_color[3];
+ float y_mult;
+
+ uint32_t store_ambient_texture;
+ uint32_t pad[3];
+ };
+
+ SdfgiIntegrateShaderRD integrate;
+ RID integrate_shader;
+ RID integrate_pipeline[INTEGRATE_MODE_MAX];
+
+ RID integrate_default_sky_uniform_set;
+
+ } sdfgi_shader;
+
+ /* SDFGI UPDATE */
+
+ int sdfgi_get_lightprobe_octahedron_size() const { return SDFGI::LIGHTPROBE_OCT_SIZE; }
+
+ /* GI */
+ enum {
+ MAX_GIPROBES = 8
+ };
+
+ // Struct for use in render buffer
+ struct RenderBuffersGI {
+ RID giprobe_textures[MAX_GIPROBES];
+ RID giprobe_buffer;
+
+ RID full_buffer;
+ RID full_dispatch;
+ RID full_mask;
+ };
+
+ // struct GI {
+ struct SDFGIData {
+ float grid_size[3];
+ uint32_t max_cascades;
+
+ uint32_t use_occlusion;
+ int32_t probe_axis_size;
+ float probe_to_uvw;
+ float normal_bias;
+
+ float lightprobe_tex_pixel_size[3];
+ float energy;
+
+ float lightprobe_uv_offset[3];
+ float y_mult;
+
+ float occlusion_clamp[3];
+ uint32_t pad3;
+
+ float occlusion_renormalize[3];
+ uint32_t pad4;
+
+ float cascade_probe_size[3];
+ uint32_t pad5;
+
+ struct ProbeCascadeData {
+ float position[3]; //offset of (0,0,0) in world coordinates
+ float to_probe; // 1/bounds * grid_size
+ int32_t probe_world_offset[3];
+ float to_cell; // 1/bounds * grid_size
+ };
+
+ ProbeCascadeData cascades[SDFGI::MAX_CASCADES];
+ };
+
+ struct GIProbeData {
+ float xform[16];
+ float bounds[3];
+ float dynamic_range;
+
+ float bias;
+ float normal_bias;
+ uint32_t blend_ambient;
+ uint32_t texture_slot;
+
+ float anisotropy_strength;
+ float ao;
+ float ao_size;
+ uint32_t mipmaps;
+ };
+
+ struct PushConstant {
+ int32_t screen_size[2];
+ float z_near;
+ float z_far;
+
+ float proj_info[4];
+ float ao_color[3];
+ uint32_t max_giprobes;
+
+ uint32_t high_quality_vct;
+ uint32_t orthogonal;
+ uint32_t pad[2];
+
+ float cam_rotation[12];
+ };
+
+ RID sdfgi_ubo;
+ enum Mode {
+ MODE_GIPROBE,
+ MODE_SDFGI,
+ MODE_COMBINED,
+ MODE_HALF_RES_GIPROBE,
+ MODE_HALF_RES_SDFGI,
+ MODE_HALF_RES_COMBINED,
+ MODE_MAX
+ };
+
+ RID default_giprobe_buffer;
+
+ bool half_resolution = false;
+ GiShaderRD shader;
+ RID shader_version;
+ RID pipelines[MODE_MAX];
+ // } gi;
+
+ RendererSceneGIRD();
+ ~RendererSceneGIRD();
+
+ // !BAS! Can we merge these two inits? Possibly, need to check
+ void init_gi(RendererStorageRD *p_storage);
+ void init_sdfgi(RendererSceneSkyRD *p_sky);
+ void free();
+
+ SDFGI *create_sdfgi(RendererSceneEnvironmentRD *p_env, const Vector3 &p_world_position, uint32_t p_requested_history_size);
+
+ void setup_giprobes(RID p_render_buffers, const Transform &p_transform, const PagedArray<RID> &p_gi_probes, uint32_t &r_gi_probes_used, RendererSceneRenderRD *p_scene_render);
+ void process_gi(RID p_render_buffers, RID p_normal_roughness_buffer, RID p_gi_probe_buffer, RID p_environment, const CameraMatrix &p_projection, const Transform &p_transform, const PagedArray<RID> &p_gi_probes, RendererSceneRenderRD *p_scene_render);
+
+ RID gi_probe_instance_create(RID p_base);
+ void debug_giprobe(RID p_gi_probe, RD::DrawListID p_draw_list, RID p_framebuffer, const CameraMatrix &p_camera_with_transform, bool p_lighting, bool p_emission, float p_alpha);
+};
+
+#endif /* !RENDERING_SERVER_SCENE_GI_RD_H */
diff --git a/servers/rendering/renderer_rd/renderer_scene_render_forward.cpp b/servers/rendering/renderer_rd/renderer_scene_render_forward.cpp
index a57dee7314..dd8bfda4d6 100644
--- a/servers/rendering/renderer_rd/renderer_scene_render_forward.cpp
+++ b/servers/rendering/renderer_rd/renderer_scene_render_forward.cpp
@@ -1130,7 +1130,7 @@ void RendererSceneRenderForward::_setup_environment(RID p_environment, RID p_ren
//vec2 tex_pixel_size = 1.0 / vec2(ivec2( (OCT_SIZE+2) * params.probe_axis_size * params.probe_axis_size, (OCT_SIZE+2) * params.probe_axis_size ) );
//vec3 probe_uv_offset = (ivec3(OCT_SIZE+2,OCT_SIZE+2,(OCT_SIZE+2) * params.probe_axis_size)) * tex_pixel_size.xyx;
- uint32_t oct_size = sdfgi_get_lightprobe_octahedron_size();
+ uint32_t oct_size = gi.sdfgi_get_lightprobe_octahedron_size();
scene_state.ubo.sdfgi_lightprobe_tex_pixel_size[0] = 1.0 / ((oct_size + 2) * scene_state.ubo.sdfgi_probe_axis_size * scene_state.ubo.sdfgi_probe_axis_size);
scene_state.ubo.sdfgi_lightprobe_tex_pixel_size[1] = 1.0 / ((oct_size + 2) * scene_state.ubo.sdfgi_probe_axis_size);
@@ -1583,6 +1583,7 @@ void RendererSceneRenderForward::_render_scene(RID p_render_buffer, const Transf
if (p_render_buffer.is_valid()) {
render_buffer = (RenderBufferDataForward *)render_buffers_get_data(p_render_buffer);
}
+ RendererSceneEnvironmentRD *env = get_environment(p_environment);
//first of all, make a new render pass
//fill up ubo
@@ -1729,7 +1730,7 @@ void RendererSceneRenderForward::_render_scene(RID p_render_buffer, const Transf
clear_color.b *= bg_energy;
if (render_buffers_has_volumetric_fog(p_render_buffer) || environment_is_fog_enabled(p_environment)) {
draw_sky_fog_only = true;
- storage->material_set_param(sky_scene_state.fog_material, "clear_color", Variant(clear_color.to_linear()));
+ storage->material_set_param(sky.sky_scene_state.fog_material, "clear_color", Variant(clear_color.to_linear()));
}
} break;
case RS::ENV_BG_COLOR: {
@@ -1739,7 +1740,7 @@ void RendererSceneRenderForward::_render_scene(RID p_render_buffer, const Transf
clear_color.b *= bg_energy;
if (render_buffers_has_volumetric_fog(p_render_buffer) || environment_is_fog_enabled(p_environment)) {
draw_sky_fog_only = true;
- storage->material_set_param(sky_scene_state.fog_material, "clear_color", Variant(clear_color.to_linear()));
+ storage->material_set_param(sky.sky_scene_state.fog_material, "clear_color", Variant(clear_color.to_linear()));
}
} break;
case RS::ENV_BG_SKY: {
@@ -1767,12 +1768,12 @@ void RendererSceneRenderForward::_render_scene(RID p_render_buffer, const Transf
projection = correction * p_cam_projection;
}
- _setup_sky(p_environment, p_render_buffer, projection, p_cam_transform, screen_size);
+ sky.setup(env, p_render_buffer, projection, p_cam_transform, screen_size, this);
- RID sky = environment_get_sky(p_environment);
- if (sky.is_valid()) {
- _update_sky(p_environment, projection, p_cam_transform);
- radiance_texture = sky_get_radiance_texture_rd(sky);
+ RID sky_rid = env->sky;
+ if (sky_rid.is_valid()) {
+ sky.update(env, projection, p_cam_transform, time);
+ radiance_texture = sky.sky_get_radiance_texture_rd(sky_rid);
} else {
// do not try to draw sky if invalid
draw_sky = false;
@@ -1890,7 +1891,7 @@ void RendererSceneRenderForward::_render_scene(RID p_render_buffer, const Transf
RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(opaque_framebuffer, RD::INITIAL_ACTION_CONTINUE, will_continue_color ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CONTINUE, will_continue_depth ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ);
RD::get_singleton()->draw_command_begin_label("Debug GIProbes");
for (int i = 0; i < (int)p_gi_probes.size(); i++) {
- _debug_giprobe(p_gi_probes[i], draw_list, opaque_framebuffer, cm, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_GI_PROBE_LIGHTING, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_GI_PROBE_EMISSION, 1.0);
+ gi.debug_giprobe(p_gi_probes[i], draw_list, opaque_framebuffer, cm, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_GI_PROBE_LIGHTING, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_GI_PROBE_EMISSION, 1.0);
}
RD::get_singleton()->draw_command_end_label();
RD::get_singleton()->draw_list_end();
@@ -1921,7 +1922,7 @@ void RendererSceneRenderForward::_render_scene(RID p_render_buffer, const Transf
projection = correction * p_cam_projection;
}
RD::get_singleton()->draw_command_begin_label("Draw Sky");
- _draw_sky(can_continue_color, can_continue_depth, opaque_framebuffer, p_environment, projection, p_cam_transform);
+ sky.draw(env, can_continue_color, can_continue_depth, opaque_framebuffer, projection, p_cam_transform, time);
RD::get_singleton()->draw_command_end_label();
}
@@ -3346,7 +3347,7 @@ RendererSceneRenderForward::RendererSceneRenderForward(RendererStorageRD *p_stor
if (is_using_radiance_cubemap_array()) {
defines += "\n#define USE_RADIANCE_CUBEMAP_ARRAY \n";
}
- defines += "\n#define SDFGI_OCT_SIZE " + itos(sdfgi_get_lightprobe_octahedron_size()) + "\n";
+ defines += "\n#define SDFGI_OCT_SIZE " + itos(gi.sdfgi_get_lightprobe_octahedron_size()) + "\n";
defines += "\n#define MAX_DIRECTIONAL_LIGHT_DATA_STRUCTS " + itos(get_max_directional_lights()) + "\n";
{
diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
index 15e963f6e4..7a6900b0c4 100644
--- a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
@@ -35,8 +35,6 @@
#include "renderer_compositor_rd.h"
#include "servers/rendering/rendering_server_default.h"
-uint64_t RendererSceneRenderRD::auto_exposure_counter = 2;
-
void get_vogel_disk(float *r_kernel, int p_sample_count) {
const float golden_angle = 2.4;
@@ -49,980 +47,42 @@ void get_vogel_disk(float *r_kernel, int p_sample_count) {
}
}
-void RendererSceneRenderRD::_clear_reflection_data(ReflectionData &rd) {
- rd.layers.clear();
- rd.radiance_base_cubemap = RID();
- if (rd.downsampled_radiance_cubemap.is_valid()) {
- RD::get_singleton()->free(rd.downsampled_radiance_cubemap);
- }
- rd.downsampled_radiance_cubemap = RID();
- rd.downsampled_layer.mipmaps.clear();
- rd.coefficient_buffer = RID();
-}
-
-void RendererSceneRenderRD::_update_reflection_data(ReflectionData &rd, int p_size, int p_mipmaps, bool p_use_array, RID p_base_cube, int p_base_layer, bool p_low_quality) {
- //recreate radiance and all data
-
- int mipmaps = p_mipmaps;
- uint32_t w = p_size, h = p_size;
-
- if (p_use_array) {
- int layers = p_low_quality ? 8 : roughness_layers;
-
- for (int i = 0; i < layers; i++) {
- ReflectionData::Layer layer;
- uint32_t mmw = w;
- uint32_t mmh = h;
- layer.mipmaps.resize(mipmaps);
- layer.views.resize(mipmaps);
- for (int j = 0; j < mipmaps; j++) {
- ReflectionData::Layer::Mipmap &mm = layer.mipmaps.write[j];
- mm.size.width = mmw;
- mm.size.height = mmh;
- for (int k = 0; k < 6; k++) {
- mm.views[k] = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), p_base_cube, p_base_layer + i * 6 + k, j);
- Vector<RID> fbtex;
- fbtex.push_back(mm.views[k]);
- mm.framebuffers[k] = RD::get_singleton()->framebuffer_create(fbtex);
- }
-
- layer.views.write[j] = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), p_base_cube, p_base_layer + i * 6, j, RD::TEXTURE_SLICE_CUBEMAP);
-
- mmw = MAX(1, mmw >> 1);
- mmh = MAX(1, mmh >> 1);
- }
-
- rd.layers.push_back(layer);
- }
-
- } else {
- mipmaps = p_low_quality ? 8 : mipmaps;
- //regular cubemap, lower quality (aliasing, less memory)
- ReflectionData::Layer layer;
- uint32_t mmw = w;
- uint32_t mmh = h;
- layer.mipmaps.resize(mipmaps);
- layer.views.resize(mipmaps);
- for (int j = 0; j < mipmaps; j++) {
- ReflectionData::Layer::Mipmap &mm = layer.mipmaps.write[j];
- mm.size.width = mmw;
- mm.size.height = mmh;
- for (int k = 0; k < 6; k++) {
- mm.views[k] = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), p_base_cube, p_base_layer + k, j);
- Vector<RID> fbtex;
- fbtex.push_back(mm.views[k]);
- mm.framebuffers[k] = RD::get_singleton()->framebuffer_create(fbtex);
- }
-
- layer.views.write[j] = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), p_base_cube, p_base_layer, j, RD::TEXTURE_SLICE_CUBEMAP);
-
- mmw = MAX(1, mmw >> 1);
- mmh = MAX(1, mmh >> 1);
- }
-
- rd.layers.push_back(layer);
- }
-
- rd.radiance_base_cubemap = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), p_base_cube, p_base_layer, 0, RD::TEXTURE_SLICE_CUBEMAP);
-
- RD::TextureFormat tf;
- tf.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT;
- tf.width = 64; // Always 64x64
- tf.height = 64;
- tf.texture_type = RD::TEXTURE_TYPE_CUBE;
- tf.array_layers = 6;
- tf.mipmaps = 7;
- tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
-
- rd.downsampled_radiance_cubemap = RD::get_singleton()->texture_create(tf, RD::TextureView());
- {
- uint32_t mmw = 64;
- uint32_t mmh = 64;
- rd.downsampled_layer.mipmaps.resize(7);
- for (int j = 0; j < rd.downsampled_layer.mipmaps.size(); j++) {
- ReflectionData::DownsampleLayer::Mipmap &mm = rd.downsampled_layer.mipmaps.write[j];
- mm.size.width = mmw;
- mm.size.height = mmh;
- mm.view = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), rd.downsampled_radiance_cubemap, 0, j, RD::TEXTURE_SLICE_CUBEMAP);
-
- mmw = MAX(1, mmw >> 1);
- mmh = MAX(1, mmh >> 1);
- }
- }
-}
-
-void RendererSceneRenderRD::_create_reflection_fast_filter(ReflectionData &rd, bool p_use_arrays) {
- storage->get_effects()->cubemap_downsample(rd.radiance_base_cubemap, rd.downsampled_layer.mipmaps[0].view, rd.downsampled_layer.mipmaps[0].size);
-
- for (int i = 1; i < rd.downsampled_layer.mipmaps.size(); i++) {
- storage->get_effects()->cubemap_downsample(rd.downsampled_layer.mipmaps[i - 1].view, rd.downsampled_layer.mipmaps[i].view, rd.downsampled_layer.mipmaps[i].size);
- }
-
- Vector<RID> views;
- if (p_use_arrays) {
- for (int i = 1; i < rd.layers.size(); i++) {
- views.push_back(rd.layers[i].views[0]);
- }
- } else {
- for (int i = 1; i < rd.layers[0].views.size(); i++) {
- views.push_back(rd.layers[0].views[i]);
- }
- }
-
- storage->get_effects()->cubemap_filter(rd.downsampled_radiance_cubemap, views, p_use_arrays);
-}
-
-void RendererSceneRenderRD::_create_reflection_importance_sample(ReflectionData &rd, bool p_use_arrays, int p_cube_side, int p_base_layer) {
- if (p_use_arrays) {
- //render directly to the layers
- storage->get_effects()->cubemap_roughness(rd.radiance_base_cubemap, rd.layers[p_base_layer].views[0], p_cube_side, sky_ggx_samples_quality, float(p_base_layer) / (rd.layers.size() - 1.0), rd.layers[p_base_layer].mipmaps[0].size.x);
- } else {
- storage->get_effects()->cubemap_roughness(rd.layers[0].views[p_base_layer - 1], rd.layers[0].views[p_base_layer], p_cube_side, sky_ggx_samples_quality, float(p_base_layer) / (rd.layers[0].mipmaps.size() - 1.0), rd.layers[0].mipmaps[p_base_layer].size.x);
- }
-}
-
-void RendererSceneRenderRD::_update_reflection_mipmaps(ReflectionData &rd, int p_start, int p_end) {
- for (int i = p_start; i < p_end; i++) {
- for (int j = 0; j < rd.layers[i].views.size() - 1; j++) {
- RID view = rd.layers[i].views[j];
- RID texture = rd.layers[i].views[j + 1];
- Size2i size = rd.layers[i].mipmaps[j + 1].size;
- storage->get_effects()->cubemap_downsample(view, texture, size);
- }
- }
-}
-
-void RendererSceneRenderRD::_sdfgi_erase(RenderBuffers *rb) {
- for (uint32_t i = 0; i < rb->sdfgi->cascades.size(); i++) {
- const SDFGI::Cascade &c = rb->sdfgi->cascades[i];
- RD::get_singleton()->free(c.light_data);
- RD::get_singleton()->free(c.light_aniso_0_tex);
- RD::get_singleton()->free(c.light_aniso_1_tex);
- RD::get_singleton()->free(c.sdf_tex);
- RD::get_singleton()->free(c.solid_cell_dispatch_buffer);
- RD::get_singleton()->free(c.solid_cell_buffer);
- RD::get_singleton()->free(c.lightprobe_history_tex);
- RD::get_singleton()->free(c.lightprobe_average_tex);
- RD::get_singleton()->free(c.lights_buffer);
- }
-
- RD::get_singleton()->free(rb->sdfgi->render_albedo);
- RD::get_singleton()->free(rb->sdfgi->render_emission);
- RD::get_singleton()->free(rb->sdfgi->render_emission_aniso);
-
- RD::get_singleton()->free(rb->sdfgi->render_sdf[0]);
- RD::get_singleton()->free(rb->sdfgi->render_sdf[1]);
-
- RD::get_singleton()->free(rb->sdfgi->render_sdf_half[0]);
- RD::get_singleton()->free(rb->sdfgi->render_sdf_half[1]);
-
- for (int i = 0; i < 8; i++) {
- RD::get_singleton()->free(rb->sdfgi->render_occlusion[i]);
- }
-
- RD::get_singleton()->free(rb->sdfgi->render_geom_facing);
-
- RD::get_singleton()->free(rb->sdfgi->lightprobe_data);
- RD::get_singleton()->free(rb->sdfgi->lightprobe_history_scroll);
- RD::get_singleton()->free(rb->sdfgi->occlusion_data);
- RD::get_singleton()->free(rb->sdfgi->ambient_texture);
-
- RD::get_singleton()->free(rb->sdfgi->cascades_ubo);
-
- memdelete(rb->sdfgi);
-
- rb->sdfgi = nullptr;
-}
-
-const Vector3i RendererSceneRenderRD::SDFGI::Cascade::DIRTY_ALL = Vector3i(0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF);
-
void RendererSceneRenderRD::sdfgi_update(RID p_render_buffers, RID p_environment, const Vector3 &p_world_position) {
- Environment *env = environment_owner.getornull(p_environment);
+ RendererSceneEnvironmentRD *env = environment_owner.getornull(p_environment);
RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers);
bool needs_sdfgi = env && env->sdfgi_enabled;
if (!needs_sdfgi) {
if (rb->sdfgi != nullptr) {
//erase it
- _sdfgi_erase(rb);
+ rb->sdfgi->erase();
+ memdelete(rb->sdfgi);
+ rb->sdfgi = nullptr;
+
_render_buffers_uniform_set_changed(p_render_buffers);
}
return;
}
static const uint32_t history_frames_to_converge[RS::ENV_SDFGI_CONVERGE_MAX] = { 5, 10, 15, 20, 25, 30 };
- uint32_t requested_history_size = history_frames_to_converge[sdfgi_frames_to_converge];
+ uint32_t requested_history_size = history_frames_to_converge[gi.sdfgi_frames_to_converge];
if (rb->sdfgi && (rb->sdfgi->cascade_mode != env->sdfgi_cascades || rb->sdfgi->min_cell_size != env->sdfgi_min_cell_size || requested_history_size != rb->sdfgi->history_size || rb->sdfgi->uses_occlusion != env->sdfgi_use_occlusion || rb->sdfgi->y_scale_mode != env->sdfgi_y_scale)) {
//configuration changed, erase
- _sdfgi_erase(rb);
+ rb->sdfgi->erase();
+ memdelete(rb->sdfgi);
+ rb->sdfgi = nullptr;
}
- SDFGI *sdfgi = rb->sdfgi;
+ RendererSceneGIRD::SDFGI *sdfgi = rb->sdfgi;
if (sdfgi == nullptr) {
- //re-create
- rb->sdfgi = memnew(SDFGI);
- sdfgi = rb->sdfgi;
- sdfgi->cascade_mode = env->sdfgi_cascades;
- sdfgi->min_cell_size = env->sdfgi_min_cell_size;
- sdfgi->uses_occlusion = env->sdfgi_use_occlusion;
- sdfgi->y_scale_mode = env->sdfgi_y_scale;
- static const float y_scale[3] = { 1.0, 1.5, 2.0 };
- sdfgi->y_mult = y_scale[sdfgi->y_scale_mode];
- static const int cascasde_size[3] = { 4, 6, 8 };
- sdfgi->cascades.resize(cascasde_size[sdfgi->cascade_mode]);
- sdfgi->probe_axis_count = SDFGI::PROBE_DIVISOR + 1;
- sdfgi->solid_cell_ratio = sdfgi_solid_cell_ratio;
- sdfgi->solid_cell_count = uint32_t(float(sdfgi->cascade_size * sdfgi->cascade_size * sdfgi->cascade_size) * sdfgi->solid_cell_ratio);
-
- float base_cell_size = sdfgi->min_cell_size;
-
- RD::TextureFormat tf_sdf;
- tf_sdf.format = RD::DATA_FORMAT_R8_UNORM;
- tf_sdf.width = sdfgi->cascade_size; // Always 64x64
- tf_sdf.height = sdfgi->cascade_size;
- tf_sdf.depth = sdfgi->cascade_size;
- tf_sdf.texture_type = RD::TEXTURE_TYPE_3D;
- tf_sdf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_CAN_COPY_TO_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT;
-
- {
- RD::TextureFormat tf_render = tf_sdf;
- tf_render.format = RD::DATA_FORMAT_R16_UINT;
- sdfgi->render_albedo = RD::get_singleton()->texture_create(tf_render, RD::TextureView());
- tf_render.format = RD::DATA_FORMAT_R32_UINT;
- sdfgi->render_emission = RD::get_singleton()->texture_create(tf_render, RD::TextureView());
- sdfgi->render_emission_aniso = RD::get_singleton()->texture_create(tf_render, RD::TextureView());
-
- tf_render.format = RD::DATA_FORMAT_R8_UNORM; //at least its easy to visualize
-
- for (int i = 0; i < 8; i++) {
- sdfgi->render_occlusion[i] = RD::get_singleton()->texture_create(tf_render, RD::TextureView());
- }
-
- tf_render.format = RD::DATA_FORMAT_R32_UINT;
- sdfgi->render_geom_facing = RD::get_singleton()->texture_create(tf_render, RD::TextureView());
-
- tf_render.format = RD::DATA_FORMAT_R8G8B8A8_UINT;
- sdfgi->render_sdf[0] = RD::get_singleton()->texture_create(tf_render, RD::TextureView());
- sdfgi->render_sdf[1] = RD::get_singleton()->texture_create(tf_render, RD::TextureView());
-
- tf_render.width /= 2;
- tf_render.height /= 2;
- tf_render.depth /= 2;
-
- sdfgi->render_sdf_half[0] = RD::get_singleton()->texture_create(tf_render, RD::TextureView());
- sdfgi->render_sdf_half[1] = RD::get_singleton()->texture_create(tf_render, RD::TextureView());
- }
-
- RD::TextureFormat tf_occlusion = tf_sdf;
- tf_occlusion.format = RD::DATA_FORMAT_R16_UINT;
- tf_occlusion.shareable_formats.push_back(RD::DATA_FORMAT_R16_UINT);
- tf_occlusion.shareable_formats.push_back(RD::DATA_FORMAT_R4G4B4A4_UNORM_PACK16);
- tf_occlusion.depth *= sdfgi->cascades.size(); //use depth for occlusion slices
- tf_occlusion.width *= 2; //use width for the other half
-
- RD::TextureFormat tf_light = tf_sdf;
- tf_light.format = RD::DATA_FORMAT_R32_UINT;
- tf_light.shareable_formats.push_back(RD::DATA_FORMAT_R32_UINT);
- tf_light.shareable_formats.push_back(RD::DATA_FORMAT_E5B9G9R9_UFLOAT_PACK32);
-
- RD::TextureFormat tf_aniso0 = tf_sdf;
- tf_aniso0.format = RD::DATA_FORMAT_R8G8B8A8_UNORM;
- RD::TextureFormat tf_aniso1 = tf_sdf;
- tf_aniso1.format = RD::DATA_FORMAT_R8G8_UNORM;
-
- int passes = nearest_shift(sdfgi->cascade_size) - 1;
-
- //store lightprobe SH
- RD::TextureFormat tf_probes;
- tf_probes.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT;
- tf_probes.width = sdfgi->probe_axis_count * sdfgi->probe_axis_count;
- tf_probes.height = sdfgi->probe_axis_count * SDFGI::SH_SIZE;
- tf_probes.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_CAN_COPY_TO_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT;
- tf_probes.texture_type = RD::TEXTURE_TYPE_2D_ARRAY;
-
- sdfgi->history_size = requested_history_size;
-
- RD::TextureFormat tf_probe_history = tf_probes;
- tf_probe_history.format = RD::DATA_FORMAT_R16G16B16A16_SINT; //signed integer because SH are signed
- tf_probe_history.array_layers = sdfgi->history_size;
-
- RD::TextureFormat tf_probe_average = tf_probes;
- tf_probe_average.format = RD::DATA_FORMAT_R32G32B32A32_SINT; //signed integer because SH are signed
- tf_probe_average.texture_type = RD::TEXTURE_TYPE_2D;
-
- sdfgi->lightprobe_history_scroll = RD::get_singleton()->texture_create(tf_probe_history, RD::TextureView());
- sdfgi->lightprobe_average_scroll = RD::get_singleton()->texture_create(tf_probe_average, RD::TextureView());
-
- {
- //octahedral lightprobes
- RD::TextureFormat tf_octprobes = tf_probes;
- tf_octprobes.array_layers = sdfgi->cascades.size() * 2;
- tf_octprobes.format = RD::DATA_FORMAT_R32_UINT; //pack well with RGBE
- tf_octprobes.width = sdfgi->probe_axis_count * sdfgi->probe_axis_count * (SDFGI::LIGHTPROBE_OCT_SIZE + 2);
- tf_octprobes.height = sdfgi->probe_axis_count * (SDFGI::LIGHTPROBE_OCT_SIZE + 2);
- tf_octprobes.shareable_formats.push_back(RD::DATA_FORMAT_R32_UINT);
- tf_octprobes.shareable_formats.push_back(RD::DATA_FORMAT_E5B9G9R9_UFLOAT_PACK32);
- //lightprobe texture is an octahedral texture
-
- sdfgi->lightprobe_data = RD::get_singleton()->texture_create(tf_octprobes, RD::TextureView());
- RD::TextureView tv;
- tv.format_override = RD::DATA_FORMAT_E5B9G9R9_UFLOAT_PACK32;
- sdfgi->lightprobe_texture = RD::get_singleton()->texture_create_shared(tv, sdfgi->lightprobe_data);
-
- //texture handling ambient data, to integrate with volumetric foc
- RD::TextureFormat tf_ambient = tf_probes;
- tf_ambient.array_layers = sdfgi->cascades.size();
- tf_ambient.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT; //pack well with RGBE
- tf_ambient.width = sdfgi->probe_axis_count * sdfgi->probe_axis_count;
- tf_ambient.height = sdfgi->probe_axis_count;
- tf_ambient.texture_type = RD::TEXTURE_TYPE_2D_ARRAY;
- //lightprobe texture is an octahedral texture
- sdfgi->ambient_texture = RD::get_singleton()->texture_create(tf_ambient, RD::TextureView());
- }
-
- sdfgi->cascades_ubo = RD::get_singleton()->uniform_buffer_create(sizeof(SDFGI::Cascade::UBO) * SDFGI::MAX_CASCADES);
-
- sdfgi->occlusion_data = RD::get_singleton()->texture_create(tf_occlusion, RD::TextureView());
- {
- RD::TextureView tv;
- tv.format_override = RD::DATA_FORMAT_R4G4B4A4_UNORM_PACK16;
- sdfgi->occlusion_texture = RD::get_singleton()->texture_create_shared(tv, sdfgi->occlusion_data);
- }
-
- for (uint32_t i = 0; i < sdfgi->cascades.size(); i++) {
- SDFGI::Cascade &cascade = sdfgi->cascades[i];
-
- /* 3D Textures */
-
- cascade.sdf_tex = RD::get_singleton()->texture_create(tf_sdf, RD::TextureView());
-
- cascade.light_data = RD::get_singleton()->texture_create(tf_light, RD::TextureView());
-
- cascade.light_aniso_0_tex = RD::get_singleton()->texture_create(tf_aniso0, RD::TextureView());
- cascade.light_aniso_1_tex = RD::get_singleton()->texture_create(tf_aniso1, RD::TextureView());
-
- {
- RD::TextureView tv;
- tv.format_override = RD::DATA_FORMAT_E5B9G9R9_UFLOAT_PACK32;
- cascade.light_tex = RD::get_singleton()->texture_create_shared(tv, cascade.light_data);
-
- RD::get_singleton()->texture_clear(cascade.light_tex, Color(0, 0, 0, 0), 0, 1, 0, 1);
- RD::get_singleton()->texture_clear(cascade.light_aniso_0_tex, Color(0, 0, 0, 0), 0, 1, 0, 1);
- RD::get_singleton()->texture_clear(cascade.light_aniso_1_tex, Color(0, 0, 0, 0), 0, 1, 0, 1);
- }
-
- cascade.cell_size = base_cell_size;
- Vector3 world_position = p_world_position;
- world_position.y *= sdfgi->y_mult;
- int32_t probe_cells = sdfgi->cascade_size / SDFGI::PROBE_DIVISOR;
- Vector3 probe_size = Vector3(1, 1, 1) * cascade.cell_size * probe_cells;
- Vector3i probe_pos = Vector3i((world_position / probe_size + Vector3(0.5, 0.5, 0.5)).floor());
- cascade.position = probe_pos * probe_cells;
-
- cascade.dirty_regions = SDFGI::Cascade::DIRTY_ALL;
-
- base_cell_size *= 2.0;
-
- /* Probe History */
-
- cascade.lightprobe_history_tex = RD::get_singleton()->texture_create(tf_probe_history, RD::TextureView());
- RD::get_singleton()->texture_clear(cascade.lightprobe_history_tex, Color(0, 0, 0, 0), 0, 1, 0, tf_probe_history.array_layers); //needs to be cleared for average to work
-
- cascade.lightprobe_average_tex = RD::get_singleton()->texture_create(tf_probe_average, RD::TextureView());
- RD::get_singleton()->texture_clear(cascade.lightprobe_average_tex, Color(0, 0, 0, 0), 0, 1, 0, 1); //needs to be cleared for average to work
-
- /* Buffers */
-
- cascade.solid_cell_buffer = RD::get_singleton()->storage_buffer_create(sizeof(SDFGI::Cascade::SolidCell) * sdfgi->solid_cell_count);
- cascade.solid_cell_dispatch_buffer = RD::get_singleton()->storage_buffer_create(sizeof(uint32_t) * 4, Vector<uint8_t>(), RD::STORAGE_BUFFER_USAGE_DISPATCH_INDIRECT);
- cascade.lights_buffer = RD::get_singleton()->storage_buffer_create(sizeof(SDGIShader::Light) * MAX(SDFGI::MAX_STATIC_LIGHTS, SDFGI::MAX_DYNAMIC_LIGHTS));
- {
- Vector<RD::Uniform> uniforms;
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
- u.binding = 1;
- u.ids.push_back(sdfgi->render_sdf[(passes & 1) ? 1 : 0]); //if passes are even, we read from buffer 0, else we read from buffer 1
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
- u.binding = 2;
- u.ids.push_back(sdfgi->render_albedo);
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
- u.binding = 3;
- for (int j = 0; j < 8; j++) {
- u.ids.push_back(sdfgi->render_occlusion[j]);
- }
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
- u.binding = 4;
- u.ids.push_back(sdfgi->render_emission);
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
- u.binding = 5;
- u.ids.push_back(sdfgi->render_emission_aniso);
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
- u.binding = 6;
- u.ids.push_back(sdfgi->render_geom_facing);
- uniforms.push_back(u);
- }
-
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
- u.binding = 7;
- u.ids.push_back(cascade.sdf_tex);
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
- u.binding = 8;
- u.ids.push_back(sdfgi->occlusion_data);
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
- u.binding = 10;
- u.ids.push_back(cascade.solid_cell_dispatch_buffer);
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
- u.binding = 11;
- u.ids.push_back(cascade.solid_cell_buffer);
- uniforms.push_back(u);
- }
-
- cascade.sdf_store_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, sdfgi_shader.preprocess.version_get_shader(sdfgi_shader.preprocess_shader, SDGIShader::PRE_PROCESS_STORE), 0);
- }
-
- {
- Vector<RD::Uniform> uniforms;
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
- u.binding = 1;
- u.ids.push_back(sdfgi->render_albedo);
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
- u.binding = 2;
- u.ids.push_back(sdfgi->render_geom_facing);
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
- u.binding = 3;
- u.ids.push_back(sdfgi->render_emission);
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
- u.binding = 4;
- u.ids.push_back(sdfgi->render_emission_aniso);
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
- u.binding = 5;
- u.ids.push_back(cascade.solid_cell_dispatch_buffer);
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
- u.binding = 6;
- u.ids.push_back(cascade.solid_cell_buffer);
- uniforms.push_back(u);
- }
-
- cascade.scroll_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, sdfgi_shader.preprocess.version_get_shader(sdfgi_shader.preprocess_shader, SDGIShader::PRE_PROCESS_SCROLL), 0);
- }
- {
- Vector<RD::Uniform> uniforms;
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
- u.binding = 1;
- for (int j = 0; j < 8; j++) {
- u.ids.push_back(sdfgi->render_occlusion[j]);
- }
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
- u.binding = 2;
- u.ids.push_back(sdfgi->occlusion_data);
- uniforms.push_back(u);
- }
-
- cascade.scroll_occlusion_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, sdfgi_shader.preprocess.version_get_shader(sdfgi_shader.preprocess_shader, SDGIShader::PRE_PROCESS_SCROLL_OCCLUSION), 0);
- }
- }
-
- //direct light
- for (uint32_t i = 0; i < sdfgi->cascades.size(); i++) {
- SDFGI::Cascade &cascade = sdfgi->cascades[i];
-
- Vector<RD::Uniform> uniforms;
- {
- RD::Uniform u;
- u.binding = 1;
- u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
- for (uint32_t j = 0; j < SDFGI::MAX_CASCADES; j++) {
- if (j < rb->sdfgi->cascades.size()) {
- u.ids.push_back(rb->sdfgi->cascades[j].sdf_tex);
- } else {
- u.ids.push_back(storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_3D_WHITE));
- }
- }
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.binding = 2;
- u.uniform_type = RD::UNIFORM_TYPE_SAMPLER;
- u.ids.push_back(storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED));
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.binding = 3;
- u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
- u.ids.push_back(cascade.solid_cell_dispatch_buffer);
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.binding = 4;
- u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
- u.ids.push_back(cascade.solid_cell_buffer);
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.binding = 5;
- u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
- u.ids.push_back(cascade.light_data);
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.binding = 6;
- u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
- u.ids.push_back(cascade.light_aniso_0_tex);
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.binding = 7;
- u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
- u.ids.push_back(cascade.light_aniso_1_tex);
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.binding = 8;
- u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER;
- u.ids.push_back(rb->sdfgi->cascades_ubo);
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.binding = 9;
- u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
- u.ids.push_back(cascade.lights_buffer);
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.binding = 10;
- u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
- u.ids.push_back(rb->sdfgi->lightprobe_texture);
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.binding = 11;
- u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
- u.ids.push_back(rb->sdfgi->occlusion_texture);
- uniforms.push_back(u);
- }
-
- cascade.sdf_direct_light_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, sdfgi_shader.direct_light.version_get_shader(sdfgi_shader.direct_light_shader, 0), 0);
- }
-
- //preprocess initialize uniform set
- {
- Vector<RD::Uniform> uniforms;
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
- u.binding = 1;
- u.ids.push_back(sdfgi->render_albedo);
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
- u.binding = 2;
- u.ids.push_back(sdfgi->render_sdf[0]);
- uniforms.push_back(u);
- }
-
- sdfgi->sdf_initialize_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, sdfgi_shader.preprocess.version_get_shader(sdfgi_shader.preprocess_shader, SDGIShader::PRE_PROCESS_JUMP_FLOOD_INITIALIZE), 0);
- }
-
- {
- Vector<RD::Uniform> uniforms;
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
- u.binding = 1;
- u.ids.push_back(sdfgi->render_albedo);
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
- u.binding = 2;
- u.ids.push_back(sdfgi->render_sdf_half[0]);
- uniforms.push_back(u);
- }
-
- sdfgi->sdf_initialize_half_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, sdfgi_shader.preprocess.version_get_shader(sdfgi_shader.preprocess_shader, SDGIShader::PRE_PROCESS_JUMP_FLOOD_INITIALIZE_HALF), 0);
- }
-
- //jump flood uniform set
- {
- Vector<RD::Uniform> uniforms;
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
- u.binding = 1;
- u.ids.push_back(sdfgi->render_sdf[0]);
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
- u.binding = 2;
- u.ids.push_back(sdfgi->render_sdf[1]);
- uniforms.push_back(u);
- }
-
- sdfgi->jump_flood_uniform_set[0] = RD::get_singleton()->uniform_set_create(uniforms, sdfgi_shader.preprocess.version_get_shader(sdfgi_shader.preprocess_shader, SDGIShader::PRE_PROCESS_JUMP_FLOOD), 0);
- SWAP(uniforms.write[0].ids.write[0], uniforms.write[1].ids.write[0]);
- sdfgi->jump_flood_uniform_set[1] = RD::get_singleton()->uniform_set_create(uniforms, sdfgi_shader.preprocess.version_get_shader(sdfgi_shader.preprocess_shader, SDGIShader::PRE_PROCESS_JUMP_FLOOD), 0);
- }
- //jump flood half uniform set
- {
- Vector<RD::Uniform> uniforms;
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
- u.binding = 1;
- u.ids.push_back(sdfgi->render_sdf_half[0]);
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
- u.binding = 2;
- u.ids.push_back(sdfgi->render_sdf_half[1]);
- uniforms.push_back(u);
- }
-
- sdfgi->jump_flood_half_uniform_set[0] = RD::get_singleton()->uniform_set_create(uniforms, sdfgi_shader.preprocess.version_get_shader(sdfgi_shader.preprocess_shader, SDGIShader::PRE_PROCESS_JUMP_FLOOD), 0);
- SWAP(uniforms.write[0].ids.write[0], uniforms.write[1].ids.write[0]);
- sdfgi->jump_flood_half_uniform_set[1] = RD::get_singleton()->uniform_set_create(uniforms, sdfgi_shader.preprocess.version_get_shader(sdfgi_shader.preprocess_shader, SDGIShader::PRE_PROCESS_JUMP_FLOOD), 0);
- }
-
- //upscale half size sdf
- {
- Vector<RD::Uniform> uniforms;
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
- u.binding = 1;
- u.ids.push_back(sdfgi->render_albedo);
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
- u.binding = 2;
- u.ids.push_back(sdfgi->render_sdf_half[(passes & 1) ? 0 : 1]); //reverse pass order because half size
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
- u.binding = 3;
- u.ids.push_back(sdfgi->render_sdf[(passes & 1) ? 0 : 1]); //reverse pass order because it needs an extra JFA pass
- uniforms.push_back(u);
- }
-
- sdfgi->upscale_jfa_uniform_set_index = (passes & 1) ? 0 : 1;
- sdfgi->sdf_upscale_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, sdfgi_shader.preprocess.version_get_shader(sdfgi_shader.preprocess_shader, SDGIShader::PRE_PROCESS_JUMP_FLOOD_UPSCALE), 0);
- }
-
- //occlusion uniform set
- {
- Vector<RD::Uniform> uniforms;
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
- u.binding = 1;
- u.ids.push_back(sdfgi->render_albedo);
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
- u.binding = 2;
- for (int i = 0; i < 8; i++) {
- u.ids.push_back(sdfgi->render_occlusion[i]);
- }
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
- u.binding = 3;
- u.ids.push_back(sdfgi->render_geom_facing);
- uniforms.push_back(u);
- }
-
- sdfgi->occlusion_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, sdfgi_shader.preprocess.version_get_shader(sdfgi_shader.preprocess_shader, SDGIShader::PRE_PROCESS_OCCLUSION), 0);
- }
-
- for (uint32_t i = 0; i < sdfgi->cascades.size(); i++) {
- //integrate uniform
-
- Vector<RD::Uniform> uniforms;
-
- {
- RD::Uniform u;
- u.binding = 1;
- u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
- for (uint32_t j = 0; j < SDFGI::MAX_CASCADES; j++) {
- if (j < sdfgi->cascades.size()) {
- u.ids.push_back(sdfgi->cascades[j].sdf_tex);
- } else {
- u.ids.push_back(storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_3D_WHITE));
- }
- }
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.binding = 2;
- u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
- for (uint32_t j = 0; j < SDFGI::MAX_CASCADES; j++) {
- if (j < sdfgi->cascades.size()) {
- u.ids.push_back(sdfgi->cascades[j].light_tex);
- } else {
- u.ids.push_back(storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_3D_WHITE));
- }
- }
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.binding = 3;
- u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
- for (uint32_t j = 0; j < SDFGI::MAX_CASCADES; j++) {
- if (j < sdfgi->cascades.size()) {
- u.ids.push_back(sdfgi->cascades[j].light_aniso_0_tex);
- } else {
- u.ids.push_back(storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_3D_WHITE));
- }
- }
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.binding = 4;
- u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
- for (uint32_t j = 0; j < SDFGI::MAX_CASCADES; j++) {
- if (j < sdfgi->cascades.size()) {
- u.ids.push_back(sdfgi->cascades[j].light_aniso_1_tex);
- } else {
- u.ids.push_back(storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_3D_WHITE));
- }
- }
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_SAMPLER;
- u.binding = 6;
- u.ids.push_back(storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED));
- uniforms.push_back(u);
- }
-
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER;
- u.binding = 7;
- u.ids.push_back(sdfgi->cascades_ubo);
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
- u.binding = 8;
- u.ids.push_back(sdfgi->lightprobe_data);
- uniforms.push_back(u);
- }
-
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
- u.binding = 9;
- u.ids.push_back(sdfgi->cascades[i].lightprobe_history_tex);
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
- u.binding = 10;
- u.ids.push_back(sdfgi->cascades[i].lightprobe_average_tex);
- uniforms.push_back(u);
- }
-
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
- u.binding = 11;
- u.ids.push_back(sdfgi->lightprobe_history_scroll);
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
- u.binding = 12;
- u.ids.push_back(sdfgi->lightprobe_average_scroll);
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
- u.binding = 13;
- RID parent_average;
- if (i < sdfgi->cascades.size() - 1) {
- parent_average = sdfgi->cascades[i + 1].lightprobe_average_tex;
- } else {
- parent_average = sdfgi->cascades[i - 1].lightprobe_average_tex; //to use something, but it won't be used
- }
- u.ids.push_back(parent_average);
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
- u.binding = 14;
- u.ids.push_back(sdfgi->ambient_texture);
- uniforms.push_back(u);
- }
-
- sdfgi->cascades[i].integrate_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, sdfgi_shader.integrate.version_get_shader(sdfgi_shader.integrate_shader, 0), 0);
- }
-
- sdfgi->bounce_feedback = env->sdfgi_bounce_feedback;
- sdfgi->energy = env->sdfgi_energy;
- sdfgi->normal_bias = env->sdfgi_normal_bias;
- sdfgi->probe_bias = env->sdfgi_probe_bias;
- sdfgi->reads_sky = env->sdfgi_read_sky_light;
+ // re-create
+ rb->sdfgi = gi.create_sdfgi(env, p_world_position, requested_history_size);
_render_buffers_uniform_set_changed(p_render_buffers);
-
- return; //done. all levels will need to be rendered which its going to take a bit
- }
-
- //check for updates
-
- sdfgi->bounce_feedback = env->sdfgi_bounce_feedback;
- sdfgi->energy = env->sdfgi_energy;
- sdfgi->normal_bias = env->sdfgi_normal_bias;
- sdfgi->probe_bias = env->sdfgi_probe_bias;
- sdfgi->reads_sky = env->sdfgi_read_sky_light;
-
- int32_t drag_margin = (sdfgi->cascade_size / SDFGI::PROBE_DIVISOR) / 2;
-
- for (uint32_t i = 0; i < sdfgi->cascades.size(); i++) {
- SDFGI::Cascade &cascade = sdfgi->cascades[i];
- cascade.dirty_regions = Vector3i();
-
- Vector3 probe_half_size = Vector3(1, 1, 1) * cascade.cell_size * float(sdfgi->cascade_size / SDFGI::PROBE_DIVISOR) * 0.5;
- probe_half_size = Vector3(0, 0, 0);
-
- Vector3 world_position = p_world_position;
- world_position.y *= sdfgi->y_mult;
- Vector3i pos_in_cascade = Vector3i((world_position + probe_half_size) / cascade.cell_size);
-
- for (int j = 0; j < 3; j++) {
- if (pos_in_cascade[j] < cascade.position[j]) {
- while (pos_in_cascade[j] < (cascade.position[j] - drag_margin)) {
- cascade.position[j] -= drag_margin * 2;
- cascade.dirty_regions[j] += drag_margin * 2;
- }
- } else if (pos_in_cascade[j] > cascade.position[j]) {
- while (pos_in_cascade[j] > (cascade.position[j] + drag_margin)) {
- cascade.position[j] += drag_margin * 2;
- cascade.dirty_regions[j] -= drag_margin * 2;
- }
- }
-
- if (cascade.dirty_regions[j] == 0) {
- continue; // not dirty
- } else if (uint32_t(ABS(cascade.dirty_regions[j])) >= sdfgi->cascade_size) {
- //moved too much, just redraw everything (make all dirty)
- cascade.dirty_regions = SDFGI::Cascade::DIRTY_ALL;
- break;
- }
- }
-
- if (cascade.dirty_regions != Vector3i() && cascade.dirty_regions != SDFGI::Cascade::DIRTY_ALL) {
- //see how much the total dirty volume represents from the total volume
- uint32_t total_volume = sdfgi->cascade_size * sdfgi->cascade_size * sdfgi->cascade_size;
- uint32_t safe_volume = 1;
- for (int j = 0; j < 3; j++) {
- safe_volume *= sdfgi->cascade_size - ABS(cascade.dirty_regions[j]);
- }
- uint32_t dirty_volume = total_volume - safe_volume;
- if (dirty_volume > (safe_volume / 2)) {
- //more than half the volume is dirty, make all dirty so its only rendered once
- cascade.dirty_regions = SDFGI::Cascade::DIRTY_ALL;
- }
- }
+ } else {
+ //check for updates
+ rb->sdfgi->update(env, p_world_position);
}
}
@@ -1037,9 +97,9 @@ int RendererSceneRenderRD::sdfgi_get_pending_region_count(RID p_render_buffers)
int dirty_count = 0;
for (uint32_t i = 0; i < rb->sdfgi->cascades.size(); i++) {
- const SDFGI::Cascade &c = rb->sdfgi->cascades[i];
+ const RendererSceneGIRD::SDFGI::Cascade &c = rb->sdfgi->cascades[i];
- if (c.dirty_regions == SDFGI::Cascade::DIRTY_ALL) {
+ if (c.dirty_regions == RendererSceneGIRD::SDFGI::Cascade::DIRTY_ALL) {
dirty_count++;
} else {
for (int j = 0; j < 3; j++) {
@@ -1053,72 +113,15 @@ int RendererSceneRenderRD::sdfgi_get_pending_region_count(RID p_render_buffers)
return dirty_count;
}
-int RendererSceneRenderRD::_sdfgi_get_pending_region_data(RID p_render_buffers, int p_region, Vector3i &r_local_offset, Vector3i &r_local_size, AABB &r_bounds) const {
- RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers);
- ERR_FAIL_COND_V(rb == nullptr, -1);
- ERR_FAIL_COND_V(rb->sdfgi == nullptr, -1);
-
- int dirty_count = 0;
- for (uint32_t i = 0; i < rb->sdfgi->cascades.size(); i++) {
- const SDFGI::Cascade &c = rb->sdfgi->cascades[i];
-
- if (c.dirty_regions == SDFGI::Cascade::DIRTY_ALL) {
- if (dirty_count == p_region) {
- r_local_offset = Vector3i();
- r_local_size = Vector3i(1, 1, 1) * rb->sdfgi->cascade_size;
-
- r_bounds.position = Vector3((Vector3i(1, 1, 1) * -int32_t(rb->sdfgi->cascade_size >> 1) + c.position)) * c.cell_size * Vector3(1, 1.0 / rb->sdfgi->y_mult, 1);
- r_bounds.size = Vector3(r_local_size) * c.cell_size * Vector3(1, 1.0 / rb->sdfgi->y_mult, 1);
- return i;
- }
- dirty_count++;
- } else {
- for (int j = 0; j < 3; j++) {
- if (c.dirty_regions[j] != 0) {
- if (dirty_count == p_region) {
- Vector3i from = Vector3i(0, 0, 0);
- Vector3i to = Vector3i(1, 1, 1) * rb->sdfgi->cascade_size;
-
- if (c.dirty_regions[j] > 0) {
- //fill from the beginning
- to[j] = c.dirty_regions[j];
- } else {
- //fill from the end
- from[j] = to[j] + c.dirty_regions[j];
- }
-
- for (int k = 0; k < j; k++) {
- // "chip" away previous regions to avoid re-voxelizing the same thing
- if (c.dirty_regions[k] > 0) {
- from[k] += c.dirty_regions[k];
- } else if (c.dirty_regions[k] < 0) {
- to[k] += c.dirty_regions[k];
- }
- }
-
- r_local_offset = from;
- r_local_size = to - from;
-
- r_bounds.position = Vector3(from + Vector3i(1, 1, 1) * -int32_t(rb->sdfgi->cascade_size >> 1) + c.position) * c.cell_size * Vector3(1, 1.0 / rb->sdfgi->y_mult, 1);
- r_bounds.size = Vector3(r_local_size) * c.cell_size * Vector3(1, 1.0 / rb->sdfgi->y_mult, 1);
-
- return i;
- }
-
- dirty_count++;
- }
- }
- }
- }
- return -1;
-}
-
AABB RendererSceneRenderRD::sdfgi_get_pending_region_bounds(RID p_render_buffers, int p_region) const {
AABB bounds;
Vector3i from;
Vector3i size;
+ RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers);
+ ERR_FAIL_COND_V(rb == nullptr, AABB());
+ ERR_FAIL_COND_V(rb->sdfgi == nullptr, AABB());
- int c = _sdfgi_get_pending_region_data(p_render_buffers, p_region, from, size, bounds);
+ int c = rb->sdfgi->get_pending_region_data(p_region, from, size, bounds);
ERR_FAIL_COND_V(c == -1, AABB());
return bounds;
}
@@ -1127,1956 +130,179 @@ uint32_t RendererSceneRenderRD::sdfgi_get_pending_region_cascade(RID p_render_bu
AABB bounds;
Vector3i from;
Vector3i size;
-
- return _sdfgi_get_pending_region_data(p_render_buffers, p_region, from, size, bounds);
-}
-
-void RendererSceneRenderRD::_sdfgi_update_cascades(RID p_render_buffers) {
RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers);
- ERR_FAIL_COND(rb == nullptr);
- if (rb->sdfgi == nullptr) {
- return;
- }
-
- //update cascades
- SDFGI::Cascade::UBO cascade_data[SDFGI::MAX_CASCADES];
- int32_t probe_divisor = rb->sdfgi->cascade_size / SDFGI::PROBE_DIVISOR;
-
- for (uint32_t i = 0; i < rb->sdfgi->cascades.size(); i++) {
- Vector3 pos = Vector3((Vector3i(1, 1, 1) * -int32_t(rb->sdfgi->cascade_size >> 1) + rb->sdfgi->cascades[i].position)) * rb->sdfgi->cascades[i].cell_size;
-
- cascade_data[i].offset[0] = pos.x;
- cascade_data[i].offset[1] = pos.y;
- cascade_data[i].offset[2] = pos.z;
- cascade_data[i].to_cell = 1.0 / rb->sdfgi->cascades[i].cell_size;
- cascade_data[i].probe_offset[0] = rb->sdfgi->cascades[i].position.x / probe_divisor;
- cascade_data[i].probe_offset[1] = rb->sdfgi->cascades[i].position.y / probe_divisor;
- cascade_data[i].probe_offset[2] = rb->sdfgi->cascades[i].position.z / probe_divisor;
- cascade_data[i].pad = 0;
- }
-
- RD::get_singleton()->buffer_update(rb->sdfgi->cascades_ubo, 0, sizeof(SDFGI::Cascade::UBO) * SDFGI::MAX_CASCADES, cascade_data, RD::BARRIER_MASK_COMPUTE);
-}
-
-void RendererSceneRenderRD::_sdfgi_update_light(RID p_render_buffers, RID p_environment) {
- RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers);
- ERR_FAIL_COND(rb == nullptr);
- if (rb->sdfgi == nullptr) {
- return;
- }
-
- RD::get_singleton()->draw_command_begin_label("SDFGI Update dynamic Light");
-
- /* Update dynamic light */
-
- RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
- RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, sdfgi_shader.direct_light_pipeline[SDGIShader::DIRECT_LIGHT_MODE_DYNAMIC]);
-
- SDGIShader::DirectLightPushConstant push_constant;
-
- push_constant.grid_size[0] = rb->sdfgi->cascade_size;
- push_constant.grid_size[1] = rb->sdfgi->cascade_size;
- push_constant.grid_size[2] = rb->sdfgi->cascade_size;
- push_constant.max_cascades = rb->sdfgi->cascades.size();
- push_constant.probe_axis_size = rb->sdfgi->probe_axis_count;
- push_constant.bounce_feedback = rb->sdfgi->bounce_feedback;
- push_constant.y_mult = rb->sdfgi->y_mult;
- push_constant.use_occlusion = rb->sdfgi->uses_occlusion;
-
- for (uint32_t i = 0; i < rb->sdfgi->cascades.size(); i++) {
- SDFGI::Cascade &cascade = rb->sdfgi->cascades[i];
- push_constant.light_count = rb->sdfgi->cascade_dynamic_light_count[i];
- push_constant.cascade = i;
-
- if (rb->sdfgi->cascades[i].all_dynamic_lights_dirty || sdfgi_frames_to_update_light == RS::ENV_SDFGI_UPDATE_LIGHT_IN_1_FRAME) {
- push_constant.process_offset = 0;
- push_constant.process_increment = 1;
- } else {
- static uint32_t frames_to_update_table[RS::ENV_SDFGI_UPDATE_LIGHT_MAX] = {
- 1, 2, 4, 8, 16
- };
-
- uint32_t frames_to_update = frames_to_update_table[sdfgi_frames_to_update_light];
-
- push_constant.process_offset = RSG::rasterizer->get_frame_number() % frames_to_update;
- push_constant.process_increment = frames_to_update;
- }
- rb->sdfgi->cascades[i].all_dynamic_lights_dirty = false;
-
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, cascade.sdf_direct_light_uniform_set, 0);
- RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(SDGIShader::DirectLightPushConstant));
- RD::get_singleton()->compute_list_dispatch_indirect(compute_list, cascade.solid_cell_dispatch_buffer, 0);
- }
- RD::get_singleton()->compute_list_end(RD::BARRIER_MASK_COMPUTE);
- RD::get_singleton()->draw_command_end_label();
-}
-
-void RendererSceneRenderRD::_sdfgi_update_probes(RID p_render_buffers, RID p_environment) {
- RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers);
- ERR_FAIL_COND(rb == nullptr);
- if (rb->sdfgi == nullptr) {
- return;
- }
-
- RD::get_singleton()->draw_command_begin_label("SDFGI Update Probes");
-
- Environment *env = environment_owner.getornull(p_environment);
-
- SDGIShader::IntegratePushConstant push_constant;
- push_constant.grid_size[1] = rb->sdfgi->cascade_size;
- push_constant.grid_size[2] = rb->sdfgi->cascade_size;
- push_constant.grid_size[0] = rb->sdfgi->cascade_size;
- push_constant.max_cascades = rb->sdfgi->cascades.size();
- push_constant.probe_axis_size = rb->sdfgi->probe_axis_count;
- push_constant.history_index = rb->sdfgi->render_pass % rb->sdfgi->history_size;
- push_constant.history_size = rb->sdfgi->history_size;
- static const uint32_t ray_count[RS::ENV_SDFGI_RAY_COUNT_MAX] = { 4, 8, 16, 32, 64, 96, 128 };
- push_constant.ray_count = ray_count[sdfgi_ray_count];
- push_constant.ray_bias = rb->sdfgi->probe_bias;
- push_constant.image_size[0] = rb->sdfgi->probe_axis_count * rb->sdfgi->probe_axis_count;
- push_constant.image_size[1] = rb->sdfgi->probe_axis_count;
- push_constant.store_ambient_texture = env->volumetric_fog_enabled;
-
- RID sky_uniform_set = sdfgi_shader.integrate_default_sky_uniform_set;
- push_constant.sky_mode = SDGIShader::IntegratePushConstant::SKY_MODE_DISABLED;
- push_constant.y_mult = rb->sdfgi->y_mult;
-
- if (rb->sdfgi->reads_sky && env) {
- push_constant.sky_energy = env->bg_energy;
-
- if (env->background == RS::ENV_BG_CLEAR_COLOR) {
- push_constant.sky_mode = SDGIShader::IntegratePushConstant::SKY_MODE_COLOR;
- Color c = storage->get_default_clear_color().to_linear();
- push_constant.sky_color[0] = c.r;
- push_constant.sky_color[1] = c.g;
- push_constant.sky_color[2] = c.b;
- } else if (env->background == RS::ENV_BG_COLOR) {
- push_constant.sky_mode = SDGIShader::IntegratePushConstant::SKY_MODE_COLOR;
- Color c = env->bg_color;
- push_constant.sky_color[0] = c.r;
- push_constant.sky_color[1] = c.g;
- push_constant.sky_color[2] = c.b;
-
- } else if (env->background == RS::ENV_BG_SKY) {
- Sky *sky = sky_owner.getornull(env->sky);
- if (sky && sky->radiance.is_valid()) {
- if (sky->sdfgi_integrate_sky_uniform_set.is_null() || !RD::get_singleton()->uniform_set_is_valid(sky->sdfgi_integrate_sky_uniform_set)) {
- Vector<RD::Uniform> uniforms;
-
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
- u.binding = 0;
- u.ids.push_back(sky->radiance);
- uniforms.push_back(u);
- }
-
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_SAMPLER;
- u.binding = 1;
- u.ids.push_back(storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED));
- uniforms.push_back(u);
- }
-
- sky->sdfgi_integrate_sky_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, sdfgi_shader.integrate.version_get_shader(sdfgi_shader.integrate_shader, 0), 1);
- }
- sky_uniform_set = sky->sdfgi_integrate_sky_uniform_set;
- push_constant.sky_mode = SDGIShader::IntegratePushConstant::SKY_MODE_SKY;
- }
- }
- }
-
- rb->sdfgi->render_pass++;
-
- RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(true);
- RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, sdfgi_shader.integrate_pipeline[SDGIShader::INTEGRATE_MODE_PROCESS]);
-
- int32_t probe_divisor = rb->sdfgi->cascade_size / SDFGI::PROBE_DIVISOR;
- for (uint32_t i = 0; i < rb->sdfgi->cascades.size(); i++) {
- push_constant.cascade = i;
- push_constant.world_offset[0] = rb->sdfgi->cascades[i].position.x / probe_divisor;
- push_constant.world_offset[1] = rb->sdfgi->cascades[i].position.y / probe_divisor;
- push_constant.world_offset[2] = rb->sdfgi->cascades[i].position.z / probe_divisor;
-
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, rb->sdfgi->cascades[i].integrate_uniform_set, 0);
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, sky_uniform_set, 1);
-
- RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(SDGIShader::IntegratePushConstant));
- RD::get_singleton()->compute_list_dispatch_threads(compute_list, rb->sdfgi->probe_axis_count * rb->sdfgi->probe_axis_count, rb->sdfgi->probe_axis_count, 1);
- }
-
- //end later after raster to avoid barriering on layout changes
- //RD::get_singleton()->compute_list_end(RD::BARRIER_MASK_NO_BARRIER);
-
- RD::get_singleton()->draw_command_end_label();
-}
-
-void RendererSceneRenderRD::_sdfgi_store_probes(RID p_render_buffers) {
- RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers);
- ERR_FAIL_COND(rb == nullptr);
- if (rb->sdfgi == nullptr) {
- return;
- }
-
- RD::get_singleton()->barrier(RD::BARRIER_MASK_COMPUTE, RD::BARRIER_MASK_COMPUTE);
- RD::get_singleton()->draw_command_begin_label("SDFGI Store Probes");
-
- SDGIShader::IntegratePushConstant push_constant;
- push_constant.grid_size[1] = rb->sdfgi->cascade_size;
- push_constant.grid_size[2] = rb->sdfgi->cascade_size;
- push_constant.grid_size[0] = rb->sdfgi->cascade_size;
- push_constant.max_cascades = rb->sdfgi->cascades.size();
- push_constant.probe_axis_size = rb->sdfgi->probe_axis_count;
- push_constant.history_index = rb->sdfgi->render_pass % rb->sdfgi->history_size;
- push_constant.history_size = rb->sdfgi->history_size;
- static const uint32_t ray_count[RS::ENV_SDFGI_RAY_COUNT_MAX] = { 4, 8, 16, 32, 64, 96, 128 };
- push_constant.ray_count = ray_count[sdfgi_ray_count];
- push_constant.ray_bias = rb->sdfgi->probe_bias;
- push_constant.image_size[0] = rb->sdfgi->probe_axis_count * rb->sdfgi->probe_axis_count;
- push_constant.image_size[1] = rb->sdfgi->probe_axis_count;
- push_constant.store_ambient_texture = false;
-
- push_constant.sky_mode = 0;
- push_constant.y_mult = rb->sdfgi->y_mult;
-
- // Then store values into the lightprobe texture. Separating these steps has a small performance hit, but it allows for multiple bounces
- RENDER_TIMESTAMP("Average Probes");
-
- RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
- RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, sdfgi_shader.integrate_pipeline[SDGIShader::INTEGRATE_MODE_STORE]);
-
- //convert to octahedral to store
- push_constant.image_size[0] *= SDFGI::LIGHTPROBE_OCT_SIZE;
- push_constant.image_size[1] *= SDFGI::LIGHTPROBE_OCT_SIZE;
-
- for (uint32_t i = 0; i < rb->sdfgi->cascades.size(); i++) {
- push_constant.cascade = i;
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, rb->sdfgi->cascades[i].integrate_uniform_set, 0);
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, sdfgi_shader.integrate_default_sky_uniform_set, 1);
- RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(SDGIShader::IntegratePushConstant));
- RD::get_singleton()->compute_list_dispatch_threads(compute_list, rb->sdfgi->probe_axis_count * rb->sdfgi->probe_axis_count * SDFGI::LIGHTPROBE_OCT_SIZE, rb->sdfgi->probe_axis_count * SDFGI::LIGHTPROBE_OCT_SIZE, 1);
- }
-
- RD::get_singleton()->compute_list_end(RD::BARRIER_MASK_COMPUTE);
-
- RD::get_singleton()->draw_command_end_label();
-}
-void RendererSceneRenderRD::_setup_giprobes(RID p_render_buffers, const Transform &p_transform, const PagedArray<RID> &p_gi_probes, uint32_t &r_gi_probes_used) {
- r_gi_probes_used = 0;
- RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers);
- ERR_FAIL_COND(rb == nullptr);
-
- RD::get_singleton()->draw_command_begin_label("GIProbes Setup");
-
- RID gi_probe_buffer = render_buffers_get_gi_probe_buffer(p_render_buffers);
- GI::GIProbeData gi_probe_data[RenderBuffers::MAX_GIPROBES];
-
- bool giprobes_changed = false;
-
- Transform to_camera;
- to_camera.origin = p_transform.origin; //only translation, make local
-
- for (int i = 0; i < RenderBuffers::MAX_GIPROBES; i++) {
- RID texture;
- if (i < (int)p_gi_probes.size()) {
- GIProbeInstance *gipi = gi_probe_instance_owner.getornull(p_gi_probes[i]);
-
- if (gipi) {
- texture = gipi->texture;
- GI::GIProbeData &gipd = gi_probe_data[i];
-
- RID base_probe = gipi->probe;
-
- Transform to_cell = storage->gi_probe_get_to_cell_xform(gipi->probe) * gipi->transform.affine_inverse() * to_camera;
-
- gipd.xform[0] = to_cell.basis.elements[0][0];
- gipd.xform[1] = to_cell.basis.elements[1][0];
- gipd.xform[2] = to_cell.basis.elements[2][0];
- gipd.xform[3] = 0;
- gipd.xform[4] = to_cell.basis.elements[0][1];
- gipd.xform[5] = to_cell.basis.elements[1][1];
- gipd.xform[6] = to_cell.basis.elements[2][1];
- gipd.xform[7] = 0;
- gipd.xform[8] = to_cell.basis.elements[0][2];
- gipd.xform[9] = to_cell.basis.elements[1][2];
- gipd.xform[10] = to_cell.basis.elements[2][2];
- gipd.xform[11] = 0;
- gipd.xform[12] = to_cell.origin.x;
- gipd.xform[13] = to_cell.origin.y;
- gipd.xform[14] = to_cell.origin.z;
- gipd.xform[15] = 1;
-
- Vector3 bounds = storage->gi_probe_get_octree_size(base_probe);
-
- gipd.bounds[0] = bounds.x;
- gipd.bounds[1] = bounds.y;
- gipd.bounds[2] = bounds.z;
-
- gipd.dynamic_range = storage->gi_probe_get_dynamic_range(base_probe) * storage->gi_probe_get_energy(base_probe);
- gipd.bias = storage->gi_probe_get_bias(base_probe);
- gipd.normal_bias = storage->gi_probe_get_normal_bias(base_probe);
- gipd.blend_ambient = !storage->gi_probe_is_interior(base_probe);
- gipd.anisotropy_strength = 0;
- gipd.ao = storage->gi_probe_get_ao(base_probe);
- gipd.ao_size = Math::pow(storage->gi_probe_get_ao_size(base_probe), 4.0f);
- gipd.mipmaps = gipi->mipmaps.size();
- }
-
- r_gi_probes_used++;
- }
-
- if (texture == RID()) {
- texture = storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_3D_WHITE);
- }
-
- if (texture != rb->giprobe_textures[i]) {
- giprobes_changed = true;
- rb->giprobe_textures[i] = texture;
- }
- }
-
- if (giprobes_changed) {
- if (RD::get_singleton()->uniform_set_is_valid(rb->gi_uniform_set)) {
- RD::get_singleton()->free(rb->gi_uniform_set);
- }
- rb->gi_uniform_set = RID();
- if (rb->volumetric_fog) {
- if (RD::get_singleton()->uniform_set_is_valid(rb->volumetric_fog->uniform_set)) {
- RD::get_singleton()->free(rb->volumetric_fog->uniform_set);
- RD::get_singleton()->free(rb->volumetric_fog->uniform_set2);
- }
- rb->volumetric_fog->uniform_set = RID();
- rb->volumetric_fog->uniform_set2 = RID();
- }
- }
-
- if (p_gi_probes.size() > 0) {
- RD::get_singleton()->buffer_update(gi_probe_buffer, 0, sizeof(GI::GIProbeData) * MIN((uint64_t)RenderBuffers::MAX_GIPROBES, p_gi_probes.size()), gi_probe_data, RD::BARRIER_MASK_COMPUTE);
- }
-
- RD::get_singleton()->draw_command_end_label();
-}
-
-void RendererSceneRenderRD::_pre_process_gi(RID p_render_buffers, const Transform &p_transform) {
- // Do the required buffer transfers and setup before the depth-pre pass, this way GI can
- // run in parallel during depth-pre pass and shadow rendering.
- RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers);
- ERR_FAIL_COND(rb == nullptr);
-
- /* Update Cascades UBO */
-
- if (rb->sdfgi) {
- /* Update general SDFGI Buffer */
-
- _sdfgi_update_cascades(p_render_buffers);
-
- GI::SDFGIData sdfgi_data;
-
- sdfgi_data.grid_size[0] = rb->sdfgi->cascade_size;
- sdfgi_data.grid_size[1] = rb->sdfgi->cascade_size;
- sdfgi_data.grid_size[2] = rb->sdfgi->cascade_size;
-
- sdfgi_data.max_cascades = rb->sdfgi->cascades.size();
- sdfgi_data.probe_axis_size = rb->sdfgi->probe_axis_count;
- sdfgi_data.cascade_probe_size[0] = sdfgi_data.probe_axis_size - 1; //float version for performance
- sdfgi_data.cascade_probe_size[1] = sdfgi_data.probe_axis_size - 1;
- sdfgi_data.cascade_probe_size[2] = sdfgi_data.probe_axis_size - 1;
-
- float csize = rb->sdfgi->cascade_size;
- sdfgi_data.probe_to_uvw = 1.0 / float(sdfgi_data.cascade_probe_size[0]);
- sdfgi_data.use_occlusion = rb->sdfgi->uses_occlusion;
- //sdfgi_data.energy = rb->sdfgi->energy;
-
- sdfgi_data.y_mult = rb->sdfgi->y_mult;
-
- float cascade_voxel_size = (csize / sdfgi_data.cascade_probe_size[0]);
- float occlusion_clamp = (cascade_voxel_size - 0.5) / cascade_voxel_size;
- sdfgi_data.occlusion_clamp[0] = occlusion_clamp;
- sdfgi_data.occlusion_clamp[1] = occlusion_clamp;
- sdfgi_data.occlusion_clamp[2] = occlusion_clamp;
- sdfgi_data.normal_bias = (rb->sdfgi->normal_bias / csize) * sdfgi_data.cascade_probe_size[0];
-
- //vec2 tex_pixel_size = 1.0 / vec2(ivec2( (OCT_SIZE+2) * params.probe_axis_size * params.probe_axis_size, (OCT_SIZE+2) * params.probe_axis_size ) );
- //vec3 probe_uv_offset = (ivec3(OCT_SIZE+2,OCT_SIZE+2,(OCT_SIZE+2) * params.probe_axis_size)) * tex_pixel_size.xyx;
-
- uint32_t oct_size = SDFGI::LIGHTPROBE_OCT_SIZE;
-
- sdfgi_data.lightprobe_tex_pixel_size[0] = 1.0 / ((oct_size + 2) * sdfgi_data.probe_axis_size * sdfgi_data.probe_axis_size);
- sdfgi_data.lightprobe_tex_pixel_size[1] = 1.0 / ((oct_size + 2) * sdfgi_data.probe_axis_size);
- sdfgi_data.lightprobe_tex_pixel_size[2] = 1.0;
-
- sdfgi_data.energy = rb->sdfgi->energy;
-
- sdfgi_data.lightprobe_uv_offset[0] = float(oct_size + 2) * sdfgi_data.lightprobe_tex_pixel_size[0];
- sdfgi_data.lightprobe_uv_offset[1] = float(oct_size + 2) * sdfgi_data.lightprobe_tex_pixel_size[1];
- sdfgi_data.lightprobe_uv_offset[2] = float((oct_size + 2) * sdfgi_data.probe_axis_size) * sdfgi_data.lightprobe_tex_pixel_size[0];
-
- sdfgi_data.occlusion_renormalize[0] = 0.5;
- sdfgi_data.occlusion_renormalize[1] = 1.0;
- sdfgi_data.occlusion_renormalize[2] = 1.0 / float(sdfgi_data.max_cascades);
-
- int32_t probe_divisor = rb->sdfgi->cascade_size / SDFGI::PROBE_DIVISOR;
-
- for (uint32_t i = 0; i < sdfgi_data.max_cascades; i++) {
- GI::SDFGIData::ProbeCascadeData &c = sdfgi_data.cascades[i];
- Vector3 pos = Vector3((Vector3i(1, 1, 1) * -int32_t(rb->sdfgi->cascade_size >> 1) + rb->sdfgi->cascades[i].position)) * rb->sdfgi->cascades[i].cell_size;
- Vector3 cam_origin = p_transform.origin;
- cam_origin.y *= rb->sdfgi->y_mult;
- pos -= cam_origin; //make pos local to camera, to reduce numerical error
- c.position[0] = pos.x;
- c.position[1] = pos.y;
- c.position[2] = pos.z;
- c.to_probe = 1.0 / (float(rb->sdfgi->cascade_size) * rb->sdfgi->cascades[i].cell_size / float(rb->sdfgi->probe_axis_count - 1));
-
- Vector3i probe_ofs = rb->sdfgi->cascades[i].position / probe_divisor;
- c.probe_world_offset[0] = probe_ofs.x;
- c.probe_world_offset[1] = probe_ofs.y;
- c.probe_world_offset[2] = probe_ofs.z;
-
- c.to_cell = 1.0 / rb->sdfgi->cascades[i].cell_size;
- }
-
- RD::get_singleton()->buffer_update(gi.sdfgi_ubo, 0, sizeof(GI::SDFGIData), &sdfgi_data, RD::BARRIER_MASK_COMPUTE);
-
- /* Update dynamic lights in SDFGI cascades */
-
- for (uint32_t i = 0; i < rb->sdfgi->cascades.size(); i++) {
- SDFGI::Cascade &cascade = rb->sdfgi->cascades[i];
-
- SDGIShader::Light lights[SDFGI::MAX_DYNAMIC_LIGHTS];
- uint32_t idx = 0;
- for (uint32_t j = 0; j < (uint32_t)render_state.sdfgi_update_data->directional_lights->size(); j++) {
- if (idx == SDFGI::MAX_DYNAMIC_LIGHTS) {
- break;
- }
-
- LightInstance *li = light_instance_owner.getornull(render_state.sdfgi_update_data->directional_lights->get(j));
- ERR_CONTINUE(!li);
-
- if (storage->light_directional_is_sky_only(li->light)) {
- continue;
- }
-
- Vector3 dir = -li->transform.basis.get_axis(Vector3::AXIS_Z);
- dir.y *= rb->sdfgi->y_mult;
- dir.normalize();
- lights[idx].direction[0] = dir.x;
- lights[idx].direction[1] = dir.y;
- lights[idx].direction[2] = dir.z;
- Color color = storage->light_get_color(li->light);
- color = color.to_linear();
- lights[idx].color[0] = color.r;
- lights[idx].color[1] = color.g;
- lights[idx].color[2] = color.b;
- lights[idx].type = RS::LIGHT_DIRECTIONAL;
- lights[idx].energy = storage->light_get_param(li->light, RS::LIGHT_PARAM_ENERGY);
- lights[idx].has_shadow = storage->light_has_shadow(li->light);
-
- idx++;
- }
-
- AABB cascade_aabb;
- cascade_aabb.position = Vector3((Vector3i(1, 1, 1) * -int32_t(rb->sdfgi->cascade_size >> 1) + cascade.position)) * cascade.cell_size;
- cascade_aabb.size = Vector3(1, 1, 1) * rb->sdfgi->cascade_size * cascade.cell_size;
-
- for (uint32_t j = 0; j < render_state.sdfgi_update_data->positional_light_count; j++) {
- if (idx == SDFGI::MAX_DYNAMIC_LIGHTS) {
- break;
- }
-
- LightInstance *li = light_instance_owner.getornull(render_state.sdfgi_update_data->positional_light_instances[j]);
- ERR_CONTINUE(!li);
-
- uint32_t max_sdfgi_cascade = storage->light_get_max_sdfgi_cascade(li->light);
- if (i > max_sdfgi_cascade) {
- continue;
- }
-
- if (!cascade_aabb.intersects(li->aabb)) {
- continue;
- }
-
- Vector3 dir = -li->transform.basis.get_axis(Vector3::AXIS_Z);
- //faster to not do this here
- //dir.y *= rb->sdfgi->y_mult;
- //dir.normalize();
- lights[idx].direction[0] = dir.x;
- lights[idx].direction[1] = dir.y;
- lights[idx].direction[2] = dir.z;
- Vector3 pos = li->transform.origin;
- pos.y *= rb->sdfgi->y_mult;
- lights[idx].position[0] = pos.x;
- lights[idx].position[1] = pos.y;
- lights[idx].position[2] = pos.z;
- Color color = storage->light_get_color(li->light);
- color = color.to_linear();
- lights[idx].color[0] = color.r;
- lights[idx].color[1] = color.g;
- lights[idx].color[2] = color.b;
- lights[idx].type = storage->light_get_type(li->light);
- lights[idx].energy = storage->light_get_param(li->light, RS::LIGHT_PARAM_ENERGY);
- lights[idx].has_shadow = storage->light_has_shadow(li->light);
- lights[idx].attenuation = storage->light_get_param(li->light, RS::LIGHT_PARAM_ATTENUATION);
- lights[idx].radius = storage->light_get_param(li->light, RS::LIGHT_PARAM_RANGE);
- lights[idx].cos_spot_angle = Math::cos(Math::deg2rad(storage->light_get_param(li->light, RS::LIGHT_PARAM_SPOT_ANGLE)));
- lights[idx].inv_spot_attenuation = 1.0f / storage->light_get_param(li->light, RS::LIGHT_PARAM_SPOT_ATTENUATION);
-
- idx++;
- }
-
- if (idx > 0) {
- RD::get_singleton()->buffer_update(cascade.lights_buffer, 0, idx * sizeof(SDGIShader::Light), lights, RD::BARRIER_MASK_COMPUTE);
- }
-
- rb->sdfgi->cascade_dynamic_light_count[i] = idx;
- }
- }
-}
-
-void RendererSceneRenderRD::_process_gi(RID p_render_buffers, RID p_normal_roughness_buffer, RID p_gi_probe_buffer, RID p_environment, const CameraMatrix &p_projection, const Transform &p_transform, const PagedArray<RID> &p_gi_probes) {
- RD::get_singleton()->draw_command_begin_label("GI Render");
-
- RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers);
- ERR_FAIL_COND(rb == nullptr);
- Environment *env = environment_owner.getornull(p_environment);
-
- if (rb->ambient_buffer.is_null() || rb->using_half_size_gi != gi.half_resolution) {
- if (rb->ambient_buffer.is_valid()) {
- RD::get_singleton()->free(rb->ambient_buffer);
- RD::get_singleton()->free(rb->reflection_buffer);
- }
-
- RD::TextureFormat tf;
- tf.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT;
- tf.width = rb->width;
- tf.height = rb->height;
- if (gi.half_resolution) {
- tf.width >>= 1;
- tf.height >>= 1;
- }
- tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT;
- rb->reflection_buffer = RD::get_singleton()->texture_create(tf, RD::TextureView());
- rb->ambient_buffer = RD::get_singleton()->texture_create(tf, RD::TextureView());
- rb->using_half_size_gi = gi.half_resolution;
-
- _render_buffers_uniform_set_changed(p_render_buffers);
- }
-
- GI::PushConstant push_constant;
-
- push_constant.screen_size[0] = rb->width;
- push_constant.screen_size[1] = rb->height;
- push_constant.z_near = p_projection.get_z_near();
- push_constant.z_far = p_projection.get_z_far();
- push_constant.orthogonal = p_projection.is_orthogonal();
- push_constant.proj_info[0] = -2.0f / (rb->width * p_projection.matrix[0][0]);
- push_constant.proj_info[1] = -2.0f / (rb->height * p_projection.matrix[1][1]);
- push_constant.proj_info[2] = (1.0f - p_projection.matrix[0][2]) / p_projection.matrix[0][0];
- push_constant.proj_info[3] = (1.0f + p_projection.matrix[1][2]) / p_projection.matrix[1][1];
- push_constant.max_giprobes = MIN((uint64_t)RenderBuffers::MAX_GIPROBES, p_gi_probes.size());
- push_constant.high_quality_vct = gi_probe_quality == RS::GI_PROBE_QUALITY_HIGH;
-
- bool use_sdfgi = rb->sdfgi != nullptr;
- bool use_giprobes = push_constant.max_giprobes > 0;
-
- if (env) {
- push_constant.ao_color[0] = env->ao_color.r;
- push_constant.ao_color[1] = env->ao_color.g;
- push_constant.ao_color[2] = env->ao_color.b;
- } else {
- push_constant.ao_color[0] = 0;
- push_constant.ao_color[1] = 0;
- push_constant.ao_color[2] = 0;
- }
-
- push_constant.cam_rotation[0] = p_transform.basis[0][0];
- push_constant.cam_rotation[1] = p_transform.basis[1][0];
- push_constant.cam_rotation[2] = p_transform.basis[2][0];
- push_constant.cam_rotation[3] = 0;
- push_constant.cam_rotation[4] = p_transform.basis[0][1];
- push_constant.cam_rotation[5] = p_transform.basis[1][1];
- push_constant.cam_rotation[6] = p_transform.basis[2][1];
- push_constant.cam_rotation[7] = 0;
- push_constant.cam_rotation[8] = p_transform.basis[0][2];
- push_constant.cam_rotation[9] = p_transform.basis[1][2];
- push_constant.cam_rotation[10] = p_transform.basis[2][2];
- push_constant.cam_rotation[11] = 0;
-
- if (rb->gi_uniform_set.is_null() || !RD::get_singleton()->uniform_set_is_valid(rb->gi_uniform_set)) {
- Vector<RD::Uniform> uniforms;
- {
- RD::Uniform u;
- u.binding = 1;
- u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
- for (uint32_t j = 0; j < SDFGI::MAX_CASCADES; j++) {
- if (rb->sdfgi && j < rb->sdfgi->cascades.size()) {
- u.ids.push_back(rb->sdfgi->cascades[j].sdf_tex);
- } else {
- u.ids.push_back(storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_3D_WHITE));
- }
- }
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.binding = 2;
- u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
- for (uint32_t j = 0; j < SDFGI::MAX_CASCADES; j++) {
- if (rb->sdfgi && j < rb->sdfgi->cascades.size()) {
- u.ids.push_back(rb->sdfgi->cascades[j].light_tex);
- } else {
- u.ids.push_back(storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_3D_WHITE));
- }
- }
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.binding = 3;
- u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
- for (uint32_t j = 0; j < SDFGI::MAX_CASCADES; j++) {
- if (rb->sdfgi && j < rb->sdfgi->cascades.size()) {
- u.ids.push_back(rb->sdfgi->cascades[j].light_aniso_0_tex);
- } else {
- u.ids.push_back(storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_3D_WHITE));
- }
- }
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.binding = 4;
- u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
- for (uint32_t j = 0; j < SDFGI::MAX_CASCADES; j++) {
- if (rb->sdfgi && j < rb->sdfgi->cascades.size()) {
- u.ids.push_back(rb->sdfgi->cascades[j].light_aniso_1_tex);
- } else {
- u.ids.push_back(storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_3D_WHITE));
- }
- }
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
- u.binding = 5;
- if (rb->sdfgi) {
- u.ids.push_back(rb->sdfgi->occlusion_texture);
- } else {
- u.ids.push_back(storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_3D_WHITE));
- }
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_SAMPLER;
- u.binding = 6;
- u.ids.push_back(storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED));
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_SAMPLER;
- u.binding = 7;
- u.ids.push_back(storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED));
- uniforms.push_back(u);
- }
-
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
- u.binding = 9;
- u.ids.push_back(rb->ambient_buffer);
- uniforms.push_back(u);
- }
-
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
- u.binding = 10;
- u.ids.push_back(rb->reflection_buffer);
- uniforms.push_back(u);
- }
-
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
- u.binding = 11;
- if (rb->sdfgi) {
- u.ids.push_back(rb->sdfgi->lightprobe_texture);
- } else {
- u.ids.push_back(storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_2D_ARRAY_WHITE));
- }
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
- u.binding = 12;
- u.ids.push_back(rb->depth_texture);
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
- u.binding = 13;
- u.ids.push_back(p_normal_roughness_buffer);
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
- u.binding = 14;
- RID buffer = p_gi_probe_buffer.is_valid() ? p_gi_probe_buffer : storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_BLACK);
- u.ids.push_back(buffer);
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER;
- u.binding = 15;
- u.ids.push_back(gi.sdfgi_ubo);
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER;
- u.binding = 16;
- u.ids.push_back(rb->giprobe_buffer);
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
- u.binding = 17;
- for (int i = 0; i < RenderBuffers::MAX_GIPROBES; i++) {
- u.ids.push_back(rb->giprobe_textures[i]);
- }
- uniforms.push_back(u);
- }
-
- rb->gi_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, gi.shader.version_get_shader(gi.shader_version, 0), 0);
- }
-
- GI::Mode mode;
-
- if (rb->using_half_size_gi) {
- mode = (use_sdfgi && use_giprobes) ? GI::MODE_HALF_RES_COMBINED : (use_sdfgi ? GI::MODE_HALF_RES_SDFGI : GI::MODE_HALF_RES_GIPROBE);
- } else {
- mode = (use_sdfgi && use_giprobes) ? GI::MODE_COMBINED : (use_sdfgi ? GI::MODE_SDFGI : GI::MODE_GIPROBE);
- }
- RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(true);
- RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, gi.pipelines[mode]);
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, rb->gi_uniform_set, 0);
- RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(GI::PushConstant));
+ ERR_FAIL_COND_V(rb == nullptr, -1);
+ ERR_FAIL_COND_V(rb->sdfgi == nullptr, -1);
- if (rb->using_half_size_gi) {
- RD::get_singleton()->compute_list_dispatch_threads(compute_list, rb->width >> 1, rb->height >> 1, 1);
- } else {
- RD::get_singleton()->compute_list_dispatch_threads(compute_list, rb->width, rb->height, 1);
- }
- //do barrier later to allow oeverlap
- //RD::get_singleton()->compute_list_end(RD::BARRIER_MASK_NO_BARRIER); //no barriers, let other compute, raster and transfer happen at the same time
- RD::get_singleton()->draw_command_end_label();
+ return rb->sdfgi->get_pending_region_data(p_region, from, size, bounds);
}
RID RendererSceneRenderRD::sky_allocate() {
- return sky_owner.allocate_rid();
+ return sky.allocate_sky_rid();
}
void RendererSceneRenderRD::sky_initialize(RID p_rid) {
- sky_owner.initialize_rid(p_rid, Sky());
-}
-
-void RendererSceneRenderRD::_sky_invalidate(Sky *p_sky) {
- if (!p_sky->dirty) {
- p_sky->dirty = true;
- p_sky->dirty_list = dirty_sky_list;
- dirty_sky_list = p_sky;
- }
+ sky.initialize_sky_rid(p_rid);
}
void RendererSceneRenderRD::sky_set_radiance_size(RID p_sky, int p_radiance_size) {
- Sky *sky = sky_owner.getornull(p_sky);
- ERR_FAIL_COND(!sky);
- ERR_FAIL_COND(p_radiance_size < 32 || p_radiance_size > 2048);
- if (sky->radiance_size == p_radiance_size) {
- return;
- }
- sky->radiance_size = p_radiance_size;
-
- if (sky->mode == RS::SKY_MODE_REALTIME && sky->radiance_size != 256) {
- WARN_PRINT("Realtime Skies can only use a radiance size of 256. Radiance size will be set to 256 internally.");
- sky->radiance_size = 256;
- }
-
- _sky_invalidate(sky);
- if (sky->radiance.is_valid()) {
- RD::get_singleton()->free(sky->radiance);
- sky->radiance = RID();
- }
- _clear_reflection_data(sky->reflection);
+ sky.sky_set_radiance_size(p_sky, p_radiance_size);
}
void RendererSceneRenderRD::sky_set_mode(RID p_sky, RS::SkyMode p_mode) {
- Sky *sky = sky_owner.getornull(p_sky);
- ERR_FAIL_COND(!sky);
-
- if (sky->mode == p_mode) {
- return;
- }
-
- sky->mode = p_mode;
-
- if (sky->mode == RS::SKY_MODE_REALTIME && sky->radiance_size != 256) {
- WARN_PRINT("Realtime Skies can only use a radiance size of 256. Radiance size will be set to 256 internally.");
- sky_set_radiance_size(p_sky, 256);
- }
-
- _sky_invalidate(sky);
- if (sky->radiance.is_valid()) {
- RD::get_singleton()->free(sky->radiance);
- sky->radiance = RID();
- }
- _clear_reflection_data(sky->reflection);
+ sky.sky_set_mode(p_sky, p_mode);
}
void RendererSceneRenderRD::sky_set_material(RID p_sky, RID p_material) {
- Sky *sky = sky_owner.getornull(p_sky);
- ERR_FAIL_COND(!sky);
- sky->material = p_material;
- _sky_invalidate(sky);
+ sky.sky_set_material(p_sky, p_material);
}
Ref<Image> RendererSceneRenderRD::sky_bake_panorama(RID p_sky, float p_energy, bool p_bake_irradiance, const Size2i &p_size) {
- Sky *sky = sky_owner.getornull(p_sky);
- ERR_FAIL_COND_V(!sky, Ref<Image>());
-
- _update_dirty_skys();
-
- if (sky->radiance.is_valid()) {
- RD::TextureFormat tf;
- tf.format = RD::DATA_FORMAT_R32G32B32A32_SFLOAT;
- tf.width = p_size.width;
- tf.height = p_size.height;
- 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());
- storage->get_effects()->copy_cubemap_to_panorama(sky->radiance, rad_tex, p_size, p_bake_irradiance ? roughness_layers : 0, sky->reflection.layers.size() > 1);
- Vector<uint8_t> data = RD::get_singleton()->texture_get_data(rad_tex, 0);
- RD::get_singleton()->free(rad_tex);
-
- Ref<Image> img;
- img.instance();
- img->create(p_size.width, p_size.height, false, Image::FORMAT_RGBAF, data);
- for (int i = 0; i < p_size.width; i++) {
- for (int j = 0; j < p_size.height; j++) {
- Color c = img->get_pixel(i, j);
- c.r *= p_energy;
- c.g *= p_energy;
- c.b *= p_energy;
- img->set_pixel(i, j, c);
- }
- }
- return img;
- }
-
- return Ref<Image>();
-}
-
-void RendererSceneRenderRD::_update_dirty_skys() {
- Sky *sky = dirty_sky_list;
-
- while (sky) {
- bool texture_set_dirty = false;
- //update sky configuration if texture is missing
-
- if (sky->radiance.is_null()) {
- int mipmaps = Image::get_image_required_mipmaps(sky->radiance_size, sky->radiance_size, Image::FORMAT_RGBAH) + 1;
-
- uint32_t w = sky->radiance_size, h = sky->radiance_size;
- int layers = roughness_layers;
- if (sky->mode == RS::SKY_MODE_REALTIME) {
- layers = 8;
- if (roughness_layers != 8) {
- WARN_PRINT("When using REALTIME skies, roughness_layers should be set to 8 in the project settings for best quality reflections");
- }
- }
-
- if (sky_use_cubemap_array) {
- //array (higher quality, 6 times more memory)
- RD::TextureFormat tf;
- tf.array_layers = layers * 6;
- tf.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT;
- tf.texture_type = RD::TEXTURE_TYPE_CUBE_ARRAY;
- tf.mipmaps = mipmaps;
- tf.width = w;
- tf.height = h;
- tf.usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT;
-
- sky->radiance = RD::get_singleton()->texture_create(tf, RD::TextureView());
-
- _update_reflection_data(sky->reflection, sky->radiance_size, mipmaps, true, sky->radiance, 0, sky->mode == RS::SKY_MODE_REALTIME);
-
- } else {
- //regular cubemap, lower quality (aliasing, less memory)
- RD::TextureFormat tf;
- tf.array_layers = 6;
- tf.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT;
- tf.texture_type = RD::TEXTURE_TYPE_CUBE;
- tf.mipmaps = MIN(mipmaps, layers);
- tf.width = w;
- tf.height = h;
- tf.usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT;
-
- sky->radiance = RD::get_singleton()->texture_create(tf, RD::TextureView());
-
- _update_reflection_data(sky->reflection, sky->radiance_size, MIN(mipmaps, layers), false, sky->radiance, 0, sky->mode == RS::SKY_MODE_REALTIME);
- }
- texture_set_dirty = true;
- }
-
- // Create subpass buffers if they haven't been created already
- if (sky->half_res_pass.is_null() && !RD::get_singleton()->texture_is_valid(sky->half_res_pass) && sky->screen_size.x >= 4 && sky->screen_size.y >= 4) {
- RD::TextureFormat tformat;
- tformat.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT;
- tformat.width = sky->screen_size.x / 2;
- tformat.height = sky->screen_size.y / 2;
- tformat.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
- tformat.texture_type = RD::TEXTURE_TYPE_2D;
-
- sky->half_res_pass = RD::get_singleton()->texture_create(tformat, RD::TextureView());
- Vector<RID> texs;
- texs.push_back(sky->half_res_pass);
- sky->half_res_framebuffer = RD::get_singleton()->framebuffer_create(texs);
- texture_set_dirty = true;
- }
-
- if (sky->quarter_res_pass.is_null() && !RD::get_singleton()->texture_is_valid(sky->quarter_res_pass) && sky->screen_size.x >= 4 && sky->screen_size.y >= 4) {
- RD::TextureFormat tformat;
- tformat.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT;
- tformat.width = sky->screen_size.x / 4;
- tformat.height = sky->screen_size.y / 4;
- tformat.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
- tformat.texture_type = RD::TEXTURE_TYPE_2D;
-
- sky->quarter_res_pass = RD::get_singleton()->texture_create(tformat, RD::TextureView());
- Vector<RID> texs;
- texs.push_back(sky->quarter_res_pass);
- sky->quarter_res_framebuffer = RD::get_singleton()->framebuffer_create(texs);
- texture_set_dirty = true;
- }
-
- if (texture_set_dirty) {
- for (int i = 0; i < SKY_TEXTURE_SET_MAX; i++) {
- if (sky->texture_uniform_sets[i].is_valid() && RD::get_singleton()->uniform_set_is_valid(sky->texture_uniform_sets[i])) {
- RD::get_singleton()->free(sky->texture_uniform_sets[i]);
- sky->texture_uniform_sets[i] = RID();
- }
- }
- }
-
- sky->reflection.dirty = true;
- sky->processing_layer = 0;
-
- Sky *next = sky->dirty_list;
- sky->dirty_list = nullptr;
- sky->dirty = false;
- sky = next;
- }
-
- dirty_sky_list = nullptr;
-}
-
-RID RendererSceneRenderRD::sky_get_radiance_texture_rd(RID p_sky) const {
- Sky *sky = sky_owner.getornull(p_sky);
- ERR_FAIL_COND_V(!sky, RID());
-
- return sky->radiance;
-}
-
-RID RendererSceneRenderRD::sky_get_radiance_uniform_set_rd(RID p_sky, RID p_shader, int p_set) const {
- Sky *sky = sky_owner.getornull(p_sky);
- ERR_FAIL_COND_V(!sky, RID());
-
- if (sky->uniform_set.is_null() || !RD::get_singleton()->uniform_set_is_valid(sky->uniform_set)) {
- sky->uniform_set = RID();
- if (sky->radiance.is_valid()) {
- Vector<RD::Uniform> uniforms;
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
- u.binding = 0;
- u.ids.push_back(sky->radiance);
- uniforms.push_back(u);
- }
-
- sky->uniform_set = RD::get_singleton()->uniform_set_create(uniforms, p_shader, p_set);
- }
- }
-
- return sky->uniform_set;
-}
-
-RID RendererSceneRenderRD::_get_sky_textures(Sky *p_sky, SkyTextureSetVersion p_version) {
- if (p_sky->texture_uniform_sets[p_version].is_valid() && RD::get_singleton()->uniform_set_is_valid(p_sky->texture_uniform_sets[p_version])) {
- return p_sky->texture_uniform_sets[p_version];
- }
- Vector<RD::Uniform> uniforms;
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
- u.binding = 0;
- if (p_sky->radiance.is_valid() && p_version <= SKY_TEXTURE_SET_QUARTER_RES) {
- u.ids.push_back(p_sky->radiance);
- } else {
- u.ids.push_back(storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_CUBEMAP_BLACK));
- }
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
- u.binding = 1; // half res
- if (p_sky->half_res_pass.is_valid() && p_version != SKY_TEXTURE_SET_HALF_RES && p_version != SKY_TEXTURE_SET_CUBEMAP_HALF_RES) {
- if (p_version >= SKY_TEXTURE_SET_CUBEMAP) {
- u.ids.push_back(p_sky->reflection.layers[0].views[1]);
- } else {
- u.ids.push_back(p_sky->half_res_pass);
- }
- } else {
- if (p_version < SKY_TEXTURE_SET_CUBEMAP) {
- u.ids.push_back(storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_WHITE));
- } else {
- u.ids.push_back(storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_CUBEMAP_BLACK));
- }
- }
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
- u.binding = 2; // quarter res
- if (p_sky->quarter_res_pass.is_valid() && p_version != SKY_TEXTURE_SET_QUARTER_RES && p_version != SKY_TEXTURE_SET_CUBEMAP_QUARTER_RES) {
- if (p_version >= SKY_TEXTURE_SET_CUBEMAP) {
- u.ids.push_back(p_sky->reflection.layers[0].views[2]);
- } else {
- u.ids.push_back(p_sky->quarter_res_pass);
- }
- } else {
- if (p_version < SKY_TEXTURE_SET_CUBEMAP) {
- u.ids.push_back(storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_WHITE));
- } else {
- u.ids.push_back(storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_CUBEMAP_BLACK));
- }
- }
- uniforms.push_back(u);
- }
-
- p_sky->texture_uniform_sets[p_version] = RD::get_singleton()->uniform_set_create(uniforms, sky_shader.default_shader_rd, SKY_SET_TEXTURES);
- return p_sky->texture_uniform_sets[p_version];
-}
-
-RID RendererSceneRenderRD::sky_get_material(RID p_sky) const {
- Sky *sky = sky_owner.getornull(p_sky);
- ERR_FAIL_COND_V(!sky, RID());
-
- return sky->material;
-}
-
-void RendererSceneRenderRD::_draw_sky(bool p_can_continue_color, bool p_can_continue_depth, RID p_fb, RID p_environment, const CameraMatrix &p_projection, const Transform &p_transform) {
- ERR_FAIL_COND(!is_environment(p_environment));
-
- SkyMaterialData *material = nullptr;
-
- Sky *sky = sky_owner.getornull(environment_get_sky(p_environment));
-
- RID sky_material;
-
- RS::EnvironmentBG background = environment_get_background(p_environment);
-
- if (!(background == RS::ENV_BG_CLEAR_COLOR || background == RS::ENV_BG_COLOR) || sky) {
- ERR_FAIL_COND(!sky);
- sky_material = sky_get_material(environment_get_sky(p_environment));
-
- if (sky_material.is_valid()) {
- material = (SkyMaterialData *)storage->material_get_data(sky_material, RendererStorageRD::SHADER_TYPE_SKY);
- if (!material || !material->shader_data->valid) {
- material = nullptr;
- }
- }
-
- if (!material) {
- sky_material = sky_shader.default_material;
- material = (SkyMaterialData *)storage->material_get_data(sky_material, RendererStorageRD::SHADER_TYPE_SKY);
- }
- }
-
- if (background == RS::ENV_BG_CLEAR_COLOR || background == RS::ENV_BG_COLOR) {
- sky_material = sky_scene_state.fog_material;
- material = (SkyMaterialData *)storage->material_get_data(sky_material, RendererStorageRD::SHADER_TYPE_SKY);
- }
-
- ERR_FAIL_COND(!material);
-
- SkyShaderData *shader_data = material->shader_data;
-
- ERR_FAIL_COND(!shader_data);
-
- Basis sky_transform = environment_get_sky_orientation(p_environment);
- sky_transform.invert();
-
- float multiplier = environment_get_bg_energy(p_environment);
- float custom_fov = environment_get_sky_custom_fov(p_environment);
- // Camera
- CameraMatrix camera;
-
- if (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(custom_fov, aspect, near_plane, far_plane);
-
- } else {
- camera = p_projection;
- }
-
- sky_transform = p_transform.basis * sky_transform;
-
- if (shader_data->uses_quarter_res) {
- PipelineCacheRD *pipeline = &shader_data->pipelines[SKY_VERSION_QUARTER_RES];
-
- RID texture_uniform_set = _get_sky_textures(sky, SKY_TEXTURE_SET_QUARTER_RES);
-
- Vector<Color> clear_colors;
- clear_colors.push_back(Color(0.0, 0.0, 0.0));
-
- RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(sky->quarter_res_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_DISCARD, clear_colors);
- storage->get_effects()->render_sky(draw_list, time, sky->quarter_res_framebuffer, sky_scene_state.uniform_set, sky_scene_state.fog_uniform_set, pipeline, material->uniform_set, texture_uniform_set, camera, sky_transform, multiplier, p_transform.origin);
- RD::get_singleton()->draw_list_end();
- }
-
- if (shader_data->uses_half_res) {
- PipelineCacheRD *pipeline = &shader_data->pipelines[SKY_VERSION_HALF_RES];
-
- RID texture_uniform_set = _get_sky_textures(sky, SKY_TEXTURE_SET_HALF_RES);
-
- Vector<Color> clear_colors;
- clear_colors.push_back(Color(0.0, 0.0, 0.0));
-
- RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(sky->half_res_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_DISCARD, clear_colors);
- storage->get_effects()->render_sky(draw_list, time, sky->half_res_framebuffer, sky_scene_state.uniform_set, sky_scene_state.fog_uniform_set, pipeline, material->uniform_set, texture_uniform_set, camera, sky_transform, multiplier, p_transform.origin);
- RD::get_singleton()->draw_list_end();
- }
-
- PipelineCacheRD *pipeline = &shader_data->pipelines[SKY_VERSION_BACKGROUND];
-
- RID texture_uniform_set;
- if (sky) {
- texture_uniform_set = _get_sky_textures(sky, SKY_TEXTURE_SET_BACKGROUND);
- } else {
- texture_uniform_set = sky_scene_state.fog_only_texture_uniform_set;
- }
-
- RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_fb, RD::INITIAL_ACTION_CONTINUE, p_can_continue_color ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CONTINUE, p_can_continue_depth ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ);
- storage->get_effects()->render_sky(draw_list, time, p_fb, sky_scene_state.uniform_set, sky_scene_state.fog_uniform_set, pipeline, material->uniform_set, texture_uniform_set, camera, sky_transform, multiplier, p_transform.origin);
- RD::get_singleton()->draw_list_end();
-}
-
-void RendererSceneRenderRD::_setup_sky(RID p_environment, RID p_render_buffers, const CameraMatrix &p_projection, const Transform &p_transform, const Size2i p_screen_size) {
- ERR_FAIL_COND(!is_environment(p_environment));
-
- SkyMaterialData *material = nullptr;
-
- Sky *sky = sky_owner.getornull(environment_get_sky(p_environment));
-
- RID sky_material;
-
- SkyShaderData *shader_data = nullptr;
-
- RS::EnvironmentBG background = environment_get_background(p_environment);
-
- if (!(background == RS::ENV_BG_CLEAR_COLOR || background == RS::ENV_BG_COLOR) || sky) {
- ERR_FAIL_COND(!sky);
- sky_material = sky_get_material(environment_get_sky(p_environment));
-
- if (sky_material.is_valid()) {
- material = (SkyMaterialData *)storage->material_get_data(sky_material, RendererStorageRD::SHADER_TYPE_SKY);
- if (!material || !material->shader_data->valid) {
- material = nullptr;
- }
- }
-
- if (!material) {
- sky_material = sky_shader.default_material;
- material = (SkyMaterialData *)storage->material_get_data(sky_material, RendererStorageRD::SHADER_TYPE_SKY);
- }
-
- ERR_FAIL_COND(!material);
-
- shader_data = material->shader_data;
-
- ERR_FAIL_COND(!shader_data);
- }
-
- if (sky) {
- // Invalidate supbass buffers if screen size changes
- if (sky->screen_size != p_screen_size) {
- sky->screen_size = p_screen_size;
- sky->screen_size.x = sky->screen_size.x < 4 ? 4 : sky->screen_size.x;
- sky->screen_size.y = sky->screen_size.y < 4 ? 4 : sky->screen_size.y;
- if (shader_data->uses_half_res) {
- if (sky->half_res_pass.is_valid()) {
- RD::get_singleton()->free(sky->half_res_pass);
- sky->half_res_pass = RID();
- }
- _sky_invalidate(sky);
- }
- if (shader_data->uses_quarter_res) {
- if (sky->quarter_res_pass.is_valid()) {
- RD::get_singleton()->free(sky->quarter_res_pass);
- sky->quarter_res_pass = RID();
- }
- _sky_invalidate(sky);
- }
- }
-
- // Create new subpass buffers if necessary
- if ((shader_data->uses_half_res && sky->half_res_pass.is_null()) ||
- (shader_data->uses_quarter_res && sky->quarter_res_pass.is_null()) ||
- sky->radiance.is_null()) {
- _sky_invalidate(sky);
- _update_dirty_skys();
- }
-
- if (shader_data->uses_time && time - sky->prev_time > 0.00001) {
- sky->prev_time = time;
- sky->reflection.dirty = true;
- RenderingServerDefault::redraw_request();
- }
-
- if (material != sky->prev_material) {
- sky->prev_material = material;
- sky->reflection.dirty = true;
- }
-
- if (material->uniform_set_updated) {
- material->uniform_set_updated = false;
- sky->reflection.dirty = true;
- }
-
- if (!p_transform.origin.is_equal_approx(sky->prev_position) && shader_data->uses_position) {
- sky->prev_position = p_transform.origin;
- sky->reflection.dirty = true;
- }
-
- if (shader_data->uses_light) {
- // Check whether the directional_light_buffer changes
- bool light_data_dirty = false;
-
- if (sky_scene_state.ubo.directional_light_count != sky_scene_state.last_frame_directional_light_count) {
- light_data_dirty = true;
- for (uint32_t i = sky_scene_state.ubo.directional_light_count; i < sky_scene_state.max_directional_lights; i++) {
- sky_scene_state.directional_lights[i].enabled = false;
- }
- }
- if (!light_data_dirty) {
- for (uint32_t i = 0; i < sky_scene_state.ubo.directional_light_count; i++) {
- if (sky_scene_state.directional_lights[i].direction[0] != sky_scene_state.last_frame_directional_lights[i].direction[0] ||
- sky_scene_state.directional_lights[i].direction[1] != sky_scene_state.last_frame_directional_lights[i].direction[1] ||
- sky_scene_state.directional_lights[i].direction[2] != sky_scene_state.last_frame_directional_lights[i].direction[2] ||
- sky_scene_state.directional_lights[i].energy != sky_scene_state.last_frame_directional_lights[i].energy ||
- sky_scene_state.directional_lights[i].color[0] != sky_scene_state.last_frame_directional_lights[i].color[0] ||
- sky_scene_state.directional_lights[i].color[1] != sky_scene_state.last_frame_directional_lights[i].color[1] ||
- sky_scene_state.directional_lights[i].color[2] != sky_scene_state.last_frame_directional_lights[i].color[2] ||
- sky_scene_state.directional_lights[i].enabled != sky_scene_state.last_frame_directional_lights[i].enabled ||
- sky_scene_state.directional_lights[i].size != sky_scene_state.last_frame_directional_lights[i].size) {
- light_data_dirty = true;
- break;
- }
- }
- }
-
- if (light_data_dirty) {
- RD::get_singleton()->buffer_update(sky_scene_state.directional_light_buffer, 0, sizeof(SkyDirectionalLightData) * sky_scene_state.max_directional_lights, sky_scene_state.directional_lights);
-
- RendererSceneRenderRD::SkyDirectionalLightData *temp = sky_scene_state.last_frame_directional_lights;
- sky_scene_state.last_frame_directional_lights = sky_scene_state.directional_lights;
- sky_scene_state.directional_lights = temp;
- sky_scene_state.last_frame_directional_light_count = sky_scene_state.ubo.directional_light_count;
- sky->reflection.dirty = true;
- }
- }
- }
-
- //setup fog variables
- sky_scene_state.ubo.volumetric_fog_enabled = false;
- if (p_render_buffers.is_valid()) {
- if (render_buffers_has_volumetric_fog(p_render_buffers)) {
- sky_scene_state.ubo.volumetric_fog_enabled = true;
-
- float fog_end = render_buffers_get_volumetric_fog_end(p_render_buffers);
- if (fog_end > 0.0) {
- sky_scene_state.ubo.volumetric_fog_inv_length = 1.0 / fog_end;
- } else {
- sky_scene_state.ubo.volumetric_fog_inv_length = 1.0;
- }
-
- float fog_detail_spread = render_buffers_get_volumetric_fog_detail_spread(p_render_buffers); //reverse lookup
- if (fog_detail_spread > 0.0) {
- sky_scene_state.ubo.volumetric_fog_detail_spread = 1.0 / fog_detail_spread;
- } else {
- sky_scene_state.ubo.volumetric_fog_detail_spread = 1.0;
- }
- }
-
- RID fog_uniform_set = render_buffers_get_volumetric_fog_sky_uniform_set(p_render_buffers);
-
- if (fog_uniform_set != RID()) {
- sky_scene_state.fog_uniform_set = fog_uniform_set;
- } else {
- sky_scene_state.fog_uniform_set = sky_scene_state.default_fog_uniform_set;
- }
- }
-
- sky_scene_state.ubo.z_far = p_projection.get_z_far();
- sky_scene_state.ubo.fog_enabled = environment_is_fog_enabled(p_environment);
- sky_scene_state.ubo.fog_density = environment_get_fog_density(p_environment);
- sky_scene_state.ubo.fog_aerial_perspective = environment_get_fog_aerial_perspective(p_environment);
- Color fog_color = environment_get_fog_light_color(p_environment).to_linear();
- float fog_energy = environment_get_fog_light_energy(p_environment);
- sky_scene_state.ubo.fog_light_color[0] = fog_color.r * fog_energy;
- sky_scene_state.ubo.fog_light_color[1] = fog_color.g * fog_energy;
- sky_scene_state.ubo.fog_light_color[2] = fog_color.b * fog_energy;
- sky_scene_state.ubo.fog_sun_scatter = environment_get_fog_sun_scatter(p_environment);
-
- RD::get_singleton()->buffer_update(sky_scene_state.uniform_buffer, 0, sizeof(SkySceneState::UBO), &sky_scene_state.ubo);
-}
-
-void RendererSceneRenderRD::_update_sky(RID p_environment, const CameraMatrix &p_projection, const Transform &p_transform) {
- ERR_FAIL_COND(!is_environment(p_environment));
-
- Sky *sky = sky_owner.getornull(environment_get_sky(p_environment));
- ERR_FAIL_COND(!sky);
-
- RID sky_material = sky_get_material(environment_get_sky(p_environment));
-
- SkyMaterialData *material = nullptr;
-
- if (sky_material.is_valid()) {
- material = (SkyMaterialData *)storage->material_get_data(sky_material, RendererStorageRD::SHADER_TYPE_SKY);
- if (!material || !material->shader_data->valid) {
- material = nullptr;
- }
- }
-
- if (!material) {
- sky_material = sky_shader.default_material;
- material = (SkyMaterialData *)storage->material_get_data(sky_material, RendererStorageRD::SHADER_TYPE_SKY);
- }
-
- ERR_FAIL_COND(!material);
-
- SkyShaderData *shader_data = material->shader_data;
-
- ERR_FAIL_COND(!shader_data);
-
- float multiplier = environment_get_bg_energy(p_environment);
-
- bool update_single_frame = sky->mode == RS::SKY_MODE_REALTIME || sky->mode == RS::SKY_MODE_QUALITY;
- RS::SkyMode sky_mode = sky->mode;
-
- if (sky_mode == RS::SKY_MODE_AUTOMATIC) {
- if (shader_data->uses_time || shader_data->uses_position) {
- update_single_frame = true;
- sky_mode = RS::SKY_MODE_REALTIME;
- } else if (shader_data->uses_light || shader_data->ubo_size > 0) {
- update_single_frame = false;
- sky_mode = RS::SKY_MODE_INCREMENTAL;
- } else {
- update_single_frame = true;
- sky_mode = RS::SKY_MODE_QUALITY;
- }
- }
-
- if (sky->processing_layer == 0 && sky_mode == RS::SKY_MODE_INCREMENTAL) {
- // On the first frame after creating sky, rebuild in single frame
- update_single_frame = true;
- sky_mode = RS::SKY_MODE_QUALITY;
- }
-
- int max_processing_layer = sky_use_cubemap_array ? sky->reflection.layers.size() : sky->reflection.layers[0].mipmaps.size();
-
- // Update radiance cubemap
- if (sky->reflection.dirty && (sky->processing_layer >= max_processing_layer || update_single_frame)) {
- static const Vector3 view_normals[6] = {
- Vector3(+1, 0, 0),
- Vector3(-1, 0, 0),
- Vector3(0, +1, 0),
- Vector3(0, -1, 0),
- Vector3(0, 0, +1),
- Vector3(0, 0, -1)
- };
- static const Vector3 view_up[6] = {
- Vector3(0, -1, 0),
- Vector3(0, -1, 0),
- Vector3(0, 0, +1),
- Vector3(0, 0, -1),
- Vector3(0, -1, 0),
- Vector3(0, -1, 0)
- };
-
- CameraMatrix cm;
- cm.set_perspective(90, 1, 0.01, 10.0);
- CameraMatrix correction;
- correction.set_depth_correction(true);
- cm = correction * cm;
-
- if (shader_data->uses_quarter_res) {
- PipelineCacheRD *pipeline = &shader_data->pipelines[SKY_VERSION_CUBEMAP_QUARTER_RES];
-
- Vector<Color> clear_colors;
- clear_colors.push_back(Color(0.0, 0.0, 0.0));
- RD::DrawListID cubemap_draw_list;
-
- for (int i = 0; i < 6; i++) {
- Transform local_view;
- local_view.set_look_at(Vector3(0, 0, 0), view_normals[i], view_up[i]);
- RID texture_uniform_set = _get_sky_textures(sky, SKY_TEXTURE_SET_CUBEMAP_QUARTER_RES);
-
- cubemap_draw_list = RD::get_singleton()->draw_list_begin(sky->reflection.layers[0].mipmaps[2].framebuffers[i], RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD);
- storage->get_effects()->render_sky(cubemap_draw_list, time, sky->reflection.layers[0].mipmaps[2].framebuffers[i], sky_scene_state.uniform_set, sky_scene_state.fog_uniform_set, pipeline, material->uniform_set, texture_uniform_set, cm, local_view.basis, multiplier, p_transform.origin);
- RD::get_singleton()->draw_list_end();
- }
- }
-
- if (shader_data->uses_half_res) {
- PipelineCacheRD *pipeline = &shader_data->pipelines[SKY_VERSION_CUBEMAP_HALF_RES];
-
- Vector<Color> clear_colors;
- clear_colors.push_back(Color(0.0, 0.0, 0.0));
- RD::DrawListID cubemap_draw_list;
-
- for (int i = 0; i < 6; i++) {
- Transform local_view;
- local_view.set_look_at(Vector3(0, 0, 0), view_normals[i], view_up[i]);
- RID texture_uniform_set = _get_sky_textures(sky, SKY_TEXTURE_SET_CUBEMAP_HALF_RES);
-
- cubemap_draw_list = RD::get_singleton()->draw_list_begin(sky->reflection.layers[0].mipmaps[1].framebuffers[i], RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD);
- storage->get_effects()->render_sky(cubemap_draw_list, time, sky->reflection.layers[0].mipmaps[1].framebuffers[i], sky_scene_state.uniform_set, sky_scene_state.fog_uniform_set, pipeline, material->uniform_set, texture_uniform_set, cm, local_view.basis, multiplier, p_transform.origin);
- RD::get_singleton()->draw_list_end();
- }
- }
-
- RD::DrawListID cubemap_draw_list;
- PipelineCacheRD *pipeline = &shader_data->pipelines[SKY_VERSION_CUBEMAP];
-
- for (int i = 0; i < 6; i++) {
- Transform local_view;
- local_view.set_look_at(Vector3(0, 0, 0), view_normals[i], view_up[i]);
- RID texture_uniform_set = _get_sky_textures(sky, SKY_TEXTURE_SET_CUBEMAP);
-
- cubemap_draw_list = RD::get_singleton()->draw_list_begin(sky->reflection.layers[0].mipmaps[0].framebuffers[i], RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD);
- storage->get_effects()->render_sky(cubemap_draw_list, time, sky->reflection.layers[0].mipmaps[0].framebuffers[i], sky_scene_state.uniform_set, sky_scene_state.fog_uniform_set, pipeline, material->uniform_set, texture_uniform_set, cm, local_view.basis, multiplier, p_transform.origin);
- RD::get_singleton()->draw_list_end();
- }
-
- if (sky_mode == RS::SKY_MODE_REALTIME) {
- _create_reflection_fast_filter(sky->reflection, sky_use_cubemap_array);
- if (sky_use_cubemap_array) {
- _update_reflection_mipmaps(sky->reflection, 0, sky->reflection.layers.size());
- }
- } else {
- if (update_single_frame) {
- for (int i = 1; i < max_processing_layer; i++) {
- _create_reflection_importance_sample(sky->reflection, sky_use_cubemap_array, 10, i);
- }
- if (sky_use_cubemap_array) {
- _update_reflection_mipmaps(sky->reflection, 0, sky->reflection.layers.size());
- }
- } else {
- if (sky_use_cubemap_array) {
- // Multi-Frame so just update the first array level
- _update_reflection_mipmaps(sky->reflection, 0, 1);
- }
- }
- sky->processing_layer = 1;
- }
-
- sky->reflection.dirty = false;
-
- } else {
- if (sky_mode == RS::SKY_MODE_INCREMENTAL && sky->processing_layer < max_processing_layer) {
- _create_reflection_importance_sample(sky->reflection, sky_use_cubemap_array, 10, sky->processing_layer);
-
- if (sky_use_cubemap_array) {
- _update_reflection_mipmaps(sky->reflection, sky->processing_layer, sky->processing_layer + 1);
- }
-
- sky->processing_layer++;
- }
- }
-}
-
-/* SKY SHADER */
-
-void RendererSceneRenderRD::SkyShaderData::set_code(const String &p_code) {
- //compile
-
- code = p_code;
- valid = false;
- ubo_size = 0;
- uniforms.clear();
-
- if (code == String()) {
- return; //just invalid, but no error
- }
-
- ShaderCompilerRD::GeneratedCode gen_code;
- ShaderCompilerRD::IdentifierActions actions;
-
- 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;
-
- RendererSceneRenderRD *scene_singleton = (RendererSceneRenderRD *)RendererSceneRenderRD::singleton;
-
- Error err = scene_singleton->sky_shader.compiler.compile(RS::SHADER_SKY, code, &actions, path, gen_code);
-
- ERR_FAIL_COND(err != OK);
-
- if (version.is_null()) {
- version = scene_singleton->sky_shader.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
-
- scene_singleton->sky_shader.shader.version_set_code(version, gen_code.uniforms, gen_code.vertex_global, gen_code.vertex, gen_code.fragment_global, gen_code.light, gen_code.fragment, gen_code.defines);
- ERR_FAIL_COND(!scene_singleton->sky_shader.shader.version_is_valid(version));
-
- ubo_size = gen_code.uniform_total_size;
- ubo_offsets = gen_code.uniform_offsets;
- texture_uniforms = gen_code.texture_uniforms;
-
- //update pipelines
-
- for (int i = 0; i < SKY_VERSION_MAX; i++) {
- RD::PipelineDepthStencilState depth_stencil_state;
- depth_stencil_state.enable_depth_test = true;
- depth_stencil_state.depth_compare_operator = RD::COMPARE_OP_LESS_OR_EQUAL;
-
- RID shader_variant = scene_singleton->sky_shader.shader.version_get_shader(version, i);
- pipelines[i].setup(shader_variant, RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), depth_stencil_state, RD::PipelineColorBlendState::create_disabled(), 0);
- }
-
- valid = true;
-}
-
-void RendererSceneRenderRD::SkyShaderData::set_default_texture_param(const StringName &p_name, RID p_texture) {
- if (!p_texture.is_valid()) {
- default_texture_params.erase(p_name);
- } else {
- default_texture_params[p_name] = p_texture;
- }
-}
-
-void RendererSceneRenderRD::SkyShaderData::get_param_list(List<PropertyInfo> *p_param_list) const {
- Map<int, StringName> order;
-
- for (Map<StringName, ShaderLanguage::ShaderNode::Uniform>::Element *E = uniforms.front(); E; E = E->next()) {
- if (E->get().scope == ShaderLanguage::ShaderNode::Uniform::SCOPE_GLOBAL || E->get().scope == ShaderLanguage::ShaderNode::Uniform::SCOPE_INSTANCE) {
- continue;
- }
-
- if (E->get().texture_order >= 0) {
- order[E->get().texture_order + 100000] = E->key();
- } else {
- order[E->get().order] = E->key();
- }
- }
-
- for (Map<int, StringName>::Element *E = order.front(); E; E = E->next()) {
- PropertyInfo pi = ShaderLanguage::uniform_to_property_info(uniforms[E->get()]);
- pi.name = E->get();
- p_param_list->push_back(pi);
- }
-}
-
-void RendererSceneRenderRD::SkyShaderData::get_instance_param_list(List<RendererStorage::InstanceShaderParam> *p_param_list) const {
- for (Map<StringName, ShaderLanguage::ShaderNode::Uniform>::Element *E = uniforms.front(); E; E = E->next()) {
- if (E->get().scope != ShaderLanguage::ShaderNode::Uniform::SCOPE_INSTANCE) {
- continue;
- }
-
- RendererStorage::InstanceShaderParam p;
- p.info = ShaderLanguage::uniform_to_property_info(E->get());
- p.info.name = E->key(); //supply name
- p.index = E->get().instance_index;
- p.default_value = ShaderLanguage::constant_value_to_variant(E->get().default_value, E->get().type, E->get().hint);
- p_param_list->push_back(p);
- }
-}
-
-bool RendererSceneRenderRD::SkyShaderData::is_param_texture(const StringName &p_param) const {
- if (!uniforms.has(p_param)) {
- return false;
- }
-
- return uniforms[p_param].texture_order >= 0;
-}
-
-bool RendererSceneRenderRD::SkyShaderData::is_animated() const {
- return false;
-}
-
-bool RendererSceneRenderRD::SkyShaderData::casts_shadows() const {
- return false;
-}
-
-Variant RendererSceneRenderRD::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.hint);
- }
- return Variant();
-}
-
-RS::ShaderNativeSourceCode RendererSceneRenderRD::SkyShaderData::get_native_source_code() const {
- RendererSceneRenderRD *scene_singleton = (RendererSceneRenderRD *)RendererSceneRenderRD::singleton;
-
- return scene_singleton->sky_shader.shader.version_get_native_source_code(version);
-}
-
-RendererSceneRenderRD::SkyShaderData::SkyShaderData() {
- valid = false;
-}
-
-RendererSceneRenderRD::SkyShaderData::~SkyShaderData() {
- RendererSceneRenderRD *scene_singleton = (RendererSceneRenderRD *)RendererSceneRenderRD::singleton;
- ERR_FAIL_COND(!scene_singleton);
- //pipeline variants will clear themselves if shader is gone
- if (version.is_valid()) {
- scene_singleton->sky_shader.shader.version_free(version);
- }
-}
-
-RendererStorageRD::ShaderData *RendererSceneRenderRD::_create_sky_shader_func() {
- SkyShaderData *shader_data = memnew(SkyShaderData);
- return shader_data;
-}
-
-void RendererSceneRenderRD::SkyMaterialData::update_parameters(const Map<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty) {
- RendererSceneRenderRD *scene_singleton = (RendererSceneRenderRD *)RendererSceneRenderRD::singleton;
-
- uniform_set_updated = true;
-
- if ((uint32_t)ubo_data.size() != shader_data->ubo_size) {
- p_uniform_dirty = true;
- if (uniform_buffer.is_valid()) {
- RD::get_singleton()->free(uniform_buffer);
- uniform_buffer = RID();
- }
-
- ubo_data.resize(shader_data->ubo_size);
- if (ubo_data.size()) {
- uniform_buffer = RD::get_singleton()->uniform_buffer_create(ubo_data.size());
- memset(ubo_data.ptrw(), 0, ubo_data.size()); //clear
- }
-
- //clear previous uniform set
- if (uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(uniform_set)) {
- RD::get_singleton()->free(uniform_set);
- uniform_set = RID();
- }
- }
-
- //check whether buffer changed
- if (p_uniform_dirty && ubo_data.size()) {
- update_uniform_buffer(shader_data->uniforms, shader_data->ubo_offsets.ptr(), p_parameters, ubo_data.ptrw(), ubo_data.size(), false);
- RD::get_singleton()->buffer_update(uniform_buffer, 0, ubo_data.size(), ubo_data.ptrw());
- }
-
- uint32_t tex_uniform_count = shader_data->texture_uniforms.size();
-
- if ((uint32_t)texture_cache.size() != tex_uniform_count) {
- texture_cache.resize(tex_uniform_count);
- p_textures_dirty = true;
-
- //clear previous uniform set
- if (uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(uniform_set)) {
- RD::get_singleton()->free(uniform_set);
- uniform_set = RID();
- }
- }
-
- if (p_textures_dirty && tex_uniform_count) {
- update_textures(p_parameters, shader_data->default_texture_params, shader_data->texture_uniforms, texture_cache.ptrw(), true);
- }
-
- if (shader_data->ubo_size == 0 && shader_data->texture_uniforms.size() == 0) {
- // This material does not require an uniform set, so don't create it.
- return;
- }
-
- if (!p_textures_dirty && uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(uniform_set)) {
- //no reason to update uniform set, only UBO (or nothing) was needed to update
- return;
- }
-
- Vector<RD::Uniform> uniforms;
-
- {
- if (shader_data->ubo_size) {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER;
- u.binding = 0;
- u.ids.push_back(uniform_buffer);
- uniforms.push_back(u);
- }
-
- const RID *textures = texture_cache.ptrw();
- for (uint32_t i = 0; i < tex_uniform_count; i++) {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
- u.binding = 1 + i;
- u.ids.push_back(textures[i]);
- uniforms.push_back(u);
- }
- }
-
- uniform_set = RD::get_singleton()->uniform_set_create(uniforms, scene_singleton->sky_shader.shader.version_get_shader(shader_data->version, 0), SKY_SET_MATERIAL);
-}
-
-RendererSceneRenderRD::SkyMaterialData::~SkyMaterialData() {
- if (uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(uniform_set)) {
- RD::get_singleton()->free(uniform_set);
- }
-
- if (uniform_buffer.is_valid()) {
- RD::get_singleton()->free(uniform_buffer);
- }
-}
-
-RendererStorageRD::MaterialData *RendererSceneRenderRD::_create_sky_material_func(SkyShaderData *p_shader) {
- SkyMaterialData *material_data = memnew(SkyMaterialData);
- material_data->shader_data = p_shader;
- material_data->last_frame = false;
- //update will happen later anyway so do nothing.
- return material_data;
+ return sky.sky_bake_panorama(p_sky, p_energy, p_bake_irradiance, p_size);
}
RID RendererSceneRenderRD::environment_allocate() {
return environment_owner.allocate_rid();
}
void RendererSceneRenderRD::environment_initialize(RID p_rid) {
- environment_owner.initialize_rid(p_rid, Environment());
+ environment_owner.initialize_rid(p_rid, RendererSceneEnvironmentRD());
}
void RendererSceneRenderRD::environment_set_background(RID p_env, RS::EnvironmentBG p_bg) {
- Environment *env = environment_owner.getornull(p_env);
+ RendererSceneEnvironmentRD *env = environment_owner.getornull(p_env);
ERR_FAIL_COND(!env);
env->background = p_bg;
}
void RendererSceneRenderRD::environment_set_sky(RID p_env, RID p_sky) {
- Environment *env = environment_owner.getornull(p_env);
+ RendererSceneEnvironmentRD *env = environment_owner.getornull(p_env);
ERR_FAIL_COND(!env);
env->sky = p_sky;
}
void RendererSceneRenderRD::environment_set_sky_custom_fov(RID p_env, float p_scale) {
- Environment *env = environment_owner.getornull(p_env);
+ RendererSceneEnvironmentRD *env = environment_owner.getornull(p_env);
ERR_FAIL_COND(!env);
env->sky_custom_fov = p_scale;
}
void RendererSceneRenderRD::environment_set_sky_orientation(RID p_env, const Basis &p_orientation) {
- Environment *env = environment_owner.getornull(p_env);
+ RendererSceneEnvironmentRD *env = environment_owner.getornull(p_env);
ERR_FAIL_COND(!env);
env->sky_orientation = p_orientation;
}
void RendererSceneRenderRD::environment_set_bg_color(RID p_env, const Color &p_color) {
- Environment *env = environment_owner.getornull(p_env);
+ RendererSceneEnvironmentRD *env = environment_owner.getornull(p_env);
ERR_FAIL_COND(!env);
env->bg_color = p_color;
}
void RendererSceneRenderRD::environment_set_bg_energy(RID p_env, float p_energy) {
- Environment *env = environment_owner.getornull(p_env);
+ RendererSceneEnvironmentRD *env = environment_owner.getornull(p_env);
ERR_FAIL_COND(!env);
env->bg_energy = p_energy;
}
void RendererSceneRenderRD::environment_set_canvas_max_layer(RID p_env, int p_max_layer) {
- Environment *env = environment_owner.getornull(p_env);
+ RendererSceneEnvironmentRD *env = environment_owner.getornull(p_env);
ERR_FAIL_COND(!env);
env->canvas_max_layer = p_max_layer;
}
void RendererSceneRenderRD::environment_set_ambient_light(RID p_env, const Color &p_color, RS::EnvironmentAmbientSource p_ambient, float p_energy, float p_sky_contribution, RS::EnvironmentReflectionSource p_reflection_source, const Color &p_ao_color) {
- Environment *env = environment_owner.getornull(p_env);
+ RendererSceneEnvironmentRD *env = environment_owner.getornull(p_env);
ERR_FAIL_COND(!env);
- env->ambient_light = p_color;
- env->ambient_source = p_ambient;
- env->ambient_light_energy = p_energy;
- env->ambient_sky_contribution = p_sky_contribution;
- env->reflection_source = p_reflection_source;
- env->ao_color = p_ao_color;
+ env->set_ambient_light(p_color, p_ambient, p_energy, p_sky_contribution, p_reflection_source, p_ao_color);
}
RS::EnvironmentBG RendererSceneRenderRD::environment_get_background(RID p_env) const {
- Environment *env = environment_owner.getornull(p_env);
+ RendererSceneEnvironmentRD *env = environment_owner.getornull(p_env);
ERR_FAIL_COND_V(!env, RS::ENV_BG_MAX);
return env->background;
}
RID RendererSceneRenderRD::environment_get_sky(RID p_env) const {
- Environment *env = environment_owner.getornull(p_env);
+ RendererSceneEnvironmentRD *env = environment_owner.getornull(p_env);
ERR_FAIL_COND_V(!env, RID());
return env->sky;
}
float RendererSceneRenderRD::environment_get_sky_custom_fov(RID p_env) const {
- Environment *env = environment_owner.getornull(p_env);
+ RendererSceneEnvironmentRD *env = environment_owner.getornull(p_env);
ERR_FAIL_COND_V(!env, 0);
return env->sky_custom_fov;
}
Basis RendererSceneRenderRD::environment_get_sky_orientation(RID p_env) const {
- Environment *env = environment_owner.getornull(p_env);
+ RendererSceneEnvironmentRD *env = environment_owner.getornull(p_env);
ERR_FAIL_COND_V(!env, Basis());
return env->sky_orientation;
}
Color RendererSceneRenderRD::environment_get_bg_color(RID p_env) const {
- Environment *env = environment_owner.getornull(p_env);
+ RendererSceneEnvironmentRD *env = environment_owner.getornull(p_env);
ERR_FAIL_COND_V(!env, Color());
return env->bg_color;
}
float RendererSceneRenderRD::environment_get_bg_energy(RID p_env) const {
- Environment *env = environment_owner.getornull(p_env);
+ RendererSceneEnvironmentRD *env = environment_owner.getornull(p_env);
ERR_FAIL_COND_V(!env, 0);
return env->bg_energy;
}
int RendererSceneRenderRD::environment_get_canvas_max_layer(RID p_env) const {
- Environment *env = environment_owner.getornull(p_env);
+ RendererSceneEnvironmentRD *env = environment_owner.getornull(p_env);
ERR_FAIL_COND_V(!env, 0);
return env->canvas_max_layer;
}
Color RendererSceneRenderRD::environment_get_ambient_light_color(RID p_env) const {
- Environment *env = environment_owner.getornull(p_env);
+ RendererSceneEnvironmentRD *env = environment_owner.getornull(p_env);
ERR_FAIL_COND_V(!env, Color());
return env->ambient_light;
}
RS::EnvironmentAmbientSource RendererSceneRenderRD::environment_get_ambient_source(RID p_env) const {
- Environment *env = environment_owner.getornull(p_env);
+ RendererSceneEnvironmentRD *env = environment_owner.getornull(p_env);
ERR_FAIL_COND_V(!env, RS::ENV_AMBIENT_SOURCE_BG);
return env->ambient_source;
}
float RendererSceneRenderRD::environment_get_ambient_light_energy(RID p_env) const {
- Environment *env = environment_owner.getornull(p_env);
+ RendererSceneEnvironmentRD *env = environment_owner.getornull(p_env);
ERR_FAIL_COND_V(!env, 0);
return env->ambient_light_energy;
}
float RendererSceneRenderRD::environment_get_ambient_sky_contribution(RID p_env) const {
- Environment *env = environment_owner.getornull(p_env);
+ RendererSceneEnvironmentRD *env = environment_owner.getornull(p_env);
ERR_FAIL_COND_V(!env, 0);
return env->ambient_sky_contribution;
}
RS::EnvironmentReflectionSource RendererSceneRenderRD::environment_get_reflection_source(RID p_env) const {
- Environment *env = environment_owner.getornull(p_env);
+ RendererSceneEnvironmentRD *env = environment_owner.getornull(p_env);
ERR_FAIL_COND_V(!env, RS::ENV_REFLECTION_SOURCE_DISABLED);
return env->reflection_source;
}
Color RendererSceneRenderRD::environment_get_ao_color(RID p_env) const {
- Environment *env = environment_owner.getornull(p_env);
+ RendererSceneEnvironmentRD *env = environment_owner.getornull(p_env);
ERR_FAIL_COND_V(!env, Color());
return env->ao_color;
}
void RendererSceneRenderRD::environment_set_tonemap(RID p_env, RS::EnvironmentToneMapper p_tone_mapper, float p_exposure, float p_white, bool p_auto_exposure, float p_min_luminance, float p_max_luminance, float p_auto_exp_speed, float p_auto_exp_scale) {
- Environment *env = environment_owner.getornull(p_env);
+ RendererSceneEnvironmentRD *env = environment_owner.getornull(p_env);
ERR_FAIL_COND(!env);
- env->exposure = p_exposure;
- env->tone_mapper = p_tone_mapper;
- if (!env->auto_exposure && p_auto_exposure) {
- env->auto_exposure_version = ++auto_exposure_counter;
- }
- env->auto_exposure = p_auto_exposure;
- env->white = p_white;
- env->min_luminance = p_min_luminance;
- env->max_luminance = p_max_luminance;
- env->auto_exp_speed = p_auto_exp_speed;
- env->auto_exp_scale = p_auto_exp_scale;
+ env->set_tonemap(p_tone_mapper, p_exposure, p_white, p_auto_exposure, p_min_luminance, p_max_luminance, p_auto_exp_speed, p_auto_exp_scale);
}
void RendererSceneRenderRD::environment_set_glow(RID p_env, bool p_enable, Vector<float> p_levels, float p_intensity, float p_strength, float p_mix, float p_bloom_threshold, RS::EnvironmentGlowBlendMode p_blend_mode, float p_hdr_bleed_threshold, float p_hdr_bleed_scale, float p_hdr_luminance_cap) {
- Environment *env = environment_owner.getornull(p_env);
+ RendererSceneEnvironmentRD *env = environment_owner.getornull(p_env);
ERR_FAIL_COND(!env);
- ERR_FAIL_COND_MSG(p_levels.size() != 7, "Size of array of glow levels must be 7");
- env->glow_enabled = p_enable;
- env->glow_levels = p_levels;
- env->glow_intensity = p_intensity;
- env->glow_strength = p_strength;
- env->glow_mix = p_mix;
- env->glow_bloom = p_bloom_threshold;
- env->glow_blend_mode = p_blend_mode;
- env->glow_hdr_bleed_threshold = p_hdr_bleed_threshold;
- env->glow_hdr_bleed_scale = p_hdr_bleed_scale;
- env->glow_hdr_luminance_cap = p_hdr_luminance_cap;
+ env->set_glow(p_enable, p_levels, p_intensity, p_strength, p_mix, p_bloom_threshold, p_blend_mode, p_hdr_bleed_threshold, p_hdr_bleed_scale, p_hdr_luminance_cap);
}
void RendererSceneRenderRD::environment_glow_set_use_bicubic_upscale(bool p_enable) {
@@ -3088,100 +314,76 @@ void RendererSceneRenderRD::environment_glow_set_use_high_quality(bool p_enable)
}
void RendererSceneRenderRD::environment_set_sdfgi(RID p_env, bool p_enable, RS::EnvironmentSDFGICascades p_cascades, float p_min_cell_size, RS::EnvironmentSDFGIYScale p_y_scale, bool p_use_occlusion, float p_bounce_feedback, bool p_read_sky, float p_energy, float p_normal_bias, float p_probe_bias) {
- Environment *env = environment_owner.getornull(p_env);
+ RendererSceneEnvironmentRD *env = environment_owner.getornull(p_env);
ERR_FAIL_COND(!env);
if (low_end) {
return;
}
- env->sdfgi_enabled = p_enable;
- env->sdfgi_cascades = p_cascades;
- env->sdfgi_min_cell_size = p_min_cell_size;
- env->sdfgi_use_occlusion = p_use_occlusion;
- env->sdfgi_bounce_feedback = p_bounce_feedback;
- env->sdfgi_read_sky_light = p_read_sky;
- env->sdfgi_energy = p_energy;
- env->sdfgi_normal_bias = p_normal_bias;
- env->sdfgi_probe_bias = p_probe_bias;
- env->sdfgi_y_scale = p_y_scale;
+ env->set_sdfgi(p_enable, p_cascades, p_min_cell_size, p_y_scale, p_use_occlusion, p_bounce_feedback, p_read_sky, p_energy, p_normal_bias, p_probe_bias);
}
void RendererSceneRenderRD::environment_set_fog(RID p_env, bool p_enable, const Color &p_light_color, float p_light_energy, float p_sun_scatter, float p_density, float p_height, float p_height_density, float p_fog_aerial_perspective) {
- Environment *env = environment_owner.getornull(p_env);
+ RendererSceneEnvironmentRD *env = environment_owner.getornull(p_env);
ERR_FAIL_COND(!env);
- env->fog_enabled = p_enable;
- env->fog_light_color = p_light_color;
- env->fog_light_energy = p_light_energy;
- env->fog_sun_scatter = p_sun_scatter;
- env->fog_density = p_density;
- env->fog_height = p_height;
- env->fog_height_density = p_height_density;
- env->fog_aerial_perspective = p_fog_aerial_perspective;
+ env->set_fog(p_enable, p_light_color, p_light_energy, p_sun_scatter, p_density, p_height, p_height_density, p_fog_aerial_perspective);
}
bool RendererSceneRenderRD::environment_is_fog_enabled(RID p_env) const {
- const Environment *env = environment_owner.getornull(p_env);
+ const RendererSceneEnvironmentRD *env = environment_owner.getornull(p_env);
ERR_FAIL_COND_V(!env, false);
return env->fog_enabled;
}
Color RendererSceneRenderRD::environment_get_fog_light_color(RID p_env) const {
- const Environment *env = environment_owner.getornull(p_env);
+ const RendererSceneEnvironmentRD *env = environment_owner.getornull(p_env);
ERR_FAIL_COND_V(!env, Color());
return env->fog_light_color;
}
float RendererSceneRenderRD::environment_get_fog_light_energy(RID p_env) const {
- const Environment *env = environment_owner.getornull(p_env);
+ const RendererSceneEnvironmentRD *env = environment_owner.getornull(p_env);
ERR_FAIL_COND_V(!env, 0);
return env->fog_light_energy;
}
float RendererSceneRenderRD::environment_get_fog_sun_scatter(RID p_env) const {
- const Environment *env = environment_owner.getornull(p_env);
+ const RendererSceneEnvironmentRD *env = environment_owner.getornull(p_env);
ERR_FAIL_COND_V(!env, 0);
return env->fog_sun_scatter;
}
float RendererSceneRenderRD::environment_get_fog_density(RID p_env) const {
- const Environment *env = environment_owner.getornull(p_env);
+ const RendererSceneEnvironmentRD *env = environment_owner.getornull(p_env);
ERR_FAIL_COND_V(!env, 0);
return env->fog_density;
}
float RendererSceneRenderRD::environment_get_fog_height(RID p_env) const {
- const Environment *env = environment_owner.getornull(p_env);
+ const RendererSceneEnvironmentRD *env = environment_owner.getornull(p_env);
ERR_FAIL_COND_V(!env, 0);
return env->fog_height;
}
float RendererSceneRenderRD::environment_get_fog_height_density(RID p_env) const {
- const Environment *env = environment_owner.getornull(p_env);
+ const RendererSceneEnvironmentRD *env = environment_owner.getornull(p_env);
ERR_FAIL_COND_V(!env, 0);
return env->fog_height_density;
}
float RendererSceneRenderRD::environment_get_fog_aerial_perspective(RID p_env) const {
- const Environment *env = environment_owner.getornull(p_env);
+ const RendererSceneEnvironmentRD *env = environment_owner.getornull(p_env);
ERR_FAIL_COND_V(!env, 0);
return env->fog_aerial_perspective;
}
void RendererSceneRenderRD::environment_set_volumetric_fog(RID p_env, bool p_enable, float p_density, const Color &p_light, float p_light_energy, float p_length, float p_detail_spread, float p_gi_inject, bool p_temporal_reprojection, float p_temporal_reprojection_amount) {
- Environment *env = environment_owner.getornull(p_env);
+ RendererSceneEnvironmentRD *env = environment_owner.getornull(p_env);
ERR_FAIL_COND(!env);
if (low_end) {
return;
}
- env->volumetric_fog_enabled = p_enable;
- env->volumetric_fog_density = p_density;
- env->volumetric_fog_light = p_light;
- env->volumetric_fog_light_energy = p_light_energy;
- env->volumetric_fog_length = p_length;
- env->volumetric_fog_detail_spread = p_detail_spread;
- env->volumetric_fog_gi_inject = p_gi_inject;
- env->volumetric_fog_temporal_reprojection = p_temporal_reprojection;
- env->volumetric_fog_temporal_reprojection_amount = p_temporal_reprojection_amount;
+ env->set_volumetric_fog(p_enable, p_density, p_light, p_light_energy, p_length, p_detail_spread, p_gi_inject, p_temporal_reprojection, p_temporal_reprojection_amount);
}
void RendererSceneRenderRD::environment_set_volumetric_fog_volume_size(int p_size, int p_depth) {
@@ -3194,29 +396,25 @@ void RendererSceneRenderRD::environment_set_volumetric_fog_filter_active(bool p_
}
void RendererSceneRenderRD::environment_set_sdfgi_ray_count(RS::EnvironmentSDFGIRayCount p_ray_count) {
- sdfgi_ray_count = p_ray_count;
+ gi.sdfgi_ray_count = p_ray_count;
}
void RendererSceneRenderRD::environment_set_sdfgi_frames_to_converge(RS::EnvironmentSDFGIFramesToConverge p_frames) {
- sdfgi_frames_to_converge = p_frames;
+ gi.sdfgi_frames_to_converge = p_frames;
}
void RendererSceneRenderRD::environment_set_sdfgi_frames_to_update_light(RS::EnvironmentSDFGIFramesToUpdateLight p_update) {
- sdfgi_frames_to_update_light = p_update;
+ gi.sdfgi_frames_to_update_light = p_update;
}
void RendererSceneRenderRD::environment_set_ssr(RID p_env, bool p_enable, int p_max_steps, float p_fade_int, float p_fade_out, float p_depth_tolerance) {
- Environment *env = environment_owner.getornull(p_env);
+ RendererSceneEnvironmentRD *env = environment_owner.getornull(p_env);
ERR_FAIL_COND(!env);
if (low_end) {
return;
}
- env->ssr_enabled = p_enable;
- env->ssr_max_steps = p_max_steps;
- env->ssr_fade_in = p_fade_int;
- env->ssr_fade_out = p_fade_out;
- env->ssr_depth_tolerance = p_depth_tolerance;
+ env->set_ssr(p_enable, p_max_steps, p_fade_int, p_fade_out, p_depth_tolerance);
}
void RendererSceneRenderRD::environment_set_ssr_roughness_quality(RS::EnvironmentSSRRoughnessQuality p_quality) {
@@ -3228,22 +426,14 @@ RS::EnvironmentSSRRoughnessQuality RendererSceneRenderRD::environment_get_ssr_ro
}
void RendererSceneRenderRD::environment_set_ssao(RID p_env, bool p_enable, float p_radius, float p_intensity, float p_power, float p_detail, float p_horizon, float p_sharpness, float p_light_affect, float p_ao_channel_affect) {
- Environment *env = environment_owner.getornull(p_env);
+ RendererSceneEnvironmentRD *env = environment_owner.getornull(p_env);
ERR_FAIL_COND(!env);
if (low_end) {
return;
}
- env->ssao_enabled = p_enable;
- env->ssao_radius = p_radius;
- env->ssao_intensity = p_intensity;
- env->ssao_power = p_power;
- env->ssao_detail = p_detail;
- env->ssao_horizon = p_horizon;
- env->ssao_sharpness = p_sharpness;
- env->ssao_direct_light_affect = p_light_affect;
- env->ssao_ao_channel_affect = p_ao_channel_affect;
+ env->set_ssao(p_enable, p_radius, p_intensity, p_power, p_detail, p_horizon, p_sharpness, p_light_affect, p_ao_channel_affect);
}
void RendererSceneRenderRD::environment_set_ssao_quality(RS::EnvironmentSSAOQuality p_quality, bool p_half_size, float p_adaptive_target, int p_blur_passes, float p_fadeout_from, float p_fadeout_to) {
@@ -3256,30 +446,30 @@ void RendererSceneRenderRD::environment_set_ssao_quality(RS::EnvironmentSSAOQual
}
bool RendererSceneRenderRD::environment_is_ssao_enabled(RID p_env) const {
- Environment *env = environment_owner.getornull(p_env);
+ RendererSceneEnvironmentRD *env = environment_owner.getornull(p_env);
ERR_FAIL_COND_V(!env, false);
return env->ssao_enabled;
}
float RendererSceneRenderRD::environment_get_ssao_ao_affect(RID p_env) const {
- Environment *env = environment_owner.getornull(p_env);
+ RendererSceneEnvironmentRD *env = environment_owner.getornull(p_env);
ERR_FAIL_COND_V(!env, 0.0);
return env->ssao_ao_channel_affect;
}
float RendererSceneRenderRD::environment_get_ssao_light_affect(RID p_env) const {
- Environment *env = environment_owner.getornull(p_env);
+ RendererSceneEnvironmentRD *env = environment_owner.getornull(p_env);
ERR_FAIL_COND_V(!env, 0.0);
return env->ssao_direct_light_affect;
}
bool RendererSceneRenderRD::environment_is_ssr_enabled(RID p_env) const {
- Environment *env = environment_owner.getornull(p_env);
+ RendererSceneEnvironmentRD *env = environment_owner.getornull(p_env);
ERR_FAIL_COND_V(!env, false);
return env->ssr_enabled;
}
bool RendererSceneRenderRD::environment_is_sdfgi_enabled(RID p_env) const {
- Environment *env = environment_owner.getornull(p_env);
+ RendererSceneEnvironmentRD *env = environment_owner.getornull(p_env);
ERR_FAIL_COND_V(!env, false);
return env->sdfgi_enabled;
}
@@ -3289,7 +479,7 @@ bool RendererSceneRenderRD::is_environment(RID p_env) const {
}
Ref<Image> RendererSceneRenderRD::environment_bake_panorama(RID p_env, bool p_bake_irradiance, const Size2i &p_size) {
- Environment *env = environment_owner.getornull(p_env);
+ RendererSceneEnvironmentRD *env = environment_owner.getornull(p_env);
ERR_FAIL_COND_V(!env, Ref<Image>());
if (env->background == RS::ENV_BG_CAMERA_FEED || env->background == RS::ENV_BG_CANVAS || env->background == RS::ENV_BG_KEEP) {
@@ -3359,7 +549,7 @@ void RendererSceneRenderRD::reflection_atlas_set_size(RID p_ref_atlas, int p_ref
RD::get_singleton()->free(ra->depth_buffer);
ra->depth_buffer = RID();
for (int i = 0; i < ra->reflections.size(); i++) {
- _clear_reflection_data(ra->reflections.write[i].data);
+ ra->reflections.write[i].data.clear_reflection_data();
if (ra->reflections[i].owner.is_null()) {
continue;
}
@@ -3463,7 +653,7 @@ bool RendererSceneRenderRD::reflection_probe_instance_begin_render(RID p_instanc
}
if (atlas->reflection.is_null()) {
- int mipmaps = MIN(roughness_layers, Image::get_image_required_mipmaps(atlas->size, atlas->size, Image::FORMAT_RGBAH) + 1);
+ int mipmaps = MIN(sky.roughness_layers, Image::get_image_required_mipmaps(atlas->size, atlas->size, Image::FORMAT_RGBAH) + 1);
mipmaps = storage->reflection_probe_get_update_mode(rpi->probe) == RS::REFLECTION_PROBE_UPDATE_ALWAYS ? 8 : mipmaps; // always use 8 mipmaps with real time filtering
{
//reflection atlas was unused, create:
@@ -3488,7 +678,7 @@ bool RendererSceneRenderRD::reflection_probe_instance_begin_render(RID p_instanc
}
atlas->reflections.resize(atlas->count);
for (int i = 0; i < atlas->count; i++) {
- _update_reflection_data(atlas->reflections.write[i].data, atlas->size, mipmaps, false, atlas->reflection, i * 6, storage->reflection_probe_get_update_mode(rpi->probe) == RS::REFLECTION_PROBE_UPDATE_ALWAYS);
+ atlas->reflections.write[i].data.update_reflection_data(atlas->size, mipmaps, false, atlas->reflection, i * 6, storage->reflection_probe_get_update_mode(rpi->probe) == RS::REFLECTION_PROBE_UPDATE_ALWAYS, sky.roughness_layers);
for (int j = 0; j < 6; j++) {
Vector<RID> fb;
fb.push_back(atlas->reflections.write[i].data.layers[0].mipmaps[0].views[j]);
@@ -3548,7 +738,7 @@ bool RendererSceneRenderRD::reflection_probe_instance_postprocess_step(RID p_ins
if (storage->reflection_probe_get_update_mode(rpi->probe) == RS::REFLECTION_PROBE_UPDATE_ALWAYS) {
// Using real time reflections, all roughness is done in one step
- _create_reflection_fast_filter(atlas->reflections.write[rpi->atlas_index].data, false);
+ atlas->reflections.write[rpi->atlas_index].data.create_reflection_fast_filter(storage, false);
rpi->rendering = false;
rpi->processing_side = 0;
rpi->processing_layer = 1;
@@ -3556,7 +746,7 @@ bool RendererSceneRenderRD::reflection_probe_instance_postprocess_step(RID p_ins
}
if (rpi->processing_layer > 1) {
- _create_reflection_importance_sample(atlas->reflections.write[rpi->atlas_index].data, false, 10, rpi->processing_layer);
+ atlas->reflections.write[rpi->atlas_index].data.create_reflection_importance_sample(storage, false, 10, rpi->processing_layer, sky.sky_ggx_samples_quality);
rpi->processing_layer++;
if (rpi->processing_layer == atlas->reflections[rpi->atlas_index].data.layers[0].mipmaps.size()) {
rpi->rendering = false;
@@ -3567,7 +757,7 @@ bool RendererSceneRenderRD::reflection_probe_instance_postprocess_step(RID p_ins
return false;
} else {
- _create_reflection_importance_sample(atlas->reflections.write[rpi->atlas_index].data, false, rpi->processing_side, rpi->processing_layer);
+ atlas->reflections.write[rpi->atlas_index].data.create_reflection_importance_sample(storage, false, rpi->processing_side, rpi->processing_layer, sky.sky_ggx_samples_quality);
}
rpi->processing_side++;
@@ -4142,21 +1332,18 @@ void RendererSceneRenderRD::lightmap_instance_set_transform(RID p_lightmap, cons
/////////////////////////////////
RID RendererSceneRenderRD::gi_probe_instance_create(RID p_base) {
- GIProbeInstance gi_probe;
- gi_probe.probe = p_base;
- RID rid = gi_probe_instance_owner.make_rid(gi_probe);
- return rid;
+ return gi.gi_probe_instance_create(p_base);
}
void RendererSceneRenderRD::gi_probe_instance_set_transform_to_data(RID p_probe, const Transform &p_xform) {
- GIProbeInstance *gi_probe = gi_probe_instance_owner.getornull(p_probe);
+ RendererSceneGIRD::GIProbeInstance *gi_probe = gi.gi_probe_instance_owner.getornull(p_probe);
ERR_FAIL_COND(!gi_probe);
gi_probe->transform = p_xform;
}
bool RendererSceneRenderRD::gi_probe_needs_update(RID p_probe) const {
- GIProbeInstance *gi_probe = gi_probe_instance_owner.getornull(p_probe);
+ RendererSceneGIRD::GIProbeInstance *gi_probe = gi.gi_probe_instance_owner.getornull(p_probe);
ERR_FAIL_COND_V(!gi_probe, false);
if (low_end) {
@@ -4168,791 +1355,14 @@ bool RendererSceneRenderRD::gi_probe_needs_update(RID p_probe) const {
}
void RendererSceneRenderRD::gi_probe_update(RID p_probe, bool p_update_light_instances, const Vector<RID> &p_light_instances, const PagedArray<GeometryInstance *> &p_dynamic_objects) {
- GIProbeInstance *gi_probe = gi_probe_instance_owner.getornull(p_probe);
+ RendererSceneGIRD::GIProbeInstance *gi_probe = gi.gi_probe_instance_owner.getornull(p_probe);
ERR_FAIL_COND(!gi_probe);
if (low_end) {
return;
}
- uint32_t data_version = storage->gi_probe_get_data_version(gi_probe->probe);
-
- // (RE)CREATE IF NEEDED
-
- if (gi_probe->last_probe_data_version != data_version) {
- //need to re-create everything
- if (gi_probe->texture.is_valid()) {
- RD::get_singleton()->free(gi_probe->texture);
- RD::get_singleton()->free(gi_probe->write_buffer);
- gi_probe->mipmaps.clear();
- }
-
- for (int i = 0; i < gi_probe->dynamic_maps.size(); i++) {
- RD::get_singleton()->free(gi_probe->dynamic_maps[i].texture);
- RD::get_singleton()->free(gi_probe->dynamic_maps[i].depth);
- }
-
- gi_probe->dynamic_maps.clear();
-
- Vector3i octree_size = storage->gi_probe_get_octree_size(gi_probe->probe);
-
- if (octree_size != Vector3i()) {
- //can create a 3D texture
- Vector<int> levels = storage->gi_probe_get_level_counts(gi_probe->probe);
-
- RD::TextureFormat tf;
- tf.format = RD::DATA_FORMAT_R8G8B8A8_UNORM;
- tf.width = octree_size.x;
- tf.height = octree_size.y;
- tf.depth = octree_size.z;
- tf.texture_type = RD::TEXTURE_TYPE_3D;
- tf.mipmaps = levels.size();
-
- tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_CAN_COPY_TO_BIT;
-
- gi_probe->texture = RD::get_singleton()->texture_create(tf, RD::TextureView());
-
- RD::get_singleton()->texture_clear(gi_probe->texture, Color(0, 0, 0, 0), 0, levels.size(), 0, 1);
-
- {
- int total_elements = 0;
- for (int i = 0; i < levels.size(); i++) {
- total_elements += levels[i];
- }
-
- gi_probe->write_buffer = RD::get_singleton()->storage_buffer_create(total_elements * 16);
- }
-
- for (int i = 0; i < levels.size(); i++) {
- GIProbeInstance::Mipmap mipmap;
- mipmap.texture = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), gi_probe->texture, 0, i, RD::TEXTURE_SLICE_3D);
- mipmap.level = levels.size() - i - 1;
- mipmap.cell_offset = 0;
- for (uint32_t j = 0; j < mipmap.level; j++) {
- mipmap.cell_offset += levels[j];
- }
- mipmap.cell_count = levels[mipmap.level];
-
- Vector<RD::Uniform> uniforms;
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
- u.binding = 1;
- u.ids.push_back(storage->gi_probe_get_octree_buffer(gi_probe->probe));
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
- u.binding = 2;
- u.ids.push_back(storage->gi_probe_get_data_buffer(gi_probe->probe));
- uniforms.push_back(u);
- }
-
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
- u.binding = 4;
- u.ids.push_back(gi_probe->write_buffer);
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
- u.binding = 9;
- u.ids.push_back(storage->gi_probe_get_sdf_texture(gi_probe->probe));
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_SAMPLER;
- u.binding = 10;
- u.ids.push_back(storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED));
- uniforms.push_back(u);
- }
-
- {
- Vector<RD::Uniform> copy_uniforms = uniforms;
- if (i == 0) {
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER;
- u.binding = 3;
- u.ids.push_back(gi_probe_lights_uniform);
- copy_uniforms.push_back(u);
- }
-
- mipmap.uniform_set = RD::get_singleton()->uniform_set_create(copy_uniforms, giprobe_lighting_shader_version_shaders[GI_PROBE_SHADER_VERSION_COMPUTE_LIGHT], 0);
-
- copy_uniforms = uniforms; //restore
-
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
- u.binding = 5;
- u.ids.push_back(gi_probe->texture);
- copy_uniforms.push_back(u);
- }
- mipmap.second_bounce_uniform_set = RD::get_singleton()->uniform_set_create(copy_uniforms, giprobe_lighting_shader_version_shaders[GI_PROBE_SHADER_VERSION_COMPUTE_SECOND_BOUNCE], 0);
- } else {
- mipmap.uniform_set = RD::get_singleton()->uniform_set_create(copy_uniforms, giprobe_lighting_shader_version_shaders[GI_PROBE_SHADER_VERSION_COMPUTE_MIPMAP], 0);
- }
- }
-
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
- u.binding = 5;
- u.ids.push_back(mipmap.texture);
- uniforms.push_back(u);
- }
-
- mipmap.write_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, giprobe_lighting_shader_version_shaders[GI_PROBE_SHADER_VERSION_WRITE_TEXTURE], 0);
-
- gi_probe->mipmaps.push_back(mipmap);
- }
-
- {
- uint32_t dynamic_map_size = MAX(MAX(octree_size.x, octree_size.y), octree_size.z);
- uint32_t oversample = nearest_power_of_2_templated(4);
- int mipmap_index = 0;
-
- while (mipmap_index < gi_probe->mipmaps.size()) {
- GIProbeInstance::DynamicMap dmap;
-
- if (oversample > 0) {
- dmap.size = dynamic_map_size * (1 << oversample);
- dmap.mipmap = -1;
- oversample--;
- } else {
- dmap.size = dynamic_map_size >> mipmap_index;
- dmap.mipmap = mipmap_index;
- mipmap_index++;
- }
-
- RD::TextureFormat dtf;
- dtf.width = dmap.size;
- dtf.height = dmap.size;
- dtf.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT;
- dtf.usage_bits = RD::TEXTURE_USAGE_STORAGE_BIT;
-
- if (gi_probe->dynamic_maps.size() == 0) {
- dtf.usage_bits |= RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
- }
- dmap.texture = RD::get_singleton()->texture_create(dtf, RD::TextureView());
-
- if (gi_probe->dynamic_maps.size() == 0) {
- //render depth for first one
- dtf.format = RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_D32_SFLOAT, RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) ? RD::DATA_FORMAT_D32_SFLOAT : RD::DATA_FORMAT_X8_D24_UNORM_PACK32;
- dtf.usage_bits = RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
- dmap.fb_depth = RD::get_singleton()->texture_create(dtf, RD::TextureView());
- }
-
- //just use depth as-is
- dtf.format = RD::DATA_FORMAT_R32_SFLOAT;
- dtf.usage_bits = RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
-
- dmap.depth = RD::get_singleton()->texture_create(dtf, RD::TextureView());
-
- if (gi_probe->dynamic_maps.size() == 0) {
- dtf.format = RD::DATA_FORMAT_R8G8B8A8_UNORM;
- dtf.usage_bits = RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
- dmap.albedo = RD::get_singleton()->texture_create(dtf, RD::TextureView());
- dmap.normal = RD::get_singleton()->texture_create(dtf, RD::TextureView());
- dmap.orm = RD::get_singleton()->texture_create(dtf, RD::TextureView());
-
- Vector<RID> fb;
- fb.push_back(dmap.albedo);
- fb.push_back(dmap.normal);
- fb.push_back(dmap.orm);
- fb.push_back(dmap.texture); //emission
- fb.push_back(dmap.depth);
- fb.push_back(dmap.fb_depth);
-
- dmap.fb = RD::get_singleton()->framebuffer_create(fb);
-
- {
- Vector<RD::Uniform> uniforms;
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER;
- u.binding = 3;
- u.ids.push_back(gi_probe_lights_uniform);
- uniforms.push_back(u);
- }
-
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
- u.binding = 5;
- u.ids.push_back(dmap.albedo);
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
- u.binding = 6;
- u.ids.push_back(dmap.normal);
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
- u.binding = 7;
- u.ids.push_back(dmap.orm);
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
- u.binding = 8;
- u.ids.push_back(dmap.fb_depth);
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
- u.binding = 9;
- u.ids.push_back(storage->gi_probe_get_sdf_texture(gi_probe->probe));
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_SAMPLER;
- u.binding = 10;
- u.ids.push_back(storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED));
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
- u.binding = 11;
- u.ids.push_back(dmap.texture);
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
- u.binding = 12;
- u.ids.push_back(dmap.depth);
- uniforms.push_back(u);
- }
-
- dmap.uniform_set = RD::get_singleton()->uniform_set_create(uniforms, giprobe_lighting_shader_version_shaders[GI_PROBE_SHADER_VERSION_DYNAMIC_OBJECT_LIGHTING], 0);
- }
- } else {
- bool plot = dmap.mipmap >= 0;
- bool write = dmap.mipmap < (gi_probe->mipmaps.size() - 1);
-
- Vector<RD::Uniform> uniforms;
-
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
- u.binding = 5;
- u.ids.push_back(gi_probe->dynamic_maps[gi_probe->dynamic_maps.size() - 1].texture);
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
- u.binding = 6;
- u.ids.push_back(gi_probe->dynamic_maps[gi_probe->dynamic_maps.size() - 1].depth);
- uniforms.push_back(u);
- }
-
- if (write) {
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
- u.binding = 7;
- u.ids.push_back(dmap.texture);
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
- u.binding = 8;
- u.ids.push_back(dmap.depth);
- uniforms.push_back(u);
- }
- }
-
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
- u.binding = 9;
- u.ids.push_back(storage->gi_probe_get_sdf_texture(gi_probe->probe));
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_SAMPLER;
- u.binding = 10;
- u.ids.push_back(storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED));
- uniforms.push_back(u);
- }
-
- if (plot) {
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
- u.binding = 11;
- u.ids.push_back(gi_probe->mipmaps[dmap.mipmap].texture);
- uniforms.push_back(u);
- }
- }
-
- dmap.uniform_set = RD::get_singleton()->uniform_set_create(
- uniforms,
- giprobe_lighting_shader_version_shaders[(write && plot) ? GI_PROBE_SHADER_VERSION_DYNAMIC_SHRINK_WRITE_PLOT : (write ? GI_PROBE_SHADER_VERSION_DYNAMIC_SHRINK_WRITE : GI_PROBE_SHADER_VERSION_DYNAMIC_SHRINK_PLOT)],
- 0);
- }
-
- gi_probe->dynamic_maps.push_back(dmap);
- }
- }
- }
-
- gi_probe->last_probe_data_version = data_version;
- p_update_light_instances = true; //just in case
-
- _base_uniforms_changed();
- }
-
- // UDPDATE TIME
-
- if (gi_probe->has_dynamic_object_data) {
- //if it has dynamic object data, it needs to be cleared
- RD::get_singleton()->texture_clear(gi_probe->texture, Color(0, 0, 0, 0), 0, gi_probe->mipmaps.size(), 0, 1);
- }
-
- uint32_t light_count = 0;
-
- if (p_update_light_instances || p_dynamic_objects.size() > 0) {
- light_count = MIN(gi_probe_max_lights, (uint32_t)p_light_instances.size());
-
- {
- Transform to_cell = storage->gi_probe_get_to_cell_xform(gi_probe->probe);
- Transform to_probe_xform = (gi_probe->transform * to_cell.affine_inverse()).affine_inverse();
- //update lights
-
- for (uint32_t i = 0; i < light_count; i++) {
- GIProbeLight &l = gi_probe_lights[i];
- RID light_instance = p_light_instances[i];
- RID light = light_instance_get_base_light(light_instance);
-
- l.type = storage->light_get_type(light);
- if (l.type == RS::LIGHT_DIRECTIONAL && storage->light_directional_is_sky_only(light)) {
- light_count--;
- continue;
- }
-
- l.attenuation = storage->light_get_param(light, RS::LIGHT_PARAM_ATTENUATION);
- l.energy = storage->light_get_param(light, RS::LIGHT_PARAM_ENERGY) * storage->light_get_param(light, RS::LIGHT_PARAM_INDIRECT_ENERGY);
- l.radius = to_cell.basis.xform(Vector3(storage->light_get_param(light, RS::LIGHT_PARAM_RANGE), 0, 0)).length();
- Color color = storage->light_get_color(light).to_linear();
- l.color[0] = color.r;
- l.color[1] = color.g;
- l.color[2] = color.b;
-
- l.cos_spot_angle = Math::cos(Math::deg2rad(storage->light_get_param(light, RS::LIGHT_PARAM_SPOT_ANGLE)));
- l.inv_spot_attenuation = 1.0f / storage->light_get_param(light, RS::LIGHT_PARAM_SPOT_ATTENUATION);
-
- Transform xform = light_instance_get_base_transform(light_instance);
-
- Vector3 pos = to_probe_xform.xform(xform.origin);
- Vector3 dir = to_probe_xform.basis.xform(-xform.basis.get_axis(2)).normalized();
-
- l.position[0] = pos.x;
- l.position[1] = pos.y;
- l.position[2] = pos.z;
-
- l.direction[0] = dir.x;
- l.direction[1] = dir.y;
- l.direction[2] = dir.z;
-
- l.has_shadow = storage->light_has_shadow(light);
- }
-
- RD::get_singleton()->buffer_update(gi_probe_lights_uniform, 0, sizeof(GIProbeLight) * light_count, gi_probe_lights);
- }
- }
-
- if (gi_probe->has_dynamic_object_data || p_update_light_instances || p_dynamic_objects.size()) {
- // PROCESS MIPMAPS
- if (gi_probe->mipmaps.size()) {
- //can update mipmaps
-
- Vector3i probe_size = storage->gi_probe_get_octree_size(gi_probe->probe);
-
- GIProbePushConstant push_constant;
-
- push_constant.limits[0] = probe_size.x;
- push_constant.limits[1] = probe_size.y;
- push_constant.limits[2] = probe_size.z;
- push_constant.stack_size = gi_probe->mipmaps.size();
- push_constant.emission_scale = 1.0;
- push_constant.propagation = storage->gi_probe_get_propagation(gi_probe->probe);
- push_constant.dynamic_range = storage->gi_probe_get_dynamic_range(gi_probe->probe);
- push_constant.light_count = light_count;
- push_constant.aniso_strength = 0;
-
- /* print_line("probe update to version " + itos(gi_probe->last_probe_version));
- print_line("propagation " + rtos(push_constant.propagation));
- print_line("dynrange " + rtos(push_constant.dynamic_range));
- */
- RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
-
- int passes;
- if (p_update_light_instances) {
- passes = storage->gi_probe_is_using_two_bounces(gi_probe->probe) ? 2 : 1;
- } else {
- passes = 1; //only re-blitting is necessary
- }
- int wg_size = 64;
- int wg_limit_x = RD::get_singleton()->limit_get(RD::LIMIT_MAX_COMPUTE_WORKGROUP_COUNT_X);
-
- for (int pass = 0; pass < passes; pass++) {
- if (p_update_light_instances) {
- for (int i = 0; i < gi_probe->mipmaps.size(); i++) {
- if (i == 0) {
- RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, giprobe_lighting_shader_version_pipelines[pass == 0 ? GI_PROBE_SHADER_VERSION_COMPUTE_LIGHT : GI_PROBE_SHADER_VERSION_COMPUTE_SECOND_BOUNCE]);
- } else if (i == 1) {
- RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, giprobe_lighting_shader_version_pipelines[GI_PROBE_SHADER_VERSION_COMPUTE_MIPMAP]);
- }
-
- if (pass == 1 || i > 0) {
- RD::get_singleton()->compute_list_add_barrier(compute_list); //wait til previous step is done
- }
- if (pass == 0 || i > 0) {
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, gi_probe->mipmaps[i].uniform_set, 0);
- } else {
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, gi_probe->mipmaps[i].second_bounce_uniform_set, 0);
- }
-
- push_constant.cell_offset = gi_probe->mipmaps[i].cell_offset;
- push_constant.cell_count = gi_probe->mipmaps[i].cell_count;
-
- int wg_todo = (gi_probe->mipmaps[i].cell_count - 1) / wg_size + 1;
- while (wg_todo) {
- int wg_count = MIN(wg_todo, wg_limit_x);
- RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(GIProbePushConstant));
- RD::get_singleton()->compute_list_dispatch(compute_list, wg_count, 1, 1);
- wg_todo -= wg_count;
- push_constant.cell_offset += wg_count * wg_size;
- }
- }
-
- RD::get_singleton()->compute_list_add_barrier(compute_list); //wait til previous step is done
- }
-
- RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, giprobe_lighting_shader_version_pipelines[GI_PROBE_SHADER_VERSION_WRITE_TEXTURE]);
-
- for (int i = 0; i < gi_probe->mipmaps.size(); i++) {
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, gi_probe->mipmaps[i].write_uniform_set, 0);
-
- push_constant.cell_offset = gi_probe->mipmaps[i].cell_offset;
- push_constant.cell_count = gi_probe->mipmaps[i].cell_count;
-
- int wg_todo = (gi_probe->mipmaps[i].cell_count - 1) / wg_size + 1;
- while (wg_todo) {
- int wg_count = MIN(wg_todo, wg_limit_x);
- RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(GIProbePushConstant));
- RD::get_singleton()->compute_list_dispatch(compute_list, wg_count, 1, 1);
- wg_todo -= wg_count;
- push_constant.cell_offset += wg_count * wg_size;
- }
- }
- }
-
- RD::get_singleton()->compute_list_end();
- }
- }
-
- gi_probe->has_dynamic_object_data = false; //clear until dynamic object data is used again
-
- if (p_dynamic_objects.size() && gi_probe->dynamic_maps.size()) {
- Vector3i octree_size = storage->gi_probe_get_octree_size(gi_probe->probe);
- int multiplier = gi_probe->dynamic_maps[0].size / MAX(MAX(octree_size.x, octree_size.y), octree_size.z);
-
- Transform oversample_scale;
- oversample_scale.basis.scale(Vector3(multiplier, multiplier, multiplier));
-
- Transform to_cell = oversample_scale * storage->gi_probe_get_to_cell_xform(gi_probe->probe);
- Transform to_world_xform = gi_probe->transform * to_cell.affine_inverse();
- Transform to_probe_xform = to_world_xform.affine_inverse();
-
- AABB probe_aabb(Vector3(), octree_size);
-
- //this could probably be better parallelized in compute..
- for (int i = 0; i < (int)p_dynamic_objects.size(); i++) {
- GeometryInstance *instance = p_dynamic_objects[i];
-
- //transform aabb to giprobe
- AABB aabb = (to_probe_xform * geometry_instance_get_transform(instance)).xform(geometry_instance_get_aabb(instance));
-
- //this needs to wrap to grid resolution to avoid jitter
- //also extend margin a bit just in case
- Vector3i begin = aabb.position - Vector3i(1, 1, 1);
- Vector3i end = aabb.position + aabb.size + Vector3i(1, 1, 1);
-
- for (int j = 0; j < 3; j++) {
- if ((end[j] - begin[j]) & 1) {
- end[j]++; //for half extents split, it needs to be even
- }
- begin[j] = MAX(begin[j], 0);
- end[j] = MIN(end[j], octree_size[j] * multiplier);
- }
-
- //aabb = aabb.intersection(probe_aabb); //intersect
- aabb.position = begin;
- aabb.size = end - begin;
-
- //print_line("aabb: " + aabb);
-
- for (int j = 0; j < 6; j++) {
- //if (j != 0 && j != 3) {
- // continue;
- //}
- static const Vector3 render_z[6] = {
- Vector3(1, 0, 0),
- Vector3(0, 1, 0),
- Vector3(0, 0, 1),
- Vector3(-1, 0, 0),
- Vector3(0, -1, 0),
- Vector3(0, 0, -1),
- };
- static const Vector3 render_up[6] = {
- Vector3(0, 1, 0),
- Vector3(0, 0, 1),
- Vector3(0, 1, 0),
- Vector3(0, 1, 0),
- Vector3(0, 0, 1),
- Vector3(0, 1, 0),
- };
-
- Vector3 render_dir = render_z[j];
- Vector3 up_dir = render_up[j];
-
- Vector3 center = aabb.position + aabb.size * 0.5;
- Transform xform;
- xform.set_look_at(center - aabb.size * 0.5 * render_dir, center, up_dir);
-
- Vector3 x_dir = xform.basis.get_axis(0).abs();
- int x_axis = int(Vector3(0, 1, 2).dot(x_dir));
- Vector3 y_dir = xform.basis.get_axis(1).abs();
- int y_axis = int(Vector3(0, 1, 2).dot(y_dir));
- Vector3 z_dir = -xform.basis.get_axis(2);
- int z_axis = int(Vector3(0, 1, 2).dot(z_dir.abs()));
-
- Rect2i rect(aabb.position[x_axis], aabb.position[y_axis], aabb.size[x_axis], aabb.size[y_axis]);
- bool x_flip = bool(Vector3(1, 1, 1).dot(xform.basis.get_axis(0)) < 0);
- bool y_flip = bool(Vector3(1, 1, 1).dot(xform.basis.get_axis(1)) < 0);
- bool z_flip = bool(Vector3(1, 1, 1).dot(xform.basis.get_axis(2)) > 0);
-
- CameraMatrix cm;
- cm.set_orthogonal(-rect.size.width / 2, rect.size.width / 2, -rect.size.height / 2, rect.size.height / 2, 0.0001, aabb.size[z_axis]);
-
- if (cull_argument.size() == 0) {
- cull_argument.push_back(nullptr);
- }
- cull_argument[0] = instance;
-
- _render_material(to_world_xform * xform, cm, true, cull_argument, gi_probe->dynamic_maps[0].fb, Rect2i(Vector2i(), rect.size));
-
- GIProbeDynamicPushConstant push_constant;
- zeromem(&push_constant, sizeof(GIProbeDynamicPushConstant));
- push_constant.limits[0] = octree_size.x;
- push_constant.limits[1] = octree_size.y;
- push_constant.limits[2] = octree_size.z;
- push_constant.light_count = p_light_instances.size();
- push_constant.x_dir[0] = x_dir[0];
- push_constant.x_dir[1] = x_dir[1];
- push_constant.x_dir[2] = x_dir[2];
- push_constant.y_dir[0] = y_dir[0];
- push_constant.y_dir[1] = y_dir[1];
- push_constant.y_dir[2] = y_dir[2];
- push_constant.z_dir[0] = z_dir[0];
- push_constant.z_dir[1] = z_dir[1];
- push_constant.z_dir[2] = z_dir[2];
- push_constant.z_base = xform.origin[z_axis];
- push_constant.z_sign = (z_flip ? -1.0 : 1.0);
- push_constant.pos_multiplier = float(1.0) / multiplier;
- push_constant.dynamic_range = storage->gi_probe_get_dynamic_range(gi_probe->probe);
- push_constant.flip_x = x_flip;
- push_constant.flip_y = y_flip;
- push_constant.rect_pos[0] = rect.position[0];
- push_constant.rect_pos[1] = rect.position[1];
- push_constant.rect_size[0] = rect.size[0];
- push_constant.rect_size[1] = rect.size[1];
- push_constant.prev_rect_ofs[0] = 0;
- push_constant.prev_rect_ofs[1] = 0;
- push_constant.prev_rect_size[0] = 0;
- push_constant.prev_rect_size[1] = 0;
- push_constant.on_mipmap = false;
- push_constant.propagation = storage->gi_probe_get_propagation(gi_probe->probe);
- push_constant.pad[0] = 0;
- push_constant.pad[1] = 0;
- push_constant.pad[2] = 0;
-
- //process lighting
- RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
- RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, giprobe_lighting_shader_version_pipelines[GI_PROBE_SHADER_VERSION_DYNAMIC_OBJECT_LIGHTING]);
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, gi_probe->dynamic_maps[0].uniform_set, 0);
- RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(GIProbeDynamicPushConstant));
- RD::get_singleton()->compute_list_dispatch(compute_list, (rect.size.x - 1) / 8 + 1, (rect.size.y - 1) / 8 + 1, 1);
- //print_line("rect: " + itos(i) + ": " + rect);
-
- for (int k = 1; k < gi_probe->dynamic_maps.size(); k++) {
- // enlarge the rect if needed so all pixels fit when downscaled,
- // this ensures downsampling is smooth and optimal because no pixels are left behind
-
- //x
- if (rect.position.x & 1) {
- rect.size.x++;
- push_constant.prev_rect_ofs[0] = 1; //this is used to ensure reading is also optimal
- } else {
- push_constant.prev_rect_ofs[0] = 0;
- }
- if (rect.size.x & 1) {
- rect.size.x++;
- }
-
- rect.position.x >>= 1;
- rect.size.x = MAX(1, rect.size.x >> 1);
-
- //y
- if (rect.position.y & 1) {
- rect.size.y++;
- push_constant.prev_rect_ofs[1] = 1;
- } else {
- push_constant.prev_rect_ofs[1] = 0;
- }
- if (rect.size.y & 1) {
- rect.size.y++;
- }
-
- rect.position.y >>= 1;
- rect.size.y = MAX(1, rect.size.y >> 1);
-
- //shrink limits to ensure plot does not go outside map
- if (gi_probe->dynamic_maps[k].mipmap > 0) {
- for (int l = 0; l < 3; l++) {
- push_constant.limits[l] = MAX(1, push_constant.limits[l] >> 1);
- }
- }
-
- //print_line("rect: " + itos(i) + ": " + rect);
- push_constant.rect_pos[0] = rect.position[0];
- push_constant.rect_pos[1] = rect.position[1];
- push_constant.prev_rect_size[0] = push_constant.rect_size[0];
- push_constant.prev_rect_size[1] = push_constant.rect_size[1];
- push_constant.rect_size[0] = rect.size[0];
- push_constant.rect_size[1] = rect.size[1];
- push_constant.on_mipmap = gi_probe->dynamic_maps[k].mipmap > 0;
-
- RD::get_singleton()->compute_list_add_barrier(compute_list);
-
- if (gi_probe->dynamic_maps[k].mipmap < 0) {
- RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, giprobe_lighting_shader_version_pipelines[GI_PROBE_SHADER_VERSION_DYNAMIC_SHRINK_WRITE]);
- } else if (k < gi_probe->dynamic_maps.size() - 1) {
- RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, giprobe_lighting_shader_version_pipelines[GI_PROBE_SHADER_VERSION_DYNAMIC_SHRINK_WRITE_PLOT]);
- } else {
- RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, giprobe_lighting_shader_version_pipelines[GI_PROBE_SHADER_VERSION_DYNAMIC_SHRINK_PLOT]);
- }
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, gi_probe->dynamic_maps[k].uniform_set, 0);
- RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(GIProbeDynamicPushConstant));
- RD::get_singleton()->compute_list_dispatch(compute_list, (rect.size.x - 1) / 8 + 1, (rect.size.y - 1) / 8 + 1, 1);
- }
-
- RD::get_singleton()->compute_list_end();
- }
- }
-
- gi_probe->has_dynamic_object_data = true; //clear until dynamic object data is used again
- }
-
- gi_probe->last_probe_version = storage->gi_probe_get_version(gi_probe->probe);
-}
-
-void RendererSceneRenderRD::_debug_giprobe(RID p_gi_probe, RD::DrawListID p_draw_list, RID p_framebuffer, const CameraMatrix &p_camera_with_transform, bool p_lighting, bool p_emission, float p_alpha) {
- GIProbeInstance *gi_probe = gi_probe_instance_owner.getornull(p_gi_probe);
- ERR_FAIL_COND(!gi_probe);
-
- if (gi_probe->mipmaps.size() == 0) {
- return;
- }
-
- CameraMatrix transform = (p_camera_with_transform * CameraMatrix(gi_probe->transform)) * CameraMatrix(storage->gi_probe_get_to_cell_xform(gi_probe->probe).affine_inverse());
-
- int level = 0;
- Vector3i octree_size = storage->gi_probe_get_octree_size(gi_probe->probe);
-
- GIProbeDebugPushConstant push_constant;
- push_constant.alpha = p_alpha;
- push_constant.dynamic_range = storage->gi_probe_get_dynamic_range(gi_probe->probe);
- push_constant.cell_offset = gi_probe->mipmaps[level].cell_offset;
- push_constant.level = level;
-
- push_constant.bounds[0] = octree_size.x >> level;
- push_constant.bounds[1] = octree_size.y >> level;
- push_constant.bounds[2] = octree_size.z >> level;
- push_constant.pad = 0;
-
- for (int i = 0; i < 4; i++) {
- for (int j = 0; j < 4; j++) {
- push_constant.projection[i * 4 + j] = transform.matrix[i][j];
- }
- }
-
- if (giprobe_debug_uniform_set.is_valid()) {
- RD::get_singleton()->free(giprobe_debug_uniform_set);
- }
- Vector<RD::Uniform> uniforms;
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
- u.binding = 1;
- u.ids.push_back(storage->gi_probe_get_data_buffer(gi_probe->probe));
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
- u.binding = 2;
- u.ids.push_back(gi_probe->texture);
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_SAMPLER;
- u.binding = 3;
- u.ids.push_back(storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED));
- uniforms.push_back(u);
- }
-
- int cell_count;
- if (!p_emission && p_lighting && gi_probe->has_dynamic_object_data) {
- cell_count = push_constant.bounds[0] * push_constant.bounds[1] * push_constant.bounds[2];
- } else {
- cell_count = gi_probe->mipmaps[level].cell_count;
- }
-
- giprobe_debug_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, giprobe_debug_shader_version_shaders[0], 0);
-
- int giprobe_debug_pipeline = GI_PROBE_DEBUG_COLOR;
- if (p_emission) {
- giprobe_debug_pipeline = GI_PROBE_DEBUG_EMISSION;
- } else if (p_lighting) {
- giprobe_debug_pipeline = gi_probe->has_dynamic_object_data ? GI_PROBE_DEBUG_LIGHT_FULL : GI_PROBE_DEBUG_LIGHT;
- }
- RD::get_singleton()->draw_list_bind_render_pipeline(
- p_draw_list,
- giprobe_debug_shader_version_pipelines[giprobe_debug_pipeline].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_framebuffer)));
- RD::get_singleton()->draw_list_bind_uniform_set(p_draw_list, giprobe_debug_uniform_set, 0);
- RD::get_singleton()->draw_list_set_push_constant(p_draw_list, &push_constant, sizeof(GIProbeDebugPushConstant));
- RD::get_singleton()->draw_list_draw(p_draw_list, false, cell_count, 36);
+ gi_probe->update(p_update_light_instances, p_light_instances, p_dynamic_objects, this);
}
void RendererSceneRenderRD::_debug_sdfgi_probes(RID p_render_buffers, RD::DrawListID p_draw_list, RID p_framebuffer, const CameraMatrix &p_camera_with_transform) {
@@ -4963,132 +1373,7 @@ void RendererSceneRenderRD::_debug_sdfgi_probes(RID p_render_buffers, RD::DrawLi
return; //nothing to debug
}
- SDGIShader::DebugProbesPushConstant push_constant;
-
- for (int i = 0; i < 4; i++) {
- for (int j = 0; j < 4; j++) {
- push_constant.projection[i * 4 + j] = p_camera_with_transform.matrix[i][j];
- }
- }
-
- //gen spheres from strips
- uint32_t band_points = 16;
- push_constant.band_power = 4;
- push_constant.sections_in_band = ((band_points / 2) - 1);
- push_constant.band_mask = band_points - 2;
- push_constant.section_arc = Math_TAU / float(push_constant.sections_in_band);
- push_constant.y_mult = rb->sdfgi->y_mult;
-
- uint32_t total_points = push_constant.sections_in_band * band_points;
- uint32_t total_probes = rb->sdfgi->probe_axis_count * rb->sdfgi->probe_axis_count * rb->sdfgi->probe_axis_count;
-
- push_constant.grid_size[0] = rb->sdfgi->cascade_size;
- push_constant.grid_size[1] = rb->sdfgi->cascade_size;
- push_constant.grid_size[2] = rb->sdfgi->cascade_size;
- push_constant.cascade = 0;
-
- push_constant.probe_axis_size = rb->sdfgi->probe_axis_count;
-
- if (!rb->sdfgi->debug_probes_uniform_set.is_valid() || !RD::get_singleton()->uniform_set_is_valid(rb->sdfgi->debug_probes_uniform_set)) {
- Vector<RD::Uniform> uniforms;
- {
- RD::Uniform u;
- u.binding = 1;
- u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER;
- u.ids.push_back(rb->sdfgi->cascades_ubo);
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.binding = 2;
- u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
- u.ids.push_back(rb->sdfgi->lightprobe_texture);
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.binding = 3;
- u.uniform_type = RD::UNIFORM_TYPE_SAMPLER;
- u.ids.push_back(storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED));
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.binding = 4;
- u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
- u.ids.push_back(rb->sdfgi->occlusion_texture);
- uniforms.push_back(u);
- }
-
- rb->sdfgi->debug_probes_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, sdfgi_shader.debug_probes.version_get_shader(sdfgi_shader.debug_probes_shader, 0), 0);
- }
-
- RD::get_singleton()->draw_list_bind_render_pipeline(p_draw_list, sdfgi_shader.debug_probes_pipeline[SDGIShader::PROBE_DEBUG_PROBES].get_render_pipeline(RD::INVALID_FORMAT_ID, RD::get_singleton()->framebuffer_get_format(p_framebuffer)));
- RD::get_singleton()->draw_list_bind_uniform_set(p_draw_list, rb->sdfgi->debug_probes_uniform_set, 0);
- RD::get_singleton()->draw_list_set_push_constant(p_draw_list, &push_constant, sizeof(SDGIShader::DebugProbesPushConstant));
- RD::get_singleton()->draw_list_draw(p_draw_list, false, total_probes, total_points);
-
- if (sdfgi_debug_probe_dir != Vector3()) {
- print_line("CLICK DEBUG ME?");
- uint32_t cascade = 0;
- Vector3 offset = Vector3((Vector3i(1, 1, 1) * -int32_t(rb->sdfgi->cascade_size >> 1) + rb->sdfgi->cascades[cascade].position)) * rb->sdfgi->cascades[cascade].cell_size * Vector3(1.0, 1.0 / rb->sdfgi->y_mult, 1.0);
- Vector3 probe_size = rb->sdfgi->cascades[cascade].cell_size * (rb->sdfgi->cascade_size / SDFGI::PROBE_DIVISOR) * Vector3(1.0, 1.0 / rb->sdfgi->y_mult, 1.0);
- Vector3 ray_from = sdfgi_debug_probe_pos;
- Vector3 ray_to = sdfgi_debug_probe_pos + sdfgi_debug_probe_dir * rb->sdfgi->cascades[cascade].cell_size * Math::sqrt(3.0) * rb->sdfgi->cascade_size;
- float sphere_radius = 0.2;
- float closest_dist = 1e20;
- sdfgi_debug_probe_enabled = false;
-
- Vector3i probe_from = rb->sdfgi->cascades[cascade].position / (rb->sdfgi->cascade_size / SDFGI::PROBE_DIVISOR);
- for (int i = 0; i < (SDFGI::PROBE_DIVISOR + 1); i++) {
- for (int j = 0; j < (SDFGI::PROBE_DIVISOR + 1); j++) {
- for (int k = 0; k < (SDFGI::PROBE_DIVISOR + 1); k++) {
- Vector3 pos = offset + probe_size * Vector3(i, j, k);
- Vector3 res;
- if (Geometry3D::segment_intersects_sphere(ray_from, ray_to, pos, sphere_radius, &res)) {
- float d = ray_from.distance_to(res);
- if (d < closest_dist) {
- closest_dist = d;
- sdfgi_debug_probe_enabled = true;
- sdfgi_debug_probe_index = probe_from + Vector3i(i, j, k);
- }
- }
- }
- }
- }
-
- if (sdfgi_debug_probe_enabled) {
- print_line("found: " + sdfgi_debug_probe_index);
- } else {
- print_line("no found");
- }
- sdfgi_debug_probe_dir = Vector3();
- }
-
- if (sdfgi_debug_probe_enabled) {
- uint32_t cascade = 0;
- uint32_t probe_cells = (rb->sdfgi->cascade_size / SDFGI::PROBE_DIVISOR);
- Vector3i probe_from = rb->sdfgi->cascades[cascade].position / probe_cells;
- Vector3i ofs = sdfgi_debug_probe_index - probe_from;
- if (ofs.x < 0 || ofs.y < 0 || ofs.z < 0) {
- return;
- }
- if (ofs.x > SDFGI::PROBE_DIVISOR || ofs.y > SDFGI::PROBE_DIVISOR || ofs.z > SDFGI::PROBE_DIVISOR) {
- return;
- }
-
- uint32_t mult = (SDFGI::PROBE_DIVISOR + 1);
- uint32_t index = ofs.z * mult * mult + ofs.y * mult + ofs.x;
-
- push_constant.probe_debug_index = index;
-
- uint32_t cell_count = probe_cells * 2 * probe_cells * 2 * probe_cells * 2;
-
- RD::get_singleton()->draw_list_bind_render_pipeline(p_draw_list, sdfgi_shader.debug_probes_pipeline[SDGIShader::PROBE_DEBUG_VISIBILITY].get_render_pipeline(RD::INVALID_FORMAT_ID, RD::get_singleton()->framebuffer_get_format(p_framebuffer)));
- RD::get_singleton()->draw_list_bind_uniform_set(p_draw_list, rb->sdfgi->debug_probes_uniform_set, 0);
- RD::get_singleton()->draw_list_set_push_constant(p_draw_list, &push_constant, sizeof(SDGIShader::DebugProbesPushConstant));
- RD::get_singleton()->draw_list_draw(p_draw_list, false, cell_count, total_points);
- }
+ rb->sdfgi->debug_probes(p_draw_list, p_framebuffer, p_camera_with_transform);
}
////////////////////////////////
@@ -5277,7 +1562,7 @@ void RendererSceneRenderRD::_process_ssr(RID p_render_buffers, RID p_dest_frameb
return;
}
- Environment *env = environment_owner.getornull(p_environment);
+ RendererSceneEnvironmentRD *env = environment_owner.getornull(p_environment);
ERR_FAIL_COND(!env);
ERR_FAIL_COND(!env->ssr_enabled);
@@ -5322,7 +1607,7 @@ void RendererSceneRenderRD::_process_ssao(RID p_render_buffers, RID p_environmen
RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers);
ERR_FAIL_COND(!rb);
- Environment *env = environment_owner.getornull(p_environment);
+ RendererSceneEnvironmentRD *env = environment_owner.getornull(p_environment);
ERR_FAIL_COND(!env);
RENDER_TIMESTAMP("Process SSAO");
@@ -5468,7 +1753,7 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(RID p_rende
RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers);
ERR_FAIL_COND(!rb);
- Environment *env = environment_owner.getornull(p_environment);
+ RendererSceneEnvironmentRD *env = environment_owner.getornull(p_environment);
//glow (if enabled)
CameraEffects *camfx = camera_effects_owner.getornull(p_camera_effects);
@@ -5661,7 +1946,7 @@ void RendererSceneRenderRD::_render_buffers_debug_draw(RID p_render_buffers, RID
}
void RendererSceneRenderRD::environment_set_adjustment(RID p_env, bool p_enable, float p_brightness, float p_contrast, float p_saturation, bool p_use_1d_color_correction, RID p_color_correction) {
- Environment *env = environment_owner.getornull(p_env);
+ RendererSceneEnvironmentRD *env = environment_owner.getornull(p_env);
ERR_FAIL_COND(!env);
env->adjustments_enabled = p_enable;
@@ -5672,152 +1957,6 @@ void RendererSceneRenderRD::environment_set_adjustment(RID p_env, bool p_enable,
env->color_correction = p_color_correction;
}
-void RendererSceneRenderRD::_sdfgi_debug_draw(RID p_render_buffers, const CameraMatrix &p_projection, const Transform &p_transform) {
- RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers);
- ERR_FAIL_COND(!rb);
-
- if (!rb->sdfgi) {
- return; //eh
- }
-
- if (!rb->sdfgi->debug_uniform_set.is_valid() || !RD::get_singleton()->uniform_set_is_valid(rb->sdfgi->debug_uniform_set)) {
- Vector<RD::Uniform> uniforms;
- {
- RD::Uniform u;
- u.binding = 1;
- u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
- for (uint32_t i = 0; i < SDFGI::MAX_CASCADES; i++) {
- if (i < rb->sdfgi->cascades.size()) {
- u.ids.push_back(rb->sdfgi->cascades[i].sdf_tex);
- } else {
- u.ids.push_back(storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_3D_WHITE));
- }
- }
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.binding = 2;
- u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
- for (uint32_t i = 0; i < SDFGI::MAX_CASCADES; i++) {
- if (i < rb->sdfgi->cascades.size()) {
- u.ids.push_back(rb->sdfgi->cascades[i].light_tex);
- } else {
- u.ids.push_back(storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_3D_WHITE));
- }
- }
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.binding = 3;
- u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
- for (uint32_t i = 0; i < SDFGI::MAX_CASCADES; i++) {
- if (i < rb->sdfgi->cascades.size()) {
- u.ids.push_back(rb->sdfgi->cascades[i].light_aniso_0_tex);
- } else {
- u.ids.push_back(storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_3D_WHITE));
- }
- }
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.binding = 4;
- u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
- for (uint32_t i = 0; i < SDFGI::MAX_CASCADES; i++) {
- if (i < rb->sdfgi->cascades.size()) {
- u.ids.push_back(rb->sdfgi->cascades[i].light_aniso_1_tex);
- } else {
- u.ids.push_back(storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_3D_WHITE));
- }
- }
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.binding = 5;
- u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
- u.ids.push_back(rb->sdfgi->occlusion_texture);
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.binding = 8;
- u.uniform_type = RD::UNIFORM_TYPE_SAMPLER;
- u.ids.push_back(storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED));
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.binding = 9;
- u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER;
- u.ids.push_back(rb->sdfgi->cascades_ubo);
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.binding = 10;
- u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
- u.ids.push_back(rb->texture);
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.binding = 11;
- u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
- u.ids.push_back(rb->sdfgi->lightprobe_texture);
- uniforms.push_back(u);
- }
- rb->sdfgi->debug_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, sdfgi_shader.debug_shader_version, 0);
- }
-
- RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
- RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, sdfgi_shader.debug_pipeline);
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, rb->sdfgi->debug_uniform_set, 0);
-
- SDGIShader::DebugPushConstant push_constant;
- push_constant.grid_size[0] = rb->sdfgi->cascade_size;
- push_constant.grid_size[1] = rb->sdfgi->cascade_size;
- push_constant.grid_size[2] = rb->sdfgi->cascade_size;
- push_constant.max_cascades = rb->sdfgi->cascades.size();
- push_constant.screen_size[0] = rb->width;
- push_constant.screen_size[1] = rb->height;
- push_constant.probe_axis_size = rb->sdfgi->probe_axis_count;
- push_constant.use_occlusion = rb->sdfgi->uses_occlusion;
- push_constant.y_mult = rb->sdfgi->y_mult;
-
- Vector2 vp_half = p_projection.get_viewport_half_extents();
- push_constant.cam_extent[0] = vp_half.x;
- push_constant.cam_extent[1] = vp_half.y;
- push_constant.cam_extent[2] = -p_projection.get_z_near();
-
- push_constant.cam_transform[0] = p_transform.basis.elements[0][0];
- push_constant.cam_transform[1] = p_transform.basis.elements[1][0];
- push_constant.cam_transform[2] = p_transform.basis.elements[2][0];
- push_constant.cam_transform[3] = 0;
- push_constant.cam_transform[4] = p_transform.basis.elements[0][1];
- push_constant.cam_transform[5] = p_transform.basis.elements[1][1];
- push_constant.cam_transform[6] = p_transform.basis.elements[2][1];
- push_constant.cam_transform[7] = 0;
- push_constant.cam_transform[8] = p_transform.basis.elements[0][2];
- push_constant.cam_transform[9] = p_transform.basis.elements[1][2];
- push_constant.cam_transform[10] = p_transform.basis.elements[2][2];
- push_constant.cam_transform[11] = 0;
- push_constant.cam_transform[12] = p_transform.origin.x;
- push_constant.cam_transform[13] = p_transform.origin.y;
- push_constant.cam_transform[14] = p_transform.origin.z;
- push_constant.cam_transform[15] = 1;
-
- RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(SDGIShader::DebugPushConstant));
-
- RD::get_singleton()->compute_list_dispatch_threads(compute_list, rb->width, rb->height, 1);
- RD::get_singleton()->compute_list_end();
-
- Size2 rtsize = storage->render_target_get_size(rb->render_target);
- storage->get_effects()->copy_to_fb_rect(rb->texture, storage->render_target_get_rd_framebuffer(rb->render_target), Rect2(Vector2(), rtsize), true);
-}
-
RID RendererSceneRenderRD::render_buffers_get_back_buffer_texture(RID p_render_buffers) {
RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers);
ERR_FAIL_COND_V(!rb, RID());
@@ -5837,14 +1976,14 @@ RID RendererSceneRenderRD::render_buffers_get_ao_texture(RID p_render_buffers) {
RID RendererSceneRenderRD::render_buffers_get_gi_probe_buffer(RID p_render_buffers) {
RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers);
ERR_FAIL_COND_V(!rb, RID());
- if (rb->giprobe_buffer.is_null()) {
- rb->giprobe_buffer = RD::get_singleton()->uniform_buffer_create(sizeof(GI::GIProbeData) * RenderBuffers::MAX_GIPROBES);
+ if (rb->gi.giprobe_buffer.is_null()) {
+ rb->gi.giprobe_buffer = RD::get_singleton()->uniform_buffer_create(sizeof(RendererSceneGIRD::GIProbeData) * RendererSceneGIRD::MAX_GIPROBES);
}
- return rb->giprobe_buffer;
+ return rb->gi.giprobe_buffer;
}
RID RendererSceneRenderRD::render_buffers_get_default_gi_probe_buffer() {
- return default_giprobe_buffer;
+ return gi.default_giprobe_buffer;
}
RID RendererSceneRenderRD::render_buffers_get_gi_ambient_texture(RID p_render_buffers) {
@@ -5893,7 +2032,7 @@ Vector3i RendererSceneRenderRD::render_buffers_get_sdfgi_cascade_probe_offset(RI
ERR_FAIL_COND_V(!rb, Vector3i());
ERR_FAIL_COND_V(!rb->sdfgi, Vector3i());
ERR_FAIL_UNSIGNED_INDEX_V(p_cascade, rb->sdfgi->cascades.size(), Vector3i());
- int32_t probe_divisor = rb->sdfgi->cascade_size / SDFGI::PROBE_DIVISOR;
+ int32_t probe_divisor = rb->sdfgi->cascade_size / RendererSceneGIRD::SDFGI::PROBE_DIVISOR;
return rb->sdfgi->cascades[p_cascade].position / probe_divisor;
}
@@ -6142,11 +2281,11 @@ void RendererSceneRenderRD::directional_shadow_quality_set(RS::ShadowQuality p_q
}
int RendererSceneRenderRD::get_roughness_layers() const {
- return roughness_layers;
+ return sky.roughness_layers;
}
bool RendererSceneRenderRD::is_using_radiance_cubemap_array() const {
- return sky_use_cubemap_array;
+ return sky.sky_use_cubemap_array;
}
RendererSceneRenderRD::RenderBufferData *RendererSceneRenderRD::render_buffers_get_data(RID p_render_buffers) {
@@ -6223,7 +2362,7 @@ void RendererSceneRenderRD::_setup_reflections(const PagedArray<RID> &p_reflecti
}
if (cluster.reflection_count) {
- RD::get_singleton()->buffer_update(cluster.reflection_buffer, 0, cluster.reflection_count * sizeof(ReflectionData), cluster.reflections, RD::BARRIER_MASK_RASTER | RD::BARRIER_MASK_COMPUTE);
+ RD::get_singleton()->buffer_update(cluster.reflection_buffer, 0, cluster.reflection_count * sizeof(RendererSceneSkyRD::ReflectionData), cluster.reflections, RD::BARRIER_MASK_RASTER | RD::BARRIER_MASK_COMPUTE);
}
}
@@ -6232,7 +2371,7 @@ void RendererSceneRenderRD::_setup_lights(const PagedArray<RID> &p_lights, const
r_directional_light_count = 0;
r_positional_light_count = 0;
- sky_scene_state.ubo.directional_light_count = 0;
+ sky.sky_scene_state.ubo.directional_light_count = 0;
Plane camera_plane(p_camera_transform.origin, -p_camera_transform.basis.get_axis(Vector3::AXIS_Z).normalized());
@@ -6252,8 +2391,8 @@ void RendererSceneRenderRD::_setup_lights(const PagedArray<RID> &p_lights, const
switch (type) {
case RS::LIGHT_DIRECTIONAL: {
// Copy to SkyDirectionalLightData
- if (r_directional_light_count < sky_scene_state.max_directional_lights) {
- SkyDirectionalLightData &sky_light_data = sky_scene_state.directional_lights[r_directional_light_count];
+ if (r_directional_light_count < sky.sky_scene_state.max_directional_lights) {
+ RendererSceneSkyRD::SkyDirectionalLightData &sky_light_data = sky.sky_scene_state.directional_lights[r_directional_light_count];
Transform light_transform = li->transform;
Vector3 world_direction = light_transform.basis.xform(Vector3(0, 0, 1)).normalized();
@@ -6281,7 +2420,7 @@ void RendererSceneRenderRD::_setup_lights(const PagedArray<RID> &p_lights, const
angular_diameter = 0.0;
}
sky_light_data.size = angular_diameter;
- sky_scene_state.ubo.directional_light_count++;
+ sky.sky_scene_state.ubo.directional_light_count++;
}
if (r_directional_light_count >= cluster.max_directional_lights || storage->light_directional_is_sky_only(base)) {
@@ -6816,7 +2955,7 @@ void RendererSceneRenderRD::_volumetric_fog_erase(RenderBuffers *rb) {
void RendererSceneRenderRD::_update_volumetric_fog(RID p_render_buffers, RID p_environment, const CameraMatrix &p_cam_projection, const Transform &p_cam_transform, RID p_shadow_atlas, int p_directional_light_count, bool p_use_directional_shadows, int p_positional_light_count, int p_gi_probe_count) {
RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers);
ERR_FAIL_COND(!rb);
- Environment *env = environment_owner.getornull(p_environment);
+ RendererSceneEnvironmentRD *env = environment_owner.getornull(p_environment);
float ratio = float(rb->width) / float((rb->width + rb->height) / 2);
uint32_t target_width = uint32_t(float(volumetric_fog_size) * ratio);
@@ -6873,7 +3012,7 @@ void RendererSceneRenderRD::_update_volumetric_fog(RID p_render_buffers, RID p_e
uniforms.push_back(u);
}
- rb->volumetric_fog->sky_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, sky_shader.default_shader_rd, SKY_SET_FOG);
+ rb->volumetric_fog->sky_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, sky.sky_shader.default_shader_rd, RendererSceneSkyRD::SKY_SET_FOG);
}
//update volumetric fog
@@ -6984,8 +3123,8 @@ void RendererSceneRenderRD::_update_volumetric_fog(RID p_render_buffers, RID p_e
RD::Uniform u;
u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
u.binding = 12;
- for (int i = 0; i < RenderBuffers::MAX_GIPROBES; i++) {
- u.ids.push_back(rb->giprobe_textures[i]);
+ for (int i = 0; i < RendererSceneGIRD::MAX_GIPROBES; i++) {
+ u.ids.push_back(rb->gi.giprobe_textures[i]);
}
uniforms.push_back(u);
}
@@ -7224,7 +3363,14 @@ bool RendererSceneRenderRD::_needs_post_prepass_render(bool p_use_gi) {
void RendererSceneRenderRD::_post_prepass_render(bool p_use_gi) {
if (render_state.render_buffers.is_valid()) {
if (p_use_gi) {
- _sdfgi_update_probes(render_state.render_buffers, render_state.environment);
+ RenderBuffers *rb = render_buffers_owner.getornull(render_state.render_buffers);
+ ERR_FAIL_COND(rb == nullptr);
+ if (rb->sdfgi == nullptr) {
+ return;
+ }
+
+ RendererSceneEnvironmentRD *env = environment_owner.getornull(render_state.environment);
+ rb->sdfgi->update_probes(env, sky.sky_owner.getornull(env->sky));
}
}
}
@@ -7241,7 +3387,13 @@ void RendererSceneRenderRD::_pre_opaque_render(bool p_use_ssao, bool p_use_gi, R
// Render shadows while GI is rendering, due to how barriers are handled, this should happen at the same time
if (render_state.render_buffers.is_valid() && p_use_gi) {
- _sdfgi_store_probes(render_state.render_buffers);
+ RenderBuffers *rb = render_buffers_owner.getornull(render_state.render_buffers);
+ ERR_FAIL_COND(rb == nullptr);
+ if (rb->sdfgi == nullptr) {
+ return;
+ }
+
+ rb->sdfgi->store_probes();
}
render_state.cube_shadows.clear();
@@ -7308,7 +3460,7 @@ void RendererSceneRenderRD::_pre_opaque_render(bool p_use_ssao, bool p_use_gi, R
//start GI
if (render_gi) {
- _process_gi(render_state.render_buffers, p_normal_roughness_buffer, p_gi_probe_buffer, render_state.environment, render_state.cam_projection, render_state.cam_transform, *render_state.gi_probes);
+ gi.process_gi(render_state.render_buffers, p_normal_roughness_buffer, p_gi_probe_buffer, render_state.environment, render_state.cam_projection, render_state.cam_transform, *render_state.gi_probes, this);
}
//Do shadow rendering (in parallel with GI)
@@ -7368,6 +3520,13 @@ void RendererSceneRenderRD::_pre_opaque_render(bool p_use_ssao, bool p_use_gi, R
}
void RendererSceneRenderRD::render_scene(RID p_render_buffers, const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, const PagedArray<GeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_gi_probes, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_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) {
+ // getting this here now so we can direct call a bunch of things more easily
+ RenderBuffers *rb = nullptr;
+ if (p_render_buffers.is_valid()) {
+ rb = render_buffers_owner.getornull(p_render_buffers);
+ ERR_FAIL_COND(!rb); // !BAS! Do we fail here or skip the parts that won't work. can't really see a case why we would be rendering without buffers....
+ }
+
//assign render data
{
render_state.render_buffers = p_render_buffers;
@@ -7404,19 +3563,17 @@ void RendererSceneRenderRD::render_scene(RID p_render_buffers, const Transform &
}
//sdfgi first
- if (p_render_buffers.is_valid()) {
+ if (rb != nullptr && rb->sdfgi != nullptr) {
for (int i = 0; i < render_state.render_sdfgi_region_count; i++) {
- _render_sdfgi_region(p_render_buffers, render_state.render_sdfgi_regions[i].region, render_state.render_sdfgi_regions[i].instances);
+ rb->sdfgi->render_region(p_render_buffers, render_state.render_sdfgi_regions[i].region, render_state.render_sdfgi_regions[i].instances, this);
}
if (render_state.sdfgi_update_data->update_static) {
- _render_sdfgi_static_lights(p_render_buffers, render_state.sdfgi_update_data->static_cascade_count, p_sdfgi_update_data->static_cascade_indices, render_state.sdfgi_update_data->static_positional_lights);
+ rb->sdfgi->render_static_lights(p_render_buffers, render_state.sdfgi_update_data->static_cascade_count, p_sdfgi_update_data->static_cascade_indices, render_state.sdfgi_update_data->static_positional_lights, this);
}
}
Color clear_color;
if (p_render_buffers.is_valid()) {
- RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers);
- ERR_FAIL_COND(!rb);
clear_color = storage->render_target_get_clear_request_color(rb->render_target);
} else {
clear_color = storage->get_default_clear_color();
@@ -7424,15 +3581,15 @@ void RendererSceneRenderRD::render_scene(RID p_render_buffers, const Transform &
//assign render indices to giprobes
for (uint32_t i = 0; i < (uint32_t)p_gi_probes.size(); i++) {
- GIProbeInstance *giprobe_inst = gi_probe_instance_owner.getornull(p_gi_probes[i]);
+ RendererSceneGIRD::GIProbeInstance *giprobe_inst = gi.gi_probe_instance_owner.getornull(p_gi_probes[i]);
if (giprobe_inst) {
giprobe_inst->render_index = i;
}
}
if (render_buffers_owner.owns(render_state.render_buffers)) {
- RenderBuffers *rb = render_buffers_owner.getornull(render_state.render_buffers);
- current_cluster_builder = rb->cluster_builder;
+ RenderBuffers *rs_rb = render_buffers_owner.getornull(render_state.render_buffers);
+ current_cluster_builder = rs_rb->cluster_builder;
} else if (reflection_probe_instance_owner.owns(render_state.reflection_probe)) {
ReflectionProbeInstance *rpi = reflection_probe_instance_owner.getornull(render_state.reflection_probe);
ReflectionAtlas *ra = reflection_atlas_owner.getornull(rpi->atlas);
@@ -7447,14 +3604,17 @@ void RendererSceneRenderRD::render_scene(RID p_render_buffers, const Transform &
current_cluster_builder = nullptr;
}
- if (p_render_buffers.is_valid()) {
- _pre_process_gi(p_render_buffers, p_cam_transform);
+ if (rb != nullptr && rb->sdfgi != nullptr) {
+ rb->sdfgi->update_cascades();
+
+ rb->sdfgi->pre_process_gi(p_cam_transform, this);
}
render_state.gi_probe_count = 0;
- if (render_state.render_buffers.is_valid()) {
- _setup_giprobes(render_state.render_buffers, render_state.cam_transform, *render_state.gi_probes, render_state.gi_probe_count);
- _sdfgi_update_light(render_state.render_buffers, render_state.environment);
+ if (rb != nullptr && rb->sdfgi != nullptr) {
+ gi.setup_giprobes(render_state.render_buffers, render_state.cam_transform, *render_state.gi_probes, render_state.gi_probe_count, this);
+
+ rb->sdfgi->update_light();
}
render_state.depth_prepass_used = false;
@@ -7487,8 +3647,8 @@ void RendererSceneRenderRD::render_scene(RID p_render_buffers, const Transform &
_render_buffers_post_process_and_tonemap(p_render_buffers, p_environment, p_camera_effects, p_cam_projection);
_render_buffers_debug_draw(p_render_buffers, p_shadow_atlas);
- if (debug_draw == RS::VIEWPORT_DEBUG_DRAW_SDFGI) {
- _sdfgi_debug_draw(p_render_buffers, p_cam_projection, p_cam_transform);
+ if (debug_draw == RS::VIEWPORT_DEBUG_DRAW_SDFGI && rb != nullptr && rb->sdfgi != nullptr) {
+ rb->sdfgi->debug_draw(p_cam_projection, p_cam_transform, rb->width, rb->height, rb->render_target, rb->texture);
}
}
}
@@ -7668,366 +3828,6 @@ void RendererSceneRenderRD::render_material(const Transform &p_cam_transform, co
_render_material(p_cam_transform, p_cam_projection, p_cam_ortogonal, p_instances, p_framebuffer, p_region);
}
-void RendererSceneRenderRD::_render_sdfgi_region(RID p_render_buffers, int p_region, const PagedArray<GeometryInstance *> &p_instances) {
- //print_line("rendering region " + itos(p_region));
- RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers);
- ERR_FAIL_COND(!rb);
- ERR_FAIL_COND(!rb->sdfgi);
- AABB bounds;
- Vector3i from;
- Vector3i size;
-
- int cascade_prev = _sdfgi_get_pending_region_data(p_render_buffers, p_region - 1, from, size, bounds);
- int cascade_next = _sdfgi_get_pending_region_data(p_render_buffers, p_region + 1, from, size, bounds);
- int cascade = _sdfgi_get_pending_region_data(p_render_buffers, p_region, from, size, bounds);
- ERR_FAIL_COND(cascade < 0);
-
- if (cascade_prev != cascade) {
- //initialize render
- RD::get_singleton()->texture_clear(rb->sdfgi->render_albedo, Color(0, 0, 0, 0), 0, 1, 0, 1);
- RD::get_singleton()->texture_clear(rb->sdfgi->render_emission, Color(0, 0, 0, 0), 0, 1, 0, 1);
- RD::get_singleton()->texture_clear(rb->sdfgi->render_emission_aniso, Color(0, 0, 0, 0), 0, 1, 0, 1);
- RD::get_singleton()->texture_clear(rb->sdfgi->render_geom_facing, Color(0, 0, 0, 0), 0, 1, 0, 1);
- }
-
- //print_line("rendering cascade " + itos(p_region) + " objects: " + itos(p_cull_count) + " bounds: " + bounds + " from: " + from + " size: " + size + " cell size: " + rtos(rb->sdfgi->cascades[cascade].cell_size));
- _render_sdfgi(p_render_buffers, from, size, bounds, p_instances, rb->sdfgi->render_albedo, rb->sdfgi->render_emission, rb->sdfgi->render_emission_aniso, rb->sdfgi->render_geom_facing);
-
- if (cascade_next != cascade) {
- RD::get_singleton()->draw_command_begin_label("SDFGI Pre-Process Cascade");
-
- RENDER_TIMESTAMP(">SDFGI Update SDF");
- //done rendering! must update SDF
- //clear dispatch indirect data
-
- SDGIShader::PreprocessPushConstant push_constant;
- zeromem(&push_constant, sizeof(SDGIShader::PreprocessPushConstant));
-
- RENDER_TIMESTAMP("Scroll SDF");
-
- //scroll
- if (rb->sdfgi->cascades[cascade].dirty_regions != SDFGI::Cascade::DIRTY_ALL) {
- //for scroll
- Vector3i dirty = rb->sdfgi->cascades[cascade].dirty_regions;
- push_constant.scroll[0] = dirty.x;
- push_constant.scroll[1] = dirty.y;
- push_constant.scroll[2] = dirty.z;
- } else {
- //for no scroll
- push_constant.scroll[0] = 0;
- push_constant.scroll[1] = 0;
- push_constant.scroll[2] = 0;
- }
-
- rb->sdfgi->cascades[cascade].all_dynamic_lights_dirty = true;
-
- push_constant.grid_size = rb->sdfgi->cascade_size;
- push_constant.cascade = cascade;
-
- if (rb->sdfgi->cascades[cascade].dirty_regions != SDFGI::Cascade::DIRTY_ALL) {
- RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
-
- //must pre scroll existing data because not all is dirty
- RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, sdfgi_shader.preprocess_pipeline[SDGIShader::PRE_PROCESS_SCROLL]);
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, rb->sdfgi->cascades[cascade].scroll_uniform_set, 0);
-
- RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(SDGIShader::PreprocessPushConstant));
- RD::get_singleton()->compute_list_dispatch_indirect(compute_list, rb->sdfgi->cascades[cascade].solid_cell_dispatch_buffer, 0);
- // no barrier do all together
-
- RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, sdfgi_shader.preprocess_pipeline[SDGIShader::PRE_PROCESS_SCROLL_OCCLUSION]);
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, rb->sdfgi->cascades[cascade].scroll_occlusion_uniform_set, 0);
-
- Vector3i dirty = rb->sdfgi->cascades[cascade].dirty_regions;
- Vector3i groups;
- groups.x = rb->sdfgi->cascade_size - ABS(dirty.x);
- groups.y = rb->sdfgi->cascade_size - ABS(dirty.y);
- groups.z = rb->sdfgi->cascade_size - ABS(dirty.z);
-
- RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(SDGIShader::PreprocessPushConstant));
- RD::get_singleton()->compute_list_dispatch_threads(compute_list, groups.x, groups.y, groups.z);
-
- //no barrier, continue together
-
- {
- //scroll probes and their history also
-
- SDGIShader::IntegratePushConstant ipush_constant;
- ipush_constant.grid_size[1] = rb->sdfgi->cascade_size;
- ipush_constant.grid_size[2] = rb->sdfgi->cascade_size;
- ipush_constant.grid_size[0] = rb->sdfgi->cascade_size;
- ipush_constant.max_cascades = rb->sdfgi->cascades.size();
- ipush_constant.probe_axis_size = rb->sdfgi->probe_axis_count;
- ipush_constant.history_index = 0;
- ipush_constant.history_size = rb->sdfgi->history_size;
- ipush_constant.ray_count = 0;
- ipush_constant.ray_bias = 0;
- ipush_constant.sky_mode = 0;
- ipush_constant.sky_energy = 0;
- ipush_constant.sky_color[0] = 0;
- ipush_constant.sky_color[1] = 0;
- ipush_constant.sky_color[2] = 0;
- ipush_constant.y_mult = rb->sdfgi->y_mult;
- ipush_constant.store_ambient_texture = false;
-
- ipush_constant.image_size[0] = rb->sdfgi->probe_axis_count * rb->sdfgi->probe_axis_count;
- ipush_constant.image_size[1] = rb->sdfgi->probe_axis_count;
-
- int32_t probe_divisor = rb->sdfgi->cascade_size / SDFGI::PROBE_DIVISOR;
- ipush_constant.cascade = cascade;
- ipush_constant.world_offset[0] = rb->sdfgi->cascades[cascade].position.x / probe_divisor;
- ipush_constant.world_offset[1] = rb->sdfgi->cascades[cascade].position.y / probe_divisor;
- ipush_constant.world_offset[2] = rb->sdfgi->cascades[cascade].position.z / probe_divisor;
-
- ipush_constant.scroll[0] = dirty.x / probe_divisor;
- ipush_constant.scroll[1] = dirty.y / probe_divisor;
- ipush_constant.scroll[2] = dirty.z / probe_divisor;
-
- RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, sdfgi_shader.integrate_pipeline[SDGIShader::INTEGRATE_MODE_SCROLL]);
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, rb->sdfgi->cascades[cascade].integrate_uniform_set, 0);
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, sdfgi_shader.integrate_default_sky_uniform_set, 1);
- RD::get_singleton()->compute_list_set_push_constant(compute_list, &ipush_constant, sizeof(SDGIShader::IntegratePushConstant));
- RD::get_singleton()->compute_list_dispatch_threads(compute_list, rb->sdfgi->probe_axis_count * rb->sdfgi->probe_axis_count, rb->sdfgi->probe_axis_count, 1);
-
- RD::get_singleton()->compute_list_add_barrier(compute_list);
-
- RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, sdfgi_shader.integrate_pipeline[SDGIShader::INTEGRATE_MODE_SCROLL_STORE]);
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, rb->sdfgi->cascades[cascade].integrate_uniform_set, 0);
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, sdfgi_shader.integrate_default_sky_uniform_set, 1);
- RD::get_singleton()->compute_list_set_push_constant(compute_list, &ipush_constant, sizeof(SDGIShader::IntegratePushConstant));
- RD::get_singleton()->compute_list_dispatch_threads(compute_list, rb->sdfgi->probe_axis_count * rb->sdfgi->probe_axis_count, rb->sdfgi->probe_axis_count, 1);
-
- RD::get_singleton()->compute_list_add_barrier(compute_list);
-
- if (rb->sdfgi->bounce_feedback > 0.0) {
- //multibounce requires this to be stored so direct light can read from it
-
- RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, sdfgi_shader.integrate_pipeline[SDGIShader::INTEGRATE_MODE_STORE]);
-
- //convert to octahedral to store
- ipush_constant.image_size[0] *= SDFGI::LIGHTPROBE_OCT_SIZE;
- ipush_constant.image_size[1] *= SDFGI::LIGHTPROBE_OCT_SIZE;
-
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, rb->sdfgi->cascades[cascade].integrate_uniform_set, 0);
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, sdfgi_shader.integrate_default_sky_uniform_set, 1);
- RD::get_singleton()->compute_list_set_push_constant(compute_list, &ipush_constant, sizeof(SDGIShader::IntegratePushConstant));
- RD::get_singleton()->compute_list_dispatch_threads(compute_list, rb->sdfgi->probe_axis_count * rb->sdfgi->probe_axis_count * SDFGI::LIGHTPROBE_OCT_SIZE, rb->sdfgi->probe_axis_count * SDFGI::LIGHTPROBE_OCT_SIZE, 1);
- }
- }
-
- //ok finally barrier
- RD::get_singleton()->compute_list_end();
- }
-
- //clear dispatch indirect data
- uint32_t dispatch_indirct_data[4] = { 0, 0, 0, 0 };
- RD::get_singleton()->buffer_update(rb->sdfgi->cascades[cascade].solid_cell_dispatch_buffer, 0, sizeof(uint32_t) * 4, dispatch_indirct_data);
-
- RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
-
- bool half_size = true; //much faster, very little difference
- static const int optimized_jf_group_size = 8;
-
- if (half_size) {
- push_constant.grid_size >>= 1;
-
- uint32_t cascade_half_size = rb->sdfgi->cascade_size >> 1;
- RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, sdfgi_shader.preprocess_pipeline[SDGIShader::PRE_PROCESS_JUMP_FLOOD_INITIALIZE_HALF]);
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, rb->sdfgi->sdf_initialize_half_uniform_set, 0);
- RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(SDGIShader::PreprocessPushConstant));
- RD::get_singleton()->compute_list_dispatch_threads(compute_list, cascade_half_size, cascade_half_size, cascade_half_size);
- RD::get_singleton()->compute_list_add_barrier(compute_list);
-
- //must start with regular jumpflood
-
- push_constant.half_size = true;
- {
- RENDER_TIMESTAMP("SDFGI Jump Flood (Half Size)");
-
- uint32_t s = cascade_half_size;
-
- RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, sdfgi_shader.preprocess_pipeline[SDGIShader::PRE_PROCESS_JUMP_FLOOD]);
-
- int jf_us = 0;
- //start with regular jump flood for very coarse reads, as this is impossible to optimize
- while (s > 1) {
- s /= 2;
- push_constant.step_size = s;
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, rb->sdfgi->jump_flood_half_uniform_set[jf_us], 0);
- RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(SDGIShader::PreprocessPushConstant));
- RD::get_singleton()->compute_list_dispatch_threads(compute_list, cascade_half_size, cascade_half_size, cascade_half_size);
- RD::get_singleton()->compute_list_add_barrier(compute_list);
- jf_us = jf_us == 0 ? 1 : 0;
-
- if (cascade_half_size / (s / 2) >= optimized_jf_group_size) {
- break;
- }
- }
-
- RENDER_TIMESTAMP("SDFGI Jump Flood Optimized (Half Size)");
-
- //continue with optimized jump flood for smaller reads
- RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, sdfgi_shader.preprocess_pipeline[SDGIShader::PRE_PROCESS_JUMP_FLOOD_OPTIMIZED]);
- while (s > 1) {
- s /= 2;
- push_constant.step_size = s;
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, rb->sdfgi->jump_flood_half_uniform_set[jf_us], 0);
- RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(SDGIShader::PreprocessPushConstant));
- RD::get_singleton()->compute_list_dispatch_threads(compute_list, cascade_half_size, cascade_half_size, cascade_half_size);
- RD::get_singleton()->compute_list_add_barrier(compute_list);
- jf_us = jf_us == 0 ? 1 : 0;
- }
- }
-
- // restore grid size for last passes
- push_constant.grid_size = rb->sdfgi->cascade_size;
-
- RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, sdfgi_shader.preprocess_pipeline[SDGIShader::PRE_PROCESS_JUMP_FLOOD_UPSCALE]);
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, rb->sdfgi->sdf_upscale_uniform_set, 0);
- RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(SDGIShader::PreprocessPushConstant));
- RD::get_singleton()->compute_list_dispatch_threads(compute_list, rb->sdfgi->cascade_size, rb->sdfgi->cascade_size, rb->sdfgi->cascade_size);
- RD::get_singleton()->compute_list_add_barrier(compute_list);
-
- //run one pass of fullsize jumpflood to fix up half size arctifacts
-
- push_constant.half_size = false;
- push_constant.step_size = 1;
- RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, sdfgi_shader.preprocess_pipeline[SDGIShader::PRE_PROCESS_JUMP_FLOOD_OPTIMIZED]);
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, rb->sdfgi->jump_flood_uniform_set[rb->sdfgi->upscale_jfa_uniform_set_index], 0);
- RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(SDGIShader::PreprocessPushConstant));
- RD::get_singleton()->compute_list_dispatch_threads(compute_list, rb->sdfgi->cascade_size, rb->sdfgi->cascade_size, rb->sdfgi->cascade_size);
- RD::get_singleton()->compute_list_add_barrier(compute_list);
-
- } else {
- //full size jumpflood
- RENDER_TIMESTAMP("SDFGI Jump Flood");
-
- RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, sdfgi_shader.preprocess_pipeline[SDGIShader::PRE_PROCESS_JUMP_FLOOD_INITIALIZE]);
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, rb->sdfgi->sdf_initialize_uniform_set, 0);
- RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(SDGIShader::PreprocessPushConstant));
- RD::get_singleton()->compute_list_dispatch_threads(compute_list, rb->sdfgi->cascade_size, rb->sdfgi->cascade_size, rb->sdfgi->cascade_size);
-
- RD::get_singleton()->compute_list_add_barrier(compute_list);
-
- push_constant.half_size = false;
- {
- uint32_t s = rb->sdfgi->cascade_size;
-
- RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, sdfgi_shader.preprocess_pipeline[SDGIShader::PRE_PROCESS_JUMP_FLOOD]);
-
- int jf_us = 0;
- //start with regular jump flood for very coarse reads, as this is impossible to optimize
- while (s > 1) {
- s /= 2;
- push_constant.step_size = s;
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, rb->sdfgi->jump_flood_uniform_set[jf_us], 0);
- RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(SDGIShader::PreprocessPushConstant));
- RD::get_singleton()->compute_list_dispatch_threads(compute_list, rb->sdfgi->cascade_size, rb->sdfgi->cascade_size, rb->sdfgi->cascade_size);
- RD::get_singleton()->compute_list_add_barrier(compute_list);
- jf_us = jf_us == 0 ? 1 : 0;
-
- if (rb->sdfgi->cascade_size / (s / 2) >= optimized_jf_group_size) {
- break;
- }
- }
-
- RENDER_TIMESTAMP("SDFGI Jump Flood Optimized");
-
- //continue with optimized jump flood for smaller reads
- RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, sdfgi_shader.preprocess_pipeline[SDGIShader::PRE_PROCESS_JUMP_FLOOD_OPTIMIZED]);
- while (s > 1) {
- s /= 2;
- push_constant.step_size = s;
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, rb->sdfgi->jump_flood_uniform_set[jf_us], 0);
- RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(SDGIShader::PreprocessPushConstant));
- RD::get_singleton()->compute_list_dispatch_threads(compute_list, rb->sdfgi->cascade_size, rb->sdfgi->cascade_size, rb->sdfgi->cascade_size);
- RD::get_singleton()->compute_list_add_barrier(compute_list);
- jf_us = jf_us == 0 ? 1 : 0;
- }
- }
- }
-
- RENDER_TIMESTAMP("SDFGI Occlusion");
-
- // occlusion
- {
- uint32_t probe_size = rb->sdfgi->cascade_size / SDFGI::PROBE_DIVISOR;
- Vector3i probe_global_pos = rb->sdfgi->cascades[cascade].position / probe_size;
-
- RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, sdfgi_shader.preprocess_pipeline[SDGIShader::PRE_PROCESS_OCCLUSION]);
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, rb->sdfgi->occlusion_uniform_set, 0);
- for (int i = 0; i < 8; i++) {
- //dispatch all at once for performance
- Vector3i offset(i & 1, (i >> 1) & 1, (i >> 2) & 1);
-
- if ((probe_global_pos.x & 1) != 0) {
- offset.x = (offset.x + 1) & 1;
- }
- if ((probe_global_pos.y & 1) != 0) {
- offset.y = (offset.y + 1) & 1;
- }
- if ((probe_global_pos.z & 1) != 0) {
- offset.z = (offset.z + 1) & 1;
- }
- push_constant.probe_offset[0] = offset.x;
- push_constant.probe_offset[1] = offset.y;
- push_constant.probe_offset[2] = offset.z;
- push_constant.occlusion_index = i;
- RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(SDGIShader::PreprocessPushConstant));
-
- Vector3i groups = Vector3i(probe_size + 1, probe_size + 1, probe_size + 1) - offset; //if offset, it's one less probe per axis to compute
- RD::get_singleton()->compute_list_dispatch(compute_list, groups.x, groups.y, groups.z);
- }
- RD::get_singleton()->compute_list_add_barrier(compute_list);
- }
-
- RENDER_TIMESTAMP("SDFGI Store");
-
- // store
- RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, sdfgi_shader.preprocess_pipeline[SDGIShader::PRE_PROCESS_STORE]);
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, rb->sdfgi->cascades[cascade].sdf_store_uniform_set, 0);
- RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(SDGIShader::PreprocessPushConstant));
- RD::get_singleton()->compute_list_dispatch_threads(compute_list, rb->sdfgi->cascade_size, rb->sdfgi->cascade_size, rb->sdfgi->cascade_size);
-
- RD::get_singleton()->compute_list_end();
-
- //clear these textures, as they will have previous garbage on next draw
- RD::get_singleton()->texture_clear(rb->sdfgi->cascades[cascade].light_tex, Color(0, 0, 0, 0), 0, 1, 0, 1);
- RD::get_singleton()->texture_clear(rb->sdfgi->cascades[cascade].light_aniso_0_tex, Color(0, 0, 0, 0), 0, 1, 0, 1);
- RD::get_singleton()->texture_clear(rb->sdfgi->cascades[cascade].light_aniso_1_tex, Color(0, 0, 0, 0), 0, 1, 0, 1);
-
-#if 0
- Vector<uint8_t> data = RD::get_singleton()->texture_get_data(rb->sdfgi->cascades[cascade].sdf, 0);
- Ref<Image> img;
- img.instance();
- for (uint32_t i = 0; i < rb->sdfgi->cascade_size; i++) {
- Vector<uint8_t> subarr = data.subarray(128 * 128 * i, 128 * 128 * (i + 1) - 1);
- img->create(rb->sdfgi->cascade_size, rb->sdfgi->cascade_size, false, Image::FORMAT_L8, subarr);
- img->save_png("res://cascade_sdf_" + itos(cascade) + "_" + itos(i) + ".png");
- }
-
- //finalize render and update sdf
-#endif
-
-#if 0
- Vector<uint8_t> data = RD::get_singleton()->texture_get_data(rb->sdfgi->render_albedo, 0);
- Ref<Image> img;
- img.instance();
- for (uint32_t i = 0; i < rb->sdfgi->cascade_size; i++) {
- Vector<uint8_t> subarr = data.subarray(128 * 128 * i * 2, 128 * 128 * (i + 1) * 2 - 1);
- img->create(rb->sdfgi->cascade_size, rb->sdfgi->cascade_size, false, Image::FORMAT_RGB565, subarr);
- img->convert(Image::FORMAT_RGBA8);
- img->save_png("res://cascade_" + itos(cascade) + "_" + itos(i) + ".png");
- }
-
- //finalize render and update sdf
-#endif
-
- RENDER_TIMESTAMP("<SDFGI Update SDF");
- RD::get_singleton()->draw_command_end_label();
- }
-}
-
void RendererSceneRenderRD::render_particle_collider_heightfield(RID p_collider, const Transform &p_transform, const PagedArray<GeometryInstance *> &p_instances) {
ERR_FAIL_COND(!storage->particles_collision_is_heightfield(p_collider));
Vector3 extents = storage->particles_collision_get_extents(p_collider) * p_transform.basis.get_scale();
@@ -8045,133 +3845,15 @@ void RendererSceneRenderRD::render_particle_collider_heightfield(RID p_collider,
_render_particle_collider_heightfield(fb, cam_xform, cm, p_instances);
}
-void RendererSceneRenderRD::_render_sdfgi_static_lights(RID p_render_buffers, uint32_t p_cascade_count, const uint32_t *p_cascade_indices, const PagedArray<RID> *p_positional_light_cull_result) {
- RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers);
- ERR_FAIL_COND(!rb);
- ERR_FAIL_COND(!rb->sdfgi);
-
- RD::get_singleton()->draw_command_begin_label("SDFGI Render Static Lighs");
-
- _sdfgi_update_cascades(p_render_buffers); //need cascades updated for this
-
- SDGIShader::Light lights[SDFGI::MAX_STATIC_LIGHTS];
- uint32_t light_count[SDFGI::MAX_STATIC_LIGHTS];
-
- for (uint32_t i = 0; i < p_cascade_count; i++) {
- ERR_CONTINUE(p_cascade_indices[i] >= rb->sdfgi->cascades.size());
-
- SDFGI::Cascade &cc = rb->sdfgi->cascades[p_cascade_indices[i]];
-
- { //fill light buffer
-
- AABB cascade_aabb;
- cascade_aabb.position = Vector3((Vector3i(1, 1, 1) * -int32_t(rb->sdfgi->cascade_size >> 1) + cc.position)) * cc.cell_size;
- cascade_aabb.size = Vector3(1, 1, 1) * rb->sdfgi->cascade_size * cc.cell_size;
-
- int idx = 0;
-
- for (uint32_t j = 0; j < (uint32_t)p_positional_light_cull_result[i].size(); j++) {
- if (idx == SDFGI::MAX_STATIC_LIGHTS) {
- break;
- }
-
- LightInstance *li = light_instance_owner.getornull(p_positional_light_cull_result[i][j]);
- ERR_CONTINUE(!li);
-
- uint32_t max_sdfgi_cascade = storage->light_get_max_sdfgi_cascade(li->light);
- if (p_cascade_indices[i] > max_sdfgi_cascade) {
- continue;
- }
-
- if (!cascade_aabb.intersects(li->aabb)) {
- continue;
- }
-
- lights[idx].type = storage->light_get_type(li->light);
-
- Vector3 dir = -li->transform.basis.get_axis(Vector3::AXIS_Z);
- if (lights[idx].type == RS::LIGHT_DIRECTIONAL) {
- dir.y *= rb->sdfgi->y_mult; //only makes sense for directional
- dir.normalize();
- }
- lights[idx].direction[0] = dir.x;
- lights[idx].direction[1] = dir.y;
- lights[idx].direction[2] = dir.z;
- Vector3 pos = li->transform.origin;
- pos.y *= rb->sdfgi->y_mult;
- lights[idx].position[0] = pos.x;
- lights[idx].position[1] = pos.y;
- lights[idx].position[2] = pos.z;
- Color color = storage->light_get_color(li->light);
- color = color.to_linear();
- lights[idx].color[0] = color.r;
- lights[idx].color[1] = color.g;
- lights[idx].color[2] = color.b;
- lights[idx].energy = storage->light_get_param(li->light, RS::LIGHT_PARAM_ENERGY);
- lights[idx].has_shadow = storage->light_has_shadow(li->light);
- lights[idx].attenuation = storage->light_get_param(li->light, RS::LIGHT_PARAM_ATTENUATION);
- lights[idx].radius = storage->light_get_param(li->light, RS::LIGHT_PARAM_RANGE);
- lights[idx].cos_spot_angle = Math::cos(Math::deg2rad(storage->light_get_param(li->light, RS::LIGHT_PARAM_SPOT_ANGLE)));
- lights[idx].inv_spot_attenuation = 1.0f / storage->light_get_param(li->light, RS::LIGHT_PARAM_SPOT_ATTENUATION);
-
- idx++;
- }
-
- if (idx > 0) {
- RD::get_singleton()->buffer_update(cc.lights_buffer, 0, idx * sizeof(SDGIShader::Light), lights);
- }
-
- light_count[i] = idx;
- }
- }
-
- /* Static Lights */
- RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
-
- RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, sdfgi_shader.direct_light_pipeline[SDGIShader::DIRECT_LIGHT_MODE_STATIC]);
-
- SDGIShader::DirectLightPushConstant dl_push_constant;
-
- dl_push_constant.grid_size[0] = rb->sdfgi->cascade_size;
- dl_push_constant.grid_size[1] = rb->sdfgi->cascade_size;
- dl_push_constant.grid_size[2] = rb->sdfgi->cascade_size;
- dl_push_constant.max_cascades = rb->sdfgi->cascades.size();
- dl_push_constant.probe_axis_size = rb->sdfgi->probe_axis_count;
- dl_push_constant.bounce_feedback = 0.0; // this is static light, do not multibounce yet
- dl_push_constant.y_mult = rb->sdfgi->y_mult;
- dl_push_constant.use_occlusion = rb->sdfgi->uses_occlusion;
-
- //all must be processed
- dl_push_constant.process_offset = 0;
- dl_push_constant.process_increment = 1;
-
- for (uint32_t i = 0; i < p_cascade_count; i++) {
- ERR_CONTINUE(p_cascade_indices[i] >= rb->sdfgi->cascades.size());
-
- SDFGI::Cascade &cc = rb->sdfgi->cascades[p_cascade_indices[i]];
-
- dl_push_constant.light_count = light_count[i];
- dl_push_constant.cascade = p_cascade_indices[i];
-
- if (dl_push_constant.light_count > 0) {
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, cc.sdf_direct_light_uniform_set, 0);
- RD::get_singleton()->compute_list_set_push_constant(compute_list, &dl_push_constant, sizeof(SDGIShader::DirectLightPushConstant));
- RD::get_singleton()->compute_list_dispatch_indirect(compute_list, cc.solid_cell_dispatch_buffer, 0);
- }
- }
-
- RD::get_singleton()->compute_list_end();
-
- RD::get_singleton()->draw_command_end_label();
-}
-
bool RendererSceneRenderRD::free(RID p_rid) {
if (render_buffers_owner.owns(p_rid)) {
RenderBuffers *rb = render_buffers_owner.getornull(p_rid);
_free_render_buffer_data(rb);
memdelete(rb->data);
if (rb->sdfgi) {
- _sdfgi_erase(rb);
+ rb->sdfgi->erase();
+ memdelete(rb->sdfgi);
+ rb->sdfgi = nullptr;
}
if (rb->volumetric_fog) {
_volumetric_fog_erase(rb);
@@ -8202,8 +3884,8 @@ bool RendererSceneRenderRD::free(RID p_rid) {
decal_instance_owner.free(p_rid);
} else if (lightmap_instance_owner.owns(p_rid)) {
lightmap_instance_owner.free(p_rid);
- } else if (gi_probe_instance_owner.owns(p_rid)) {
- GIProbeInstance *gi_probe = gi_probe_instance_owner.getornull(p_rid);
+ } else if (gi.gi_probe_instance_owner.owns(p_rid)) {
+ RendererSceneGIRD::GIProbeInstance *gi_probe = gi.gi_probe_instance_owner.getornull(p_rid);
if (gi_probe->texture.is_valid()) {
RD::get_singleton()->free(gi_probe->texture);
RD::get_singleton()->free(gi_probe->write_buffer);
@@ -8214,37 +3896,10 @@ bool RendererSceneRenderRD::free(RID p_rid) {
RD::get_singleton()->free(gi_probe->dynamic_maps[i].depth);
}
- gi_probe_instance_owner.free(p_rid);
- } else if (sky_owner.owns(p_rid)) {
- _update_dirty_skys();
- Sky *sky = sky_owner.getornull(p_rid);
-
- if (sky->radiance.is_valid()) {
- RD::get_singleton()->free(sky->radiance);
- sky->radiance = RID();
- }
- _clear_reflection_data(sky->reflection);
-
- if (sky->uniform_buffer.is_valid()) {
- RD::get_singleton()->free(sky->uniform_buffer);
- sky->uniform_buffer = RID();
- }
-
- if (sky->half_res_pass.is_valid()) {
- RD::get_singleton()->free(sky->half_res_pass);
- sky->half_res_pass = RID();
- }
-
- if (sky->quarter_res_pass.is_valid()) {
- RD::get_singleton()->free(sky->quarter_res_pass);
- sky->quarter_res_pass = RID();
- }
-
- if (sky->material.is_valid()) {
- storage->free(sky->material);
- }
-
- sky_owner.free(p_rid);
+ gi.gi_probe_instance_owner.free(p_rid);
+ } else if (sky.sky_owner.owns(p_rid)) {
+ sky.update_dirty_skys();
+ sky.free_sky(p_rid);
} else if (light_instance_owner.owns(p_rid)) {
LightInstance *light_instance = light_instance_owner.getornull(p_rid);
@@ -8278,7 +3933,7 @@ void RendererSceneRenderRD::set_debug_draw_mode(RS::ViewportDebugDraw p_debug_dr
}
void RendererSceneRenderRD::update() {
- _update_dirty_skys();
+ sky.update_dirty_skys();
}
void RendererSceneRenderRD::set_time(double p_time, double p_step) {
@@ -8404,8 +4059,8 @@ TypedArray<Image> RendererSceneRenderRD::bake_render_uv2(RID p_base, const Vecto
}
void RendererSceneRenderRD::sdfgi_set_debug_probe_select(const Vector3 &p_position, const Vector3 &p_dir) {
- sdfgi_debug_probe_pos = p_position;
- sdfgi_debug_probe_dir = p_dir;
+ gi.sdfgi_debug_probe_pos = p_position;
+ gi.sdfgi_debug_probe_dir = p_dir;
}
RendererSceneRenderRD *RendererSceneRenderRD::singleton = nullptr;
@@ -8441,14 +4096,6 @@ RendererSceneRenderRD::RendererSceneRenderRD(RendererStorageRD *p_storage) {
storage = p_storage;
singleton = this;
- roughness_layers = GLOBAL_GET("rendering/reflections/sky_reflections/roughness_layers");
- sky_ggx_samples_quality = GLOBAL_GET("rendering/reflections/sky_reflections/ggx_samples");
- sky_use_cubemap_array = GLOBAL_GET("rendering/reflections/sky_reflections/texture_array_reflections");
-
- sdfgi_ray_count = RS::EnvironmentSDFGIRayCount(CLAMP(int32_t(GLOBAL_GET("rendering/global_illumination/sdfgi/probe_ray_count")), 0, int32_t(RS::ENV_SDFGI_RAY_COUNT_MAX - 1)));
- sdfgi_frames_to_converge = RS::EnvironmentSDFGIFramesToConverge(CLAMP(int32_t(GLOBAL_GET("rendering/global_illumination/sdfgi/frames_to_converge")), 0, int32_t(RS::ENV_SDFGI_CONVERGE_MAX - 1)));
- sdfgi_frames_to_update_light = RS::EnvironmentSDFGIFramesToUpdateLight(CLAMP(int32_t(GLOBAL_GET("rendering/global_illumination/sdfgi/frames_to_update_lights")), 0, int32_t(RS::ENV_SDFGI_UPDATE_LIGHT_MAX - 1)));
-
directional_shadow.size = GLOBAL_GET("rendering/shadows/directional_shadow/size");
directional_shadow.use_16_bits = GLOBAL_GET("rendering/shadows/directional_shadow/16_bits");
@@ -8461,388 +4108,16 @@ RendererSceneRenderRD::RendererSceneRenderRD(RendererStorageRD *p_storage) {
}
if (!low_end) {
- //kinda complicated to compute the amount of slots, we try to use as many as we can
-
- gi_probe_max_lights = 32;
-
- gi_probe_lights = memnew_arr(GIProbeLight, gi_probe_max_lights);
- gi_probe_lights_uniform = RD::get_singleton()->uniform_buffer_create(gi_probe_max_lights * sizeof(GIProbeLight));
- gi_probe_quality = RS::GIProbeQuality(CLAMP(int(GLOBAL_GET("rendering/global_illumination/gi_probes/quality")), 0, 1));
-
- String defines = "\n#define MAX_LIGHTS " + itos(gi_probe_max_lights) + "\n";
-
- Vector<String> versions;
- versions.push_back("\n#define MODE_COMPUTE_LIGHT\n");
- versions.push_back("\n#define MODE_SECOND_BOUNCE\n");
- versions.push_back("\n#define MODE_UPDATE_MIPMAPS\n");
- versions.push_back("\n#define MODE_WRITE_TEXTURE\n");
- versions.push_back("\n#define MODE_DYNAMIC\n#define MODE_DYNAMIC_LIGHTING\n");
- versions.push_back("\n#define MODE_DYNAMIC\n#define MODE_DYNAMIC_SHRINK\n#define MODE_DYNAMIC_SHRINK_WRITE\n");
- versions.push_back("\n#define MODE_DYNAMIC\n#define MODE_DYNAMIC_SHRINK\n#define MODE_DYNAMIC_SHRINK_PLOT\n");
- versions.push_back("\n#define MODE_DYNAMIC\n#define MODE_DYNAMIC_SHRINK\n#define MODE_DYNAMIC_SHRINK_PLOT\n#define MODE_DYNAMIC_SHRINK_WRITE\n");
-
- giprobe_shader.initialize(versions, defines);
- giprobe_lighting_shader_version = giprobe_shader.version_create();
- for (int i = 0; i < GI_PROBE_SHADER_VERSION_MAX; i++) {
- giprobe_lighting_shader_version_shaders[i] = giprobe_shader.version_get_shader(giprobe_lighting_shader_version, i);
- giprobe_lighting_shader_version_pipelines[i] = RD::get_singleton()->compute_pipeline_create(giprobe_lighting_shader_version_shaders[i]);
- }
- }
-
- if (!low_end) {
- String defines;
- Vector<String> versions;
- versions.push_back("\n#define MODE_DEBUG_COLOR\n");
- versions.push_back("\n#define MODE_DEBUG_LIGHT\n");
- versions.push_back("\n#define MODE_DEBUG_EMISSION\n");
- versions.push_back("\n#define MODE_DEBUG_LIGHT\n#define MODE_DEBUG_LIGHT_FULL\n");
-
- giprobe_debug_shader.initialize(versions, defines);
- giprobe_debug_shader_version = giprobe_debug_shader.version_create();
- for (int i = 0; i < GI_PROBE_DEBUG_MAX; i++) {
- giprobe_debug_shader_version_shaders[i] = giprobe_debug_shader.version_get_shader(giprobe_debug_shader_version, i);
-
- RD::PipelineRasterizationState rs;
- rs.cull_mode = RD::POLYGON_CULL_FRONT;
- RD::PipelineDepthStencilState ds;
- ds.enable_depth_test = true;
- ds.enable_depth_write = true;
- ds.depth_compare_operator = RD::COMPARE_OP_LESS_OR_EQUAL;
-
- giprobe_debug_shader_version_pipelines[i].setup(giprobe_debug_shader_version_shaders[i], RD::RENDER_PRIMITIVE_TRIANGLES, rs, RD::PipelineMultisampleState(), ds, RD::PipelineColorBlendState::create_disabled(), 0);
- }
+ gi.init_gi(storage);
}
/* SKY SHADER */
- {
- // Start with the directional lights for the sky
- sky_scene_state.max_directional_lights = 4;
- uint32_t directional_light_buffer_size = sky_scene_state.max_directional_lights * sizeof(SkyDirectionalLightData);
- sky_scene_state.directional_lights = memnew_arr(SkyDirectionalLightData, sky_scene_state.max_directional_lights);
- sky_scene_state.last_frame_directional_lights = memnew_arr(SkyDirectionalLightData, sky_scene_state.max_directional_lights);
- sky_scene_state.last_frame_directional_light_count = sky_scene_state.max_directional_lights + 1;
- sky_scene_state.directional_light_buffer = RD::get_singleton()->uniform_buffer_create(directional_light_buffer_size);
-
- String defines = "\n#define MAX_DIRECTIONAL_LIGHT_DATA_STRUCTS " + itos(sky_scene_state.max_directional_lights) + "\n";
-
- // Initialize sky
- Vector<String> sky_modes;
- sky_modes.push_back(""); // Full size
- sky_modes.push_back("\n#define USE_HALF_RES_PASS\n"); // Half Res
- sky_modes.push_back("\n#define USE_QUARTER_RES_PASS\n"); // Quarter res
- sky_modes.push_back("\n#define USE_CUBEMAP_PASS\n"); // Cubemap
- sky_modes.push_back("\n#define USE_CUBEMAP_PASS\n#define USE_HALF_RES_PASS\n"); // Half Res Cubemap
- sky_modes.push_back("\n#define USE_CUBEMAP_PASS\n#define USE_QUARTER_RES_PASS\n"); // Quarter res Cubemap
- sky_shader.shader.initialize(sky_modes, defines);
- }
-
- // register our shader funds
- storage->shader_set_data_request_function(RendererStorageRD::SHADER_TYPE_SKY, _create_sky_shader_funcs);
- storage->material_set_data_request_function(RendererStorageRD::SHADER_TYPE_SKY, _create_sky_material_funcs);
-
- {
- ShaderCompilerRD::DefaultIdentifierActions actions;
-
- actions.renames["COLOR"] = "color";
- actions.renames["ALPHA"] = "alpha";
- actions.renames["EYEDIR"] = "cube_normal";
- actions.renames["POSITION"] = "params.position_multiplier.xyz";
- actions.renames["SKY_COORDS"] = "panorama_coords";
- actions.renames["SCREEN_UV"] = "uv";
- actions.renames["TIME"] = "params.time";
- actions.renames["HALF_RES_COLOR"] = "half_res_color";
- actions.renames["QUARTER_RES_COLOR"] = "quarter_res_color";
- actions.renames["RADIANCE"] = "radiance";
- actions.renames["FOG"] = "custom_fog";
- actions.renames["LIGHT0_ENABLED"] = "directional_lights.data[0].enabled";
- actions.renames["LIGHT0_DIRECTION"] = "directional_lights.data[0].direction_energy.xyz";
- actions.renames["LIGHT0_ENERGY"] = "directional_lights.data[0].direction_energy.w";
- actions.renames["LIGHT0_COLOR"] = "directional_lights.data[0].color_size.xyz";
- actions.renames["LIGHT0_SIZE"] = "directional_lights.data[0].color_size.w";
- actions.renames["LIGHT1_ENABLED"] = "directional_lights.data[1].enabled";
- actions.renames["LIGHT1_DIRECTION"] = "directional_lights.data[1].direction_energy.xyz";
- actions.renames["LIGHT1_ENERGY"] = "directional_lights.data[1].direction_energy.w";
- actions.renames["LIGHT1_COLOR"] = "directional_lights.data[1].color_size.xyz";
- actions.renames["LIGHT1_SIZE"] = "directional_lights.data[1].color_size.w";
- actions.renames["LIGHT2_ENABLED"] = "directional_lights.data[2].enabled";
- actions.renames["LIGHT2_DIRECTION"] = "directional_lights.data[2].direction_energy.xyz";
- actions.renames["LIGHT2_ENERGY"] = "directional_lights.data[2].direction_energy.w";
- actions.renames["LIGHT2_COLOR"] = "directional_lights.data[2].color_size.xyz";
- actions.renames["LIGHT2_SIZE"] = "directional_lights.data[2].color_size.w";
- actions.renames["LIGHT3_ENABLED"] = "directional_lights.data[3].enabled";
- actions.renames["LIGHT3_DIRECTION"] = "directional_lights.data[3].direction_energy.xyz";
- actions.renames["LIGHT3_ENERGY"] = "directional_lights.data[3].direction_energy.w";
- actions.renames["LIGHT3_COLOR"] = "directional_lights.data[3].color_size.xyz";
- actions.renames["LIGHT3_SIZE"] = "directional_lights.data[3].color_size.w";
- 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.data";
-
- sky_shader.compiler.initialize(actions);
- }
-
- {
- // default material and shader for sky shader
- sky_shader.default_shader = storage->shader_allocate();
- storage->shader_initialize(sky_shader.default_shader);
-
- storage->shader_set_code(sky_shader.default_shader, "shader_type sky; void fragment() { COLOR = vec3(0.0); } \n");
-
- sky_shader.default_material = storage->material_allocate();
- storage->material_initialize(sky_shader.default_material);
-
- storage->material_set_shader(sky_shader.default_material, sky_shader.default_shader);
-
- SkyMaterialData *md = (SkyMaterialData *)storage->material_get_data(sky_shader.default_material, RendererStorageRD::SHADER_TYPE_SKY);
- sky_shader.default_shader_rd = sky_shader.shader.version_get_shader(md->shader_data->version, SKY_VERSION_BACKGROUND);
-
- sky_scene_state.uniform_buffer = RD::get_singleton()->uniform_buffer_create(sizeof(SkySceneState::UBO));
-
- Vector<RD::Uniform> uniforms;
-
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_SAMPLER;
- u.binding = 0;
- u.ids.resize(12);
- RID *ids_ptr = u.ids.ptrw();
- ids_ptr[0] = storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
- ids_ptr[1] = storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
- ids_ptr[2] = storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
- ids_ptr[3] = storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
- ids_ptr[4] = storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
- ids_ptr[5] = storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
- ids_ptr[6] = storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED);
- ids_ptr[7] = storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED);
- ids_ptr[8] = storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED);
- ids_ptr[9] = storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED);
- ids_ptr[10] = storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED);
- ids_ptr[11] = storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED);
- uniforms.push_back(u);
- }
-
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
- u.binding = 1;
- u.ids.push_back(storage->global_variables_get_storage_buffer());
- uniforms.push_back(u);
- }
-
- {
- RD::Uniform u;
- u.binding = 2;
- u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER;
- u.ids.push_back(sky_scene_state.uniform_buffer);
- uniforms.push_back(u);
- }
-
- {
- RD::Uniform u;
- u.binding = 3;
- u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER;
- u.ids.push_back(sky_scene_state.directional_light_buffer);
- uniforms.push_back(u);
- }
-
- sky_scene_state.uniform_set = RD::get_singleton()->uniform_set_create(uniforms, sky_shader.default_shader_rd, SKY_SET_UNIFORMS);
- }
-
- {
- Vector<RD::Uniform> uniforms;
- {
- RD::Uniform u;
- u.binding = 0;
- u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
- RID vfog = storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_3D_WHITE);
- u.ids.push_back(vfog);
- uniforms.push_back(u);
- }
-
- sky_scene_state.default_fog_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, sky_shader.default_shader_rd, SKY_SET_FOG);
- }
-
- {
- // Need defaults for using fog with clear color
- sky_scene_state.fog_shader = storage->shader_allocate();
- storage->shader_initialize(sky_scene_state.fog_shader);
-
- storage->shader_set_code(sky_scene_state.fog_shader, "shader_type sky; uniform vec4 clear_color; void fragment() { COLOR = clear_color.rgb; } \n");
- sky_scene_state.fog_material = storage->material_allocate();
- storage->material_initialize(sky_scene_state.fog_material);
-
- storage->material_set_shader(sky_scene_state.fog_material, sky_scene_state.fog_shader);
-
- Vector<RD::Uniform> uniforms;
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
- u.binding = 0;
- u.ids.push_back(storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_CUBEMAP_BLACK));
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
- u.binding = 1;
- u.ids.push_back(storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_WHITE));
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
- u.binding = 2;
- u.ids.push_back(storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_WHITE));
- uniforms.push_back(u);
- }
-
- sky_scene_state.fog_only_texture_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, sky_shader.default_shader_rd, SKY_SET_TEXTURES);
- }
+ sky.init(storage);
if (!low_end) {
//SDFGI
- {
- Vector<String> preprocess_modes;
- preprocess_modes.push_back("\n#define MODE_SCROLL\n");
- preprocess_modes.push_back("\n#define MODE_SCROLL_OCCLUSION\n");
- preprocess_modes.push_back("\n#define MODE_INITIALIZE_JUMP_FLOOD\n");
- preprocess_modes.push_back("\n#define MODE_INITIALIZE_JUMP_FLOOD_HALF\n");
- preprocess_modes.push_back("\n#define MODE_JUMPFLOOD\n");
- preprocess_modes.push_back("\n#define MODE_JUMPFLOOD_OPTIMIZED\n");
- preprocess_modes.push_back("\n#define MODE_UPSCALE_JUMP_FLOOD\n");
- preprocess_modes.push_back("\n#define MODE_OCCLUSION\n");
- preprocess_modes.push_back("\n#define MODE_STORE\n");
- String defines = "\n#define OCCLUSION_SIZE " + itos(SDFGI::CASCADE_SIZE / SDFGI::PROBE_DIVISOR) + "\n";
- sdfgi_shader.preprocess.initialize(preprocess_modes, defines);
- sdfgi_shader.preprocess_shader = sdfgi_shader.preprocess.version_create();
- for (int i = 0; i < SDGIShader::PRE_PROCESS_MAX; i++) {
- sdfgi_shader.preprocess_pipeline[i] = RD::get_singleton()->compute_pipeline_create(sdfgi_shader.preprocess.version_get_shader(sdfgi_shader.preprocess_shader, i));
- }
- }
-
- {
- //calculate tables
- String defines = "\n#define OCT_SIZE " + itos(SDFGI::LIGHTPROBE_OCT_SIZE) + "\n";
-
- Vector<String> direct_light_modes;
- direct_light_modes.push_back("\n#define MODE_PROCESS_STATIC\n");
- direct_light_modes.push_back("\n#define MODE_PROCESS_DYNAMIC\n");
- sdfgi_shader.direct_light.initialize(direct_light_modes, defines);
- sdfgi_shader.direct_light_shader = sdfgi_shader.direct_light.version_create();
- for (int i = 0; i < SDGIShader::DIRECT_LIGHT_MODE_MAX; i++) {
- sdfgi_shader.direct_light_pipeline[i] = RD::get_singleton()->compute_pipeline_create(sdfgi_shader.direct_light.version_get_shader(sdfgi_shader.direct_light_shader, i));
- }
- }
-
- {
- //calculate tables
- String defines = "\n#define OCT_SIZE " + itos(SDFGI::LIGHTPROBE_OCT_SIZE) + "\n";
- defines += "\n#define SH_SIZE " + itos(SDFGI::SH_SIZE) + "\n";
- if (sky_use_cubemap_array) {
- defines += "\n#define USE_CUBEMAP_ARRAY\n";
- }
-
- Vector<String> integrate_modes;
- integrate_modes.push_back("\n#define MODE_PROCESS\n");
- integrate_modes.push_back("\n#define MODE_STORE\n");
- integrate_modes.push_back("\n#define MODE_SCROLL\n");
- integrate_modes.push_back("\n#define MODE_SCROLL_STORE\n");
- sdfgi_shader.integrate.initialize(integrate_modes, defines);
- sdfgi_shader.integrate_shader = sdfgi_shader.integrate.version_create();
-
- for (int i = 0; i < SDGIShader::INTEGRATE_MODE_MAX; i++) {
- sdfgi_shader.integrate_pipeline[i] = RD::get_singleton()->compute_pipeline_create(sdfgi_shader.integrate.version_get_shader(sdfgi_shader.integrate_shader, i));
- }
-
- {
- Vector<RD::Uniform> uniforms;
-
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
- u.binding = 0;
- u.ids.push_back(storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_CUBEMAP_WHITE));
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_SAMPLER;
- u.binding = 1;
- u.ids.push_back(storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED));
- uniforms.push_back(u);
- }
-
- sdfgi_shader.integrate_default_sky_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, sdfgi_shader.integrate.version_get_shader(sdfgi_shader.integrate_shader, 0), 1);
- }
- }
- //GK
- {
- //calculate tables
- String defines = "\n#define SDFGI_OCT_SIZE " + itos(SDFGI::LIGHTPROBE_OCT_SIZE) + "\n";
- Vector<String> gi_modes;
- gi_modes.push_back("\n#define USE_GIPROBES\n");
- gi_modes.push_back("\n#define USE_SDFGI\n");
- gi_modes.push_back("\n#define USE_SDFGI\n\n#define USE_GIPROBES\n");
- gi_modes.push_back("\n#define MODE_HALF_RES\n#define USE_GIPROBES\n");
- gi_modes.push_back("\n#define MODE_HALF_RES\n#define USE_SDFGI\n");
- gi_modes.push_back("\n#define MODE_HALF_RES\n#define USE_SDFGI\n\n#define USE_GIPROBES\n");
-
- gi.shader.initialize(gi_modes, defines);
- gi.shader_version = gi.shader.version_create();
- for (int i = 0; i < GI::MODE_MAX; i++) {
- gi.pipelines[i] = RD::get_singleton()->compute_pipeline_create(gi.shader.version_get_shader(gi.shader_version, i));
- }
-
- gi.sdfgi_ubo = RD::get_singleton()->uniform_buffer_create(sizeof(GI::SDFGIData));
- }
- {
- String defines = "\n#define OCT_SIZE " + itos(SDFGI::LIGHTPROBE_OCT_SIZE) + "\n";
- Vector<String> debug_modes;
- debug_modes.push_back("");
- sdfgi_shader.debug.initialize(debug_modes, defines);
- sdfgi_shader.debug_shader = sdfgi_shader.debug.version_create();
- sdfgi_shader.debug_shader_version = sdfgi_shader.debug.version_get_shader(sdfgi_shader.debug_shader, 0);
- sdfgi_shader.debug_pipeline = RD::get_singleton()->compute_pipeline_create(sdfgi_shader.debug_shader_version);
- }
- {
- String defines = "\n#define OCT_SIZE " + itos(SDFGI::LIGHTPROBE_OCT_SIZE) + "\n";
-
- Vector<String> versions;
- versions.push_back("\n#define MODE_PROBES\n");
- versions.push_back("\n#define MODE_VISIBILITY\n");
-
- sdfgi_shader.debug_probes.initialize(versions, defines);
- sdfgi_shader.debug_probes_shader = sdfgi_shader.debug_probes.version_create();
-
- {
- RD::PipelineRasterizationState rs;
- rs.cull_mode = RD::POLYGON_CULL_DISABLED;
- RD::PipelineDepthStencilState ds;
- ds.enable_depth_test = true;
- ds.enable_depth_write = true;
- ds.depth_compare_operator = RD::COMPARE_OP_LESS_OR_EQUAL;
- for (int i = 0; i < SDGIShader::PROBE_DEBUG_MAX; i++) {
- RID debug_probes_shader_version = sdfgi_shader.debug_probes.version_get_shader(sdfgi_shader.debug_probes_shader, i);
- sdfgi_shader.debug_probes_pipeline[i].setup(debug_probes_shader_version, RD::RENDER_PRIMITIVE_TRIANGLE_STRIPS, rs, RD::PipelineMultisampleState(), ds, RD::PipelineColorBlendState::create_disabled(), 0);
- }
- }
- }
- default_giprobe_buffer = RD::get_singleton()->uniform_buffer_create(sizeof(GI::GIProbeData) * RenderBuffers::MAX_GIPROBES);
+ gi.init_sdfgi(&sky);
}
{ //decals
@@ -8935,40 +4210,27 @@ RendererSceneRenderRD::~RendererSceneRenderRD() {
RD::get_singleton()->free(E->get().cubemap);
}
- if (sky_scene_state.uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(sky_scene_state.uniform_set)) {
- RD::get_singleton()->free(sky_scene_state.uniform_set);
+ if (sky.sky_scene_state.uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(sky.sky_scene_state.uniform_set)) {
+ RD::get_singleton()->free(sky.sky_scene_state.uniform_set);
}
if (!low_end) {
- RD::get_singleton()->free(default_giprobe_buffer);
- RD::get_singleton()->free(gi_probe_lights_uniform);
- RD::get_singleton()->free(gi.sdfgi_ubo);
-
- giprobe_debug_shader.version_free(giprobe_debug_shader_version);
- giprobe_shader.version_free(giprobe_lighting_shader_version);
- gi.shader.version_free(gi.shader_version);
- sdfgi_shader.debug_probes.version_free(sdfgi_shader.debug_probes_shader);
- sdfgi_shader.debug.version_free(sdfgi_shader.debug_shader);
- sdfgi_shader.direct_light.version_free(sdfgi_shader.direct_light_shader);
- sdfgi_shader.integrate.version_free(sdfgi_shader.integrate_shader);
- sdfgi_shader.preprocess.version_free(sdfgi_shader.preprocess_shader);
+ gi.free();
volumetric_fog.shader.version_free(volumetric_fog.shader_version);
RD::get_singleton()->free(volumetric_fog.params_ubo);
-
- memdelete_arr(gi_probe_lights);
}
- SkyMaterialData *md = (SkyMaterialData *)storage->material_get_data(sky_shader.default_material, RendererStorageRD::SHADER_TYPE_SKY);
- sky_shader.shader.version_free(md->shader_data->version);
- RD::get_singleton()->free(sky_scene_state.directional_light_buffer);
- RD::get_singleton()->free(sky_scene_state.uniform_buffer);
- memdelete_arr(sky_scene_state.directional_lights);
- memdelete_arr(sky_scene_state.last_frame_directional_lights);
- storage->free(sky_shader.default_shader);
- storage->free(sky_shader.default_material);
- storage->free(sky_scene_state.fog_shader);
- storage->free(sky_scene_state.fog_material);
+ RendererSceneSkyRD::SkyMaterialData *md = (RendererSceneSkyRD::SkyMaterialData *)storage->material_get_data(sky.sky_shader.default_material, RendererStorageRD::SHADER_TYPE_SKY);
+ sky.sky_shader.shader.version_free(md->shader_data->version);
+ RD::get_singleton()->free(sky.sky_scene_state.directional_light_buffer);
+ RD::get_singleton()->free(sky.sky_scene_state.uniform_buffer);
+ memdelete_arr(sky.sky_scene_state.directional_lights);
+ memdelete_arr(sky.sky_scene_state.last_frame_directional_lights);
+ storage->free(sky.sky_shader.default_shader);
+ storage->free(sky.sky_shader.default_material);
+ storage->free(sky.sky_scene_state.fog_shader);
+ storage->free(sky.sky_scene_state.fog_material);
memdelete_arr(directional_penumbra_shadow_kernel);
memdelete_arr(directional_soft_shadow_kernel);
memdelete_arr(penumbra_shadow_kernel);
diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.h b/servers/rendering/renderer_rd/renderer_scene_render_rd.h
index e4eaa93212..d41dd3358d 100644
--- a/servers/rendering/renderer_rd/renderer_scene_render_rd.h
+++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.h
@@ -35,68 +35,21 @@
#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/renderer_scene_environment_rd.h"
+#include "servers/rendering/renderer_rd/renderer_scene_gi_rd.h"
+#include "servers/rendering/renderer_rd/renderer_scene_sky_rd.h"
#include "servers/rendering/renderer_rd/renderer_storage_rd.h"
-#include "servers/rendering/renderer_rd/shaders/gi.glsl.gen.h"
-#include "servers/rendering/renderer_rd/shaders/giprobe.glsl.gen.h"
-#include "servers/rendering/renderer_rd/shaders/giprobe_debug.glsl.gen.h"
-#include "servers/rendering/renderer_rd/shaders/sdfgi_debug.glsl.gen.h"
-#include "servers/rendering/renderer_rd/shaders/sdfgi_debug_probes.glsl.gen.h"
-#include "servers/rendering/renderer_rd/shaders/sdfgi_direct_light.glsl.gen.h"
-#include "servers/rendering/renderer_rd/shaders/sdfgi_integrate.glsl.gen.h"
-#include "servers/rendering/renderer_rd/shaders/sdfgi_preprocess.glsl.gen.h"
-#include "servers/rendering/renderer_rd/shaders/sky.glsl.gen.h"
#include "servers/rendering/renderer_rd/shaders/volumetric_fog.glsl.gen.h"
#include "servers/rendering/renderer_scene_render.h"
#include "servers/rendering/rendering_device.h"
class RendererSceneRenderRD : public RendererSceneRender {
+ friend RendererSceneSkyRD;
+ friend RendererSceneGIRD;
+
protected:
double time;
-
- // Skys need less info from Directional Lights than the normal shaders
- struct SkyDirectionalLightData {
- float direction[3];
- float energy;
- float color[3];
- float size;
- uint32_t enabled;
- uint32_t pad[3];
- };
-
- struct SkySceneState {
- struct UBO {
- uint32_t volumetric_fog_enabled;
- float volumetric_fog_inv_length;
- float volumetric_fog_detail_spread;
-
- float fog_aerial_perspective;
-
- float fog_light_color[3];
- float fog_sun_scatter;
-
- uint32_t fog_enabled;
- float fog_density;
-
- float z_far;
- uint32_t directional_light_count;
- };
-
- UBO ubo;
-
- SkyDirectionalLightData *directional_lights;
- SkyDirectionalLightData *last_frame_directional_lights;
- uint32_t max_directional_lights;
- uint32_t last_frame_directional_light_count;
- RID directional_light_buffer;
- RID uniform_set;
- RID uniform_buffer;
- RID fog_uniform_set;
- RID default_fog_uniform_set;
-
- RID fog_shader;
- RID fog_material;
- RID fog_only_texture_uniform_set;
- } sky_scene_state;
+ double time_step = 0;
struct RenderBufferData {
virtual void configure(RID p_color_buffer, RID p_depth_buffer, int p_width, int p_height, RS::ViewportMSAA p_msaa) = 0;
@@ -107,7 +60,6 @@ protected:
void _setup_lights(const PagedArray<RID> &p_lights, const Transform &p_camera_transform, RID p_shadow_atlas, bool p_using_shadows, uint32_t &r_directional_light_count, uint32_t &r_positional_light_count);
void _setup_decals(const PagedArray<RID> &p_decals, const Transform &p_camera_inverse_xform);
void _setup_reflections(const PagedArray<RID> &p_reflections, const Transform &p_camera_inverse_transform, RID p_environment);
- void _setup_giprobes(RID p_render_buffers, const Transform &p_transform, const PagedArray<RID> &p_gi_probes, uint32_t &r_gi_probes_used);
virtual void _render_scene(RID p_render_buffer, const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, const PagedArray<GeometryInstance *> &p_instances, const PagedArray<RID> &p_gi_probes, const PagedArray<RID> &p_lightmaps, RID p_environment, RID p_cluster_buffer, uint32_t p_cluster_size, uint32_t p_cluster_max_elements, RID p_camera_effects, RID p_shadow_atlas, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, const Color &p_default_color, float p_screen_lod_threshold) = 0;
@@ -121,7 +73,6 @@ protected:
virtual void _render_sdfgi(RID p_render_buffers, const Vector3i &p_from, const Vector3i &p_size, const AABB &p_bounds, const PagedArray<GeometryInstance *> &p_instances, const RID &p_albedo_texture, const RID &p_emission_texture, const RID &p_emission_aniso_texture, const RID &p_geom_facing_texture) = 0;
virtual void _render_particle_collider_heightfield(RID p_fb, const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, const PagedArray<GeometryInstance *> &p_instances) = 0;
- virtual void _debug_giprobe(RID p_gi_probe, RenderingDevice::DrawListID p_draw_list, RID p_framebuffer, const CameraMatrix &p_camera_with_transform, bool p_lighting, bool p_emission, float p_alpha);
void _debug_sdfgi_probes(RID p_render_buffers, RD::DrawListID p_draw_list, RID p_framebuffer, const CameraMatrix &p_camera_with_transform);
RenderBufferData *render_buffers_get_data(RID p_render_buffers);
@@ -134,12 +85,6 @@ protected:
void _process_ssr(RID p_render_buffers, RID p_dest_framebuffer, RID p_normal_buffer, RID p_specular_buffer, RID p_metallic, const Color &p_metallic_mask, RID p_environment, const CameraMatrix &p_projection, bool p_use_additive);
void _process_sss(RID p_render_buffers, const CameraMatrix &p_camera);
- void _setup_sky(RID p_environment, RID p_render_buffers, const CameraMatrix &p_projection, const Transform &p_transform, const Size2i p_screen_size);
- void _update_sky(RID p_environment, const CameraMatrix &p_projection, const Transform &p_transform);
- void _draw_sky(bool p_can_continue_color, bool p_can_continue_depth, RID p_fb, RID p_environment, const CameraMatrix &p_projection, const Transform &p_transform);
- void _pre_process_gi(RID p_render_buffers, const Transform &p_transform);
- void _process_gi(RID p_render_buffers, RID p_normal_roughness_buffer, RID p_gi_probe_buffer, RID p_environment, const CameraMatrix &p_projection, const Transform &p_transform, const PagedArray<RID> &p_gi_probes);
-
bool _needs_post_prepass_render(bool p_use_gi);
void _post_prepass_render(bool p_use_gi);
void _pre_resolve_render(bool p_use_gi);
@@ -150,191 +95,24 @@ protected:
// needed for a single argument calls (material and uv2)
PagedArrayPool<GeometryInstance *> cull_argument_pool;
PagedArray<GeometryInstance *> cull_argument; //need this to exist
+
+ RendererSceneGIRD gi;
+ RendererSceneSkyRD sky;
+
+ RendererSceneEnvironmentRD *get_environment(RID p_environment) {
+ if (p_environment.is_valid()) {
+ return environment_owner.getornull(p_environment);
+ } else {
+ return nullptr;
+ }
+ }
+
private:
RS::ViewportDebugDraw debug_draw = RS::VIEWPORT_DEBUG_DRAW_DISABLED;
- double time_step = 0;
static RendererSceneRenderRD *singleton;
- int roughness_layers;
-
RendererStorageRD *storage;
- struct ReflectionData {
- struct Layer {
- struct Mipmap {
- RID framebuffers[6];
- RID views[6];
- Size2i size;
- };
- Vector<Mipmap> mipmaps; //per-face view
- Vector<RID> views; // per-cubemap view
- };
-
- struct DownsampleLayer {
- struct Mipmap {
- RID view;
- Size2i size;
- };
- Vector<Mipmap> mipmaps;
- };
-
- RID radiance_base_cubemap; //cubemap for first layer, first cubemap
- RID downsampled_radiance_cubemap;
- DownsampleLayer downsampled_layer;
- RID coefficient_buffer;
-
- bool dirty = true;
-
- Vector<Layer> layers;
- };
-
- void _clear_reflection_data(ReflectionData &rd);
- void _update_reflection_data(ReflectionData &rd, int p_size, int p_mipmaps, bool p_use_array, RID p_base_cube, int p_base_layer, bool p_low_quality);
- void _create_reflection_fast_filter(ReflectionData &rd, bool p_use_arrays);
- void _create_reflection_importance_sample(ReflectionData &rd, bool p_use_arrays, int p_cube_side, int p_base_layer);
- void _update_reflection_mipmaps(ReflectionData &rd, int p_start, int p_end);
-
- /* Sky shader */
-
- enum SkyVersion {
- SKY_VERSION_BACKGROUND,
- SKY_VERSION_HALF_RES,
- SKY_VERSION_QUARTER_RES,
- SKY_VERSION_CUBEMAP,
- SKY_VERSION_CUBEMAP_HALF_RES,
- SKY_VERSION_CUBEMAP_QUARTER_RES,
- SKY_VERSION_MAX
- };
-
- struct SkyShader {
- SkyShaderRD shader;
- ShaderCompilerRD compiler;
-
- RID default_shader;
- RID default_material;
- RID default_shader_rd;
- } sky_shader;
-
- struct SkyShaderData : public RendererStorageRD::ShaderData {
- bool valid;
- RID version;
-
- PipelineCacheRD pipelines[SKY_VERSION_MAX];
- Map<StringName, ShaderLanguage::ShaderNode::Uniform> uniforms;
- Vector<ShaderCompilerRD::GeneratedCode::Texture> texture_uniforms;
-
- Vector<uint32_t> ubo_offsets;
- uint32_t ubo_size;
-
- String path;
- String code;
- Map<StringName, 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);
- virtual void get_param_list(List<PropertyInfo> *p_param_list) const;
- virtual void get_instance_param_list(List<RendererStorage::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();
- };
-
- RendererStorageRD::ShaderData *_create_sky_shader_func();
- static RendererStorageRD::ShaderData *_create_sky_shader_funcs() {
- return static_cast<RendererSceneRenderRD *>(singleton)->_create_sky_shader_func();
- };
-
- struct SkyMaterialData : public RendererStorageRD::MaterialData {
- uint64_t last_frame;
- SkyShaderData *shader_data;
- RID uniform_buffer;
- RID uniform_set;
- Vector<RID> texture_cache;
- Vector<uint8_t> ubo_data;
- bool uniform_set_updated;
-
- 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 ~SkyMaterialData();
- };
-
- RendererStorageRD::MaterialData *_create_sky_material_func(SkyShaderData *p_shader);
- static RendererStorageRD::MaterialData *_create_sky_material_funcs(RendererStorageRD::ShaderData *p_shader) {
- return static_cast<RendererSceneRenderRD *>(singleton)->_create_sky_material_func(static_cast<SkyShaderData *>(p_shader));
- };
-
- enum SkyTextureSetVersion {
- SKY_TEXTURE_SET_BACKGROUND,
- SKY_TEXTURE_SET_HALF_RES,
- SKY_TEXTURE_SET_QUARTER_RES,
- SKY_TEXTURE_SET_CUBEMAP,
- SKY_TEXTURE_SET_CUBEMAP_HALF_RES,
- SKY_TEXTURE_SET_CUBEMAP_QUARTER_RES,
- SKY_TEXTURE_SET_MAX
- };
-
- enum SkySet {
- SKY_SET_UNIFORMS,
- SKY_SET_MATERIAL,
- SKY_SET_TEXTURES,
- SKY_SET_FOG,
- SKY_SET_MAX
- };
-
- /* SKY */
- struct Sky {
- RID radiance;
- RID half_res_pass;
- RID half_res_framebuffer;
- RID quarter_res_pass;
- RID quarter_res_framebuffer;
- Size2i screen_size;
-
- RID texture_uniform_sets[SKY_TEXTURE_SET_MAX];
- RID uniform_set;
-
- RID material;
- RID uniform_buffer;
-
- int radiance_size = 256;
-
- RS::SkyMode mode = RS::SKY_MODE_AUTOMATIC;
-
- ReflectionData reflection;
- bool dirty = false;
- int processing_layer = 0;
- Sky *dirty_list = nullptr;
-
- //State to track when radiance cubemap needs updating
- SkyMaterialData *prev_material;
- Vector3 prev_position;
- float prev_time;
-
- RID sdfgi_integrate_sky_uniform_set;
- };
-
- Sky *dirty_sky_list = nullptr;
-
- void _sky_invalidate(Sky *p_sky);
- void _update_dirty_skys();
- RID _get_sky_textures(Sky *p_sky, SkyTextureSetVersion p_version);
-
- uint32_t sky_ggx_samples_quality;
- bool sky_use_cubemap_array;
-
- mutable RID_Owner<Sky, true> sky_owner;
-
/* REFLECTION ATLAS */
struct ReflectionAtlas {
@@ -347,7 +125,7 @@ private:
struct Reflection {
RID owner;
- ReflectionData data;
+ RendererSceneSkyRD::ReflectionData data;
RID fbs[6];
};
@@ -397,151 +175,6 @@ private:
mutable RID_Owner<LightmapInstance> lightmap_instance_owner;
- /* GIPROBE INSTANCE */
-
- struct GIProbeLight {
- uint32_t type;
- float energy;
- float radius;
- float attenuation;
-
- float color[3];
- float cos_spot_angle;
-
- float position[3];
- float inv_spot_attenuation;
-
- float direction[3];
- uint32_t has_shadow;
- };
-
- struct GIProbePushConstant {
- int32_t limits[3];
- uint32_t stack_size;
-
- float emission_scale;
- float propagation;
- float dynamic_range;
- uint32_t light_count;
-
- uint32_t cell_offset;
- uint32_t cell_count;
- float aniso_strength;
- uint32_t pad;
- };
-
- struct GIProbeDynamicPushConstant {
- int32_t limits[3];
- uint32_t light_count;
- int32_t x_dir[3];
- float z_base;
- int32_t y_dir[3];
- float z_sign;
- int32_t z_dir[3];
- float pos_multiplier;
- uint32_t rect_pos[2];
- uint32_t rect_size[2];
- uint32_t prev_rect_ofs[2];
- uint32_t prev_rect_size[2];
- uint32_t flip_x;
- uint32_t flip_y;
- float dynamic_range;
- uint32_t on_mipmap;
- float propagation;
- float pad[3];
- };
-
- struct GIProbeInstance {
- RID probe;
- RID texture;
- RID write_buffer;
-
- struct Mipmap {
- RID texture;
- RID uniform_set;
- RID second_bounce_uniform_set;
- RID write_uniform_set;
- uint32_t level;
- uint32_t cell_offset;
- uint32_t cell_count;
- };
- Vector<Mipmap> mipmaps;
-
- struct DynamicMap {
- RID texture; //color normally, or emission on first pass
- RID fb_depth; //actual depth buffer for the first pass, float depth for later passes
- RID depth; //actual depth buffer for the first pass, float depth for later passes
- RID normal; //normal buffer for the first pass
- RID albedo; //emission buffer for the first pass
- RID orm; //orm buffer for the first pass
- RID fb; //used for rendering, only valid on first map
- RID uniform_set;
- uint32_t size;
- int mipmap; // mipmap to write to, -1 if no mipmap assigned
- };
-
- Vector<DynamicMap> dynamic_maps;
-
- int slot = -1;
- uint32_t last_probe_version = 0;
- uint32_t last_probe_data_version = 0;
-
- //uint64_t last_pass = 0;
- uint32_t render_index = 0;
-
- bool has_dynamic_object_data = false;
-
- Transform transform;
- };
-
- GIProbeLight *gi_probe_lights;
- uint32_t gi_probe_max_lights;
- RID gi_probe_lights_uniform;
-
- enum {
- GI_PROBE_SHADER_VERSION_COMPUTE_LIGHT,
- GI_PROBE_SHADER_VERSION_COMPUTE_SECOND_BOUNCE,
- GI_PROBE_SHADER_VERSION_COMPUTE_MIPMAP,
- GI_PROBE_SHADER_VERSION_WRITE_TEXTURE,
- GI_PROBE_SHADER_VERSION_DYNAMIC_OBJECT_LIGHTING,
- GI_PROBE_SHADER_VERSION_DYNAMIC_SHRINK_WRITE,
- GI_PROBE_SHADER_VERSION_DYNAMIC_SHRINK_PLOT,
- GI_PROBE_SHADER_VERSION_DYNAMIC_SHRINK_WRITE_PLOT,
- GI_PROBE_SHADER_VERSION_MAX
- };
- GiprobeShaderRD giprobe_shader;
- RID giprobe_lighting_shader_version;
- RID giprobe_lighting_shader_version_shaders[GI_PROBE_SHADER_VERSION_MAX];
- RID giprobe_lighting_shader_version_pipelines[GI_PROBE_SHADER_VERSION_MAX];
-
- mutable RID_Owner<GIProbeInstance> gi_probe_instance_owner;
-
- RS::GIProbeQuality gi_probe_quality = RS::GI_PROBE_QUALITY_HIGH;
-
- enum {
- GI_PROBE_DEBUG_COLOR,
- GI_PROBE_DEBUG_LIGHT,
- GI_PROBE_DEBUG_EMISSION,
- GI_PROBE_DEBUG_LIGHT_FULL,
- GI_PROBE_DEBUG_MAX
- };
-
- struct GIProbeDebugPushConstant {
- float projection[16];
- uint32_t cell_offset;
- float dynamic_range;
- float alpha;
- uint32_t level;
- int32_t bounds[3];
- uint32_t pad;
- };
-
- GiprobeDebugShaderRD giprobe_debug_shader;
- RID giprobe_debug_shader_version;
- RID giprobe_debug_shader_version_shaders[GI_PROBE_DEBUG_MAX];
- PipelineCacheRD giprobe_debug_shader_version_pipelines[GI_PROBE_DEBUG_MAX];
- RID giprobe_debug_uniform_set;
-
/* SHADOW ATLAS */
struct ShadowShrinkStage {
@@ -690,111 +323,6 @@ private:
/* ENVIRONMENT */
- struct Environment {
- // BG
- RS::EnvironmentBG background = RS::ENV_BG_CLEAR_COLOR;
- RID sky;
- float sky_custom_fov = 0.0;
- Basis sky_orientation;
- Color bg_color;
- float bg_energy = 1.0;
- int canvas_max_layer = 0;
- RS::EnvironmentAmbientSource ambient_source = RS::ENV_AMBIENT_SOURCE_BG;
- Color ambient_light;
- float ambient_light_energy = 1.0;
- float ambient_sky_contribution = 1.0;
- RS::EnvironmentReflectionSource reflection_source = RS::ENV_REFLECTION_SOURCE_BG;
- Color ao_color;
-
- /// Tonemap
-
- RS::EnvironmentToneMapper tone_mapper;
- float exposure = 1.0;
- float white = 1.0;
- bool auto_exposure = false;
- float min_luminance = 0.2;
- float max_luminance = 8.0;
- float auto_exp_speed = 0.2;
- float auto_exp_scale = 0.5;
- uint64_t auto_exposure_version = 0;
-
- // Fog
- bool fog_enabled = false;
- Color fog_light_color = Color(0.5, 0.6, 0.7);
- float fog_light_energy = 1.0;
- float fog_sun_scatter = 0.0;
- float fog_density = 0.001;
- float fog_height = 0.0;
- float fog_height_density = 0.0; //can be negative to invert effect
- float fog_aerial_perspective = 0.0;
-
- /// Volumetric Fog
- ///
- bool volumetric_fog_enabled = false;
- float volumetric_fog_density = 0.01;
- Color volumetric_fog_light = Color(0, 0, 0);
- float volumetric_fog_light_energy = 0.0;
- float volumetric_fog_length = 64.0;
- float volumetric_fog_detail_spread = 2.0;
- float volumetric_fog_gi_inject = 0.0;
- bool volumetric_fog_temporal_reprojection = true;
- float volumetric_fog_temporal_reprojection_amount = 0.9;
-
- /// Glow
-
- bool glow_enabled = false;
- Vector<float> glow_levels;
- float glow_intensity = 0.8;
- float glow_strength = 1.0;
- float glow_bloom = 0.0;
- float glow_mix = 0.01;
- RS::EnvironmentGlowBlendMode glow_blend_mode = RS::ENV_GLOW_BLEND_MODE_SOFTLIGHT;
- float glow_hdr_bleed_threshold = 1.0;
- float glow_hdr_luminance_cap = 12.0;
- float glow_hdr_bleed_scale = 2.0;
-
- /// SSAO
-
- bool ssao_enabled = false;
- float ssao_radius = 1.0;
- float ssao_intensity = 2.0;
- float ssao_power = 1.5;
- float ssao_detail = 0.5;
- float ssao_horizon = 0.06;
- float ssao_sharpness = 0.98;
- float ssao_direct_light_affect = 0.0;
- float ssao_ao_channel_affect = 0.0;
-
- /// SSR
- ///
- bool ssr_enabled = false;
- int ssr_max_steps = 64;
- float ssr_fade_in = 0.15;
- float ssr_fade_out = 2.0;
- float ssr_depth_tolerance = 0.2;
-
- /// SDFGI
- bool sdfgi_enabled = false;
- RS::EnvironmentSDFGICascades sdfgi_cascades;
- float sdfgi_min_cell_size = 0.2;
- bool sdfgi_use_occlusion = false;
- float sdfgi_bounce_feedback = 0.0;
- bool sdfgi_read_sky_light = false;
- float sdfgi_energy = 1.0;
- float sdfgi_normal_bias = 1.1;
- float sdfgi_probe_bias = 1.1;
- RS::EnvironmentSDFGIYScale sdfgi_y_scale = RS::ENV_SDFGI_Y_SCALE_DISABLED;
-
- /// Adjustments
-
- bool adjustments_enabled = false;
- float adjustments_brightness = 1.0f;
- float adjustments_contrast = 1.0f;
- float adjustments_saturation = 1.0f;
- bool use_1d_color_correction = false;
- RID color_correction = RID();
- };
-
RS::EnvironmentSSAOQuality ssao_quality = RS::ENV_SSAO_QUALITY_MEDIUM;
bool ssao_half_size = false;
bool ssao_using_half_size = false;
@@ -807,9 +335,7 @@ private:
bool glow_high_quality = false;
RS::EnvironmentSSRRoughnessQuality ssr_roughness_quality = RS::ENV_SSR_ROUGNESS_QUALITY_LOW;
- static uint64_t auto_exposure_counter;
-
- mutable RID_Owner<Environment, true> environment_owner;
+ mutable RID_Owner<RendererSceneEnvironmentRD, true> environment_owner;
/* CAMERA EFFECTS */
@@ -842,14 +368,9 @@ private:
ClusterBuilderSharedDataRD cluster_builder_shared;
ClusterBuilderRD *current_cluster_builder = nullptr;
- struct SDFGI;
struct VolumetricFog;
struct RenderBuffers {
- enum {
- MAX_GIPROBES = 8
- };
-
RenderBufferData *data = nullptr;
int width = 0, height = 0;
RS::ViewportMSAA msaa = RS::VIEWPORT_MSAA_DISABLED;
@@ -864,7 +385,7 @@ private:
RID depth_texture; //main depth texture
RID gi_uniform_set;
- SDFGI *sdfgi = nullptr;
+ RendererSceneGIRD::SDFGI *sdfgi = nullptr;
VolumetricFog *volumetric_fog = nullptr;
ClusterBuilderRD *cluster_builder = nullptr;
@@ -906,414 +427,14 @@ private:
RID blur_radius[2];
} ssr;
- RID giprobe_textures[MAX_GIPROBES];
- RID giprobe_buffer;
-
RID ambient_buffer;
RID reflection_buffer;
bool using_half_size_gi = false;
- struct GI {
- RID full_buffer;
- RID full_dispatch;
- RID full_mask;
- } gi;
+ RendererSceneGIRD::RenderBuffersGI gi;
};
- RID default_giprobe_buffer;
-
- /* SDFGI */
-
- struct SDFGI {
- enum {
- MAX_CASCADES = 8,
- CASCADE_SIZE = 128,
- PROBE_DIVISOR = 16,
- ANISOTROPY_SIZE = 6,
- MAX_DYNAMIC_LIGHTS = 128,
- MAX_STATIC_LIGHTS = 1024,
- LIGHTPROBE_OCT_SIZE = 6,
- SH_SIZE = 16
- };
-
- struct Cascade {
- struct UBO {
- float offset[3];
- float to_cell;
- int32_t probe_offset[3];
- uint32_t pad;
- };
-
- //cascade blocks are full-size for volume (128^3), half size for albedo/emission
- RID sdf_tex;
- RID light_tex;
- RID light_aniso_0_tex;
- RID light_aniso_1_tex;
-
- RID light_data;
- RID light_aniso_0_data;
- RID light_aniso_1_data;
-
- struct SolidCell { // this struct is unused, but remains as reference for size
- uint32_t position;
- uint32_t albedo;
- uint32_t static_light;
- uint32_t static_light_aniso;
- };
-
- RID solid_cell_dispatch_buffer; //buffer for indirect compute dispatch
- RID solid_cell_buffer;
-
- RID lightprobe_history_tex;
- RID lightprobe_average_tex;
-
- float cell_size;
- Vector3i position;
-
- static const Vector3i DIRTY_ALL;
- Vector3i dirty_regions; //(0,0,0 is not dirty, negative is refresh from the end, DIRTY_ALL is refresh all.
-
- RID sdf_store_uniform_set;
- RID sdf_direct_light_uniform_set;
- RID scroll_uniform_set;
- RID scroll_occlusion_uniform_set;
- RID integrate_uniform_set;
- RID lights_buffer;
-
- bool all_dynamic_lights_dirty = true;
- };
-
- //used for rendering (voxelization)
- RID render_albedo;
- RID render_emission;
- RID render_emission_aniso;
- RID render_occlusion[8];
- RID render_geom_facing;
-
- RID render_sdf[2];
- RID render_sdf_half[2];
-
- //used for ping pong processing in cascades
- RID sdf_initialize_uniform_set;
- RID sdf_initialize_half_uniform_set;
- RID jump_flood_uniform_set[2];
- RID jump_flood_half_uniform_set[2];
- RID sdf_upscale_uniform_set;
- int upscale_jfa_uniform_set_index;
- RID occlusion_uniform_set;
-
- uint32_t cascade_size = 128;
-
- LocalVector<Cascade> cascades;
-
- RID lightprobe_texture;
- RID lightprobe_data;
- RID occlusion_texture;
- RID occlusion_data;
- RID ambient_texture; //integrates with volumetric fog
-
- RID lightprobe_history_scroll; //used for scrolling lightprobes
- RID lightprobe_average_scroll; //used for scrolling lightprobes
-
- uint32_t history_size = 0;
- float solid_cell_ratio = 0;
- uint32_t solid_cell_count = 0;
-
- RS::EnvironmentSDFGICascades cascade_mode;
- float min_cell_size = 0;
- uint32_t probe_axis_count = 0; //amount of probes per axis, this is an odd number because it encloses endpoints
-
- RID debug_uniform_set;
- RID debug_probes_uniform_set;
- RID cascades_ubo;
-
- bool uses_occlusion = false;
- float bounce_feedback = 0.0;
- bool reads_sky = false;
- float energy = 1.0;
- float normal_bias = 1.1;
- float probe_bias = 1.1;
- RS::EnvironmentSDFGIYScale y_scale_mode = RS::ENV_SDFGI_Y_SCALE_DISABLED;
-
- float y_mult = 1.0;
-
- uint32_t render_pass = 0;
-
- int32_t cascade_dynamic_light_count[SDFGI::MAX_CASCADES]; //used dynamically
- };
-
- void _sdfgi_update_light(RID p_render_buffers, RID p_environment);
- void _sdfgi_update_probes(RID p_render_buffers, RID p_environment);
- void _sdfgi_store_probes(RID p_render_buffers);
-
- RS::EnvironmentSDFGIRayCount sdfgi_ray_count = RS::ENV_SDFGI_RAY_COUNT_16;
- RS::EnvironmentSDFGIFramesToConverge sdfgi_frames_to_converge = RS::ENV_SDFGI_CONVERGE_IN_10_FRAMES;
- RS::EnvironmentSDFGIFramesToUpdateLight sdfgi_frames_to_update_light = RS::ENV_SDFGI_UPDATE_LIGHT_IN_4_FRAMES;
-
- float sdfgi_solid_cell_ratio = 0.25;
- Vector3 sdfgi_debug_probe_pos;
- Vector3 sdfgi_debug_probe_dir;
- bool sdfgi_debug_probe_enabled = false;
- Vector3i sdfgi_debug_probe_index;
-
- struct SDGIShader {
- enum SDFGIPreprocessShaderVersion {
- PRE_PROCESS_SCROLL,
- PRE_PROCESS_SCROLL_OCCLUSION,
- PRE_PROCESS_JUMP_FLOOD_INITIALIZE,
- PRE_PROCESS_JUMP_FLOOD_INITIALIZE_HALF,
- PRE_PROCESS_JUMP_FLOOD,
- PRE_PROCESS_JUMP_FLOOD_OPTIMIZED,
- PRE_PROCESS_JUMP_FLOOD_UPSCALE,
- PRE_PROCESS_OCCLUSION,
- PRE_PROCESS_STORE,
- PRE_PROCESS_MAX
- };
-
- struct PreprocessPushConstant {
- int32_t scroll[3];
- int32_t grid_size;
-
- int32_t probe_offset[3];
- int32_t step_size;
-
- int32_t half_size;
- uint32_t occlusion_index;
- int32_t cascade;
- uint32_t pad;
- };
-
- SdfgiPreprocessShaderRD preprocess;
- RID preprocess_shader;
- RID preprocess_pipeline[PRE_PROCESS_MAX];
-
- struct DebugPushConstant {
- float grid_size[3];
- uint32_t max_cascades;
-
- int32_t screen_size[2];
- uint32_t use_occlusion;
- float y_mult;
-
- float cam_extent[3];
- uint32_t probe_axis_size;
-
- float cam_transform[16];
- };
-
- SdfgiDebugShaderRD debug;
- RID debug_shader;
- RID debug_shader_version;
- RID debug_pipeline;
-
- enum ProbeDebugMode {
- PROBE_DEBUG_PROBES,
- PROBE_DEBUG_VISIBILITY,
- PROBE_DEBUG_MAX
- };
-
- struct DebugProbesPushConstant {
- float projection[16];
-
- uint32_t band_power;
- uint32_t sections_in_band;
- uint32_t band_mask;
- float section_arc;
-
- float grid_size[3];
- uint32_t cascade;
-
- uint32_t pad;
- float y_mult;
- int32_t probe_debug_index;
- int32_t probe_axis_size;
- };
-
- SdfgiDebugProbesShaderRD debug_probes;
- RID debug_probes_shader;
- RID debug_probes_shader_version;
-
- PipelineCacheRD debug_probes_pipeline[PROBE_DEBUG_MAX];
-
- struct Light {
- float color[3];
- float energy;
-
- float direction[3];
- uint32_t has_shadow;
-
- float position[3];
- float attenuation;
-
- uint32_t type;
- float cos_spot_angle;
- float inv_spot_attenuation;
- float radius;
-
- float shadow_color[4];
- };
-
- struct DirectLightPushConstant {
- float grid_size[3];
- uint32_t max_cascades;
-
- uint32_t cascade;
- uint32_t light_count;
- uint32_t process_offset;
- uint32_t process_increment;
-
- int32_t probe_axis_size;
- float bounce_feedback;
- float y_mult;
- uint32_t use_occlusion;
- };
-
- enum {
- DIRECT_LIGHT_MODE_STATIC,
- DIRECT_LIGHT_MODE_DYNAMIC,
- DIRECT_LIGHT_MODE_MAX
- };
- SdfgiDirectLightShaderRD direct_light;
- RID direct_light_shader;
- RID direct_light_pipeline[DIRECT_LIGHT_MODE_MAX];
-
- enum {
- INTEGRATE_MODE_PROCESS,
- INTEGRATE_MODE_STORE,
- INTEGRATE_MODE_SCROLL,
- INTEGRATE_MODE_SCROLL_STORE,
- INTEGRATE_MODE_MAX
- };
- struct IntegratePushConstant {
- enum {
- SKY_MODE_DISABLED,
- SKY_MODE_COLOR,
- SKY_MODE_SKY,
- };
-
- float grid_size[3];
- uint32_t max_cascades;
-
- uint32_t probe_axis_size;
- uint32_t cascade;
- uint32_t history_index;
- uint32_t history_size;
-
- uint32_t ray_count;
- float ray_bias;
- int32_t image_size[2];
-
- int32_t world_offset[3];
- uint32_t sky_mode;
-
- int32_t scroll[3];
- float sky_energy;
-
- float sky_color[3];
- float y_mult;
-
- uint32_t store_ambient_texture;
- uint32_t pad[3];
- };
-
- SdfgiIntegrateShaderRD integrate;
- RID integrate_shader;
- RID integrate_pipeline[INTEGRATE_MODE_MAX];
-
- RID integrate_default_sky_uniform_set;
-
- } sdfgi_shader;
-
- void _sdfgi_erase(RenderBuffers *rb);
- int _sdfgi_get_pending_region_data(RID p_render_buffers, int p_region, Vector3i &r_local_offset, Vector3i &r_local_size, AABB &r_bounds) const;
- void _sdfgi_update_cascades(RID p_render_buffers);
-
/* GI */
-
- struct GI {
- struct SDFGIData {
- float grid_size[3];
- uint32_t max_cascades;
-
- uint32_t use_occlusion;
- int32_t probe_axis_size;
- float probe_to_uvw;
- float normal_bias;
-
- float lightprobe_tex_pixel_size[3];
- float energy;
-
- float lightprobe_uv_offset[3];
- float y_mult;
-
- float occlusion_clamp[3];
- uint32_t pad3;
-
- float occlusion_renormalize[3];
- uint32_t pad4;
-
- float cascade_probe_size[3];
- uint32_t pad5;
-
- struct ProbeCascadeData {
- float position[3]; //offset of (0,0,0) in world coordinates
- float to_probe; // 1/bounds * grid_size
- int32_t probe_world_offset[3];
- float to_cell; // 1/bounds * grid_size
- };
-
- ProbeCascadeData cascades[SDFGI::MAX_CASCADES];
- };
-
- struct GIProbeData {
- float xform[16];
- float bounds[3];
- float dynamic_range;
-
- float bias;
- float normal_bias;
- uint32_t blend_ambient;
- uint32_t texture_slot;
-
- float anisotropy_strength;
- float ao;
- float ao_size;
- uint32_t mipmaps;
- };
-
- struct PushConstant {
- int32_t screen_size[2];
- float z_near;
- float z_far;
-
- float proj_info[4];
- float ao_color[3];
- uint32_t max_giprobes;
-
- uint32_t high_quality_vct;
- uint32_t orthogonal;
- uint32_t pad[2];
-
- float cam_rotation[12];
- };
-
- RID sdfgi_ubo;
- enum Mode {
- MODE_GIPROBE,
- MODE_SDFGI,
- MODE_COMBINED,
- MODE_HALF_RES_GIPROBE,
- MODE_HALF_RES_SDFGI,
- MODE_HALF_RES_COMBINED,
- MODE_MAX
- };
-
- bool half_resolution = false;
- GiShaderRD shader;
- RID shader_version;
- RID pipelines[MODE_MAX];
- } gi;
-
bool screen_space_roughness_limiter = false;
float screen_space_roughness_limiter_amount = 0.25;
float screen_space_roughness_limiter_limit = 0.18;
@@ -1326,7 +447,6 @@ private:
void _render_buffers_debug_draw(RID p_render_buffers, RID p_shadow_atlas);
void _render_buffers_post_process_and_tonemap(RID p_render_buffers, RID p_environment, RID p_camera_effects, const CameraMatrix &p_projection);
- void _sdfgi_debug_draw(RID p_render_buffers, const CameraMatrix &p_projection, const Transform &p_transform);
/* Cluster */
@@ -1592,17 +712,17 @@ private:
uint64_t scene_pass = 0;
uint64_t shadow_atlas_realloc_tolerance_msec = 500;
+ /* !BAS! is this used anywhere?
struct SDFGICosineNeighbour {
uint32_t neighbour;
float weight;
};
+ */
uint32_t max_cluster_elements = 512;
bool low_end = false;
void _render_shadow_pass(RID p_light, RID p_shadow_atlas, int p_pass, const PagedArray<GeometryInstance *> &p_instances, const Plane &p_camera_plane = Plane(), float p_lod_distance_multiplier = 0, float p_screen_lod_threshold = 0.0, bool p_open_pass = true, bool p_close_pass = true, bool p_clear_region = true);
- void _render_sdfgi_region(RID p_render_buffers, int p_region, const PagedArray<GeometryInstance *> &p_instances);
- void _render_sdfgi_static_lights(RID p_render_buffers, uint32_t p_cascade_count, const uint32_t *p_cascade_indices, const PagedArray<RID> *p_positional_light_cull_result);
public:
virtual Transform geometry_instance_get_transform(GeometryInstance *p_instance) = 0;
@@ -1646,12 +766,12 @@ public:
/* SDFGI UPDATE */
- int sdfgi_get_lightprobe_octahedron_size() const { return SDFGI::LIGHTPROBE_OCT_SIZE; }
virtual void sdfgi_update(RID p_render_buffers, RID p_environment, const Vector3 &p_world_position);
virtual int sdfgi_get_pending_region_count(RID p_render_buffers) const;
virtual AABB sdfgi_get_pending_region_bounds(RID p_render_buffers, int p_region) const;
virtual uint32_t sdfgi_get_pending_region_cascade(RID p_render_buffers, int p_region) const;
RID sdfgi_get_ubo() const { return gi.sdfgi_ubo; }
+
/* SKY API */
virtual RID sky_allocate();
@@ -1662,10 +782,6 @@ public:
void sky_set_material(RID p_sky, RID p_material);
Ref<Image> sky_bake_panorama(RID p_sky, float p_energy, bool p_bake_irradiance, const Size2i &p_size);
- RID sky_get_radiance_texture_rd(RID p_sky) const;
- RID sky_get_radiance_uniform_set_rd(RID p_sky, RID p_shader, int p_set) const;
- RID sky_get_material(RID p_sky) const;
-
/* ENVIRONMENT API */
virtual RID environment_allocate();
@@ -1974,52 +1090,54 @@ public:
return li->transform;
}
+ // !BAS! Need to check which of these we move into RenderSceneGIRD or whether we keep this the way it is now
+
RID gi_probe_instance_create(RID p_base);
void gi_probe_instance_set_transform_to_data(RID p_probe, const Transform &p_xform);
bool gi_probe_needs_update(RID p_probe) const;
void gi_probe_update(RID p_probe, bool p_update_light_instances, const Vector<RID> &p_light_instances, const PagedArray<RendererSceneRender::GeometryInstance *> &p_dynamic_objects);
- void gi_probe_set_quality(RS::GIProbeQuality p_quality) { gi_probe_quality = p_quality; }
+ void gi_probe_set_quality(RS::GIProbeQuality p_quality) { gi.gi_probe_quality = p_quality; }
_FORCE_INLINE_ uint32_t gi_probe_instance_get_slot(RID p_probe) {
- GIProbeInstance *gi_probe = gi_probe_instance_owner.getornull(p_probe);
+ RendererSceneGIRD::GIProbeInstance *gi_probe = gi.gi_probe_instance_owner.getornull(p_probe);
return gi_probe->slot;
}
_FORCE_INLINE_ RID gi_probe_instance_get_base_probe(RID p_probe) {
- GIProbeInstance *gi_probe = gi_probe_instance_owner.getornull(p_probe);
+ RendererSceneGIRD::GIProbeInstance *gi_probe = gi.gi_probe_instance_owner.getornull(p_probe);
return gi_probe->probe;
}
_FORCE_INLINE_ Transform gi_probe_instance_get_transform_to_cell(RID p_probe) {
- GIProbeInstance *gi_probe = gi_probe_instance_owner.getornull(p_probe);
+ RendererSceneGIRD::GIProbeInstance *gi_probe = gi.gi_probe_instance_owner.getornull(p_probe);
return storage->gi_probe_get_to_cell_xform(gi_probe->probe) * gi_probe->transform.affine_inverse();
}
_FORCE_INLINE_ RID gi_probe_instance_get_texture(RID p_probe) {
- GIProbeInstance *gi_probe = gi_probe_instance_owner.getornull(p_probe);
+ RendererSceneGIRD::GIProbeInstance *gi_probe = gi.gi_probe_instance_owner.getornull(p_probe);
return gi_probe->texture;
}
_FORCE_INLINE_ void gi_probe_instance_set_render_index(RID p_instance, uint32_t p_render_index) {
- GIProbeInstance *gi_probe = gi_probe_instance_owner.getornull(p_instance);
+ RendererSceneGIRD::GIProbeInstance *gi_probe = gi.gi_probe_instance_owner.getornull(p_instance);
ERR_FAIL_COND(!gi_probe);
gi_probe->render_index = p_render_index;
}
_FORCE_INLINE_ uint32_t gi_probe_instance_get_render_index(RID p_instance) {
- GIProbeInstance *gi_probe = gi_probe_instance_owner.getornull(p_instance);
+ RendererSceneGIRD::GIProbeInstance *gi_probe = gi.gi_probe_instance_owner.getornull(p_instance);
ERR_FAIL_COND_V(!gi_probe, 0);
return gi_probe->render_index;
}
/*
_FORCE_INLINE_ void gi_probe_instance_set_render_pass(RID p_instance, uint32_t p_render_pass) {
- GIProbeInstance *g_probe = gi_probe_instance_owner.getornull(p_instance);
+ RendererSceneGIRD::GIProbeInstance *g_probe = gi_probe_instance_owner.getornull(p_instance);
ERR_FAIL_COND(!g_probe);
g_probe->last_pass = p_render_pass;
}
_FORCE_INLINE_ uint32_t gi_probe_instance_get_render_pass(RID p_instance) {
- GIProbeInstance *g_probe = gi_probe_instance_owner.getornull(p_instance);
+ RendererSceneGIRD::GIProbeInstance *g_probe = gi_probe_instance_owner.getornull(p_instance);
ERR_FAIL_COND_V(!g_probe, 0);
return g_probe->last_pass;
diff --git a/servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp
new file mode 100644
index 0000000000..769335ac16
--- /dev/null
+++ b/servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp
@@ -0,0 +1,1491 @@
+/*************************************************************************/
+/* renderer_scene_sky_rd.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 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 "renderer_scene_sky_rd.h"
+#include "core/config/project_settings.h"
+#include "renderer_scene_render_rd.h"
+#include "servers/rendering/rendering_server_default.h"
+
+////////////////////////////////////////////////////////////////////////////////
+// SKY SHADER
+
+void RendererSceneSkyRD::SkyShaderData::set_code(const String &p_code) {
+ //compile
+
+ code = p_code;
+ valid = false;
+ ubo_size = 0;
+ uniforms.clear();
+
+ if (code == String()) {
+ return; //just invalid, but no error
+ }
+
+ ShaderCompilerRD::GeneratedCode gen_code;
+ ShaderCompilerRD::IdentifierActions actions;
+
+ 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;
+
+ // !BAS! Contemplate making `SkyShader sky` accessible from this struct or even part of this struct.
+ RendererSceneRenderRD *scene_singleton = (RendererSceneRenderRD *)RendererSceneRenderRD::singleton;
+
+ Error err = scene_singleton->sky.sky_shader.compiler.compile(RS::SHADER_SKY, code, &actions, path, gen_code);
+
+ ERR_FAIL_COND(err != OK);
+
+ if (version.is_null()) {
+ version = scene_singleton->sky.sky_shader.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
+
+ scene_singleton->sky.sky_shader.shader.version_set_code(version, gen_code.uniforms, gen_code.vertex_global, gen_code.vertex, gen_code.fragment_global, gen_code.light, gen_code.fragment, gen_code.defines);
+ ERR_FAIL_COND(!scene_singleton->sky.sky_shader.shader.version_is_valid(version));
+
+ ubo_size = gen_code.uniform_total_size;
+ ubo_offsets = gen_code.uniform_offsets;
+ texture_uniforms = gen_code.texture_uniforms;
+
+ //update pipelines
+
+ for (int i = 0; i < SKY_VERSION_MAX; i++) {
+ RD::PipelineDepthStencilState depth_stencil_state;
+ depth_stencil_state.enable_depth_test = true;
+ depth_stencil_state.depth_compare_operator = RD::COMPARE_OP_LESS_OR_EQUAL;
+
+ RID shader_variant = scene_singleton->sky.sky_shader.shader.version_get_shader(version, i);
+ pipelines[i].setup(shader_variant, RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), depth_stencil_state, RD::PipelineColorBlendState::create_disabled(), 0);
+ }
+
+ valid = true;
+}
+
+void RendererSceneSkyRD::SkyShaderData::set_default_texture_param(const StringName &p_name, RID p_texture) {
+ if (!p_texture.is_valid()) {
+ default_texture_params.erase(p_name);
+ } else {
+ default_texture_params[p_name] = p_texture;
+ }
+}
+
+void RendererSceneSkyRD::SkyShaderData::get_param_list(List<PropertyInfo> *p_param_list) const {
+ Map<int, StringName> order;
+
+ for (Map<StringName, ShaderLanguage::ShaderNode::Uniform>::Element *E = uniforms.front(); E; E = E->next()) {
+ if (E->get().scope == ShaderLanguage::ShaderNode::Uniform::SCOPE_GLOBAL || E->get().scope == ShaderLanguage::ShaderNode::Uniform::SCOPE_INSTANCE) {
+ continue;
+ }
+
+ if (E->get().texture_order >= 0) {
+ order[E->get().texture_order + 100000] = E->key();
+ } else {
+ order[E->get().order] = E->key();
+ }
+ }
+
+ for (Map<int, StringName>::Element *E = order.front(); E; E = E->next()) {
+ PropertyInfo pi = ShaderLanguage::uniform_to_property_info(uniforms[E->get()]);
+ pi.name = E->get();
+ p_param_list->push_back(pi);
+ }
+}
+
+void RendererSceneSkyRD::SkyShaderData::get_instance_param_list(List<RendererStorage::InstanceShaderParam> *p_param_list) const {
+ for (Map<StringName, ShaderLanguage::ShaderNode::Uniform>::Element *E = uniforms.front(); E; E = E->next()) {
+ if (E->get().scope != ShaderLanguage::ShaderNode::Uniform::SCOPE_INSTANCE) {
+ continue;
+ }
+
+ RendererStorage::InstanceShaderParam p;
+ p.info = ShaderLanguage::uniform_to_property_info(E->get());
+ p.info.name = E->key(); //supply name
+ p.index = E->get().instance_index;
+ p.default_value = ShaderLanguage::constant_value_to_variant(E->get().default_value, E->get().type, E->get().hint);
+ p_param_list->push_back(p);
+ }
+}
+
+bool RendererSceneSkyRD::SkyShaderData::is_param_texture(const StringName &p_param) const {
+ if (!uniforms.has(p_param)) {
+ return false;
+ }
+
+ return uniforms[p_param].texture_order >= 0;
+}
+
+bool RendererSceneSkyRD::SkyShaderData::is_animated() const {
+ return false;
+}
+
+bool RendererSceneSkyRD::SkyShaderData::casts_shadows() const {
+ return false;
+}
+
+Variant RendererSceneSkyRD::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.hint);
+ }
+ return Variant();
+}
+
+RS::ShaderNativeSourceCode RendererSceneSkyRD::SkyShaderData::get_native_source_code() const {
+ RendererSceneRenderRD *scene_singleton = (RendererSceneRenderRD *)RendererSceneRenderRD::singleton;
+
+ return scene_singleton->sky.sky_shader.shader.version_get_native_source_code(version);
+}
+
+RendererSceneSkyRD::SkyShaderData::SkyShaderData() {
+ valid = false;
+}
+
+RendererSceneSkyRD::SkyShaderData::~SkyShaderData() {
+ RendererSceneRenderRD *scene_singleton = (RendererSceneRenderRD *)RendererSceneRenderRD::singleton;
+ ERR_FAIL_COND(!scene_singleton);
+ //pipeline variants will clear themselves if shader is gone
+ if (version.is_valid()) {
+ scene_singleton->sky.sky_shader.shader.version_free(version);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Sky material
+
+void RendererSceneSkyRD::SkyMaterialData::update_parameters(const Map<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty) {
+ RendererSceneRenderRD *scene_singleton = (RendererSceneRenderRD *)RendererSceneRenderRD::singleton;
+
+ uniform_set_updated = true;
+
+ if ((uint32_t)ubo_data.size() != shader_data->ubo_size) {
+ p_uniform_dirty = true;
+ if (uniform_buffer.is_valid()) {
+ RD::get_singleton()->free(uniform_buffer);
+ uniform_buffer = RID();
+ }
+
+ ubo_data.resize(shader_data->ubo_size);
+ if (ubo_data.size()) {
+ uniform_buffer = RD::get_singleton()->uniform_buffer_create(ubo_data.size());
+ memset(ubo_data.ptrw(), 0, ubo_data.size()); //clear
+ }
+
+ //clear previous uniform set
+ if (uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(uniform_set)) {
+ RD::get_singleton()->free(uniform_set);
+ uniform_set = RID();
+ }
+ }
+
+ //check whether buffer changed
+ if (p_uniform_dirty && ubo_data.size()) {
+ update_uniform_buffer(shader_data->uniforms, shader_data->ubo_offsets.ptr(), p_parameters, ubo_data.ptrw(), ubo_data.size(), false);
+ RD::get_singleton()->buffer_update(uniform_buffer, 0, ubo_data.size(), ubo_data.ptrw());
+ }
+
+ uint32_t tex_uniform_count = shader_data->texture_uniforms.size();
+
+ if ((uint32_t)texture_cache.size() != tex_uniform_count) {
+ texture_cache.resize(tex_uniform_count);
+ p_textures_dirty = true;
+
+ //clear previous uniform set
+ if (uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(uniform_set)) {
+ RD::get_singleton()->free(uniform_set);
+ uniform_set = RID();
+ }
+ }
+
+ if (p_textures_dirty && tex_uniform_count) {
+ update_textures(p_parameters, shader_data->default_texture_params, shader_data->texture_uniforms, texture_cache.ptrw(), true);
+ }
+
+ if (shader_data->ubo_size == 0 && shader_data->texture_uniforms.size() == 0) {
+ // This material does not require an uniform set, so don't create it.
+ return;
+ }
+
+ if (!p_textures_dirty && uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(uniform_set)) {
+ //no reason to update uniform set, only UBO (or nothing) was needed to update
+ return;
+ }
+
+ Vector<RD::Uniform> uniforms;
+
+ {
+ if (shader_data->ubo_size) {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER;
+ u.binding = 0;
+ u.ids.push_back(uniform_buffer);
+ uniforms.push_back(u);
+ }
+
+ const RID *textures = texture_cache.ptrw();
+ for (uint32_t i = 0; i < tex_uniform_count; i++) {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
+ u.binding = 1 + i;
+ u.ids.push_back(textures[i]);
+ uniforms.push_back(u);
+ }
+ }
+
+ uniform_set = RD::get_singleton()->uniform_set_create(uniforms, scene_singleton->sky.sky_shader.shader.version_get_shader(shader_data->version, 0), SKY_SET_MATERIAL);
+}
+
+RendererSceneSkyRD::SkyMaterialData::~SkyMaterialData() {
+ if (uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(uniform_set)) {
+ RD::get_singleton()->free(uniform_set);
+ }
+
+ if (uniform_buffer.is_valid()) {
+ RD::get_singleton()->free(uniform_buffer);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ReflectionData
+
+void RendererSceneSkyRD::ReflectionData::clear_reflection_data() {
+ layers.clear();
+ radiance_base_cubemap = RID();
+ if (downsampled_radiance_cubemap.is_valid()) {
+ RD::get_singleton()->free(downsampled_radiance_cubemap);
+ }
+ downsampled_radiance_cubemap = RID();
+ downsampled_layer.mipmaps.clear();
+ coefficient_buffer = RID();
+}
+
+void RendererSceneSkyRD::ReflectionData::update_reflection_data(int p_size, int p_mipmaps, bool p_use_array, RID p_base_cube, int p_base_layer, bool p_low_quality, int p_roughness_layers) {
+ //recreate radiance and all data
+
+ int mipmaps = p_mipmaps;
+ uint32_t w = p_size, h = p_size;
+
+ if (p_use_array) {
+ int num_layers = p_low_quality ? 8 : p_roughness_layers;
+
+ for (int i = 0; i < num_layers; i++) {
+ ReflectionData::Layer layer;
+ uint32_t mmw = w;
+ uint32_t mmh = h;
+ layer.mipmaps.resize(mipmaps);
+ layer.views.resize(mipmaps);
+ for (int j = 0; j < mipmaps; j++) {
+ ReflectionData::Layer::Mipmap &mm = layer.mipmaps.write[j];
+ mm.size.width = mmw;
+ mm.size.height = mmh;
+ for (int k = 0; k < 6; k++) {
+ mm.views[k] = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), p_base_cube, p_base_layer + i * 6 + k, j);
+ Vector<RID> fbtex;
+ fbtex.push_back(mm.views[k]);
+ mm.framebuffers[k] = RD::get_singleton()->framebuffer_create(fbtex);
+ }
+
+ layer.views.write[j] = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), p_base_cube, p_base_layer + i * 6, j, RD::TEXTURE_SLICE_CUBEMAP);
+
+ mmw = MAX(1, mmw >> 1);
+ mmh = MAX(1, mmh >> 1);
+ }
+
+ layers.push_back(layer);
+ }
+
+ } else {
+ mipmaps = p_low_quality ? 8 : mipmaps;
+ //regular cubemap, lower quality (aliasing, less memory)
+ ReflectionData::Layer layer;
+ uint32_t mmw = w;
+ uint32_t mmh = h;
+ layer.mipmaps.resize(mipmaps);
+ layer.views.resize(mipmaps);
+ for (int j = 0; j < mipmaps; j++) {
+ ReflectionData::Layer::Mipmap &mm = layer.mipmaps.write[j];
+ mm.size.width = mmw;
+ mm.size.height = mmh;
+ for (int k = 0; k < 6; k++) {
+ mm.views[k] = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), p_base_cube, p_base_layer + k, j);
+ Vector<RID> fbtex;
+ fbtex.push_back(mm.views[k]);
+ mm.framebuffers[k] = RD::get_singleton()->framebuffer_create(fbtex);
+ }
+
+ layer.views.write[j] = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), p_base_cube, p_base_layer, j, RD::TEXTURE_SLICE_CUBEMAP);
+
+ mmw = MAX(1, mmw >> 1);
+ mmh = MAX(1, mmh >> 1);
+ }
+
+ layers.push_back(layer);
+ }
+
+ radiance_base_cubemap = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), p_base_cube, p_base_layer, 0, RD::TEXTURE_SLICE_CUBEMAP);
+
+ RD::TextureFormat tf;
+ tf.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT;
+ tf.width = 64; // Always 64x64
+ tf.height = 64;
+ tf.texture_type = RD::TEXTURE_TYPE_CUBE;
+ tf.array_layers = 6;
+ tf.mipmaps = 7;
+ tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
+
+ downsampled_radiance_cubemap = RD::get_singleton()->texture_create(tf, RD::TextureView());
+ {
+ uint32_t mmw = 64;
+ uint32_t mmh = 64;
+ downsampled_layer.mipmaps.resize(7);
+ for (int j = 0; j < downsampled_layer.mipmaps.size(); j++) {
+ ReflectionData::DownsampleLayer::Mipmap &mm = downsampled_layer.mipmaps.write[j];
+ mm.size.width = mmw;
+ mm.size.height = mmh;
+ mm.view = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), downsampled_radiance_cubemap, 0, j, RD::TEXTURE_SLICE_CUBEMAP);
+
+ mmw = MAX(1, mmw >> 1);
+ mmh = MAX(1, mmh >> 1);
+ }
+ }
+}
+
+void RendererSceneSkyRD::ReflectionData::create_reflection_fast_filter(RendererStorageRD *p_storage, bool p_use_arrays) {
+ p_storage->get_effects()->cubemap_downsample(radiance_base_cubemap, downsampled_layer.mipmaps[0].view, downsampled_layer.mipmaps[0].size);
+
+ for (int i = 1; i < downsampled_layer.mipmaps.size(); i++) {
+ p_storage->get_effects()->cubemap_downsample(downsampled_layer.mipmaps[i - 1].view, downsampled_layer.mipmaps[i].view, downsampled_layer.mipmaps[i].size);
+ }
+
+ Vector<RID> views;
+ if (p_use_arrays) {
+ for (int i = 1; i < layers.size(); i++) {
+ views.push_back(layers[i].views[0]);
+ }
+ } else {
+ for (int i = 1; i < layers[0].views.size(); i++) {
+ views.push_back(layers[0].views[i]);
+ }
+ }
+
+ p_storage->get_effects()->cubemap_filter(downsampled_radiance_cubemap, views, p_use_arrays);
+}
+
+void RendererSceneSkyRD::ReflectionData::create_reflection_importance_sample(RendererStorageRD *p_storage, bool p_use_arrays, int p_cube_side, int p_base_layer, uint32_t p_sky_ggx_samples_quality) {
+ if (p_use_arrays) {
+ //render directly to the layers
+ p_storage->get_effects()->cubemap_roughness(radiance_base_cubemap, layers[p_base_layer].views[0], p_cube_side, p_sky_ggx_samples_quality, float(p_base_layer) / (layers.size() - 1.0), layers[p_base_layer].mipmaps[0].size.x);
+ } else {
+ p_storage->get_effects()->cubemap_roughness(
+ layers[0].views[p_base_layer - 1],
+ layers[0].views[p_base_layer],
+ p_cube_side,
+ p_sky_ggx_samples_quality,
+ float(p_base_layer) / (layers[0].mipmaps.size() - 1.0),
+ layers[0].mipmaps[p_base_layer].size.x);
+ }
+}
+
+void RendererSceneSkyRD::ReflectionData::update_reflection_mipmaps(RendererStorageRD *p_storage, int p_start, int p_end) {
+ for (int i = p_start; i < p_end; i++) {
+ for (int j = 0; j < layers[i].views.size() - 1; j++) {
+ RID view = layers[i].views[j];
+ RID texture = layers[i].views[j + 1];
+ Size2i size = layers[i].mipmaps[j + 1].size;
+ p_storage->get_effects()->cubemap_downsample(view, texture, size);
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// RendererSceneSkyRD::Sky
+
+void RendererSceneSkyRD::Sky::free(RendererStorageRD *p_storage) {
+ if (radiance.is_valid()) {
+ RD::get_singleton()->free(radiance);
+ radiance = RID();
+ }
+ reflection.clear_reflection_data();
+
+ if (uniform_buffer.is_valid()) {
+ RD::get_singleton()->free(uniform_buffer);
+ uniform_buffer = RID();
+ }
+
+ if (half_res_pass.is_valid()) {
+ RD::get_singleton()->free(half_res_pass);
+ half_res_pass = RID();
+ }
+
+ if (quarter_res_pass.is_valid()) {
+ RD::get_singleton()->free(quarter_res_pass);
+ quarter_res_pass = RID();
+ }
+
+ if (material.is_valid()) {
+ p_storage->free(material);
+ }
+}
+
+RID RendererSceneSkyRD::Sky::get_textures(RendererStorageRD *p_storage, SkyTextureSetVersion p_version, RID p_default_shader_rd) {
+ if (texture_uniform_sets[p_version].is_valid() && RD::get_singleton()->uniform_set_is_valid(texture_uniform_sets[p_version])) {
+ return texture_uniform_sets[p_version];
+ }
+ Vector<RD::Uniform> uniforms;
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
+ u.binding = 0;
+ if (radiance.is_valid() && p_version <= SKY_TEXTURE_SET_QUARTER_RES) {
+ u.ids.push_back(radiance);
+ } else {
+ u.ids.push_back(p_storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_CUBEMAP_BLACK));
+ }
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
+ u.binding = 1; // half res
+ if (half_res_pass.is_valid() && p_version != SKY_TEXTURE_SET_HALF_RES && p_version != SKY_TEXTURE_SET_CUBEMAP_HALF_RES) {
+ if (p_version >= SKY_TEXTURE_SET_CUBEMAP) {
+ u.ids.push_back(reflection.layers[0].views[1]);
+ } else {
+ u.ids.push_back(half_res_pass);
+ }
+ } else {
+ if (p_version < SKY_TEXTURE_SET_CUBEMAP) {
+ u.ids.push_back(p_storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_WHITE));
+ } else {
+ u.ids.push_back(p_storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_CUBEMAP_BLACK));
+ }
+ }
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
+ u.binding = 2; // quarter res
+ if (quarter_res_pass.is_valid() && p_version != SKY_TEXTURE_SET_QUARTER_RES && p_version != SKY_TEXTURE_SET_CUBEMAP_QUARTER_RES) {
+ if (p_version >= SKY_TEXTURE_SET_CUBEMAP) {
+ u.ids.push_back(reflection.layers[0].views[2]);
+ } else {
+ u.ids.push_back(quarter_res_pass);
+ }
+ } else {
+ if (p_version < SKY_TEXTURE_SET_CUBEMAP) {
+ u.ids.push_back(p_storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_WHITE));
+ } else {
+ u.ids.push_back(p_storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_CUBEMAP_BLACK));
+ }
+ }
+ uniforms.push_back(u);
+ }
+
+ texture_uniform_sets[p_version] = RD::get_singleton()->uniform_set_create(uniforms, p_default_shader_rd, SKY_SET_TEXTURES);
+ return texture_uniform_sets[p_version];
+}
+
+bool RendererSceneSkyRD::Sky::set_radiance_size(int p_radiance_size) {
+ ERR_FAIL_COND_V(p_radiance_size < 32 || p_radiance_size > 2048, false);
+ if (radiance_size == p_radiance_size) {
+ return false;
+ }
+ radiance_size = p_radiance_size;
+
+ if (mode == RS::SKY_MODE_REALTIME && radiance_size != 256) {
+ WARN_PRINT("Realtime Skies can only use a radiance size of 256. Radiance size will be set to 256 internally.");
+ radiance_size = 256;
+ }
+
+ if (radiance.is_valid()) {
+ RD::get_singleton()->free(radiance);
+ radiance = RID();
+ }
+ reflection.clear_reflection_data();
+
+ return true;
+}
+
+bool RendererSceneSkyRD::Sky::set_mode(RS::SkyMode p_mode) {
+ if (mode == p_mode) {
+ return false;
+ }
+
+ mode = p_mode;
+
+ if (mode == RS::SKY_MODE_REALTIME && radiance_size != 256) {
+ WARN_PRINT("Realtime Skies can only use a radiance size of 256. Radiance size will be set to 256 internally.");
+ set_radiance_size(256);
+ }
+
+ if (radiance.is_valid()) {
+ RD::get_singleton()->free(radiance);
+ radiance = RID();
+ }
+ reflection.clear_reflection_data();
+
+ return true;
+}
+
+bool RendererSceneSkyRD::Sky::set_material(RID p_material) {
+ if (material == p_material) {
+ return false;
+ }
+
+ material = p_material;
+ return true;
+}
+
+Ref<Image> RendererSceneSkyRD::Sky::bake_panorama(RendererStorageRD *p_storage, float p_energy, int p_roughness_layers, const Size2i &p_size) {
+ if (radiance.is_valid()) {
+ RD::TextureFormat tf;
+ tf.format = RD::DATA_FORMAT_R32G32B32A32_SFLOAT;
+ tf.width = p_size.width;
+ tf.height = p_size.height;
+ 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);
+ Vector<uint8_t> data = RD::get_singleton()->texture_get_data(rad_tex, 0);
+ RD::get_singleton()->free(rad_tex);
+
+ Ref<Image> img;
+ img.instance();
+ img->create(p_size.width, p_size.height, false, Image::FORMAT_RGBAF, data);
+ for (int i = 0; i < p_size.width; i++) {
+ for (int j = 0; j < p_size.height; j++) {
+ Color c = img->get_pixel(i, j);
+ c.r *= p_energy;
+ c.g *= p_energy;
+ c.b *= p_energy;
+ img->set_pixel(i, j, c);
+ }
+ }
+ return img;
+ }
+
+ return Ref<Image>();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// RendererSceneSkyRD
+
+RendererStorageRD::ShaderData *RendererSceneSkyRD::_create_sky_shader_func() {
+ SkyShaderData *shader_data = memnew(SkyShaderData);
+ return shader_data;
+}
+
+RendererStorageRD::ShaderData *RendererSceneSkyRD::_create_sky_shader_funcs() {
+ // !BAS! Why isn't _create_sky_shader_func not just static too?
+ return static_cast<RendererSceneRenderRD *>(RendererSceneRenderRD::singleton)->sky._create_sky_shader_func();
+};
+
+RendererStorageRD::MaterialData *RendererSceneSkyRD::_create_sky_material_func(SkyShaderData *p_shader) {
+ SkyMaterialData *material_data = memnew(SkyMaterialData);
+ material_data->shader_data = p_shader;
+ material_data->last_frame = false;
+ //update will happen later anyway so do nothing.
+ return material_data;
+}
+
+RendererStorageRD::MaterialData *RendererSceneSkyRD::_create_sky_material_funcs(RendererStorageRD::ShaderData *p_shader) {
+ // !BAS! same here, we could just make _create_sky_material_func static?
+ return static_cast<RendererSceneRenderRD *>(RendererSceneRenderRD::singleton)->sky._create_sky_material_func(static_cast<SkyShaderData *>(p_shader));
+};
+
+RendererSceneSkyRD::RendererSceneSkyRD() {
+ roughness_layers = GLOBAL_GET("rendering/reflections/sky_reflections/roughness_layers");
+ sky_ggx_samples_quality = GLOBAL_GET("rendering/reflections/sky_reflections/ggx_samples");
+ sky_use_cubemap_array = GLOBAL_GET("rendering/reflections/sky_reflections/texture_array_reflections");
+}
+
+void RendererSceneSkyRD::init(RendererStorageRD *p_storage) {
+ storage = p_storage;
+
+ {
+ // Start with the directional lights for the sky
+ sky_scene_state.max_directional_lights = 4;
+ uint32_t directional_light_buffer_size = sky_scene_state.max_directional_lights * sizeof(SkyDirectionalLightData);
+ sky_scene_state.directional_lights = memnew_arr(SkyDirectionalLightData, sky_scene_state.max_directional_lights);
+ sky_scene_state.last_frame_directional_lights = memnew_arr(SkyDirectionalLightData, sky_scene_state.max_directional_lights);
+ sky_scene_state.last_frame_directional_light_count = sky_scene_state.max_directional_lights + 1;
+ sky_scene_state.directional_light_buffer = RD::get_singleton()->uniform_buffer_create(directional_light_buffer_size);
+
+ String defines = "\n#define MAX_DIRECTIONAL_LIGHT_DATA_STRUCTS " + itos(sky_scene_state.max_directional_lights) + "\n";
+
+ // Initialize sky
+ Vector<String> sky_modes;
+ sky_modes.push_back(""); // Full size
+ sky_modes.push_back("\n#define USE_HALF_RES_PASS\n"); // Half Res
+ sky_modes.push_back("\n#define USE_QUARTER_RES_PASS\n"); // Quarter res
+ sky_modes.push_back("\n#define USE_CUBEMAP_PASS\n"); // Cubemap
+ sky_modes.push_back("\n#define USE_CUBEMAP_PASS\n#define USE_HALF_RES_PASS\n"); // Half Res Cubemap
+ sky_modes.push_back("\n#define USE_CUBEMAP_PASS\n#define USE_QUARTER_RES_PASS\n"); // Quarter res Cubemap
+ sky_shader.shader.initialize(sky_modes, defines);
+ }
+
+ // register our shader funds
+ storage->shader_set_data_request_function(RendererStorageRD::SHADER_TYPE_SKY, _create_sky_shader_funcs);
+ storage->material_set_data_request_function(RendererStorageRD::SHADER_TYPE_SKY, _create_sky_material_funcs);
+
+ {
+ ShaderCompilerRD::DefaultIdentifierActions actions;
+
+ actions.renames["COLOR"] = "color";
+ actions.renames["ALPHA"] = "alpha";
+ actions.renames["EYEDIR"] = "cube_normal";
+ actions.renames["POSITION"] = "params.position_multiplier.xyz";
+ actions.renames["SKY_COORDS"] = "panorama_coords";
+ actions.renames["SCREEN_UV"] = "uv";
+ actions.renames["TIME"] = "params.time";
+ actions.renames["HALF_RES_COLOR"] = "half_res_color";
+ actions.renames["QUARTER_RES_COLOR"] = "quarter_res_color";
+ actions.renames["RADIANCE"] = "radiance";
+ actions.renames["FOG"] = "custom_fog";
+ actions.renames["LIGHT0_ENABLED"] = "directional_lights.data[0].enabled";
+ actions.renames["LIGHT0_DIRECTION"] = "directional_lights.data[0].direction_energy.xyz";
+ actions.renames["LIGHT0_ENERGY"] = "directional_lights.data[0].direction_energy.w";
+ actions.renames["LIGHT0_COLOR"] = "directional_lights.data[0].color_size.xyz";
+ actions.renames["LIGHT0_SIZE"] = "directional_lights.data[0].color_size.w";
+ actions.renames["LIGHT1_ENABLED"] = "directional_lights.data[1].enabled";
+ actions.renames["LIGHT1_DIRECTION"] = "directional_lights.data[1].direction_energy.xyz";
+ actions.renames["LIGHT1_ENERGY"] = "directional_lights.data[1].direction_energy.w";
+ actions.renames["LIGHT1_COLOR"] = "directional_lights.data[1].color_size.xyz";
+ actions.renames["LIGHT1_SIZE"] = "directional_lights.data[1].color_size.w";
+ actions.renames["LIGHT2_ENABLED"] = "directional_lights.data[2].enabled";
+ actions.renames["LIGHT2_DIRECTION"] = "directional_lights.data[2].direction_energy.xyz";
+ actions.renames["LIGHT2_ENERGY"] = "directional_lights.data[2].direction_energy.w";
+ actions.renames["LIGHT2_COLOR"] = "directional_lights.data[2].color_size.xyz";
+ actions.renames["LIGHT2_SIZE"] = "directional_lights.data[2].color_size.w";
+ actions.renames["LIGHT3_ENABLED"] = "directional_lights.data[3].enabled";
+ actions.renames["LIGHT3_DIRECTION"] = "directional_lights.data[3].direction_energy.xyz";
+ actions.renames["LIGHT3_ENERGY"] = "directional_lights.data[3].direction_energy.w";
+ actions.renames["LIGHT3_COLOR"] = "directional_lights.data[3].color_size.xyz";
+ actions.renames["LIGHT3_SIZE"] = "directional_lights.data[3].color_size.w";
+ 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.data";
+
+ sky_shader.compiler.initialize(actions);
+ }
+
+ {
+ // default material and shader for sky shader
+ sky_shader.default_shader = storage->shader_allocate();
+ storage->shader_initialize(sky_shader.default_shader);
+
+ storage->shader_set_code(sky_shader.default_shader, "shader_type sky; void fragment() { COLOR = vec3(0.0); } \n");
+
+ sky_shader.default_material = storage->material_allocate();
+ storage->material_initialize(sky_shader.default_material);
+
+ storage->material_set_shader(sky_shader.default_material, sky_shader.default_shader);
+
+ SkyMaterialData *md = (SkyMaterialData *)storage->material_get_data(sky_shader.default_material, RendererStorageRD::SHADER_TYPE_SKY);
+ sky_shader.default_shader_rd = sky_shader.shader.version_get_shader(md->shader_data->version, SKY_VERSION_BACKGROUND);
+
+ sky_scene_state.uniform_buffer = RD::get_singleton()->uniform_buffer_create(sizeof(SkySceneState::UBO));
+
+ Vector<RD::Uniform> uniforms;
+
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_SAMPLER;
+ u.binding = 0;
+ u.ids.resize(12);
+ RID *ids_ptr = u.ids.ptrw();
+ ids_ptr[0] = storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
+ ids_ptr[1] = storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
+ ids_ptr[2] = storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
+ ids_ptr[3] = storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
+ ids_ptr[4] = storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
+ ids_ptr[5] = storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
+ ids_ptr[6] = storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED);
+ ids_ptr[7] = storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED);
+ ids_ptr[8] = storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED);
+ ids_ptr[9] = storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED);
+ ids_ptr[10] = storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED);
+ ids_ptr[11] = storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED);
+ uniforms.push_back(u);
+ }
+
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
+ u.binding = 1;
+ u.ids.push_back(storage->global_variables_get_storage_buffer());
+ uniforms.push_back(u);
+ }
+
+ {
+ RD::Uniform u;
+ u.binding = 2;
+ u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER;
+ u.ids.push_back(sky_scene_state.uniform_buffer);
+ uniforms.push_back(u);
+ }
+
+ {
+ RD::Uniform u;
+ u.binding = 3;
+ u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER;
+ u.ids.push_back(sky_scene_state.directional_light_buffer);
+ uniforms.push_back(u);
+ }
+
+ sky_scene_state.uniform_set = RD::get_singleton()->uniform_set_create(uniforms, sky_shader.default_shader_rd, SKY_SET_UNIFORMS);
+ }
+
+ {
+ Vector<RD::Uniform> uniforms;
+ {
+ RD::Uniform u;
+ u.binding = 0;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
+ RID vfog = storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_3D_WHITE);
+ u.ids.push_back(vfog);
+ uniforms.push_back(u);
+ }
+
+ sky_scene_state.default_fog_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, sky_shader.default_shader_rd, SKY_SET_FOG);
+ }
+
+ {
+ // Need defaults for using fog with clear color
+ sky_scene_state.fog_shader = storage->shader_allocate();
+ storage->shader_initialize(sky_scene_state.fog_shader);
+
+ storage->shader_set_code(sky_scene_state.fog_shader, "shader_type sky; uniform vec4 clear_color; void fragment() { COLOR = clear_color.rgb; } \n");
+ sky_scene_state.fog_material = storage->material_allocate();
+ storage->material_initialize(sky_scene_state.fog_material);
+
+ storage->material_set_shader(sky_scene_state.fog_material, sky_scene_state.fog_shader);
+
+ Vector<RD::Uniform> uniforms;
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
+ u.binding = 0;
+ u.ids.push_back(storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_CUBEMAP_BLACK));
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
+ u.binding = 1;
+ u.ids.push_back(storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_WHITE));
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
+ u.binding = 2;
+ u.ids.push_back(storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_WHITE));
+ uniforms.push_back(u);
+ }
+
+ sky_scene_state.fog_only_texture_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, sky_shader.default_shader_rd, SKY_SET_TEXTURES);
+ }
+}
+
+void RendererSceneSkyRD::setup(RendererSceneEnvironmentRD *p_env, RID p_render_buffers, const CameraMatrix &p_projection, const Transform &p_transform, const Size2i p_screen_size, RendererSceneRenderRD *p_scene_render) {
+ ERR_FAIL_COND(!p_env); // I guess without an environment we also can't have a sky...
+
+ SkyMaterialData *material = nullptr;
+ Sky *sky = get_sky(p_env->sky);
+
+ RID sky_material;
+
+ SkyShaderData *shader_data = nullptr;
+
+ RS::EnvironmentBG background = p_env->background;
+
+ if (!(background == RS::ENV_BG_CLEAR_COLOR || background == RS::ENV_BG_COLOR) || sky) {
+ // !BAS! Possibly silently fail here, we now get error spam when you select sky as the background but haven't setup the sky yet.
+ ERR_FAIL_COND(!sky);
+ sky_material = sky_get_material(p_env->sky);
+
+ if (sky_material.is_valid()) {
+ material = (SkyMaterialData *)storage->material_get_data(sky_material, RendererStorageRD::SHADER_TYPE_SKY);
+ if (!material || !material->shader_data->valid) {
+ material = nullptr;
+ }
+ }
+
+ if (!material) {
+ sky_material = sky_shader.default_material;
+ material = (SkyMaterialData *)storage->material_get_data(sky_material, RendererStorageRD::SHADER_TYPE_SKY);
+ }
+
+ ERR_FAIL_COND(!material);
+
+ shader_data = material->shader_data;
+
+ ERR_FAIL_COND(!shader_data);
+ }
+
+ if (sky) {
+ // Invalidate supbass buffers if screen size changes
+ if (sky->screen_size != p_screen_size) {
+ sky->screen_size = p_screen_size;
+ sky->screen_size.x = sky->screen_size.x < 4 ? 4 : sky->screen_size.x;
+ sky->screen_size.y = sky->screen_size.y < 4 ? 4 : sky->screen_size.y;
+ if (shader_data->uses_half_res) {
+ if (sky->half_res_pass.is_valid()) {
+ RD::get_singleton()->free(sky->half_res_pass);
+ sky->half_res_pass = RID();
+ }
+ invalidate_sky(sky);
+ }
+ if (shader_data->uses_quarter_res) {
+ if (sky->quarter_res_pass.is_valid()) {
+ RD::get_singleton()->free(sky->quarter_res_pass);
+ sky->quarter_res_pass = RID();
+ }
+ invalidate_sky(sky);
+ }
+ }
+
+ // Create new subpass buffers if necessary
+ if ((shader_data->uses_half_res && sky->half_res_pass.is_null()) ||
+ (shader_data->uses_quarter_res && sky->quarter_res_pass.is_null()) ||
+ sky->radiance.is_null()) {
+ invalidate_sky(sky);
+ update_dirty_skys();
+ }
+
+ if (shader_data->uses_time && p_scene_render->time - sky->prev_time > 0.00001) {
+ sky->prev_time = p_scene_render->time;
+ sky->reflection.dirty = true;
+ RenderingServerDefault::redraw_request();
+ }
+
+ if (material != sky->prev_material) {
+ sky->prev_material = material;
+ sky->reflection.dirty = true;
+ }
+
+ if (material->uniform_set_updated) {
+ material->uniform_set_updated = false;
+ sky->reflection.dirty = true;
+ }
+
+ if (!p_transform.origin.is_equal_approx(sky->prev_position) && shader_data->uses_position) {
+ sky->prev_position = p_transform.origin;
+ sky->reflection.dirty = true;
+ }
+
+ if (shader_data->uses_light) {
+ // Check whether the directional_light_buffer changes
+ bool light_data_dirty = false;
+
+ if (sky_scene_state.ubo.directional_light_count != sky_scene_state.last_frame_directional_light_count) {
+ light_data_dirty = true;
+ for (uint32_t i = sky_scene_state.ubo.directional_light_count; i < sky_scene_state.max_directional_lights; i++) {
+ sky_scene_state.directional_lights[i].enabled = false;
+ }
+ }
+ if (!light_data_dirty) {
+ for (uint32_t i = 0; i < sky_scene_state.ubo.directional_light_count; i++) {
+ if (sky_scene_state.directional_lights[i].direction[0] != sky_scene_state.last_frame_directional_lights[i].direction[0] ||
+ sky_scene_state.directional_lights[i].direction[1] != sky_scene_state.last_frame_directional_lights[i].direction[1] ||
+ sky_scene_state.directional_lights[i].direction[2] != sky_scene_state.last_frame_directional_lights[i].direction[2] ||
+ sky_scene_state.directional_lights[i].energy != sky_scene_state.last_frame_directional_lights[i].energy ||
+ sky_scene_state.directional_lights[i].color[0] != sky_scene_state.last_frame_directional_lights[i].color[0] ||
+ sky_scene_state.directional_lights[i].color[1] != sky_scene_state.last_frame_directional_lights[i].color[1] ||
+ sky_scene_state.directional_lights[i].color[2] != sky_scene_state.last_frame_directional_lights[i].color[2] ||
+ sky_scene_state.directional_lights[i].enabled != sky_scene_state.last_frame_directional_lights[i].enabled ||
+ sky_scene_state.directional_lights[i].size != sky_scene_state.last_frame_directional_lights[i].size) {
+ light_data_dirty = true;
+ break;
+ }
+ }
+ }
+
+ if (light_data_dirty) {
+ RD::get_singleton()->buffer_update(sky_scene_state.directional_light_buffer, 0, sizeof(SkyDirectionalLightData) * sky_scene_state.max_directional_lights, sky_scene_state.directional_lights);
+
+ SkyDirectionalLightData *temp = sky_scene_state.last_frame_directional_lights;
+ sky_scene_state.last_frame_directional_lights = sky_scene_state.directional_lights;
+ sky_scene_state.directional_lights = temp;
+ sky_scene_state.last_frame_directional_light_count = sky_scene_state.ubo.directional_light_count;
+ sky->reflection.dirty = true;
+ }
+ }
+ }
+
+ //setup fog variables
+ sky_scene_state.ubo.volumetric_fog_enabled = false;
+ if (p_render_buffers.is_valid()) {
+ if (p_scene_render->render_buffers_has_volumetric_fog(p_render_buffers)) {
+ sky_scene_state.ubo.volumetric_fog_enabled = true;
+
+ float fog_end = p_scene_render->render_buffers_get_volumetric_fog_end(p_render_buffers);
+ if (fog_end > 0.0) {
+ sky_scene_state.ubo.volumetric_fog_inv_length = 1.0 / fog_end;
+ } else {
+ sky_scene_state.ubo.volumetric_fog_inv_length = 1.0;
+ }
+
+ float fog_detail_spread = p_scene_render->render_buffers_get_volumetric_fog_detail_spread(p_render_buffers); //reverse lookup
+ if (fog_detail_spread > 0.0) {
+ sky_scene_state.ubo.volumetric_fog_detail_spread = 1.0 / fog_detail_spread;
+ } else {
+ sky_scene_state.ubo.volumetric_fog_detail_spread = 1.0;
+ }
+ }
+
+ RID fog_uniform_set = p_scene_render->render_buffers_get_volumetric_fog_sky_uniform_set(p_render_buffers);
+
+ if (fog_uniform_set != RID()) {
+ sky_scene_state.fog_uniform_set = fog_uniform_set;
+ } else {
+ sky_scene_state.fog_uniform_set = sky_scene_state.default_fog_uniform_set;
+ }
+ }
+
+ sky_scene_state.ubo.z_far = p_projection.get_z_far();
+ sky_scene_state.ubo.fog_enabled = p_env->fog_enabled;
+ sky_scene_state.ubo.fog_density = p_env->fog_density;
+ sky_scene_state.ubo.fog_aerial_perspective = p_env->fog_aerial_perspective;
+ Color fog_color = p_env->fog_light_color.to_linear();
+ float fog_energy = p_env->fog_light_energy;
+ sky_scene_state.ubo.fog_light_color[0] = fog_color.r * fog_energy;
+ sky_scene_state.ubo.fog_light_color[1] = fog_color.g * fog_energy;
+ sky_scene_state.ubo.fog_light_color[2] = fog_color.b * fog_energy;
+ sky_scene_state.ubo.fog_sun_scatter = p_env->fog_sun_scatter;
+
+ RD::get_singleton()->buffer_update(sky_scene_state.uniform_buffer, 0, sizeof(SkySceneState::UBO), &sky_scene_state.ubo);
+}
+
+void RendererSceneSkyRD::update(RendererSceneEnvironmentRD *p_env, const CameraMatrix &p_projection, const Transform &p_transform, double p_time) {
+ ERR_FAIL_COND(!p_env);
+
+ Sky *sky = get_sky(p_env->sky);
+ ERR_FAIL_COND(!sky);
+
+ RID sky_material = sky_get_material(p_env->sky);
+
+ SkyMaterialData *material = nullptr;
+
+ if (sky_material.is_valid()) {
+ material = (SkyMaterialData *)storage->material_get_data(sky_material, RendererStorageRD::SHADER_TYPE_SKY);
+ if (!material || !material->shader_data->valid) {
+ material = nullptr;
+ }
+ }
+
+ if (!material) {
+ sky_material = sky_shader.default_material;
+ material = (SkyMaterialData *)storage->material_get_data(sky_material, RendererStorageRD::SHADER_TYPE_SKY);
+ }
+
+ ERR_FAIL_COND(!material);
+
+ SkyShaderData *shader_data = material->shader_data;
+
+ ERR_FAIL_COND(!shader_data);
+
+ float multiplier = p_env->bg_energy;
+
+ bool update_single_frame = sky->mode == RS::SKY_MODE_REALTIME || sky->mode == RS::SKY_MODE_QUALITY;
+ RS::SkyMode sky_mode = sky->mode;
+
+ if (sky_mode == RS::SKY_MODE_AUTOMATIC) {
+ if (shader_data->uses_time || shader_data->uses_position) {
+ update_single_frame = true;
+ sky_mode = RS::SKY_MODE_REALTIME;
+ } else if (shader_data->uses_light || shader_data->ubo_size > 0) {
+ update_single_frame = false;
+ sky_mode = RS::SKY_MODE_INCREMENTAL;
+ } else {
+ update_single_frame = true;
+ sky_mode = RS::SKY_MODE_QUALITY;
+ }
+ }
+
+ if (sky->processing_layer == 0 && sky_mode == RS::SKY_MODE_INCREMENTAL) {
+ // On the first frame after creating sky, rebuild in single frame
+ update_single_frame = true;
+ sky_mode = RS::SKY_MODE_QUALITY;
+ }
+
+ int max_processing_layer = sky_use_cubemap_array ? sky->reflection.layers.size() : sky->reflection.layers[0].mipmaps.size();
+
+ // Update radiance cubemap
+ if (sky->reflection.dirty && (sky->processing_layer >= max_processing_layer || update_single_frame)) {
+ static const Vector3 view_normals[6] = {
+ Vector3(+1, 0, 0),
+ Vector3(-1, 0, 0),
+ Vector3(0, +1, 0),
+ Vector3(0, -1, 0),
+ Vector3(0, 0, +1),
+ Vector3(0, 0, -1)
+ };
+ static const Vector3 view_up[6] = {
+ Vector3(0, -1, 0),
+ Vector3(0, -1, 0),
+ Vector3(0, 0, +1),
+ Vector3(0, 0, -1),
+ Vector3(0, -1, 0),
+ Vector3(0, -1, 0)
+ };
+
+ CameraMatrix cm;
+ cm.set_perspective(90, 1, 0.01, 10.0);
+ CameraMatrix correction;
+ correction.set_depth_correction(true);
+ cm = correction * cm;
+
+ if (shader_data->uses_quarter_res) {
+ PipelineCacheRD *pipeline = &shader_data->pipelines[SKY_VERSION_CUBEMAP_QUARTER_RES];
+
+ Vector<Color> clear_colors;
+ clear_colors.push_back(Color(0.0, 0.0, 0.0));
+ RD::DrawListID cubemap_draw_list;
+
+ for (int i = 0; i < 6; i++) {
+ Transform local_view;
+ local_view.set_look_at(Vector3(0, 0, 0), view_normals[i], view_up[i]);
+ RID texture_uniform_set = sky->get_textures(storage, SKY_TEXTURE_SET_CUBEMAP_QUARTER_RES, sky_shader.default_shader_rd);
+
+ cubemap_draw_list = RD::get_singleton()->draw_list_begin(sky->reflection.layers[0].mipmaps[2].framebuffers[i], RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD);
+ storage->get_effects()->render_sky(cubemap_draw_list, p_time, sky->reflection.layers[0].mipmaps[2].framebuffers[i], sky_scene_state.uniform_set, sky_scene_state.fog_uniform_set, pipeline, material->uniform_set, texture_uniform_set, cm, local_view.basis, multiplier, p_transform.origin);
+ RD::get_singleton()->draw_list_end();
+ }
+ }
+
+ if (shader_data->uses_half_res) {
+ PipelineCacheRD *pipeline = &shader_data->pipelines[SKY_VERSION_CUBEMAP_HALF_RES];
+
+ Vector<Color> clear_colors;
+ clear_colors.push_back(Color(0.0, 0.0, 0.0));
+ RD::DrawListID cubemap_draw_list;
+
+ for (int i = 0; i < 6; i++) {
+ Transform local_view;
+ local_view.set_look_at(Vector3(0, 0, 0), view_normals[i], view_up[i]);
+ RID texture_uniform_set = sky->get_textures(storage, SKY_TEXTURE_SET_CUBEMAP_HALF_RES, sky_shader.default_shader_rd);
+
+ cubemap_draw_list = RD::get_singleton()->draw_list_begin(sky->reflection.layers[0].mipmaps[1].framebuffers[i], RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD);
+ storage->get_effects()->render_sky(cubemap_draw_list, p_time, sky->reflection.layers[0].mipmaps[1].framebuffers[i], sky_scene_state.uniform_set, sky_scene_state.fog_uniform_set, pipeline, material->uniform_set, texture_uniform_set, cm, local_view.basis, multiplier, p_transform.origin);
+ RD::get_singleton()->draw_list_end();
+ }
+ }
+
+ RD::DrawListID cubemap_draw_list;
+ PipelineCacheRD *pipeline = &shader_data->pipelines[SKY_VERSION_CUBEMAP];
+
+ for (int i = 0; i < 6; i++) {
+ Transform local_view;
+ local_view.set_look_at(Vector3(0, 0, 0), view_normals[i], view_up[i]);
+ RID texture_uniform_set = sky->get_textures(storage, SKY_TEXTURE_SET_CUBEMAP, sky_shader.default_shader_rd);
+
+ cubemap_draw_list = RD::get_singleton()->draw_list_begin(sky->reflection.layers[0].mipmaps[0].framebuffers[i], RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD);
+ storage->get_effects()->render_sky(cubemap_draw_list, p_time, sky->reflection.layers[0].mipmaps[0].framebuffers[i], sky_scene_state.uniform_set, sky_scene_state.fog_uniform_set, pipeline, material->uniform_set, texture_uniform_set, cm, local_view.basis, multiplier, p_transform.origin);
+ RD::get_singleton()->draw_list_end();
+ }
+
+ if (sky_mode == RS::SKY_MODE_REALTIME) {
+ sky->reflection.create_reflection_fast_filter(storage, sky_use_cubemap_array);
+ if (sky_use_cubemap_array) {
+ sky->reflection.update_reflection_mipmaps(storage, 0, sky->reflection.layers.size());
+ }
+ } else {
+ if (update_single_frame) {
+ for (int i = 1; i < max_processing_layer; i++) {
+ sky->reflection.create_reflection_importance_sample(storage, sky_use_cubemap_array, 10, i, sky_ggx_samples_quality);
+ }
+ if (sky_use_cubemap_array) {
+ sky->reflection.update_reflection_mipmaps(storage, 0, sky->reflection.layers.size());
+ }
+ } else {
+ if (sky_use_cubemap_array) {
+ // Multi-Frame so just update the first array level
+ sky->reflection.update_reflection_mipmaps(storage, 0, 1);
+ }
+ }
+ sky->processing_layer = 1;
+ }
+
+ sky->reflection.dirty = false;
+
+ } else {
+ if (sky_mode == RS::SKY_MODE_INCREMENTAL && sky->processing_layer < max_processing_layer) {
+ sky->reflection.create_reflection_importance_sample(storage, sky_use_cubemap_array, 10, sky->processing_layer, sky_ggx_samples_quality);
+
+ if (sky_use_cubemap_array) {
+ sky->reflection.update_reflection_mipmaps(storage, sky->processing_layer, sky->processing_layer + 1);
+ }
+
+ sky->processing_layer++;
+ }
+ }
+}
+
+void RendererSceneSkyRD::draw(RendererSceneEnvironmentRD *p_env, bool p_can_continue_color, bool p_can_continue_depth, RID p_fb, const CameraMatrix &p_projection, const Transform &p_transform, double p_time) {
+ ERR_FAIL_COND(!p_env);
+
+ Sky *sky = get_sky(p_env->sky);
+ ERR_FAIL_COND(!sky);
+
+ SkyMaterialData *material = nullptr;
+ RID sky_material;
+
+ RS::EnvironmentBG background = p_env->background;
+
+ if (!(background == RS::ENV_BG_CLEAR_COLOR || background == RS::ENV_BG_COLOR) || sky) {
+ ERR_FAIL_COND(!sky);
+ sky_material = sky_get_material(p_env->sky);
+
+ if (sky_material.is_valid()) {
+ material = (SkyMaterialData *)storage->material_get_data(sky_material, RendererStorageRD::SHADER_TYPE_SKY);
+ if (!material || !material->shader_data->valid) {
+ material = nullptr;
+ }
+ }
+
+ if (!material) {
+ sky_material = sky_shader.default_material;
+ material = (SkyMaterialData *)storage->material_get_data(sky_material, RendererStorageRD::SHADER_TYPE_SKY);
+ }
+ }
+
+ if (background == RS::ENV_BG_CLEAR_COLOR || background == RS::ENV_BG_COLOR) {
+ sky_material = sky_scene_state.fog_material;
+ material = (SkyMaterialData *)storage->material_get_data(sky_material, RendererStorageRD::SHADER_TYPE_SKY);
+ }
+
+ ERR_FAIL_COND(!material);
+
+ SkyShaderData *shader_data = material->shader_data;
+
+ ERR_FAIL_COND(!shader_data);
+
+ Basis sky_transform = p_env->sky_orientation;
+ sky_transform.invert();
+
+ float multiplier = p_env->bg_energy;
+ float custom_fov = p_env->sky_custom_fov;
+ // Camera
+ CameraMatrix camera;
+
+ if (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(custom_fov, aspect, near_plane, far_plane);
+
+ } else {
+ camera = p_projection;
+ }
+
+ sky_transform = p_transform.basis * sky_transform;
+
+ if (shader_data->uses_quarter_res) {
+ PipelineCacheRD *pipeline = &shader_data->pipelines[SKY_VERSION_QUARTER_RES];
+
+ RID texture_uniform_set = sky->get_textures(storage, SKY_TEXTURE_SET_QUARTER_RES, sky_shader.default_shader_rd);
+
+ Vector<Color> clear_colors;
+ clear_colors.push_back(Color(0.0, 0.0, 0.0));
+
+ RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(sky->quarter_res_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_DISCARD, clear_colors);
+ storage->get_effects()->render_sky(draw_list, p_time, sky->quarter_res_framebuffer, sky_scene_state.uniform_set, sky_scene_state.fog_uniform_set, pipeline, material->uniform_set, texture_uniform_set, camera, sky_transform, multiplier, p_transform.origin);
+ RD::get_singleton()->draw_list_end();
+ }
+
+ if (shader_data->uses_half_res) {
+ PipelineCacheRD *pipeline = &shader_data->pipelines[SKY_VERSION_HALF_RES];
+
+ RID texture_uniform_set = sky->get_textures(storage, SKY_TEXTURE_SET_HALF_RES, sky_shader.default_shader_rd);
+
+ Vector<Color> clear_colors;
+ clear_colors.push_back(Color(0.0, 0.0, 0.0));
+
+ RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(sky->half_res_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_DISCARD, clear_colors);
+ storage->get_effects()->render_sky(draw_list, p_time, sky->half_res_framebuffer, sky_scene_state.uniform_set, sky_scene_state.fog_uniform_set, pipeline, material->uniform_set, texture_uniform_set, camera, sky_transform, multiplier, p_transform.origin);
+ RD::get_singleton()->draw_list_end();
+ }
+
+ PipelineCacheRD *pipeline = &shader_data->pipelines[SKY_VERSION_BACKGROUND];
+
+ RID texture_uniform_set;
+ if (sky) {
+ texture_uniform_set = sky->get_textures(storage, SKY_TEXTURE_SET_BACKGROUND, sky_shader.default_shader_rd);
+ } else {
+ texture_uniform_set = sky_scene_state.fog_only_texture_uniform_set;
+ }
+
+ RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_fb, RD::INITIAL_ACTION_CONTINUE, p_can_continue_color ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CONTINUE, p_can_continue_depth ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ);
+ storage->get_effects()->render_sky(draw_list, p_time, p_fb, sky_scene_state.uniform_set, sky_scene_state.fog_uniform_set, pipeline, material->uniform_set, texture_uniform_set, camera, sky_transform, multiplier, p_transform.origin);
+ RD::get_singleton()->draw_list_end();
+}
+
+void RendererSceneSkyRD::invalidate_sky(Sky *p_sky) {
+ if (!p_sky->dirty) {
+ p_sky->dirty = true;
+ p_sky->dirty_list = dirty_sky_list;
+ dirty_sky_list = p_sky;
+ }
+}
+
+void RendererSceneSkyRD::update_dirty_skys() {
+ Sky *sky = dirty_sky_list;
+
+ while (sky) {
+ bool texture_set_dirty = false;
+ //update sky configuration if texture is missing
+
+ if (sky->radiance.is_null()) {
+ int mipmaps = Image::get_image_required_mipmaps(sky->radiance_size, sky->radiance_size, Image::FORMAT_RGBAH) + 1;
+
+ uint32_t w = sky->radiance_size, h = sky->radiance_size;
+ int layers = roughness_layers;
+ if (sky->mode == RS::SKY_MODE_REALTIME) {
+ layers = 8;
+ if (roughness_layers != 8) {
+ WARN_PRINT("When using REALTIME skies, roughness_layers should be set to 8 in the project settings for best quality reflections");
+ }
+ }
+
+ if (sky_use_cubemap_array) {
+ //array (higher quality, 6 times more memory)
+ RD::TextureFormat tf;
+ tf.array_layers = layers * 6;
+ tf.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT;
+ tf.texture_type = RD::TEXTURE_TYPE_CUBE_ARRAY;
+ tf.mipmaps = mipmaps;
+ tf.width = w;
+ tf.height = h;
+ tf.usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT;
+
+ sky->radiance = RD::get_singleton()->texture_create(tf, RD::TextureView());
+
+ sky->reflection.update_reflection_data(sky->radiance_size, mipmaps, true, sky->radiance, 0, sky->mode == RS::SKY_MODE_REALTIME, roughness_layers);
+
+ } else {
+ //regular cubemap, lower quality (aliasing, less memory)
+ RD::TextureFormat tf;
+ tf.array_layers = 6;
+ tf.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT;
+ tf.texture_type = RD::TEXTURE_TYPE_CUBE;
+ tf.mipmaps = MIN(mipmaps, layers);
+ tf.width = w;
+ tf.height = h;
+ tf.usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT;
+
+ sky->radiance = RD::get_singleton()->texture_create(tf, RD::TextureView());
+
+ sky->reflection.update_reflection_data(sky->radiance_size, MIN(mipmaps, layers), false, sky->radiance, 0, sky->mode == RS::SKY_MODE_REALTIME, roughness_layers);
+ }
+ texture_set_dirty = true;
+ }
+
+ // Create subpass buffers if they haven't been created already
+ if (sky->half_res_pass.is_null() && !RD::get_singleton()->texture_is_valid(sky->half_res_pass) && sky->screen_size.x >= 4 && sky->screen_size.y >= 4) {
+ RD::TextureFormat tformat;
+ tformat.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT;
+ tformat.width = sky->screen_size.x / 2;
+ tformat.height = sky->screen_size.y / 2;
+ tformat.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
+ tformat.texture_type = RD::TEXTURE_TYPE_2D;
+
+ sky->half_res_pass = RD::get_singleton()->texture_create(tformat, RD::TextureView());
+ Vector<RID> texs;
+ texs.push_back(sky->half_res_pass);
+ sky->half_res_framebuffer = RD::get_singleton()->framebuffer_create(texs);
+ texture_set_dirty = true;
+ }
+
+ if (sky->quarter_res_pass.is_null() && !RD::get_singleton()->texture_is_valid(sky->quarter_res_pass) && sky->screen_size.x >= 4 && sky->screen_size.y >= 4) {
+ RD::TextureFormat tformat;
+ tformat.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT;
+ tformat.width = sky->screen_size.x / 4;
+ tformat.height = sky->screen_size.y / 4;
+ tformat.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
+ tformat.texture_type = RD::TEXTURE_TYPE_2D;
+
+ sky->quarter_res_pass = RD::get_singleton()->texture_create(tformat, RD::TextureView());
+ Vector<RID> texs;
+ texs.push_back(sky->quarter_res_pass);
+ sky->quarter_res_framebuffer = RD::get_singleton()->framebuffer_create(texs);
+ texture_set_dirty = true;
+ }
+
+ if (texture_set_dirty) {
+ for (int i = 0; i < SKY_TEXTURE_SET_MAX; i++) {
+ if (sky->texture_uniform_sets[i].is_valid() && RD::get_singleton()->uniform_set_is_valid(sky->texture_uniform_sets[i])) {
+ RD::get_singleton()->free(sky->texture_uniform_sets[i]);
+ sky->texture_uniform_sets[i] = RID();
+ }
+ }
+ }
+
+ sky->reflection.dirty = true;
+ sky->processing_layer = 0;
+
+ Sky *next = sky->dirty_list;
+ sky->dirty_list = nullptr;
+ sky->dirty = false;
+ sky = next;
+ }
+
+ dirty_sky_list = nullptr;
+}
+
+RID RendererSceneSkyRD::sky_get_material(RID p_sky) const {
+ Sky *sky = get_sky(p_sky);
+ ERR_FAIL_COND_V(!sky, RID());
+
+ return sky->material;
+}
+
+RID RendererSceneSkyRD::allocate_sky_rid() {
+ return sky_owner.allocate_rid();
+}
+
+void RendererSceneSkyRD::initialize_sky_rid(RID p_rid) {
+ sky_owner.initialize_rid(p_rid, Sky());
+}
+
+RendererSceneSkyRD::Sky *RendererSceneSkyRD::get_sky(RID p_sky) const {
+ return sky_owner.getornull(p_sky);
+}
+
+void RendererSceneSkyRD::free_sky(RID p_sky) {
+ Sky *sky = get_sky(p_sky);
+ ERR_FAIL_COND(!sky);
+
+ sky->free(storage);
+ sky_owner.free(p_sky);
+}
+
+void RendererSceneSkyRD::sky_set_radiance_size(RID p_sky, int p_radiance_size) {
+ Sky *sky = get_sky(p_sky);
+ ERR_FAIL_COND(!sky);
+
+ if (sky->set_radiance_size(p_radiance_size)) {
+ invalidate_sky(sky);
+ }
+}
+
+void RendererSceneSkyRD::sky_set_mode(RID p_sky, RS::SkyMode p_mode) {
+ Sky *sky = get_sky(p_sky);
+ ERR_FAIL_COND(!sky);
+
+ if (sky->set_mode(p_mode)) {
+ invalidate_sky(sky);
+ }
+}
+
+void RendererSceneSkyRD::sky_set_material(RID p_sky, RID p_material) {
+ Sky *sky = get_sky(p_sky);
+ ERR_FAIL_COND(!sky);
+
+ if (sky->set_material(p_material)) {
+ invalidate_sky(sky);
+ }
+}
+
+Ref<Image> RendererSceneSkyRD::sky_bake_panorama(RID p_sky, float p_energy, bool p_bake_irradiance, const Size2i &p_size) {
+ Sky *sky = get_sky(p_sky);
+ ERR_FAIL_COND_V(!sky, Ref<Image>());
+
+ update_dirty_skys();
+
+ return sky->bake_panorama(storage, p_energy, p_bake_irradiance ? roughness_layers : 0, p_size);
+}
+
+RID RendererSceneSkyRD::sky_get_radiance_texture_rd(RID p_sky) const {
+ Sky *sky = get_sky(p_sky);
+ ERR_FAIL_COND_V(!sky, RID());
+
+ return sky->radiance;
+}
diff --git a/servers/rendering/renderer_rd/renderer_scene_sky_rd.h b/servers/rendering/renderer_rd/renderer_scene_sky_rd.h
new file mode 100644
index 0000000000..73390a586b
--- /dev/null
+++ b/servers/rendering/renderer_rd/renderer_scene_sky_rd.h
@@ -0,0 +1,292 @@
+/*************************************************************************/
+/* renderer_scene_sky_rd.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 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 RENDERING_SERVER_SCENE_SKY_RD_H
+#define RENDERING_SERVER_SCENE_SKY_RD_H
+
+#include "core/templates/rid_owner.h"
+#include "servers/rendering/renderer_compositor.h"
+#include "servers/rendering/renderer_rd/renderer_scene_environment_rd.h"
+#include "servers/rendering/renderer_rd/renderer_storage_rd.h"
+#include "servers/rendering/renderer_rd/shaders/sky.glsl.gen.h"
+#include "servers/rendering/renderer_scene_render.h"
+#include "servers/rendering/rendering_device.h"
+
+// Forward declare RendererSceneRenderRD so we can pass it into some of our methods, these classes are pretty tightly bound
+class RendererSceneRenderRD;
+
+class RendererSceneSkyRD {
+private:
+ RendererStorageRD *storage;
+
+public:
+ enum SkySet {
+ SKY_SET_UNIFORMS,
+ SKY_SET_MATERIAL,
+ SKY_SET_TEXTURES,
+ SKY_SET_FOG,
+ SKY_SET_MAX
+ };
+
+ enum SkyTextureSetVersion {
+ SKY_TEXTURE_SET_BACKGROUND,
+ SKY_TEXTURE_SET_HALF_RES,
+ SKY_TEXTURE_SET_QUARTER_RES,
+ SKY_TEXTURE_SET_CUBEMAP,
+ SKY_TEXTURE_SET_CUBEMAP_HALF_RES,
+ SKY_TEXTURE_SET_CUBEMAP_QUARTER_RES,
+ SKY_TEXTURE_SET_MAX
+ };
+
+ enum SkyVersion {
+ SKY_VERSION_BACKGROUND,
+ SKY_VERSION_HALF_RES,
+ SKY_VERSION_QUARTER_RES,
+ SKY_VERSION_CUBEMAP,
+ SKY_VERSION_CUBEMAP_HALF_RES,
+ SKY_VERSION_CUBEMAP_QUARTER_RES,
+ SKY_VERSION_MAX
+ };
+
+ // Skys need less info from Directional Lights than the normal shaders
+ struct SkyDirectionalLightData {
+ float direction[3];
+ float energy;
+ float color[3];
+ float size;
+ uint32_t enabled;
+ uint32_t pad[3];
+ };
+
+ struct SkySceneState {
+ struct UBO {
+ uint32_t volumetric_fog_enabled;
+ float volumetric_fog_inv_length;
+ float volumetric_fog_detail_spread;
+
+ float fog_aerial_perspective;
+
+ float fog_light_color[3];
+ float fog_sun_scatter;
+
+ uint32_t fog_enabled;
+ float fog_density;
+
+ float z_far;
+ uint32_t directional_light_count;
+ };
+
+ UBO ubo;
+
+ SkyDirectionalLightData *directional_lights;
+ SkyDirectionalLightData *last_frame_directional_lights;
+ uint32_t max_directional_lights;
+ uint32_t last_frame_directional_light_count;
+ RID directional_light_buffer;
+ RID uniform_set;
+ RID uniform_buffer;
+ RID fog_uniform_set;
+ RID default_fog_uniform_set;
+
+ RID fog_shader;
+ RID fog_material;
+ RID fog_only_texture_uniform_set;
+ } sky_scene_state;
+
+ struct ReflectionData {
+ struct Layer {
+ struct Mipmap {
+ RID framebuffers[6];
+ RID views[6];
+ Size2i size;
+ };
+ Vector<Mipmap> mipmaps; //per-face view
+ Vector<RID> views; // per-cubemap view
+ };
+
+ struct DownsampleLayer {
+ struct Mipmap {
+ RID view;
+ Size2i size;
+ };
+ Vector<Mipmap> mipmaps;
+ };
+
+ RID radiance_base_cubemap; //cubemap for first layer, first cubemap
+ RID downsampled_radiance_cubemap;
+ DownsampleLayer downsampled_layer;
+ RID coefficient_buffer;
+
+ bool dirty = true;
+
+ Vector<Layer> layers;
+
+ void clear_reflection_data();
+ void update_reflection_data(int p_size, int p_mipmaps, bool p_use_array, RID p_base_cube, int p_base_layer, bool p_low_quality, int p_roughness_layers);
+ void create_reflection_fast_filter(RendererStorageRD *p_storage, bool p_use_arrays);
+ void create_reflection_importance_sample(RendererStorageRD *p_storage, bool p_use_arrays, int p_cube_side, int p_base_layer, uint32_t p_sky_ggx_samples_quality);
+ void update_reflection_mipmaps(RendererStorageRD *p_storage, int p_start, int p_end);
+ };
+
+ struct SkyShaderData : public RendererStorageRD::ShaderData {
+ bool valid;
+ RID version;
+
+ PipelineCacheRD pipelines[SKY_VERSION_MAX];
+ Map<StringName, ShaderLanguage::ShaderNode::Uniform> uniforms;
+ Vector<ShaderCompilerRD::GeneratedCode::Texture> texture_uniforms;
+
+ Vector<uint32_t> ubo_offsets;
+ uint32_t ubo_size;
+
+ String path;
+ String code;
+ Map<StringName, 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);
+ virtual void get_param_list(List<PropertyInfo> *p_param_list) const;
+ virtual void get_instance_param_list(List<RendererStorage::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();
+ };
+
+ /* Sky shader */
+
+ struct SkyShader {
+ SkyShaderRD shader;
+ ShaderCompilerRD compiler;
+
+ RID default_shader;
+ RID default_material;
+ RID default_shader_rd;
+ } sky_shader;
+
+ struct SkyMaterialData : public RendererStorageRD::MaterialData {
+ uint64_t last_frame;
+ SkyShaderData *shader_data;
+ RID uniform_buffer;
+ RID uniform_set;
+ Vector<RID> texture_cache;
+ Vector<uint8_t> ubo_data;
+ bool uniform_set_updated;
+
+ 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 ~SkyMaterialData();
+ };
+
+ struct Sky {
+ RID radiance;
+ RID half_res_pass;
+ RID half_res_framebuffer;
+ RID quarter_res_pass;
+ RID quarter_res_framebuffer;
+ Size2i screen_size;
+
+ RID texture_uniform_sets[SKY_TEXTURE_SET_MAX];
+ RID uniform_set;
+
+ RID material;
+ RID uniform_buffer;
+
+ int radiance_size = 256;
+
+ RS::SkyMode mode = RS::SKY_MODE_AUTOMATIC;
+
+ ReflectionData reflection;
+ bool dirty = false;
+ int processing_layer = 0;
+ Sky *dirty_list = nullptr;
+
+ //State to track when radiance cubemap needs updating
+ SkyMaterialData *prev_material;
+ Vector3 prev_position;
+ float prev_time;
+
+ void free(RendererStorageRD *p_storage);
+
+ RID get_textures(RendererStorageRD *p_storage, SkyTextureSetVersion p_version, RID p_default_shader_rd);
+ 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);
+ };
+
+ uint32_t sky_ggx_samples_quality;
+ bool sky_use_cubemap_array;
+ Sky *dirty_sky_list = nullptr;
+ mutable RID_Owner<Sky, true> sky_owner;
+ int roughness_layers;
+
+ RendererStorageRD::ShaderData *_create_sky_shader_func();
+ static RendererStorageRD::ShaderData *_create_sky_shader_funcs();
+
+ RendererStorageRD::MaterialData *_create_sky_material_func(SkyShaderData *p_shader);
+ static RendererStorageRD::MaterialData *_create_sky_material_funcs(RendererStorageRD::ShaderData *p_shader);
+
+ RendererSceneSkyRD();
+
+ void init(RendererStorageRD *p_storage);
+
+ void setup(RendererSceneEnvironmentRD *p_env, RID p_render_buffers, const CameraMatrix &p_projection, const Transform &p_transform, const Size2i p_screen_size, RendererSceneRenderRD *p_scene_render);
+ void update(RendererSceneEnvironmentRD *p_env, const CameraMatrix &p_projection, const Transform &p_transform, double p_time);
+ void draw(RendererSceneEnvironmentRD *p_env, bool p_can_continue_color, bool p_can_continue_depth, RID p_fb, const CameraMatrix &p_projection, const Transform &p_transform, double p_time);
+
+ void invalidate_sky(Sky *p_sky);
+ void update_dirty_skys();
+
+ RID sky_get_material(RID p_sky) const;
+
+ RID allocate_sky_rid();
+ void initialize_sky_rid(RID p_rid);
+ Sky *get_sky(RID p_sky) const;
+ void free_sky(RID p_sky);
+ void sky_set_radiance_size(RID p_sky, int p_radiance_size);
+ void sky_set_mode(RID p_sky, RS::SkyMode p_mode);
+ void sky_set_material(RID p_sky, RID p_material);
+ Ref<Image> sky_bake_panorama(RID p_sky, float p_energy, bool p_bake_irradiance, const Size2i &p_size);
+
+ RID sky_get_radiance_texture_rd(RID p_sky) const;
+};
+
+#endif /* RENDERING_SERVER_SCENE_SKY_RD_H */
diff --git a/servers/rendering/renderer_rd/renderer_storage_rd.h b/servers/rendering/renderer_rd/renderer_storage_rd.h
index 68256dc155..cd3d4604eb 100644
--- a/servers/rendering/renderer_rd/renderer_storage_rd.h
+++ b/servers/rendering/renderer_rd/renderer_storage_rd.h
@@ -2263,7 +2263,7 @@ public:
void render_info_end_capture() {}
int get_captured_render_info(RS::RenderInfo p_info) { return 0; }
- int get_render_info(RS::RenderInfo p_info) { return 0; }
+ uint64_t get_render_info(RS::RenderInfo p_info) { return 0; }
String get_video_adapter_name() const { return String(); }
String get_video_adapter_vendor() const { return String(); }
diff --git a/servers/rendering/renderer_storage.h b/servers/rendering/renderer_storage.h
index 69ad2cc191..22cf6acb19 100644
--- a/servers/rendering/renderer_storage.h
+++ b/servers/rendering/renderer_storage.h
@@ -608,7 +608,7 @@ public:
virtual void render_info_end_capture() = 0;
virtual int get_captured_render_info(RS::RenderInfo p_info) = 0;
- virtual int get_render_info(RS::RenderInfo p_info) = 0;
+ virtual uint64_t get_render_info(RS::RenderInfo p_info) = 0;
virtual String get_video_adapter_name() const = 0;
virtual String get_video_adapter_vendor() const = 0;
diff --git a/servers/rendering/rendering_server_default.cpp b/servers/rendering/rendering_server_default.cpp
index 61d1efaf22..c6fe6a07e0 100644
--- a/servers/rendering/rendering_server_default.cpp
+++ b/servers/rendering/rendering_server_default.cpp
@@ -257,7 +257,7 @@ void RenderingServerDefault::finish() {
/* STATUS INFORMATION */
-int RenderingServerDefault::get_render_info(RenderInfo p_info) {
+uint64_t RenderingServerDefault::get_render_info(RenderInfo p_info) {
return RSG::storage->get_render_info(p_info);
}
diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h
index 73b463f6e7..c6be07a3de 100644
--- a/servers/rendering/rendering_server_default.h
+++ b/servers/rendering/rendering_server_default.h
@@ -901,7 +901,7 @@ public:
/* STATUS INFORMATION */
- virtual int get_render_info(RenderInfo p_info) override;
+ virtual uint64_t get_render_info(RenderInfo p_info) override;
virtual String get_video_adapter_name() const override;
virtual String get_video_adapter_vendor() const override;
diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp
index cb98a71e86..df1a7d58d0 100644
--- a/servers/rendering/shader_language.cpp
+++ b/servers/rendering/shader_language.cpp
@@ -558,13 +558,13 @@ ShaderLanguage::Token ShaderLanguage::_get_token() {
return _make_token(TK_ERROR, "Invalid numeric constant");
}
hexa_found = true;
- } else if (GETCHAR(i) == 'e') {
- if (hexa_found || exponent_found || float_suffix_found) {
+ } else if (GETCHAR(i) == 'e' && !hexa_found) {
+ if (exponent_found || float_suffix_found) {
return _make_token(TK_ERROR, "Invalid numeric constant");
}
exponent_found = true;
- } else if (GETCHAR(i) == 'f') {
- if (hexa_found || exponent_found) {
+ } else if (GETCHAR(i) == 'f' && !hexa_found) {
+ if (exponent_found) {
return _make_token(TK_ERROR, "Invalid numeric constant");
}
float_suffix_found = true;
@@ -5926,15 +5926,15 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
pos = _get_tkpos();
tk = _get_token();
if (tk.type != TK_SEMICOLON) {
- //all is good
_set_error("Expected ';' after discard");
+ return ERR_PARSE_ERROR;
}
p_block->statements.push_back(flow);
} else if (tk.type == TK_CF_BREAK) {
if (!p_can_break) {
- //all is good
- _set_error("Breaking is not allowed here");
+ _set_error("'break' is not allowed outside of a loop or 'switch' statement");
+ return ERR_PARSE_ERROR;
}
ControlFlowNode *flow = alloc_node<ControlFlowNode>();
@@ -5943,8 +5943,8 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
pos = _get_tkpos();
tk = _get_token();
if (tk.type != TK_SEMICOLON) {
- //all is good
_set_error("Expected ';' after break");
+ return ERR_PARSE_ERROR;
}
p_block->statements.push_back(flow);
@@ -5959,8 +5959,8 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
} else if (tk.type == TK_CF_CONTINUE) {
if (!p_can_continue) {
- //all is good
- _set_error("Continuing is not allowed here");
+ _set_error("'continue' is not allowed outside of a loop");
+ return ERR_PARSE_ERROR;
}
ControlFlowNode *flow = alloc_node<ControlFlowNode>();
@@ -5971,6 +5971,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
if (tk.type != TK_SEMICOLON) {
//all is good
_set_error("Expected ';' after continue");
+ return ERR_PARSE_ERROR;
}
p_block->statements.push_back(flow);
diff --git a/servers/rendering_server.h b/servers/rendering_server.h
index 65065841a6..6a8bb83ec1 100644
--- a/servers/rendering_server.h
+++ b/servers/rendering_server.h
@@ -1419,7 +1419,7 @@ public:
INFO_VERTEX_MEM_USED,
};
- virtual int get_render_info(RenderInfo p_info) = 0;
+ virtual uint64_t get_render_info(RenderInfo p_info) = 0;
virtual String get_video_adapter_name() const = 0;
virtual String get_video_adapter_vendor() const = 0;
diff --git a/servers/text_server.cpp b/servers/text_server.cpp
index 27fdd090f1..d6d3c11cfb 100644
--- a/servers/text_server.cpp
+++ b/servers/text_server.cpp
@@ -219,6 +219,11 @@ void TextServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("create_font_system", "name", "base_size"), &TextServer::create_font_system, DEFVAL(16));
ClassDB::bind_method(D_METHOD("create_font_resource", "filename", "base_size"), &TextServer::create_font_resource, DEFVAL(16));
ClassDB::bind_method(D_METHOD("create_font_memory", "data", "type", "base_size"), &TextServer::_create_font_memory, DEFVAL(16));
+ ClassDB::bind_method(D_METHOD("create_font_bitmap", "height", "ascent", "base_size"), &TextServer::create_font_bitmap);
+
+ ClassDB::bind_method(D_METHOD("font_bitmap_add_texture", "font", "texture"), &TextServer::font_bitmap_add_texture);
+ ClassDB::bind_method(D_METHOD("font_bitmap_add_char", "font", "char", "texture_idx", "rect", "align", "advance"), &TextServer::font_bitmap_add_char);
+ ClassDB::bind_method(D_METHOD("font_bitmap_add_kerning_pair", "font", "A", "B", "kerning"), &TextServer::font_bitmap_add_kerning_pair);
ClassDB::bind_method(D_METHOD("font_get_height", "font", "size"), &TextServer::font_get_height);
ClassDB::bind_method(D_METHOD("font_get_ascent", "font", "size"), &TextServer::font_get_ascent);
diff --git a/servers/text_server.h b/servers/text_server.h
index 3268741a74..e1429da1d1 100644
--- a/servers/text_server.h
+++ b/servers/text_server.h
@@ -192,18 +192,6 @@ public:
Vector<TextServer::Glyph> glyphs_logical;
};
- struct BitmapFontData {
- int height = 0;
- int ascent = 0;
- int charcount = 0;
- const int *char_rects = nullptr;
- int kerning_count = 0;
- const int *kernings = nullptr;
- int w = 0;
- int h = 0;
- const unsigned char *img = nullptr;
- };
-
protected:
static void _bind_methods();
@@ -236,6 +224,11 @@ public:
virtual RID create_font_system(const String &p_name, int p_base_size = 16) = 0;
virtual RID create_font_resource(const String &p_filename, int p_base_size = 16) = 0;
virtual RID create_font_memory(const uint8_t *p_data, size_t p_size, const String &p_type, int p_base_size = 16) = 0;
+ virtual RID create_font_bitmap(float p_height, float p_ascent, int p_base_size = 16) = 0;
+
+ virtual void font_bitmap_add_texture(RID p_font, const Ref<Texture> &p_texture) = 0;
+ virtual void font_bitmap_add_char(RID p_font, char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance) = 0;
+ virtual void font_bitmap_add_kerning_pair(RID p_font, char32_t p_A, char32_t p_B, int p_kerning) = 0;
virtual float font_get_height(RID p_font, int p_size) const = 0;
virtual float font_get_ascent(RID p_font, int p_size) const = 0;
diff --git a/tests/test_geometry_3d.h b/tests/test_geometry_3d.h
new file mode 100644
index 0000000000..2b2a424b2b
--- /dev/null
+++ b/tests/test_geometry_3d.h
@@ -0,0 +1,417 @@
+/*************************************************************************/
+/* test_geometry_3d.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 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 TEST_3D_GEOMETRY_H
+#define TEST_3D_GEOMETRY_H
+
+#include "core/math/geometry_3d.h"
+#include "core/math/plane.h"
+#include "core/math/random_number_generator.h"
+#include "core/math/vector3.h"
+#include "tests/test_macros.h"
+#include "vector"
+
+namespace Test3DGeometry {
+TEST_CASE("[Geometry3D] Closest Points Between Segments") {
+ struct Case {
+ Vector3 p_1, p_2, p_3, p_4;
+ Vector3 got_1, got_2;
+ Vector3 want_1, want_2;
+ Case(){};
+ Case(Vector3 p_p_1, Vector3 p_p_2, Vector3 p_p_3, Vector3 p_p_4, Vector3 p_want_1, Vector3 p_want_2) :
+ p_1(p_p_1), p_2(p_p_2), p_3(p_p_3), p_4(p_p_4), want_1(p_want_1), want_2(p_want_2){};
+ };
+ Vector<Case> tt;
+ tt.push_back(Case(Vector3(1, -1, 1), Vector3(1, 1, -1), Vector3(-1, -2, -1), Vector3(-1, 1, 1), Vector3(1, -0.2, 0.2), Vector3(-1, -0.2, 0.2)));
+ for (int i = 0; i < tt.size(); ++i) {
+ Case current_case = tt[i];
+ Geometry3D::get_closest_points_between_segments(current_case.p_1, current_case.p_2, current_case.p_3, current_case.p_4, current_case.got_1, current_case.got_2);
+ CHECK(current_case.got_1.is_equal_approx(current_case.want_1));
+ CHECK(current_case.got_2.is_equal_approx(current_case.want_2));
+ }
+}
+TEST_CASE("[Geometry3D] Closest Distance Between Segments") {
+ struct Case {
+ Vector3 p_1, p_2, p_3, p_4;
+ float want;
+ Case(){};
+ Case(Vector3 p_p_1, Vector3 p_p_2, Vector3 p_p_3, Vector3 p_p_4, float p_want) :
+ p_1(p_p_1), p_2(p_p_2), p_3(p_p_3), p_4(p_p_4), want(p_want){};
+ };
+ Vector<Case> tt;
+ tt.push_back(Case(Vector3(1, -2, 0), Vector3(1, 2, 0), Vector3(-1, 2, 0), Vector3(-1, -2, 0), 0.0f));
+ for (int i = 0; i < tt.size(); ++i) {
+ Case current_case = tt[i];
+ float out = Geometry3D::get_closest_distance_between_segments(current_case.p_1, current_case.p_2, current_case.p_3, current_case.p_4);
+ CHECK(out == current_case.want);
+ }
+}
+TEST_CASE("[Geometry3D] Build Box Planes") {
+ const Vector3 extents = Vector3(5, 5, 20);
+ Vector<Plane> box = Geometry3D::build_box_planes(extents);
+ CHECK(box.size() == 6);
+ CHECK(extents.x == box[0].d);
+ CHECK(box[0].normal == Vector3(1, 0, 0));
+ CHECK(extents.x == box[1].d);
+ CHECK(box[1].normal == Vector3(-1, 0, 0));
+ CHECK(extents.y == box[2].d);
+ CHECK(box[2].normal == Vector3(0, 1, 0));
+ CHECK(extents.y == box[3].d);
+ CHECK(box[3].normal == Vector3(0, -1, 0));
+ CHECK(extents.z == box[4].d);
+ CHECK(box[4].normal == Vector3(0, 0, 1));
+ CHECK(extents.z == box[5].d);
+ CHECK(box[5].normal == Vector3(0, 0, -1));
+}
+TEST_CASE("[Geometry3D] Build Capsule Planes") {
+ struct Case {
+ real_t radius, height;
+ int sides, lats;
+ Vector3::Axis axis;
+ int want_size;
+ Case(){};
+ Case(real_t p_radius, real_t p_height, int p_sides, int p_lats, Vector3::Axis p_axis, int p_want) :
+ radius(p_radius), height(p_height), sides(p_sides), lats(p_lats), axis(p_axis), want_size(p_want){};
+ };
+ Vector<Case> tt;
+ tt.push_back(Case(10, 20, 6, 10, Vector3::Axis(), 126));
+ for (int i = 0; i < tt.size(); ++i) {
+ Case current_case = tt[i];
+ Vector<Plane> capsule = Geometry3D::build_capsule_planes(current_case.radius, current_case.height, current_case.sides, current_case.lats, current_case.axis);
+ // Should equal (p_sides * p_lats) * 2 + p_sides
+ CHECK(capsule.size() == current_case.want_size);
+ }
+}
+TEST_CASE("[Geometry3D] Build Cylinder Planes") {
+ struct Case {
+ real_t radius, height;
+ int sides;
+ Vector3::Axis axis;
+ int want_size;
+ Case(){};
+ Case(real_t p_radius, real_t p_height, int p_sides, Vector3::Axis p_axis, int p_want) :
+ radius(p_radius), height(p_height), sides(p_sides), axis(p_axis), want_size(p_want){};
+ };
+ Vector<Case> tt;
+ tt.push_back(Case(3.0f, 10.0f, 10, Vector3::Axis(), 12));
+ for (int i = 0; i < tt.size(); ++i) {
+ Case current_case = tt[i];
+ Vector<Plane> planes = Geometry3D::build_cylinder_planes(current_case.radius, current_case.height, current_case.sides, current_case.axis);
+ CHECK(planes.size() == current_case.want_size);
+ }
+}
+TEST_CASE("[Geometry3D] Build Sphere Planes") {
+ struct Case {
+ real_t radius;
+ int lats, lons;
+ Vector3::Axis axis;
+ int want_size;
+ Case(){};
+ Case(real_t p_radius, int p_lat, int p_lons, Vector3::Axis p_axis, int p_want) :
+ radius(p_radius), lats(p_lat), lons(p_lons), axis(p_axis), want_size(p_want){};
+ };
+ Vector<Case> tt;
+ tt.push_back(Case(10.0f, 10, 3, Vector3::Axis(), 63));
+ for (int i = 0; i < tt.size(); ++i) {
+ Case current_case = tt[i];
+ Vector<Plane> planes = Geometry3D::build_sphere_planes(current_case.radius, current_case.lats, current_case.lons, current_case.axis);
+ CHECK(planes.size() == 63);
+ }
+}
+TEST_CASE("[Geometry3D] Build Convex Mesh") {
+ struct Case {
+ Vector<Plane> object;
+ int want_faces, want_edges, want_vertices;
+ Case(){};
+ Case(Vector<Plane> p_object, int p_want_faces, int p_want_edges, int p_want_vertices) :
+ object(p_object), want_faces(p_want_faces), want_edges(p_want_edges), want_vertices(p_want_vertices){};
+ };
+ Vector<Case> tt;
+ tt.push_back(Case(Geometry3D::build_box_planes(Vector3(5, 10, 5)), 6, 12, 8));
+ tt.push_back(Case(Geometry3D::build_capsule_planes(5, 5, 20, 20, Vector3::Axis()), 820, 7603, 6243));
+ tt.push_back(Case(Geometry3D::build_cylinder_planes(5, 5, 20, Vector3::Axis()), 22, 100, 80));
+ tt.push_back(Case(Geometry3D::build_sphere_planes(5, 5, 20), 220, 1011, 522));
+ for (int i = 0; i < tt.size(); ++i) {
+ Case current_case = tt[i];
+ Geometry3D::MeshData mesh = Geometry3D::build_convex_mesh(current_case.object);
+ CHECK(mesh.faces.size() == current_case.want_faces);
+ CHECK(mesh.edges.size() == current_case.want_edges);
+ CHECK(mesh.vertices.size() == current_case.want_vertices);
+ }
+}
+TEST_CASE("[Geometry3D] Clip Polygon") {
+ struct Case {
+ Plane clipping_plane;
+ Vector<Vector3> polygon;
+ bool want;
+ Case(){};
+ Case(Plane p_clipping_plane, Vector<Vector3> p_polygon, bool p_want) :
+ clipping_plane(p_clipping_plane), polygon(p_polygon), want(p_want){};
+ };
+ Vector<Case> tt;
+ Vector<Plane> box_planes = Geometry3D::build_box_planes(Vector3(5, 10, 5));
+ Vector<Vector3> box = Geometry3D::compute_convex_mesh_points(&box_planes[0], box_planes.size());
+ tt.push_back(Case(Plane(), box, true));
+ tt.push_back(Case(Plane(Vector3(0, 3, 0), Vector3(0, 1, 0)), box, false));
+ for (int i = 0; i < tt.size(); ++i) {
+ Case current_case = tt[i];
+ Vector<Vector3> output = Geometry3D::clip_polygon(current_case.polygon, current_case.clipping_plane);
+ if (current_case.want) {
+ CHECK(output == current_case.polygon);
+ } else {
+ CHECK(output != current_case.polygon);
+ }
+ }
+}
+TEST_CASE("[Geometry3D] Compute Convex Mesh Points") {
+ struct Case {
+ Vector<Plane> mesh;
+ Vector<Vector3> want;
+ Case(){};
+ Case(Vector<Plane> p_mesh, Vector<Vector3> p_want) :
+ mesh(p_mesh), want(p_want){};
+ };
+ Vector<Case> tt;
+ Vector<Vector3> cube;
+ cube.push_back(Vector3(-5, -5, -5));
+ cube.push_back(Vector3(5, -5, -5));
+ cube.push_back(Vector3(-5, 5, -5));
+ cube.push_back(Vector3(5, 5, -5));
+ cube.push_back(Vector3(-5, -5, 5));
+ cube.push_back(Vector3(5, -5, 5));
+ cube.push_back(Vector3(-5, 5, 5));
+ cube.push_back(Vector3(5, 5, 5));
+ tt.push_back(Case(Geometry3D::build_box_planes(Vector3(5, 5, 5)), cube));
+ for (int i = 0; i < tt.size(); ++i) {
+ Case current_case = tt[i];
+ Vector<Vector3> vectors = Geometry3D::compute_convex_mesh_points(&current_case.mesh[0], current_case.mesh.size());
+ CHECK(vectors == current_case.want);
+ }
+}
+TEST_CASE("[Geometry3D] Get Closest Point To Segment") {
+ struct Case {
+ Vector3 point;
+ Vector<Vector3> segment;
+ Vector3 want;
+ Case(){};
+ Case(Vector3 p_point, Vector<Vector3> p_segment, Vector3 p_want) :
+ point(p_point), segment(p_segment), want(p_want){};
+ };
+ Vector<Case> tt;
+ Vector<Vector3> test_segment;
+ test_segment.push_back(Vector3(1, 1, 1));
+ test_segment.push_back(Vector3(5, 5, 5));
+ tt.push_back(Case(Vector3(2, 1, 4), test_segment, Vector3(2.33333, 2.33333, 2.33333)));
+ for (int i = 0; i < tt.size(); ++i) {
+ Case current_case = tt[i];
+ Vector3 output = Geometry3D::get_closest_point_to_segment(current_case.point, &current_case.segment[0]);
+ CHECK(output.is_equal_approx(current_case.want));
+ }
+}
+TEST_CASE("[Geometry3D] Plane and Box Overlap") {
+ struct Case {
+ Vector3 normal, max_box;
+ float d;
+ bool want;
+ Case(){};
+ Case(Vector3 p_normal, float p_d, Vector3 p_max_box, bool p_want) :
+ normal(p_normal), max_box(p_max_box), d(p_d), want(p_want){};
+ };
+ Vector<Case> tt;
+ tt.push_back(Case(Vector3(3, 4, 2), 5, Vector3(5, 5, 5), true));
+ tt.push_back(Case(Vector3(0, 1, 0), -10, Vector3(5, 5, 5), false));
+ tt.push_back(Case(Vector3(1, 0, 0), -6, Vector3(5, 5, 5), false));
+ for (int i = 0; i < tt.size(); ++i) {
+ Case current_case = tt[i];
+ bool overlap = Geometry3D::planeBoxOverlap(current_case.normal, current_case.d, current_case.max_box);
+ CHECK(overlap == current_case.want);
+ }
+}
+TEST_CASE("[Geometry3D] Is Point in Projected Triangle") {
+ struct Case {
+ Vector3 point, v_1, v_2, v_3;
+ bool want;
+ Case(){};
+ Case(Vector3 p_point, Vector3 p_v_1, Vector3 p_v_2, Vector3 p_v_3, bool p_want) :
+ point(p_point), v_1(p_v_1), v_2(p_v_2), v_3(p_v_3), want(p_want){};
+ };
+ Vector<Case> tt;
+ tt.push_back(Case(Vector3(1, 1, 0), Vector3(3, 0, 0), Vector3(0, 3, 0), Vector3(-3, 0, 0), true));
+ tt.push_back(Case(Vector3(5, 1, 0), Vector3(3, 0, 0), Vector3(0, 3, 0), Vector3(-3, 0, 0), false));
+ tt.push_back(Case(Vector3(3, 0, 0), Vector3(3, 0, 0), Vector3(0, 3, 0), Vector3(-3, 0, 0), true));
+ for (int i = 0; i < tt.size(); ++i) {
+ Case current_case = tt[i];
+ bool output = Geometry3D::point_in_projected_triangle(current_case.point, current_case.v_1, current_case.v_2, current_case.v_3);
+ CHECK(output == current_case.want);
+ }
+}
+TEST_CASE("[Geometry3D] Does Ray Intersect Triangle") {
+ struct Case {
+ Vector3 from, direction, v_1, v_2, v_3;
+ Vector3 *result;
+ bool want;
+ Case(){};
+ Case(Vector3 p_from, Vector3 p_direction, Vector3 p_v_1, Vector3 p_v_2, Vector3 p_v_3, bool p_want) :
+ from(p_from), direction(p_direction), v_1(p_v_1), v_2(p_v_2), v_3(p_v_3), result(nullptr), want(p_want){};
+ };
+ Vector<Case> tt;
+ tt.push_back(Case(Vector3(0, 1, 1), Vector3(0, 0, -10), Vector3(0, 3, 0), Vector3(-3, 0, 0), Vector3(3, 0, 0), true));
+ tt.push_back(Case(Vector3(5, 10, 1), Vector3(0, 0, -10), Vector3(0, 3, 0), Vector3(-3, 0, 0), Vector3(3, 0, 0), false));
+ tt.push_back(Case(Vector3(0, 1, 1), Vector3(0, 0, 10), Vector3(0, 3, 0), Vector3(-3, 0, 0), Vector3(3, 0, 0), false));
+ for (int i = 0; i < tt.size(); ++i) {
+ Case current_case = tt[i];
+ bool output = Geometry3D::ray_intersects_triangle(current_case.from, current_case.direction, current_case.v_1, current_case.v_2, current_case.v_3, current_case.result);
+ CHECK(output == current_case.want);
+ }
+}
+TEST_CASE("[Geometry3D] Does Segment Intersect Convex") {
+ struct Case {
+ Vector3 from, to;
+ Vector<Plane> planes;
+ Vector3 *result, *normal;
+ bool want;
+ Case(){};
+ Case(Vector3 p_from, Vector3 p_to, Vector<Plane> p_planes, bool p_want) :
+ from(p_from), to(p_to), planes(p_planes), result(nullptr), normal(nullptr), want(p_want){};
+ };
+ Vector<Case> tt;
+ tt.push_back(Case(Vector3(10, 10, 10), Vector3(0, 0, 0), Geometry3D::build_box_planes(Vector3(5, 5, 5)), true));
+ tt.push_back(Case(Vector3(10, 10, 10), Vector3(5, 5, 5), Geometry3D::build_box_planes(Vector3(5, 5, 5)), true));
+ tt.push_back(Case(Vector3(10, 10, 10), Vector3(6, 5, 5), Geometry3D::build_box_planes(Vector3(5, 5, 5)), false));
+ for (int i = 0; i < tt.size(); ++i) {
+ Case current_case = tt[i];
+ bool output = Geometry3D::segment_intersects_convex(current_case.from, current_case.to, &current_case.planes[0], current_case.planes.size(), current_case.result, current_case.normal);
+ CHECK(output == current_case.want);
+ }
+}
+TEST_CASE("[Geometry3D] Segment Intersects Cylinder") {
+ struct Case {
+ Vector3 from, to;
+ real_t height, radius;
+ Vector3 *result, *normal;
+ bool want;
+ Case(){};
+ Case(Vector3 p_from, Vector3 p_to, real_t p_height, real_t p_radius, bool p_want) :
+ from(p_from), to(p_to), height(p_height), radius(p_radius), result(nullptr), normal(nullptr), want(p_want){};
+ };
+ Vector<Case> tt;
+ tt.push_back(Case(Vector3(10, 10, 10), Vector3(0, 0, 0), 5, 5, true));
+ tt.push_back(Case(Vector3(10, 10, 10), Vector3(6, 6, 6), 5, 5, false));
+ for (int i = 0; i < tt.size(); ++i) {
+ Case current_case = tt[i];
+ bool output = Geometry3D::segment_intersects_cylinder(current_case.from, current_case.to, current_case.height, current_case.radius, current_case.result, current_case.normal);
+ CHECK(output == current_case.want);
+ }
+}
+TEST_CASE("[Geometry3D] Segment Intersects Cylinder") {
+ struct Case {
+ Vector3 from, to, sphere_pos;
+ real_t radius;
+ Vector3 *result, *normal;
+ bool want;
+ Case(){};
+ Case(Vector3 p_from, Vector3 p_to, Vector3 p_sphere_pos, real_t p_radius, bool p_want) :
+ from(p_from), to(p_to), sphere_pos(p_sphere_pos), radius(p_radius), result(nullptr), normal(nullptr), want(p_want){};
+ };
+ Vector<Case> tt;
+ tt.push_back(Case(Vector3(10, 10, 10), Vector3(0, 0, 0), Vector3(0, 0, 0), 5, true));
+ tt.push_back(Case(Vector3(10, 10, 10), Vector3(0, 0, 2.5), Vector3(0, 0, 0), 5, true));
+ tt.push_back(Case(Vector3(10, 10, 10), Vector3(5, 5, 5), Vector3(0, 0, 0), 5, false));
+ for (int i = 0; i < tt.size(); ++i) {
+ Case current_case = tt[i];
+ bool output = Geometry3D::segment_intersects_sphere(current_case.from, current_case.to, current_case.sphere_pos, current_case.radius, current_case.result, current_case.normal);
+ CHECK(output == current_case.want);
+ }
+}
+TEST_CASE("[Geometry3D] Segment Intersects Triangle") {
+ struct Case {
+ Vector3 from, to, v_1, v_2, v_3, *result;
+ bool want;
+ Case(){};
+ Case(Vector3 p_from, Vector3 p_to, Vector3 p_v_1, Vector3 p_v_2, Vector3 p_v_3, bool p_want) :
+ from(p_from), to(p_to), v_1(p_v_1), v_2(p_v_2), v_3(p_v_3), result(nullptr), want(p_want){};
+ };
+ Vector<Case> tt;
+ tt.push_back(Case(Vector3(1, 1, 1), Vector3(-1, -1, -1), Vector3(-3, 0, 0), Vector3(0, 3, 0), Vector3(3, 0, 0), true));
+ tt.push_back(Case(Vector3(1, 1, 1), Vector3(3, 0, 0), Vector3(-3, 0, 0), Vector3(0, 3, 0), Vector3(3, 0, 0), true));
+ tt.push_back(Case(Vector3(1, 1, 1), Vector3(10, -1, -1), Vector3(-3, 0, 0), Vector3(0, 3, 0), Vector3(3, 0, 0), false));
+ for (int i = 0; i < tt.size(); ++i) {
+ Case current_case = tt[i];
+ bool output = Geometry3D::segment_intersects_triangle(current_case.from, current_case.to, current_case.v_1, current_case.v_2, current_case.v_3, current_case.result);
+ CHECK(output == current_case.want);
+ }
+}
+TEST_CASE("[Geometry3D] Triangle and Box Overlap") {
+ struct Case {
+ Vector3 box_centre;
+ Vector3 box_half_size;
+ Vector3 *tri_verts;
+ bool want;
+ Case(){};
+ Case(Vector3 p_centre, Vector3 p_half_size, Vector3 *p_verts, bool p_want) :
+ box_centre(p_centre), box_half_size(p_half_size), tri_verts(p_verts), want(p_want){};
+ };
+ Vector<Case> tt;
+ Vector3 GoodTriangle[3] = { Vector3(3, 2, 3), Vector3(2, 2, 1), Vector3(2, 1, 1) };
+ tt.push_back(Case(Vector3(0, 0, 0), Vector3(5, 5, 5), GoodTriangle, true));
+ Vector3 BadTriangle[3] = { Vector3(100, 100, 100), Vector3(-100, -100, -100), Vector3(10, 10, 10) };
+ tt.push_back(Case(Vector3(1000, 1000, 1000), Vector3(1, 1, 1), BadTriangle, false));
+ for (int i = 0; i < tt.size(); ++i) {
+ Case current_case = tt[i];
+ bool output = Geometry3D::triangle_box_overlap(current_case.box_centre, current_case.box_half_size, current_case.tri_verts);
+ CHECK(output == current_case.want);
+ }
+}
+TEST_CASE("[Geometry3D] Triangle and Sphere Intersect") {
+ struct Case {
+ Vector<Vector3> triangle;
+ Vector3 normal, sphere_pos, triangle_contact, sphere_contact;
+ real_t sphere_radius;
+ bool want;
+ Case(){};
+ Case(Vector<Vector3> p_triangle, Vector3 p_normal, Vector3 p_sphere_pos, real_t p_sphere_radius, bool p_want) :
+ triangle(p_triangle), normal(p_normal), sphere_pos(p_sphere_pos), triangle_contact(Vector3()), sphere_contact(Vector3()), sphere_radius(p_sphere_radius), want(p_want){};
+ };
+ Vector<Case> tt;
+ Vector<Vector3> triangle;
+ triangle.push_back(Vector3(3, 0, 0));
+ triangle.push_back(Vector3(-3, 0, 0));
+ triangle.push_back(Vector3(0, 3, 0));
+ tt.push_back(Case(triangle, Vector3(0, -1, 0), Vector3(0, 0, 0), 5, true));
+ tt.push_back(Case(triangle, Vector3(0, 1, 0), Vector3(0, 0, 0), 5, true));
+ tt.push_back(Case(triangle, Vector3(0, 1, 0), Vector3(20, 0, 0), 5, false));
+ for (int i = 0; i < tt.size(); ++i) {
+ Case current_case = tt[i];
+ bool output = Geometry3D::triangle_sphere_intersection_test(&current_case.triangle[0], current_case.normal, current_case.sphere_pos, current_case.sphere_radius, current_case.triangle_contact, current_case.sphere_contact);
+ CHECK(output == current_case.want);
+ }
+}
+} // namespace Test3DGeometry
+#endif
diff --git a/tests/test_main.cpp b/tests/test_main.cpp
index df43b7424f..7e9f8319a0 100644
--- a/tests/test_main.cpp
+++ b/tests/test_main.cpp
@@ -45,6 +45,7 @@
#include "test_expression.h"
#include "test_file_access.h"
#include "test_geometry_2d.h"
+#include "test_geometry_3d.h"
#include "test_gradient.h"
#include "test_gui.h"
#include "test_image.h"
@@ -72,6 +73,7 @@
#include "test_text_server.h"
#include "test_validate_testing.h"
#include "test_variant.h"
+#include "test_xml_parser.h"
#include "modules/modules_tests.gen.h"
diff --git a/tests/test_path_follow_2d.h b/tests/test_path_follow_2d.h
index 28b62de5bb..388b690060 100644
--- a/tests/test_path_follow_2d.h
+++ b/tests/test_path_follow_2d.h
@@ -45,9 +45,9 @@ TEST_CASE("[PathFollow2D] Sampling with unit offset") {
curve->add_point(Vector2(100, 100));
curve->add_point(Vector2(0, 100));
curve->add_point(Vector2(0, 0));
- const Ref<Path2D> &path = memnew(Path2D());
+ const Path2D *path = memnew(Path2D);
path->set_curve(curve);
- const Ref<PathFollow2D> &path_follow_2d = memnew(PathFollow2D());
+ const PathFollow2D *path_follow_2d = memnew(PathFollow2D);
path->add_child(path_follow_2d);
path_follow_2d->set_unit_offset(0);
@@ -76,6 +76,8 @@ TEST_CASE("[PathFollow2D] Sampling with unit offset") {
path_follow_2d->set_unit_offset(1);
CHECK(path_follow_2d->get_transform().get_origin().is_equal_approx(Vector2(0, 0)));
+
+ memdelete(path);
}
TEST_CASE("[PathFollow2D] Sampling with offset") {
@@ -85,9 +87,9 @@ TEST_CASE("[PathFollow2D] Sampling with offset") {
curve->add_point(Vector2(100, 100));
curve->add_point(Vector2(0, 100));
curve->add_point(Vector2(0, 0));
- const Ref<Path2D> &path = memnew(Path2D());
+ const Path2D *path = memnew(Path2D);
path->set_curve(curve);
- const Ref<PathFollow2D> &path_follow_2d = memnew(PathFollow2D());
+ const PathFollow2D *path_follow_2d = memnew(PathFollow2D);
path->add_child(path_follow_2d);
path_follow_2d->set_offset(0);
@@ -116,6 +118,8 @@ TEST_CASE("[PathFollow2D] Sampling with offset") {
path_follow_2d->set_offset(400);
CHECK(path_follow_2d->get_transform().get_origin().is_equal_approx(Vector2(0, 0)));
+
+ memdelete(path);
}
TEST_CASE("[PathFollow2D] Removal of a point in curve") {
@@ -123,9 +127,9 @@ TEST_CASE("[PathFollow2D] Removal of a point in curve") {
curve->add_point(Vector2(0, 0));
curve->add_point(Vector2(100, 0));
curve->add_point(Vector2(100, 100));
- const Ref<Path2D> &path = memnew(Path2D());
+ const Path2D *path = memnew(Path2D);
path->set_curve(curve);
- const Ref<PathFollow2D> &path_follow_2d = memnew(PathFollow2D());
+ const PathFollow2D *path_follow_2d = memnew(PathFollow2D);
path->add_child(path_follow_2d);
path_follow_2d->set_unit_offset(0.5);
@@ -136,15 +140,17 @@ TEST_CASE("[PathFollow2D] Removal of a point in curve") {
CHECK_MESSAGE(
path_follow_2d->get_transform().get_origin().is_equal_approx(Vector2(50, 50)),
"Path follow's position should be updated after removing a point from the curve");
+
+ memdelete(path);
}
TEST_CASE("[PathFollow2D] Setting h_offset and v_offset") {
const Ref<Curve2D> &curve = memnew(Curve2D());
curve->add_point(Vector2(0, 0));
curve->add_point(Vector2(100, 0));
- const Ref<Path2D> &path = memnew(Path2D());
+ const Path2D *path = memnew(Path2D);
path->set_curve(curve);
- const Ref<PathFollow2D> &path_follow_2d = memnew(PathFollow2D());
+ const PathFollow2D *path_follow_2d = memnew(PathFollow2D);
path->add_child(path_follow_2d);
path_follow_2d->set_unit_offset(0.5);
@@ -155,15 +161,17 @@ TEST_CASE("[PathFollow2D] Setting h_offset and v_offset") {
path_follow_2d->set_v_offset(25);
CHECK(path_follow_2d->get_transform().get_origin().is_equal_approx(Vector2(75, 25)));
+
+ memdelete(path);
}
TEST_CASE("[PathFollow2D] Unit offset out of range") {
const Ref<Curve2D> &curve = memnew(Curve2D());
curve->add_point(Vector2(0, 0));
curve->add_point(Vector2(100, 0));
- const Ref<Path2D> &path = memnew(Path2D());
+ const Path2D *path = memnew(Path2D);
path->set_curve(curve);
- const Ref<PathFollow2D> &path_follow_2d = memnew(PathFollow2D());
+ const PathFollow2D *path_follow_2d = memnew(PathFollow2D);
path->add_child(path_follow_2d);
path_follow_2d->set_loop(true);
@@ -189,15 +197,17 @@ TEST_CASE("[PathFollow2D] Unit offset out of range") {
CHECK_MESSAGE(
path_follow_2d->get_unit_offset() == 1,
"Unit Offset should be clamped at 1");
+
+ memdelete(path);
}
TEST_CASE("[PathFollow2D] Offset out of range") {
const Ref<Curve2D> &curve = memnew(Curve2D());
curve->add_point(Vector2(0, 0));
curve->add_point(Vector2(100, 0));
- const Ref<Path2D> &path = memnew(Path2D());
+ const Path2D *path = memnew(Path2D);
path->set_curve(curve);
- const Ref<PathFollow2D> &path_follow_2d = memnew(PathFollow2D());
+ const PathFollow2D *path_follow_2d = memnew(PathFollow2D);
path->add_child(path_follow_2d);
path_follow_2d->set_loop(true);
@@ -223,6 +233,8 @@ TEST_CASE("[PathFollow2D] Offset out of range") {
CHECK_MESSAGE(
path_follow_2d->get_offset() == 100,
"Offset should be clamped at 1");
+
+ memdelete(path);
}
} // namespace TestPathFollow2D
diff --git a/tests/test_path_follow_3d.h b/tests/test_path_follow_3d.h
new file mode 100644
index 0000000000..b6b4c88222
--- /dev/null
+++ b/tests/test_path_follow_3d.h
@@ -0,0 +1,220 @@
+/*************************************************************************/
+/* test_path_follow_3d.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 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 TEST_PATH_FOLLOW_3D_H
+#define TEST_PATH_FOLLOW_3D_H
+
+#include "scene/3d/path_3d.h"
+#include "scene/resources/curve.h"
+
+#include "tests/test_macros.h"
+
+namespace TestPathFollow3D {
+
+TEST_CASE("[PathFollow3D] Sampling with unit offset") {
+ const Ref<Curve3D> &curve = memnew(Curve3D());
+ curve->add_point(Vector3(0, 0, 0));
+ curve->add_point(Vector3(100, 0, 0));
+ curve->add_point(Vector3(100, 100, 0));
+ curve->add_point(Vector3(100, 100, 100));
+ curve->add_point(Vector3(100, 0, 100));
+ const Path3D *path = memnew(Path3D);
+ path->set_curve(curve);
+ const PathFollow3D *path_follow_3d = memnew(PathFollow3D);
+ path->add_child(path_follow_3d);
+
+ path_follow_3d->set_unit_offset(0);
+ CHECK(path_follow_3d->get_transform().get_origin().is_equal_approx(Vector3(0, 0, 0));
+
+ path_follow_3d->set_unit_offset(0.125);
+ CHECK(path_follow_3d->get_transform().get_origin().is_equal_approx(Vector3(50, 0, 0));
+
+ path_follow_3d->set_unit_offset(0.25);
+ CHECK(path_follow_3d->get_transform().get_origin().is_equal_approx(Vector3(100, 0, 0);
+
+ path_follow_3d->set_unit_offset(0.375);
+ CHECK(path_follow_3d->get_transform().get_origin().is_equal_approx(Vector3(100, 50, 0)));
+
+ path_follow_3d->set_unit_offset(0.5);
+ CHECK(path_follow_3d->get_transform().get_origin().is_equal_approx(Vector3(100, 100, 0)));
+
+ path_follow_3d->set_unit_offset(0.625);
+ CHECK(path_follow_3d->get_transform().get_origin().is_equal_approx(Vector3(100, 100, 50)));
+
+ path_follow_3d->set_unit_offset(0.75);
+ CHECK(path_follow_3d->get_transform().get_origin().is_equal_approx(Vector3(100, 100, 100)));
+
+ path_follow_3d->set_unit_offset(0.875);
+ CHECK(path_follow_3d->get_transform().get_origin().is_equal_approx(Vector3(100, 50, 100)));
+
+ path_follow_3d->set_unit_offset(1);
+ CHECK(path_follow_3d->get_transform().get_origin().is_equal_approx(Vector3(100, 0, 100)));
+
+ memdelete(path);
+}
+
+TEST_CASE("[PathFollow3D] Sampling with offset") {
+ const Ref<Curve3D> &curve = memnew(Curve3D());
+ curve->add_point(Vector3(0, 0, 0));
+ curve->add_point(Vector3(100, 0, 0));
+ curve->add_point(Vector3(100, 100, 0));
+ curve->add_point(Vector3(100, 100, 100));
+ curve->add_point(Vector3(100, 0, 100));
+ const Path3D *path = memnew(Path3D);
+ path->set_curve(curve);
+ const PathFollow3D *path_follow_3d = memnew(PathFollow3D);
+ path->add_child(path_follow_3d);
+
+ path_follow_3d->set_offset(0);
+ CHECK(path_follow_3d->get_transform().get_origin().is_equal_approx(Vector3(0, 0, 0));
+
+ path_follow_3d->set_offset(50);
+ CHECK(path_follow_3d->get_transform().get_origin().is_equal_approx(Vector3(50, 0, 0));
+
+ path_follow_3d->set_offset(100);
+ CHECK(path_follow_3d->get_transform().get_origin().is_equal_approx(Vector3(100, 0, 0);
+
+ path_follow_3d->set_offset(150);
+ CHECK(path_follow_3d->get_transform().get_origin().is_equal_approx(Vector3(100, 50, 0)));
+
+ path_follow_3d->set_offset(200);
+ CHECK(path_follow_3d->get_transform().get_origin().is_equal_approx(Vector3(100, 100, 0)));
+
+ path_follow_3d->set_offset(250);
+ CHECK(path_follow_3d->get_transform().get_origin().is_equal_approx(Vector3(100, 100, 50)));
+
+ path_follow_3d->set_offset(300);
+ CHECK(path_follow_3d->get_transform().get_origin().is_equal_approx(Vector3(100, 100, 100)));
+
+ path_follow_3d->set_offset(350);
+ CHECK(path_follow_3d->get_transform().get_origin().is_equal_approx(Vector3(100, 50, 100)));
+
+ path_follow_3d->set_offset(400);
+ CHECK(path_follow_3d->get_transform().get_origin().is_equal_approx(Vector3(100, 0, 100)));
+
+ memdelete(path);
+}
+
+TEST_CASE("[PathFollow3D] Removal of a point in curve") {
+ const Ref<Curve3D> &curve = memnew(Curve3D());
+ curve->add_point(Vector3(0, 0, 0));
+ curve->add_point(Vector3(100, 0, 0));
+ curve->add_point(Vector3(100, 100, 0));
+ const Path3D *path = memnew(Path3D);
+ path->set_curve(curve);
+ const PathFollow3D *path_follow_3d = memnew(PathFollow3D);
+ path->add_child(path_follow_3d);
+
+ path_follow_3d->set_unit_offset(0.5);
+ CHECK(path_follow_3d->get_transform().get_origin().is_equal_approx(Vector2(100, 0, 0)));
+
+ curve->remove_point(1);
+
+ CHECK_MESSAGE(
+ path_follow_3d->get_transform().get_origin().is_equal_approx(Vector2(50, 50, 0)),
+ "Path follow's position should be updated after removing a point from the curve");
+
+ memdelete(path);
+}
+
+TEST_CASE("[PathFollow3D] Unit offset out of range") {
+ const Ref<Curve3D> &curve = memnew(Curve3D());
+ curve->add_point(Vector3(0, 0, 0));
+ curve->add_point(Vector3(100, 0, 0));
+ const Path3D *path = memnew(Path3D);
+ path->set_curve(curve);
+ const PathFollow3D *path_follow_3d = memnew(PathFollow3D);
+ path->add_child(path_follow_3d);
+
+ path_follow_3d->set_loop(true);
+
+ path_follow_3d->set_unit_offset(-0.3);
+ CHECK_MESSAGE(
+ path_follow_3d->get_unit_offset() == 0.7,
+ "Unit Offset should loop back from the end in the opposite direction");
+
+ path_follow_3d->set_unit_offset(1.3);
+ CHECK_MESSAGE(
+ path_follow_3d->get_unit_offset() == 0.3,
+ "Unit Offset should loop back from the end in the opposite direction");
+
+ path_follow_3d->set_loop(false);
+
+ path_follow_3d->set_unit_offset(-0.3);
+ CHECK_MESSAGE(
+ path_follow_3d->get_unit_offset() == 0,
+ "Unit Offset should be clamped at 0");
+
+ path_follow_3d->set_unit_offset(1.3);
+ CHECK_MESSAGE(
+ path_follow_3d->get_unit_offset() == 1,
+ "Unit Offset should be clamped at 1");
+
+ memdelete(path);
+}
+
+TEST_CASE("[PathFollow3D] Offset out of range") {
+ const Ref<Curve3D> &curve = memnew(Curve3D());
+ curve->add_point(Vector3(0, 0, 0));
+ curve->add_point(Vector3(100, 0, 0));
+ const Path3D *path = memnew(Path3D);
+ path->set_curve(curve);
+ const PathFollow3D *path_follow_3d = memnew(PathFollow3D);
+ path->add_child(path_follow_3d);
+
+ path_follow_3d->set_loop(true);
+
+ path_follow_3d->set_offset(-50);
+ CHECK_MESSAGE(
+ path_follow_3d->get_offset() == 50,
+ "Offset should loop back from the end in the opposite direction");
+
+ path_follow_3d->set_offset(150);
+ CHECK_MESSAGE(
+ path_follow_3d->get_offset() == 50,
+ "Offset should loop back from the end in the opposite direction");
+
+ path_follow_3d->set_loop(false);
+
+ path_follow_3d->set_offset(-50);
+ CHECK_MESSAGE(
+ path_follow_3d->get_offset() == 0,
+ "Offset should be clamped at 0");
+
+ path_follow_3d->set_offset(150);
+ CHECK_MESSAGE(
+ path_follow_3d->get_offset() == 100,
+ "Offset should be clamped at max value of curve");
+
+ memdelete(path);
+}
+} // namespace TestPathFollow3D
+
+#endif // TEST_PATH_FOLLOW_3D_H
diff --git a/tests/test_string.h b/tests/test_string.h
index cc3152203e..17f24fb0d8 100644
--- a/tests/test_string.h
+++ b/tests/test_string.h
@@ -1166,6 +1166,52 @@ TEST_CASE("[String] xml_escape/unescape") {
CHECK(s.xml_escape(false).xml_unescape() == s);
}
+TEST_CASE("[String] xml_unescape") {
+ // Named entities
+ String input = "&quot;&amp;&apos;&lt;&gt;";
+ CHECK(input.xml_unescape() == "\"&\'<>");
+
+ // Numeric entities
+ input = "&#x41;&#66;";
+ CHECK(input.xml_unescape() == "AB");
+
+ input = "&#0;&x#0;More text";
+ String result = input.xml_unescape();
+ // Didn't put in a leading NUL and terminate the string
+ CHECK(input.length() > 0);
+ CHECK(input[0] != '\0');
+ // Entity should be left as-is if invalid
+ CHECK(input.xml_unescape() == input);
+
+ // Check near char32_t range
+ input = "&#xFFFFFFFF;";
+ result = input.xml_unescape();
+ CHECK(result.length() == 1);
+ CHECK(result[0] == 0xFFFFFFFF);
+ input = "&#4294967295;";
+ result = input.xml_unescape();
+ CHECK(result.length() == 1);
+ CHECK(result[0] == 0xFFFFFFFF);
+
+ // Check out of range of char32_t
+ input = "&#xFFFFFFFFF;";
+ CHECK(input.xml_unescape() == input);
+ input = "&#4294967296;";
+ CHECK(input.xml_unescape() == input);
+
+ // Shouldn't consume without ending in a ';'
+ input = "&#66";
+ CHECK(input.xml_unescape() == input);
+ input = "&#x41";
+ CHECK(input.xml_unescape() == input);
+
+ // Invalid characters should make the entity ignored
+ input = "&#x41SomeIrrelevantText;";
+ CHECK(input.xml_unescape() == input);
+ input = "&#66SomeIrrelevantText;";
+ CHECK(input.xml_unescape() == input);
+}
+
TEST_CASE("[String] Strip escapes") {
String s = "\t\tTest Test\r\n Test";
CHECK(s.strip_escapes() == "Test Test Test");
diff --git a/platform/javascript/http_request.h b/tests/test_xml_parser.h
index a9d6faade2..55de048d6a 100644
--- a/platform/javascript/http_request.h
+++ b/tests/test_xml_parser.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* http_request.h */
+/* test_xml_parser.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,46 +28,47 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef HTTP_REQUEST_H
-#define HTTP_REQUEST_H
+#ifndef TEST_XML_PARSER_H
+#define TEST_XML_PARSER_H
-#ifdef __cplusplus
-extern "C" {
-#endif
+#include <inttypes.h>
-#include "stddef.h"
+#include "core/io/xml_parser.h"
+#include "core/string/ustring.h"
-typedef enum {
- XHR_READY_STATE_UNSENT = 0,
- XHR_READY_STATE_OPENED = 1,
- XHR_READY_STATE_HEADERS_RECEIVED = 2,
- XHR_READY_STATE_LOADING = 3,
- XHR_READY_STATE_DONE = 4,
-} godot_xhr_ready_state_t;
+#include "tests/test_macros.h"
-extern int godot_xhr_new();
-extern void godot_xhr_reset(int p_xhr_id);
-extern void godot_xhr_free(int p_xhr_id);
+namespace TestXMLParser {
+TEST_CASE("[XMLParser] End-to-end") {
+ String source = "<?xml version = \"1.0\" encoding=\"UTF-8\" ?>\
+<top attr=\"attr value\">\
+ Text&lt;&#65;&#x42;&gt;\
+</top>";
+ Vector<uint8_t> buff = source.to_utf8_buffer();
-extern int godot_xhr_open(int p_xhr_id, const char *p_method, const char *p_url, const char *p_user = nullptr, const char *p_password = nullptr);
+ XMLParser parser;
+ parser.open_buffer(buff);
-extern void godot_xhr_set_request_header(int p_xhr_id, const char *p_header, const char *p_value);
+ // <?xml ...?> gets parsed as NODE_UNKNOWN
+ CHECK(parser.read() == OK);
+ CHECK(parser.get_node_type() == XMLParser::NodeType::NODE_UNKNOWN);
-extern void godot_xhr_send(int p_xhr_id, const void *p_data, int p_len);
-extern void godot_xhr_abort(int p_xhr_id);
+ CHECK(parser.read() == OK);
+ CHECK(parser.get_node_type() == XMLParser::NodeType::NODE_ELEMENT);
+ CHECK(parser.get_node_name() == "top");
+ CHECK(parser.has_attribute("attr"));
+ CHECK(parser.get_attribute_value("attr") == "attr value");
-/* this is an HTTPClient::ResponseCode, not ::Status */
-extern int godot_xhr_get_status(int p_xhr_id);
-extern godot_xhr_ready_state_t godot_xhr_get_ready_state(int p_xhr_id);
+ CHECK(parser.read() == OK);
+ CHECK(parser.get_node_type() == XMLParser::NodeType::NODE_TEXT);
+ CHECK(parser.get_node_data().lstrip(" \t") == "Text<AB>");
-extern int godot_xhr_get_response_headers_length(int p_xhr_id);
-extern void godot_xhr_get_response_headers(int p_xhr_id, char *r_dst, int p_len);
+ CHECK(parser.read() == OK);
+ CHECK(parser.get_node_type() == XMLParser::NodeType::NODE_ELEMENT_END);
+ CHECK(parser.get_node_name() == "top");
-extern int godot_xhr_get_response_length(int p_xhr_id);
-extern void godot_xhr_get_response(int p_xhr_id, void *r_dst, int p_len);
-
-#ifdef __cplusplus
+ parser.close();
}
-#endif
+} // namespace TestXMLParser
-#endif /* HTTP_REQUEST_H */
+#endif // TEST_XML_PARSER_H