diff options
116 files changed, 8573 insertions, 6692 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/SConstruct b/SConstruct index ab4fe118c8..f03fb72ff3 100644 --- a/SConstruct +++ b/SConstruct @@ -95,7 +95,7 @@ env_base.SConsignFile(".sconsign{0}.dblite".format(pickle.HIGHEST_PROTOCOL)) customs = ["custom.py"] -profile = methods.get_cmdline_bool("profile", False) +profile = ARGUMENTS.get("profile", "") if profile: if os.path.isfile(profile): customs.append(profile) diff --git a/core/core_bind.cpp b/core/core_bind.cpp index 4845f5f1ae..47c75cfa28 100644 --- a/core/core_bind.cpp +++ b/core/core_bind.cpp @@ -322,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 @@ -730,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); @@ -777,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 0cfe9bdb8b..3920116ca4 100644 --- a/core/core_bind.h +++ b/core/core_bind.h @@ -199,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/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/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_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/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/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/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/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/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/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/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/OS.xml b/doc/classes/OS.xml index de3e9a8e10..057a2b8d1a 100644 --- a/doc/classes/OS.xml +++ b/doc/classes/OS.xml @@ -549,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 b68ca2ac67..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. diff --git a/doc/classes/SceneTree.xml b/doc/classes/SceneTree.xml index f65d013bfc..c54e2f4b88 100644 --- a/doc/classes/SceneTree.xml +++ b/doc/classes/SceneTree.xml @@ -181,10 +181,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/Sprite2D.xml b/doc/classes/Sprite2D.xml index c56596423d..83235f0991 100644 --- a/doc/classes/Sprite2D.xml +++ b/doc/classes/Sprite2D.xml @@ -60,7 +60,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/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/editor/create_dialog.cpp b/editor/create_dialog.cpp index a9ed1bc2b9..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 == ""; 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/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 66c4890c45..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); @@ -7246,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(); } @@ -7254,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(); } }; @@ -7465,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/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 3960d155b6..de7996eaa2 100644 --- a/editor/project_settings_editor.cpp +++ b/editor/project_settings_editor.cpp @@ -258,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(); } 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/main/main.cpp b/main/main.cpp index 71dd3cf1ad..884caab1e9 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"); @@ -1992,7 +1994,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/misc/dist/html/editor.html b/misc/dist/html/editor.html index b0b906270b..535721f418 100644 --- a/misc/dist/html/editor.html +++ b/misc/dist/html/editor.html @@ -4,7 +4,7 @@ <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> + <title>Godot Engine Web Editor (@GODOT_VERSION@)</title> <style> *:focus { /* More visible outline for better keyboard navigation. */ @@ -205,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 /> 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/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/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 86a16c17f1..7c1a23d510 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj +++ b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj @@ -30,6 +30,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/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/platform/android/export/export.cpp b/platform/android/export/export.cpp index 088bb35f62..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. 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 cd2f1d367e..948fa8c00b 100644 --- a/platform/android/java/app/AndroidManifest.xml +++ b/platform/android/java/app/AndroidManifest.xml @@ -35,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/config.gradle b/platform/android/java/app/config.gradle index 06d1f4064e..585e517631 100644 --- a/platform/android/java/app/config.gradle +++ b/platform/android/java/app/config.gradle @@ -99,7 +99,7 @@ ext.getGodotLibraryVersion = { -> return libraryVersion } -final String PLUGIN_VALUE_SEPARATOR_REGEX = "\\|" +final String VALUE_SEPARATOR_REGEX = "\\|" // get the list of ABIs the project should be exported to ext.getExportEnabledABIs = { -> @@ -108,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); } @@ -143,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() } } @@ -163,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() } } @@ -182,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/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..50b61c0db3 100644 --- a/platform/javascript/SCsub +++ b/platform/javascript/SCsub @@ -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/js/engine/config.js b/platform/javascript/js/engine/config.js index 97fd718815..bba20dc360 100644 --- a/platform/javascript/js/engine/config.js +++ b/platform/javascript/js/engine/config.js @@ -1,100 +1,309 @@ -/** @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: [], + /** + * 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.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 {Promise} loadPromise + */ + Config.prototype.getModuleConfig = function (loadPath, loadPromise) { + let loader = loadPromise; + return { + 'print': this.onPrint, + 'printErr': this.onPrintError, + 'thisProgram': this.executable, + 'noExitRuntime': true, + 'dynamicLibraries': [`${loadPath}.side.wasm`], + 'instantiateWasm': function (imports, onSuccess) { + loader.then(function (xhr) { + WebAssembly.instantiate(xhr.response, imports).then(function (result) { + onSuccess(result['instance'], result['module']); + }); + }); + loader = 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.js b/platform/javascript/js/engine/engine.js index d14e0e5806..c51955ed3d 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,254 @@ 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. + * @returns {Promise} A Promise that resolves once the engine is loaded. + * + * @function Engine.load + */ + Engine.load = function (basePath) { if (loadPromise == null) { loadPath = basePath; loadPromise = preloader.loadPromise(`${loadPath}.wasm`); 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.')); - 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(); + /** + * 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; } - resolve(); - config = null; + Engine.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) { + Engine.unload(); + } + resolve(); + config = null; + }); + }); }); - }); - }); - return initPromise; - }; + return initPromise; + }, - /** @type {function(string, string):Object} */ - Engine.prototype.preloadFile = function (file, path) { - return preloader.preload(file, path); - }; + /** + * 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); + }, - /** @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); + /** + * 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')); + } + + 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); + }); + 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/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/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/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/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/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/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/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/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 d9aaed3713..de9c75621b 100644 --- a/scene/3d/physics_joint_3d.cpp +++ b/scene/3d/physics_joint_3d.cpp @@ -54,6 +54,7 @@ void Joint3D::_body_exit_tree() { 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(); 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 9b98f3d031..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()) { 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/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/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 8bcff4409f..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); 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/window.cpp b/scene/main/window.cpp index 8198fa41c5..e40e990cf7 100644 --- a/scene/main/window.cpp +++ b/scene/main/window.cpp @@ -1035,6 +1035,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/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/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/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/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_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/rendering/renderer_canvas_cull.cpp b/servers/rendering/renderer_canvas_cull.cpp index e34bc9acb8..081154aa1d 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 { 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/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/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(¤t_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, ¤t_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, ¤t_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(¤t_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 9d9d5a66db..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" 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 |