diff options
146 files changed, 3196 insertions, 1609 deletions
diff --git a/.github/workflows/android_builds.yml b/.github/workflows/android_builds.yml index 69ab830829..2f49f84e90 100644 --- a/.github/workflows/android_builds.yml +++ b/.github/workflows/android_builds.yml @@ -6,7 +6,7 @@ on: env: # Used for the cache key. Add version suffix to force clean build. GODOT_BASE_BRANCH: master - SCONSFLAGS: verbose=yes warnings=extra werror=yes debug_symbols=no module_text_server_fb_enabled=yes + SCONSFLAGS: verbose=yes warnings=extra werror=yes debug_symbols=no module_text_server_fb_enabled=yes strict_checks=yes concurrency: group: ci-${{github.actor}}-${{github.head_ref || github.run_number}}-${{github.ref}}-android diff --git a/.github/workflows/ios_builds.yml b/.github/workflows/ios_builds.yml index 8da6d1311e..207219f9f4 100644 --- a/.github/workflows/ios_builds.yml +++ b/.github/workflows/ios_builds.yml @@ -6,7 +6,7 @@ on: env: # Used for the cache key. Add version suffix to force clean build. GODOT_BASE_BRANCH: master - SCONSFLAGS: verbose=yes warnings=extra werror=yes debug_symbols=no module_text_server_fb_enabled=yes + SCONSFLAGS: verbose=yes warnings=extra werror=yes debug_symbols=no module_text_server_fb_enabled=yes strict_checks=yes concurrency: group: ci-${{github.actor}}-${{github.head_ref || github.run_number}}-${{github.ref}}-ios diff --git a/.github/workflows/linux_builds.yml b/.github/workflows/linux_builds.yml index 10389dc9da..010a13d921 100644 --- a/.github/workflows/linux_builds.yml +++ b/.github/workflows/linux_builds.yml @@ -6,7 +6,7 @@ on: env: # Used for the cache key. Add version suffix to force clean build. GODOT_BASE_BRANCH: master - SCONSFLAGS: verbose=yes warnings=extra werror=yes module_text_server_fb_enabled=yes + SCONSFLAGS: verbose=yes warnings=extra werror=yes module_text_server_fb_enabled=yes strict_checks=yes DOTNET_NOLOGO: true DOTNET_CLI_TELEMETRY_OPTOUT: true TSAN_OPTIONS: suppressions=misc/error_suppressions/tsan.txt diff --git a/.github/workflows/macos_builds.yml b/.github/workflows/macos_builds.yml index 4db7462b3a..938b25f134 100644 --- a/.github/workflows/macos_builds.yml +++ b/.github/workflows/macos_builds.yml @@ -6,7 +6,7 @@ on: env: # Used for the cache key. Add version suffix to force clean build. GODOT_BASE_BRANCH: master - SCONSFLAGS: verbose=yes warnings=extra werror=yes module_text_server_fb_enabled=yes + SCONSFLAGS: verbose=yes warnings=extra werror=yes module_text_server_fb_enabled=yes strict_checks=yes concurrency: group: ci-${{github.actor}}-${{github.head_ref || github.run_number}}-${{github.ref}}-macos diff --git a/.github/workflows/web_builds.yml b/.github/workflows/web_builds.yml index d3a6b5b8b8..11ef7f01c9 100644 --- a/.github/workflows/web_builds.yml +++ b/.github/workflows/web_builds.yml @@ -6,7 +6,7 @@ on: env: # Used for the cache key. Add version suffix to force clean build. GODOT_BASE_BRANCH: master - SCONSFLAGS: verbose=yes warnings=extra werror=yes debug_symbols=no use_closure_compiler=yes + SCONSFLAGS: verbose=yes warnings=extra werror=yes debug_symbols=no use_closure_compiler=yes strict_checks=yes EM_VERSION: 3.1.64 EM_CACHE_FOLDER: "emsdk-cache" diff --git a/.github/workflows/windows_builds.yml b/.github/workflows/windows_builds.yml index 0c21576517..0d2e258308 100644 --- a/.github/workflows/windows_builds.yml +++ b/.github/workflows/windows_builds.yml @@ -7,7 +7,7 @@ on: env: # Used for the cache key. Add version suffix to force clean build. GODOT_BASE_BRANCH: master - SCONSFLAGS: verbose=yes warnings=extra werror=yes module_text_server_fb_enabled=yes d3d12=yes "angle_libs=${{github.workspace}}/" + SCONSFLAGS: verbose=yes warnings=extra werror=yes module_text_server_fb_enabled=yes d3d12=yes strict_checks=yes "angle_libs=${{github.workspace}}/" SCONS_CACHE_MSVC_CONFIG: true concurrency: diff --git a/SConstruct b/SConstruct index b1819a1dab..0245531b45 100644 --- a/SConstruct +++ b/SConstruct @@ -230,7 +230,11 @@ opts.Add("custom_modules", "A list of comma-separated directory paths containing opts.Add(BoolVariable("custom_modules_recursive", "Detect custom modules recursively for each specified path.", True)) # Advanced options -opts.Add(BoolVariable("dev_mode", "Alias for dev options: verbose=yes warnings=extra werror=yes tests=yes", False)) +opts.Add( + BoolVariable( + "dev_mode", "Alias for dev options: verbose=yes warnings=extra werror=yes tests=yes strict_checks=yes", False + ) +) opts.Add(BoolVariable("tests", "Build the unit tests", False)) opts.Add(BoolVariable("fast_unsafe", "Enable unsafe options for faster rebuilds", False)) opts.Add(BoolVariable("ninja", "Use the ninja backend for faster rebuilds", False)) @@ -262,6 +266,7 @@ opts.Add( "", ) opts.Add(BoolVariable("use_precise_math_checks", "Math checks use very precise epsilon (debug option)", False)) +opts.Add(BoolVariable("strict_checks", "Enforce stricter checks (debug option)", False)) opts.Add(BoolVariable("scu_build", "Use single compilation unit build", False)) opts.Add("scu_limit", "Max includes per SCU file when using scu_build (determines RAM use)", "0") opts.Add(BoolVariable("engine_update_check", "Enable engine update checks in the Project Manager", True)) @@ -602,12 +607,16 @@ if env["dev_mode"]: env["warnings"] = ARGUMENTS.get("warnings", "extra") env["werror"] = methods.get_cmdline_bool("werror", True) env["tests"] = methods.get_cmdline_bool("tests", True) + env["strict_checks"] = methods.get_cmdline_bool("strict_checks", True) if env["production"]: env["use_static_cpp"] = methods.get_cmdline_bool("use_static_cpp", True) env["debug_symbols"] = methods.get_cmdline_bool("debug_symbols", False) # LTO "auto" means we handle the preferred option in each platform detect.py. env["lto"] = ARGUMENTS.get("lto", "auto") +if env["strict_checks"]: + env.Append(CPPDEFINES=["STRICT_CHECKS"]) + # Run SCU file generation script if in a SCU build. if env["scu_build"]: max_includes_per_scu = 8 diff --git a/core/core_bind.cpp b/core/core_bind.cpp index 750598ab20..b27981d56b 100644 --- a/core/core_bind.cpp +++ b/core/core_bind.cpp @@ -57,8 +57,11 @@ Error ResourceLoader::load_threaded_request(const String &p_path, const String & ResourceLoader::ThreadLoadStatus ResourceLoader::load_threaded_get_status(const String &p_path, Array r_progress) { float progress = 0; ::ResourceLoader::ThreadLoadStatus tls = ::ResourceLoader::load_threaded_get_status(p_path, &progress); - r_progress.resize(1); - r_progress[0] = progress; + // Default array should never be modified, it causes the hash of the method to change. + if (!ClassDB::is_default_array_arg(r_progress)) { + r_progress.resize(1); + r_progress[0] = progress; + } return (ThreadLoadStatus)tls; } @@ -131,7 +134,7 @@ ResourceUID::ID ResourceLoader::get_resource_uid(const String &p_path) { void ResourceLoader::_bind_methods() { ClassDB::bind_method(D_METHOD("load_threaded_request", "path", "type_hint", "use_sub_threads", "cache_mode"), &ResourceLoader::load_threaded_request, DEFVAL(""), DEFVAL(false), DEFVAL(CACHE_MODE_REUSE)); - ClassDB::bind_method(D_METHOD("load_threaded_get_status", "path", "progress"), &ResourceLoader::load_threaded_get_status, DEFVAL(Array())); + ClassDB::bind_method(D_METHOD("load_threaded_get_status", "path", "progress"), &ResourceLoader::load_threaded_get_status, DEFVAL_ARRAY); ClassDB::bind_method(D_METHOD("load_threaded_get", "path"), &ResourceLoader::load_threaded_get); ClassDB::bind_method(D_METHOD("load", "path", "type_hint", "cache_mode"), &ResourceLoader::load, DEFVAL(""), DEFVAL(CACHE_MODE_REUSE)); @@ -307,7 +310,10 @@ int OS::execute(const String &p_path, const Vector<String> &p_arguments, Array r String pipe; int exitcode = 0; Error err = ::OS::get_singleton()->execute(p_path, args, &pipe, &exitcode, p_read_stderr, nullptr, p_open_console); - r_output.push_back(pipe); + // Default array should never be modified, it causes the hash of the method to change. + if (!ClassDB::is_default_array_arg(r_output)) { + r_output.push_back(pipe); + } if (err != OK) { return -1; } @@ -618,7 +624,7 @@ void OS::_bind_methods() { ClassDB::bind_method(D_METHOD("get_system_font_path_for_text", "font_name", "text", "locale", "script", "weight", "stretch", "italic"), &OS::get_system_font_path_for_text, DEFVAL(String()), DEFVAL(String()), DEFVAL(400), DEFVAL(100), DEFVAL(false)); ClassDB::bind_method(D_METHOD("get_executable_path"), &OS::get_executable_path); ClassDB::bind_method(D_METHOD("read_string_from_stdin"), &OS::read_string_from_stdin); - ClassDB::bind_method(D_METHOD("execute", "path", "arguments", "output", "read_stderr", "open_console"), &OS::execute, DEFVAL(Array()), DEFVAL(false), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("execute", "path", "arguments", "output", "read_stderr", "open_console"), &OS::execute, DEFVAL_ARRAY, DEFVAL(false), DEFVAL(false)); ClassDB::bind_method(D_METHOD("execute_with_pipe", "path", "arguments", "blocking"), &OS::execute_with_pipe, DEFVAL(true)); ClassDB::bind_method(D_METHOD("create_process", "path", "arguments", "open_console"), &OS::create_process, DEFVAL(false)); ClassDB::bind_method(D_METHOD("create_instance", "arguments"), &OS::create_instance); diff --git a/core/core_bind.h b/core/core_bind.h index e4ba0e8f56..7e2686c848 100644 --- a/core/core_bind.h +++ b/core/core_bind.h @@ -73,7 +73,7 @@ public: static ResourceLoader *get_singleton() { return singleton; } Error load_threaded_request(const String &p_path, const String &p_type_hint = "", bool p_use_sub_threads = false, CacheMode p_cache_mode = CACHE_MODE_REUSE); - ThreadLoadStatus load_threaded_get_status(const String &p_path, Array r_progress = Array()); + ThreadLoadStatus load_threaded_get_status(const String &p_path, Array r_progress = ClassDB::default_array_arg); Ref<Resource> load_threaded_get(const String &p_path); Ref<Resource> load(const String &p_path, const String &p_type_hint = "", CacheMode p_cache_mode = CACHE_MODE_REUSE); @@ -166,7 +166,7 @@ public: Vector<String> get_system_font_path_for_text(const String &p_font_name, const String &p_text, const String &p_locale = String(), const String &p_script = String(), int p_weight = 400, int p_stretch = 100, bool p_italic = false) const; String get_executable_path() const; String read_string_from_stdin(); - int execute(const String &p_path, const Vector<String> &p_arguments, Array r_output = Array(), bool p_read_stderr = false, bool p_open_console = false); + int execute(const String &p_path, const Vector<String> &p_arguments, Array r_output = ClassDB::default_array_arg, bool p_read_stderr = false, bool p_open_console = false); Dictionary execute_with_pipe(const String &p_path, const Vector<String> &p_arguments, bool p_blocking = true); int create_process(const String &p_path, const Vector<String> &p_arguments, bool p_open_console = false); int create_instance(const Vector<String> &p_arguments); diff --git a/core/extension/extension_api_dump.cpp b/core/extension/extension_api_dump.cpp index 296ebc901f..4042d6b80d 100644 --- a/core/extension/extension_api_dump.cpp +++ b/core/extension/extension_api_dump.cpp @@ -88,7 +88,7 @@ static String get_property_info_type_name(const PropertyInfo &p_info) { } static String get_type_meta_name(const GodotTypeInfo::Metadata metadata) { - static const char *argmeta[11] = { "none", "int8", "int16", "int32", "int64", "uint8", "uint16", "uint32", "uint64", "float", "double" }; + static const char *argmeta[13] = { "none", "int8", "int16", "int32", "int64", "uint8", "uint16", "uint32", "uint64", "float", "double", "char16", "char32" }; return argmeta[metadata]; } diff --git a/core/extension/gdextension_interface.h b/core/extension/gdextension_interface.h index 8eb8a2ed33..9e3ce25698 100644 --- a/core/extension/gdextension_interface.h +++ b/core/extension/gdextension_interface.h @@ -420,7 +420,9 @@ typedef enum { GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_UINT32, GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_UINT64, GDEXTENSION_METHOD_ARGUMENT_METADATA_REAL_IS_FLOAT, - GDEXTENSION_METHOD_ARGUMENT_METADATA_REAL_IS_DOUBLE + GDEXTENSION_METHOD_ARGUMENT_METADATA_REAL_IS_DOUBLE, + GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_CHAR16, + GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_CHAR32, } GDExtensionClassMethodArgumentMetadata; typedef void (*GDExtensionClassMethodCall)(void *method_userdata, GDExtensionClassInstancePtr p_instance, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argument_count, GDExtensionVariantPtr r_return, GDExtensionCallError *r_error); diff --git a/core/input/input_map.cpp b/core/input/input_map.cpp index ddeee9d765..9a772c87c9 100644 --- a/core/input/input_map.cpp +++ b/core/input/input_map.cpp @@ -400,6 +400,7 @@ static const _BuiltinActionDisplayName _builtin_action_display_names[] = { { "ui_filedialog_refresh", TTRC("Refresh") }, { "ui_filedialog_show_hidden", TTRC("Show Hidden") }, { "ui_swap_input_direction ", TTRC("Swap Input Direction") }, + { "ui_unicode_start", TTRC("Start Unicode Character Input") }, { "", ""} /* clang-format on */ }; @@ -754,6 +755,10 @@ const HashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins() { inputs.push_back(InputEventKey::create_reference(Key::KP_ENTER)); default_builtin_cache.insert("ui_text_submit", inputs); + inputs = List<Ref<InputEvent>>(); + inputs.push_back(InputEventKey::create_reference(Key::U | KeyModifierMask::CTRL | KeyModifierMask::SHIFT)); + default_builtin_cache.insert("ui_unicode_start", inputs); + // ///// UI Graph Shortcuts ///// inputs = List<Ref<InputEvent>>(); diff --git a/core/object/class_db.cpp b/core/object/class_db.cpp index a65411629f..9826d73a9d 100644 --- a/core/object/class_db.cpp +++ b/core/object/class_db.cpp @@ -227,16 +227,12 @@ public: #endif bool ClassDB::_is_parent_class(const StringName &p_class, const StringName &p_inherits) { - if (!classes.has(p_class)) { - return false; - } - - StringName inherits = p_class; - while (inherits.operator String().length()) { - if (inherits == p_inherits) { + ClassInfo *c = classes.getptr(p_class); + while (c) { + if (c->name == p_inherits) { return true; } - inherits = _get_parent_class(inherits); + c = c->inherits_ptr; } return false; @@ -2310,4 +2306,11 @@ void ClassDB::cleanup() { native_structs.clear(); } +// Array to use in optional parameters on methods and the DEFVAL_ARRAY macro. +Array ClassDB::default_array_arg = Array::create_read_only(); + +bool ClassDB::is_default_array_arg(const Array &p_array) { + return p_array.is_same_instance(default_array_arg); +} + // diff --git a/core/object/class_db.h b/core/object/class_db.h index 620092a6c4..81100d7586 100644 --- a/core/object/class_db.h +++ b/core/object/class_db.h @@ -43,6 +43,7 @@ #include <type_traits> #define DEFVAL(m_defval) (m_defval) +#define DEFVAL_ARRAY DEFVAL(ClassDB::default_array_arg) #ifdef DEBUG_METHODS_ENABLED @@ -181,6 +182,9 @@ public: }; static HashMap<StringName, NativeStruct> native_structs; + static Array default_array_arg; + static bool is_default_array_arg(const Array &p_array); + private: // Non-locking variants of get_parent_class and is_parent_class. static StringName _get_parent_class(const StringName &p_class); diff --git a/core/object/ref_counted.h b/core/object/ref_counted.h index 5b358135c4..f0706b4d08 100644 --- a/core/object/ref_counted.h +++ b/core/object/ref_counted.h @@ -86,6 +86,11 @@ public: _FORCE_INLINE_ bool operator!=(const T *p_ptr) const { return reference != p_ptr; } +#ifdef STRICT_CHECKS + // Delete these to prevent raw comparisons with `nullptr`. + bool operator==(std::nullptr_t) const = delete; + bool operator!=(std::nullptr_t) const = delete; +#endif // STRICT_CHECKS _FORCE_INLINE_ bool operator<(const Ref<T> &p_r) const { return reference < p_r.reference; diff --git a/core/variant/array.cpp b/core/variant/array.cpp index 54cd1eda2f..869499e668 100644 --- a/core/variant/array.cpp +++ b/core/variant/array.cpp @@ -803,6 +803,10 @@ bool Array::is_same_typed(const Array &p_other) const { return _p->typed == p_other._p->typed; } +bool Array::is_same_instance(const Array &p_other) const { + return _p == p_other._p; +} + uint32_t Array::get_typed_builtin() const { return _p->typed.type; } @@ -815,6 +819,12 @@ Variant Array::get_typed_script() const { return _p->typed.script; } +Array Array::create_read_only() { + Array array; + array.make_read_only(); + return array; +} + void Array::make_read_only() { if (_p->read_only == nullptr) { _p->read_only = memnew(Variant); diff --git a/core/variant/array.h b/core/variant/array.h index 3aa957b312..12824ee3f6 100644 --- a/core/variant/array.h +++ b/core/variant/array.h @@ -186,12 +186,14 @@ public: void set_typed(uint32_t p_type, const StringName &p_class_name, const Variant &p_script); bool is_typed() const; bool is_same_typed(const Array &p_other) const; + bool is_same_instance(const Array &p_other) const; uint32_t get_typed_builtin() const; StringName get_typed_class_name() const; Variant get_typed_script() const; void make_read_only(); bool is_read_only() const; + static Array create_read_only(); Array(const Array &p_base, uint32_t p_type, const StringName &p_class_name, const Variant &p_script); Array(const Array &p_from); diff --git a/core/variant/dictionary.cpp b/core/variant/dictionary.cpp index f2522a4545..0754814d35 100644 --- a/core/variant/dictionary.cpp +++ b/core/variant/dictionary.cpp @@ -261,7 +261,7 @@ void Dictionary::merge(const Dictionary &p_dictionary, bool p_overwrite) { Variant key = E.key; Variant value = E.value; ERR_FAIL_COND(!_p->typed_key.validate(key, "merge")); - ERR_FAIL_COND(!_p->typed_key.validate(value, "merge")); + ERR_FAIL_COND(!_p->typed_value.validate(value, "merge")); if (p_overwrite || !has(key)) { operator[](key) = value; } diff --git a/core/variant/type_info.h b/core/variant/type_info.h index d51c80eebe..6bb703f2dd 100644 --- a/core/variant/type_info.h +++ b/core/variant/type_info.h @@ -47,7 +47,9 @@ enum Metadata { METADATA_INT_IS_UINT32, METADATA_INT_IS_UINT64, METADATA_REAL_IS_FLOAT, - METADATA_REAL_IS_DOUBLE + METADATA_REAL_IS_DOUBLE, + METADATA_INT_IS_CHAR16, + METADATA_INT_IS_CHAR32, }; } @@ -104,8 +106,8 @@ MAKE_TYPE_INFO_WITH_META(uint32_t, Variant::INT, GodotTypeInfo::METADATA_INT_IS_ MAKE_TYPE_INFO_WITH_META(int32_t, Variant::INT, GodotTypeInfo::METADATA_INT_IS_INT32) MAKE_TYPE_INFO_WITH_META(uint64_t, Variant::INT, GodotTypeInfo::METADATA_INT_IS_UINT64) MAKE_TYPE_INFO_WITH_META(int64_t, Variant::INT, GodotTypeInfo::METADATA_INT_IS_INT64) -MAKE_TYPE_INFO(char16_t, Variant::INT) -MAKE_TYPE_INFO(char32_t, Variant::INT) +MAKE_TYPE_INFO_WITH_META(char16_t, Variant::INT, GodotTypeInfo::METADATA_INT_IS_CHAR16) +MAKE_TYPE_INFO_WITH_META(char32_t, Variant::INT, GodotTypeInfo::METADATA_INT_IS_CHAR32) MAKE_TYPE_INFO_WITH_META(float, Variant::FLOAT, GodotTypeInfo::METADATA_REAL_IS_FLOAT) MAKE_TYPE_INFO_WITH_META(double, Variant::FLOAT, GodotTypeInfo::METADATA_REAL_IS_DOUBLE) diff --git a/doc/classes/CompositorEffect.xml b/doc/classes/CompositorEffect.xml index 76a3887918..9ac54edb11 100644 --- a/doc/classes/CompositorEffect.xml +++ b/doc/classes/CompositorEffect.xml @@ -57,6 +57,17 @@ var render_scene_buffers : RenderSceneBuffersRD = render_data.get_render_scene_buffers() var roughness_buffer = render_scene_buffers.get_texture("forward_clustered", "normal_roughness") [/codeblock] + The raw normal and roughness buffer is stored in an optimized format, different than the one available in Spatial shaders. When sampling the buffer, a conversion function must be applied. Use this function, copied from [url=https://github.com/godotengine/godot/blob/da5f39889f155658cef7f7ec3cc1abb94e17d815/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered_inc.glsl#L334-L341]here[/url]: + [codeblock] + vec4 normal_roughness_compatibility(vec4 p_normal_roughness) { + float roughness = p_normal_roughness.w; + if (roughness > 0.5) { + roughness = 1.0 - roughness; + } + roughness /= (127.0 / 255.0); + return vec4(normalize(p_normal_roughness.xyz * 2.0 - 1.0) * 0.5 + 0.5, roughness); + } + [/codeblock] </member> <member name="needs_separate_specular" type="bool" setter="set_needs_separate_specular" getter="get_needs_separate_specular"> If [code]true[/code] this triggers specular data being rendered to a separate buffer and combined after effects have been applied, only applicable for the Forward+ renderer. diff --git a/doc/classes/EditorSettings.xml b/doc/classes/EditorSettings.xml index 1de63b4a39..389d65e05a 100644 --- a/doc/classes/EditorSettings.xml +++ b/doc/classes/EditorSettings.xml @@ -181,6 +181,9 @@ </method> </methods> <members> + <member name="asset_library/use_threads" type="bool" setter="" getter=""> + If [code]true[/code], the Asset Library uses multiple threads for its HTTP requests. This prevents the Asset Library from blocking the main thread for every loaded asset. + </member> <member name="debugger/auto_switch_to_remote_scene_tree" type="bool" setter="" getter=""> If [code]true[/code], automatically switches to the [b]Remote[/b] scene tree when running the project from the editor. If [code]false[/code], stays on the [b]Local[/b] scene tree when running the project from the editor. </member> @@ -222,6 +225,12 @@ <member name="docks/property_editor/subresource_hue_tint" type="float" setter="" getter=""> The tint intensity to use for the subresources background in the Inspector dock. The tint is used to distinguish between different subresources in the inspector. Higher values result in a more noticeable background color difference. </member> + <member name="docks/scene_tree/ask_before_deleting_related_animation_tracks" type="bool" setter="" getter=""> + If [code]true[/code], when a node is deleted with animation tracks referencing it, a confirmation dialog appears before the tracks are deleted. The dialog will appear even when using the "Delete (No Confirm)" shortcut. + </member> + <member name="docks/scene_tree/ask_before_revoking_unique_name" type="bool" setter="" getter=""> + If [code]true[/code], displays a confirmation dialog before left-clicking the "percent" icon next to a node name in the Scene tree dock. When clicked, this icon revokes the node's scene-unique name, which can impact the behavior of scripts that rely on this scene-unique name due to identifiers not being found anymore. + </member> <member name="docks/scene_tree/auto_expand_to_selected" type="bool" setter="" getter=""> If [code]true[/code], the scene tree dock will automatically unfold nodes when a node that has folded parents is selected. </member> @@ -327,6 +336,12 @@ <member name="editors/3d/grid_yz_plane" type="bool" setter="" getter=""> If [code]true[/code], renders the grid on the YZ plane in perspective view. This can be useful for 3D side-scrolling games. </member> + <member name="editors/3d/manipulator_gizmo_opacity" type="float" setter="" getter=""> + Opacity of the default gizmo for moving, rotating, and scaling 3D nodes. + </member> + <member name="editors/3d/manipulator_gizmo_size" type="int" setter="" getter=""> + Size of the default gizmo for moving, rotating, and scaling 3D nodes. + </member> <member name="editors/3d/navigation/emulate_3_button_mouse" type="bool" setter="" getter=""> If [code]true[/code], enables 3-button mouse emulation mode. This is useful on laptops when using a trackpad. When 3-button mouse emulation mode is enabled, the pan, zoom and orbit modifiers can always be used in the 3D editor viewport, even when not holding down any mouse button. @@ -355,6 +370,12 @@ <member name="editors/3d/navigation/pan_mouse_button" type="int" setter="" getter=""> The mouse button that needs to be held down to pan in the 3D editor viewport. </member> + <member name="editors/3d/navigation/show_viewport_navigation_gizmo" type="bool" setter="" getter=""> + If [code]true[/code], shows gizmos for moving and rotating the camera in the bottom corners of the 3D editor's viewport. Useful for devices that use touch screen. + </member> + <member name="editors/3d/navigation/show_viewport_rotation_gizmo" type="bool" setter="" getter=""> + If [code]true[/code], shows a small orientation gizmo in the top-right corner of the 3D editor's viewports. + </member> <member name="editors/3d/navigation/warped_mouse_panning" type="bool" setter="" getter=""> If [code]true[/code], warps the mouse around the 3D viewport while panning in the 3D editor. This makes it possible to pan over a large area without having to exit panning and adjust the mouse cursor. </member> @@ -391,12 +412,78 @@ <member name="editors/3d_gizmos/gizmo_colors/aabb" type="Color" setter="" getter=""> The color to use for the AABB gizmo that displays the [GeometryInstance3D]'s custom [AABB]. </member> + <member name="editors/3d_gizmos/gizmo_colors/camera" type="Color" setter="" getter=""> + The 3D editor gizmo color for [Camera3D]s. + </member> + <member name="editors/3d_gizmos/gizmo_colors/csg" type="Color" setter="" getter=""> + The 3D editor gizmo color for CSG nodes (such as [CSGShape3D] or [CSGBox3D]). + </member> + <member name="editors/3d_gizmos/gizmo_colors/decal" type="Color" setter="" getter=""> + The 3D editor gizmo color for [Decal] nodes. + </member> + <member name="editors/3d_gizmos/gizmo_colors/fog_volume" type="Color" setter="" getter=""> + The 3D editor gizmo color for [FogVolume] nodes. + </member> <member name="editors/3d_gizmos/gizmo_colors/instantiated" type="Color" setter="" getter=""> The color override to use for 3D editor gizmos if the [Node3D] in question is part of an instantiated scene file (from the perspective of the current scene). </member> <member name="editors/3d_gizmos/gizmo_colors/joint" type="Color" setter="" getter=""> The 3D editor gizmo color for [Joint3D]s and [PhysicalBone3D]s. </member> + <member name="editors/3d_gizmos/gizmo_colors/joint_body_a" type="Color" setter="" getter=""> + Color for representing [member Joint3D.node_a] for some [Joint3D] types. + </member> + <member name="editors/3d_gizmos/gizmo_colors/joint_body_b" type="Color" setter="" getter=""> + Color for representing [member Joint3D.node_b] for some [Joint3D] types. + </member> + <member name="editors/3d_gizmos/gizmo_colors/lightmap_lines" type="Color" setter="" getter=""> + Color of lines displayed in baked [LightmapGI] node's grid. + </member> + <member name="editors/3d_gizmos/gizmo_colors/lightprobe_lines" type="Color" setter="" getter=""> + The 3D editor gizmo color used for [LightmapProbe] nodes. + </member> + <member name="editors/3d_gizmos/gizmo_colors/occluder" type="Color" setter="" getter=""> + The 3D editor gizmo color used for [OccluderInstance3D] nodes. + </member> + <member name="editors/3d_gizmos/gizmo_colors/particle_attractor" type="Color" setter="" getter=""> + The 3D editor gizmo color used for [GPUParticlesAttractor3D] nodes. + </member> + <member name="editors/3d_gizmos/gizmo_colors/particle_collision" type="Color" setter="" getter=""> + The 3D editor gizmo color used for [GPUParticlesCollision3D] nodes. + </member> + <member name="editors/3d_gizmos/gizmo_colors/particles" type="Color" setter="" getter=""> + The 3D editor gizmo color used for [CPUParticles3D] and [GPUParticles3D] nodes. + </member> + <member name="editors/3d_gizmos/gizmo_colors/path_tilt" type="Color" setter="" getter=""> + The 3D editor gizmo color used for [Path3D] tilt circles, which indicate the direction the [Curve3D] is tilted towards. + </member> + <member name="editors/3d_gizmos/gizmo_colors/reflection_probe" type="Color" setter="" getter=""> + The 3D editor gizmo color used for [ReflectionProbe] nodes. + </member> + <member name="editors/3d_gizmos/gizmo_colors/selected_bone" type="Color" setter="" getter=""> + The 3D editor gizmo color used for the currently selected [Skeleton3D] bone. + </member> + <member name="editors/3d_gizmos/gizmo_colors/skeleton" type="Color" setter="" getter=""> + The 3D editor gizmo color used for [Skeleton3D] nodes. + </member> + <member name="editors/3d_gizmos/gizmo_colors/stream_player_3d" type="Color" setter="" getter=""> + The 3D editor gizmo color used for [AudioStreamPlayer3D]'s emission angle. + </member> + <member name="editors/3d_gizmos/gizmo_colors/visibility_notifier" type="Color" setter="" getter=""> + The 3D editor gizmo color used for [VisibleOnScreenNotifier3D] and [VisibleOnScreenEnabler3D] nodes. + </member> + <member name="editors/3d_gizmos/gizmo_colors/voxel_gi" type="Color" setter="" getter=""> + The 3D editor gizmo color used for [VoxelGI] nodes. + </member> + <member name="editors/3d_gizmos/gizmo_settings/bone_axis_length" type="float" setter="" getter=""> + The length of [Skeleton3D] bone gizmos in the 3D editor. + </member> + <member name="editors/3d_gizmos/gizmo_settings/bone_shape" type="int" setter="" getter=""> + The shape of [Skeleton3D] bone gizmos in the 3D editor. [b]Wire[/b] is a thin line, while [b]Octahedron[/b] is a set of lines that represent a thicker hollow line pointing in a specific direction (similar to most 3D animation software). + </member> + <member name="editors/3d_gizmos/gizmo_settings/path3d_tilt_disk_size" type="float" setter="" getter=""> + Size of the disk gizmo displayed when editing [Path3D]'s tilt handles. + </member> <member name="editors/animation/autorename_animation_tracks" type="bool" setter="" getter=""> If [code]true[/code], automatically updates animation tracks' target paths when renaming or reparenting nodes in the Scene tree dock. </member> @@ -416,9 +503,26 @@ <member name="editors/animation/onion_layers_past_color" type="Color" setter="" getter=""> The modulate color to use for "past" frames displayed in the animation editor's onion skinning feature. </member> + <member name="editors/bone_mapper/handle_colors/error" type="Color" setter="" getter=""> + </member> + <member name="editors/bone_mapper/handle_colors/missing" type="Color" setter="" getter=""> + </member> + <member name="editors/bone_mapper/handle_colors/set" type="Color" setter="" getter=""> + </member> + <member name="editors/bone_mapper/handle_colors/unset" type="Color" setter="" getter=""> + </member> + <member name="editors/grid_map/editor_side" type="int" setter="" getter=""> + Specifies the side of 3D editor's viewport where GridMap's mesh palette will appear. + </member> + <member name="editors/grid_map/palette_min_width" type="int" setter="" getter=""> + Minimum width of GridMap's mesh palette side panel. + </member> <member name="editors/grid_map/pick_distance" type="float" setter="" getter=""> The maximum distance at which tiles can be placed on a GridMap, relative to the camera position (in 3D units). </member> + <member name="editors/grid_map/preview_size" type="int" setter="" getter=""> + Texture size of mesh previews generated for GridMap's MeshLibrary. + </member> <member name="editors/panning/2d_editor_pan_speed" type="int" setter="" getter=""> The panning speed when using the mouse wheel or touchscreen events in the 2D editor. This setting does not apply to panning by holding down the middle or right mouse buttons. </member> @@ -528,6 +632,13 @@ <member name="editors/visual_editors/visual_shader/port_preview_size" type="int" setter="" getter=""> The size to use for port previews in the visual shader uniforms (toggled by clicking the "eye" icon next to an output). The value is defined in pixels at 100% zoom, and will scale with zoom automatically. </member> + <member name="export/ssh/scp" type="String" setter="" getter=""> + Path to the SCP (secure copy) executable (used for remote deploy to desktop platforms). If left empty, the editor will attempt to run [code]scp[/code] from [code]PATH[/code]. + [b]Note:[/b] SCP is not the same as SFTP. Specifying the SFTP executable here will not work. + </member> + <member name="export/ssh/ssh" type="String" setter="" getter=""> + Path to the SSH executable (used for remote deploy to desktop platforms). If left empty, the editor will attempt to run [code]ssh[/code] from [code]PATH[/code]. + </member> <member name="filesystem/directories/autoscan_project_path" type="String" setter="" getter=""> The folder where projects should be scanned for (recursively), in a way similar to the project manager's [b]Scan[/b] button. This can be set to the same value as [member filesystem/directories/default_project_path] for convenience. [b]Note:[/b] Setting this path to a folder with very large amounts of files/folders can slow down the project manager startup significantly. To keep the project manager quick to start up, it is recommended to set this value to a folder as "specific" as possible. @@ -573,6 +684,12 @@ <member name="filesystem/file_dialog/thumbnail_size" type="int" setter="" getter=""> The thumbnail size to use in the editor's file dialogs (in pixels). See also [member docks/filesystem/thumbnail_size]. </member> + <member name="filesystem/file_server/password" type="String" setter="" getter=""> + Password used for file server when exporting project with remote file system. + </member> + <member name="filesystem/file_server/port" type="int" setter="" getter=""> + Port used for file server when exporting project with remote file system. + </member> <member name="filesystem/import/blender/blender_path" type="String" setter="" getter=""> The path to the directory containing the Blender executable used for converting the Blender 3D scene files [code].blend[/code] to glTF 2.0 format during import. Blender 3.0 or later is required. To enable this feature for your specific project, use [member ProjectSettings.filesystem/import/blender/enabled]. @@ -759,6 +876,12 @@ Depending on the platform and used renderer, the engine will fall back to [b]Enabled[/b] if the desired mode is not supported. [b]Note:[/b] V-Sync modes other than [b]Enabled[/b] are only supported in the Forward+ and Mobile rendering methods, not Compatibility. </member> + <member name="interface/editors/derive_script_globals_by_name" type="bool" setter="" getter=""> + If [code]true[/code], when extending a script, the global class name of the script is inserted in the script creation dialog, if it exists. If [code]false[/code], the script's file path is always inserted. + </member> + <member name="interface/editors/show_scene_tree_root_selection" type="bool" setter="" getter=""> + If [code]true[/code], the Scene dock will display buttons to quickly add a root node to a newly created scene. + </member> <member name="interface/inspector/auto_unfold_foreign_scenes" type="bool" setter="" getter=""> If [code]true[/code], automatically expands property groups in the Inspector dock when opening a scene that hasn't been opened previously. If [code]false[/code], all groups remain collapsed by default. </member> @@ -1052,6 +1175,9 @@ <member name="text_editor/appearance/whitespace/line_spacing" type="int" setter="" getter=""> The space to add between lines (in pixels). Greater line spacing can help improve readability at the cost of displaying fewer lines on screen. </member> + <member name="text_editor/behavior/files/auto_reload_and_parse_scripts_on_save" type="bool" setter="" getter=""> + If [code]true[/code], tool scripts will be automatically soft-reloaded after they are saved. + </member> <member name="text_editor/behavior/files/auto_reload_scripts_on_external_change" type="bool" setter="" getter=""> If [code]true[/code], automatically reloads scripts in the editor when they have been modified and saved by external editors. </member> @@ -1061,6 +1187,9 @@ <member name="text_editor/behavior/files/convert_indent_on_save" type="bool" setter="" getter=""> If [code]true[/code], converts indentation to match the script editor's indentation settings when saving a script. See also [member text_editor/behavior/indent/type]. </member> + <member name="text_editor/behavior/files/open_dominant_script_on_scene_change" type="bool" setter="" getter=""> + If [code]true[/code], opening a scene automatically opens the script attached to the root node, or the topmost node if the root has no script. + </member> <member name="text_editor/behavior/files/restore_scripts_on_load" type="bool" setter="" getter=""> If [code]true[/code], reopens scripts that were opened in the last session when the editor is reopened on a given project. </member> @@ -1148,6 +1277,15 @@ <member name="text_editor/completion/use_single_quotes" type="bool" setter="" getter=""> If [code]true[/code], performs string autocompletion with single quotes. If [code]false[/code], performs string autocompletion with double quotes (which matches the [url=$DOCS_URL/tutorials/scripting/gdscript/gdscript_styleguide.html]GDScript style guide[/url]). </member> + <member name="text_editor/external/exec_flags" type="String" setter="" getter=""> + The command-line arguments to pass to the external text editor that is run when [member text_editor/external/use_external_editor] is [code]true[/code]. See also [member text_editor/external/exec_path]. + </member> + <member name="text_editor/external/exec_path" type="String" setter="" getter=""> + The path to the text editor executable used to edit text files if [member text_editor/external/use_external_editor] is [code]true[/code]. + </member> + <member name="text_editor/external/use_external_editor" type="bool" setter="" getter=""> + If [code]true[/code], uses an external editor instead of the built-in Script Editor. See also [member text_editor/external/exec_path] and [member text_editor/external/exec_flags]. + </member> <member name="text_editor/help/class_reference_examples" type="int" setter="" getter=""> Controls which multi-line code blocks should be displayed in the editor help. This setting does not affect single-line code literals in the editor help. </member> @@ -1163,6 +1301,21 @@ <member name="text_editor/help/show_help_index" type="bool" setter="" getter=""> If [code]true[/code], displays a table of contents at the left of the editor help (at the location where the members overview would appear when editing a script). </member> + <member name="text_editor/help/sort_functions_alphabetically" type="bool" setter="" getter=""> + If [code]true[/code], the script's method list in the Script Editor is sorted alphabetically. + </member> + <member name="text_editor/script_list/group_help_pages" type="bool" setter="" getter=""> + If [code]true[/code], class reference pages are grouped together at the bottom of the Script Editor's script list. + </member> + <member name="text_editor/script_list/list_script_names_as" type="int" setter="" getter=""> + Specifies how script paths should be displayed in Script Editor's script list. If using the "Name" option and some scripts share the same file name, more parts of their paths are revealed to avoid conflicts. + </member> + <member name="text_editor/script_list/script_temperature_enabled" type="bool" setter="" getter=""> + If [code]true[/code], the names of recently opened scripts in the Script Editor are highlighted with the accent color, with its intensity based on how recently they were opened. + </member> + <member name="text_editor/script_list/script_temperature_history_size" type="int" setter="" getter=""> + How many script names can be highlighted at most, if [member text_editor/script_list/script_temperature_enabled] is [code]true[/code]. Scripts older than this value use the default font color. + </member> <member name="text_editor/script_list/show_members_overview" type="bool" setter="" getter=""> If [code]true[/code], displays an overview of the current script's member variables and functions at the left of the script editor. See also [member text_editor/script_list/sort_members_outline_alphabetically]. </member> @@ -1170,6 +1323,9 @@ If [code]true[/code], sorts the members outline (located at the left of the script editor) using alphabetical order. If [code]false[/code], sorts the members outline depending on the order in which members are found in the script. [b]Note:[/b] Only effective if [member text_editor/script_list/show_members_overview] is [code]true[/code]. </member> + <member name="text_editor/script_list/sort_scripts_by" type="int" setter="" getter=""> + Specifies sorting used for Script Editor's open script list. + </member> <member name="text_editor/theme/color_theme" type="String" setter="" getter=""> The syntax theme to use in the script editor. You can save your own syntax theme from your current settings by using [b]File > Theme > Save As...[/b] at the top of the script editor. The syntax theme will then be available locally in the list of color themes. @@ -1294,6 +1450,18 @@ <member name="text_editor/theme/highlighting/word_highlighted_color" type="Color" setter="" getter=""> The script editor's color for words highlighted by selecting them. Only visible if [member text_editor/appearance/caret/highlight_all_occurrences] is [code]true[/code]. </member> + <member name="text_editor/theme/line_spacing" type="int" setter="" getter=""> + The vertical line separation used in text editors, in pixels. + </member> + <member name="version_control/ssh_private_key_path" type="String" setter="" getter=""> + Path to private SSH key file for the editor's Version Control integration credentials. + </member> + <member name="version_control/ssh_public_key_path" type="String" setter="" getter=""> + Path to public SSH key file for the editor's Version Control integration credentials. + </member> + <member name="version_control/username" type="String" setter="" getter=""> + Default username for editor's Version Control integration. + </member> </members> <signals> <signal name="settings_changed"> diff --git a/doc/classes/LineEdit.xml b/doc/classes/LineEdit.xml index a37dd47914..d218f720a3 100644 --- a/doc/classes/LineEdit.xml +++ b/doc/classes/LineEdit.xml @@ -37,6 +37,18 @@ <tutorials> </tutorials> <methods> + <method name="apply_ime"> + <return type="void" /> + <description> + Applies text from the [url=https://en.wikipedia.org/wiki/Input_method]Input Method Editor[/url] (IME) and closes the IME if it is open. + </description> + </method> + <method name="cancel_ime"> + <return type="void" /> + <description> + Closes the [url=https://en.wikipedia.org/wiki/Input_method]Input Method Editor[/url] (IME) if it is open. Any text in the IME will be lost. + </description> + </method> <method name="clear"> <return type="void" /> <description> @@ -133,6 +145,12 @@ Returns the selection end column. </description> </method> + <method name="has_ime_text" qualifiers="const"> + <return type="bool" /> + <description> + Returns [code]true[/code] if the user has text in the [url=https://en.wikipedia.org/wiki/Input_method]Input Method Editor[/url] (IME). + </description> + </method> <method name="has_selection" qualifiers="const"> <return type="bool" /> <description> diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index 08427ffe83..b205b862a3 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -1386,6 +1386,10 @@ Default [InputEventAction] to undo the most recent action. [b]Note:[/b] Default [code]ui_*[/code] actions cannot be removed as they are necessary for the internal logic of several [Control]s. The events assigned to the action can however be modified. </member> + <member name="input/ui_unicode_start" type="Dictionary" setter="" getter=""> + Default [InputEventAction] to start Unicode character hexadecimal code input in a text field. + [b]Note:[/b] Default [code]ui_*[/code] actions cannot be removed as they are necessary for the internal logic of several [Control]s. The events assigned to the action can however be modified. + </member> <member name="input/ui_up" type="Dictionary" setter="" getter=""> Default [InputEventAction] to move up in the UI. [b]Note:[/b] Default [code]ui_*[/code] actions cannot be removed as they are necessary for the internal logic of several [Control]s. The events assigned to the action can however be modified. @@ -2341,6 +2345,9 @@ [b]Note:[/b] This property is only read when the project starts. To change the physics FPS at runtime, set [member Engine.physics_ticks_per_second] instead. [b]Note:[/b] Only [member physics/common/max_physics_steps_per_frame] physics ticks may be simulated per rendered frame at most. If more physics ticks have to be simulated per rendered frame to keep up with rendering, the project will appear to slow down (even if [code]delta[/code] is used consistently in physics calculations). Therefore, it is recommended to also increase [member physics/common/max_physics_steps_per_frame] if increasing [member physics/common/physics_ticks_per_second] significantly above its default value. </member> + <member name="rendering/2d/batching/item_buffer_size" type="int" setter="" getter="" default="16384"> + Maximum number of canvas item commands that can be batched into a single draw call. + </member> <member name="rendering/2d/sdf/oversize" type="int" setter="" getter="" default="1"> Controls how much of the original viewport size should be covered by the 2D signed distance field. This SDF can be sampled in [CanvasItem] shaders and is used for [GPUParticles2D] collision. Higher values allow portions of occluders located outside the viewport to still be taken into account in the generated signed distance field, at the cost of performance. If you notice particles falling through [LightOccluder2D]s as the occluders leave the viewport, increase this setting. The percentage specified is added on each axis and on both sides. For example, with the default setting of 120%, the signed distance field will cover 20% of the viewport's size outside the viewport on each side (top, right, bottom, left). @@ -2947,9 +2954,6 @@ <member name="xr/openxr/enabled" type="bool" setter="" getter="" default="false"> If [code]true[/code], Godot will setup and initialize OpenXR on startup. </member> - <member name="xr/openxr/enabled.editor" type="bool" setter="" getter="" default="false"> - If [code]true[/code], Godot will setup and initialize OpenXR on editor startup. - </member> <member name="xr/openxr/environment_blend_mode" type="int" setter="" getter="" default=""0""> Specify how OpenXR should blend in the environment. This is specific to certain AR and passthrough devices where camera images are blended in by the XR compositor. </member> diff --git a/doc/classes/ResourceImporterDynamicFont.xml b/doc/classes/ResourceImporterDynamicFont.xml index b678a04e34..3727bed8e5 100644 --- a/doc/classes/ResourceImporterDynamicFont.xml +++ b/doc/classes/ResourceImporterDynamicFont.xml @@ -54,7 +54,7 @@ Source font size used to generate MSDF textures. Higher values allow for more precision, but are slower to render and require more memory. Only increase this value if you notice a visible lack of precision in glyph rendering. Only effective if [member multichannel_signed_distance_field] is [code]true[/code]. </member> <member name="multichannel_signed_distance_field" type="bool" setter="" getter="" default="false"> - If set to [code]true[/code], the default font will use multichannel signed distance field (MSDF) for crisp rendering at any size. Since this approach does not rely on rasterizing the font every time its size changes, this allows for resizing the font in real-time without any performance penalty. Text will also not look grainy for [Control]s that are scaled down (or for [Label3D]s viewed from a long distance). + If set to [code]true[/code], the font will use multichannel signed distance field (MSDF) for crisp rendering at any size. Since this approach does not rely on rasterizing the font every time its size changes, this allows for resizing the font in real-time without any performance penalty. Text will also not look grainy for [Control]s that are scaled down (or for [Label3D]s viewed from a long distance). MSDF font rendering can be combined with [member generate_mipmaps] to further improve font rendering quality when scaled down. </member> <member name="opentype_features" type="Dictionary" setter="" getter="" default="{}"> diff --git a/doc/classes/ScriptEditor.xml b/doc/classes/ScriptEditor.xml index 5cf077c266..67a2af2932 100644 --- a/doc/classes/ScriptEditor.xml +++ b/doc/classes/ScriptEditor.xml @@ -99,6 +99,14 @@ [b]Note:[/b] The [EditorSyntaxHighlighter] will still be applied to scripts that are already opened. </description> </method> + <method name="update_docs_from_script"> + <return type="void" /> + <param index="0" name="script" type="Script" /> + <description> + Updates the documentation for the given [param script] if the script's documentation is currently open. + [b]Note:[/b] This should be called whenever the script is changed to keep the open documentation state up to date. + </description> + </method> </methods> <signals> <signal name="editor_script_changed"> diff --git a/doc/classes/TreeItem.xml b/doc/classes/TreeItem.xml index bd46c54990..861a53aaad 100644 --- a/doc/classes/TreeItem.xml +++ b/doc/classes/TreeItem.xml @@ -223,6 +223,13 @@ Returns the [Color] modulating the column's icon. </description> </method> + <method name="get_icon_overlay" qualifiers="const"> + <return type="Texture2D" /> + <param index="0" name="column" type="int" /> + <description> + Returns the given column's icon overlay [Texture2D]. + </description> + </method> <method name="get_icon_region" qualifiers="const"> <return type="Rect2" /> <param index="0" name="column" type="int" /> @@ -662,6 +669,14 @@ Modulates the given column's icon with [param modulate]. </description> </method> + <method name="set_icon_overlay"> + <return type="void" /> + <param index="0" name="column" type="int" /> + <param index="1" name="texture" type="Texture2D" /> + <description> + Sets the given cell's icon overlay [Texture2D]. The cell has to be in [constant CELL_MODE_ICON] mode, and icon has to be set. Overlay is drawn on top of icon, in the bottom left corner. + </description> + </method> <method name="set_icon_region"> <return type="void" /> <param index="0" name="column" type="int" /> diff --git a/doc/classes/Viewport.xml b/doc/classes/Viewport.xml index 350fd65197..85663e10b2 100644 --- a/doc/classes/Viewport.xml +++ b/doc/classes/Viewport.xml @@ -151,7 +151,7 @@ <method name="gui_is_dragging" qualifiers="const"> <return type="bool" /> <description> - Returns [code]true[/code] if the viewport is currently performing a drag operation. + Returns [code]true[/code] if a drag operation is currently ongoing and where the drop action could happen in this viewport. Alternative to [constant Node.NOTIFICATION_DRAG_BEGIN] and [constant Node.NOTIFICATION_DRAG_END] when you prefer polling the value. </description> </method> diff --git a/drivers/gles3/storage/texture_storage.cpp b/drivers/gles3/storage/texture_storage.cpp index 36393dde86..54012c20e9 100644 --- a/drivers/gles3/storage/texture_storage.cpp +++ b/drivers/gles3/storage/texture_storage.cpp @@ -1039,7 +1039,7 @@ Ref<Image> TextureStorage::texture_2d_get(RID p_texture) const { data.resize(data_size); ERR_FAIL_COND_V(data.is_empty(), Ref<Image>()); - image = Image::create_from_data(texture->width, texture->height, texture->mipmaps > 1, texture->real_format, data); + image = Image::create_from_data(texture->alloc_width, texture->alloc_height, texture->mipmaps > 1, texture->real_format, data); ERR_FAIL_COND_V(image->is_empty(), Ref<Image>()); if (texture->format != texture->real_format) { image->convert(texture->format); @@ -1095,7 +1095,7 @@ Ref<Image> TextureStorage::texture_2d_get(RID p_texture) const { data.resize(data_size); ERR_FAIL_COND_V(data.is_empty(), Ref<Image>()); - image = Image::create_from_data(texture->width, texture->height, false, Image::FORMAT_RGBA8, data); + image = Image::create_from_data(texture->alloc_width, texture->alloc_height, false, Image::FORMAT_RGBA8, data); ERR_FAIL_COND_V(image->is_empty(), Ref<Image>()); if (texture->format != Image::FORMAT_RGBA8) { diff --git a/drivers/unix/dir_access_unix.cpp b/drivers/unix/dir_access_unix.cpp index 489181a025..e4bad88083 100644 --- a/drivers/unix/dir_access_unix.cpp +++ b/drivers/unix/dir_access_unix.cpp @@ -419,6 +419,9 @@ Error DirAccessUnix::remove(String p_path) { } p_path = fix_path(p_path); + if (p_path.ends_with("/")) { + p_path = p_path.left(-1); + } struct stat flags = {}; if ((stat(p_path.utf8().get_data(), &flags) != 0)) { @@ -438,6 +441,9 @@ bool DirAccessUnix::is_link(String p_file) { } p_file = fix_path(p_file); + if (p_file.ends_with("/")) { + p_file = p_file.left(-1); + } struct stat flags = {}; if ((lstat(p_file.utf8().get_data(), &flags) != 0)) { @@ -453,6 +459,9 @@ String DirAccessUnix::read_link(String p_file) { } p_file = fix_path(p_file); + if (p_file.ends_with("/")) { + p_file = p_file.left(-1); + } char buf[256]; memset(buf, 0, 256); diff --git a/drivers/vulkan/rendering_device_driver_vulkan.cpp b/drivers/vulkan/rendering_device_driver_vulkan.cpp index 4ea46e8214..bd395f41e2 100644 --- a/drivers/vulkan/rendering_device_driver_vulkan.cpp +++ b/drivers/vulkan/rendering_device_driver_vulkan.cpp @@ -1772,16 +1772,17 @@ RDD::TextureID RenderingDeviceDriverVulkan::texture_create_from_extension(uint64 tex_info->vk_view = vk_image_view; tex_info->rd_format = p_format; tex_info->vk_view_create_info = image_view_create_info; - +#ifdef DEBUG_ENABLED + tex_info->created_from_extension = true; +#endif return TextureID(tex_info); } RDD::TextureID RenderingDeviceDriverVulkan::texture_create_shared(TextureID p_original_texture, const TextureView &p_view) { const TextureInfo *owner_tex_info = (const TextureInfo *)p_original_texture.id; #ifdef DEBUG_ENABLED - ERR_FAIL_COND_V(!owner_tex_info->allocation.handle, TextureID()); + ERR_FAIL_COND_V(!owner_tex_info->allocation.handle && !owner_tex_info->created_from_extension, TextureID()); #endif - VkImageViewCreateInfo image_view_create_info = owner_tex_info->vk_view_create_info; image_view_create_info.format = RD_TO_VK_FORMAT[p_view.format]; image_view_create_info.components.r = (VkComponentSwizzle)p_view.swizzle_r; @@ -1837,7 +1838,7 @@ RDD::TextureID RenderingDeviceDriverVulkan::texture_create_shared(TextureID p_or RDD::TextureID RenderingDeviceDriverVulkan::texture_create_shared_from_slice(TextureID p_original_texture, const TextureView &p_view, TextureSliceType p_slice_type, uint32_t p_layer, uint32_t p_layers, uint32_t p_mipmap, uint32_t p_mipmaps) { const TextureInfo *owner_tex_info = (const TextureInfo *)p_original_texture.id; #ifdef DEBUG_ENABLED - ERR_FAIL_COND_V(!owner_tex_info->allocation.handle, TextureID()); + ERR_FAIL_COND_V(!owner_tex_info->allocation.handle && !owner_tex_info->created_from_extension, TextureID()); #endif VkImageViewCreateInfo image_view_create_info = owner_tex_info->vk_view_create_info; diff --git a/drivers/vulkan/rendering_device_driver_vulkan.h b/drivers/vulkan/rendering_device_driver_vulkan.h index 2615d9824d..81f4256941 100644 --- a/drivers/vulkan/rendering_device_driver_vulkan.h +++ b/drivers/vulkan/rendering_device_driver_vulkan.h @@ -213,6 +213,9 @@ public: VmaAllocation handle = nullptr; VmaAllocationInfo info = {}; } allocation; // All 0/null if just a view. +#ifdef DEBUG_ENABLED + bool created_from_extension = false; +#endif }; VkSampleCountFlagBits _ensure_supported_sample_count(TextureSamples p_requested_sample_count); diff --git a/editor/animation_bezier_editor.cpp b/editor/animation_bezier_editor.cpp index 24fcfd3930..66ebd07c2a 100644 --- a/editor/animation_bezier_editor.cpp +++ b/editor/animation_bezier_editor.cpp @@ -35,6 +35,7 @@ #include "editor/editor_string_names.h" #include "editor/editor_undo_redo_manager.h" #include "editor/gui/editor_spin_slider.h" +#include "editor/plugins/animation_player_editor_plugin.h" #include "editor/themes/editor_scale.h" #include "scene/gui/view_panner.h" #include "scene/resources/text_line.h" @@ -869,6 +870,11 @@ void AnimationBezierTrackEdit::_change_selected_keys_handle_mode(Animation::Hand undo_redo->add_undo_method(animation.ptr(), "bezier_track_set_key_out_handle", track_key_pair.first, track_key_pair.second, animation->bezier_track_get_key_out_handle(track_key_pair.first, track_key_pair.second)); undo_redo->add_do_method(editor, "_bezier_track_set_key_handle_mode", animation.ptr(), track_key_pair.first, track_key_pair.second, p_mode, p_auto ? Animation::HANDLE_SET_MODE_AUTO : Animation::HANDLE_SET_MODE_RESET); } + AnimationPlayerEditor *ape = AnimationPlayerEditor::get_singleton(); + if (ape) { + undo_redo->add_do_method(ape, "_animation_update_key_frame"); + undo_redo->add_undo_method(ape, "_animation_update_key_frame"); + } undo_redo->commit_action(); } @@ -1560,6 +1566,11 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) { undo_redo->add_do_method(animation.ptr(), "bezier_track_set_key_out_handle", moving_handle_track, moving_handle_key, moving_handle_right, ratio); undo_redo->add_undo_method(animation.ptr(), "bezier_track_set_key_out_handle", moving_handle_track, moving_handle_key, animation->bezier_track_get_key_out_handle(moving_handle_track, moving_handle_key), ratio); } + AnimationPlayerEditor *ape = AnimationPlayerEditor::get_singleton(); + if (ape) { + undo_redo->add_do_method(ape, "_animation_update_key_frame"); + undo_redo->add_undo_method(ape, "_animation_update_key_frame"); + } undo_redo->commit_action(); moving_handle = 0; queue_redraw(); @@ -1673,6 +1684,11 @@ void AnimationBezierTrackEdit::_menu_selected(int p_index) { undo_redo->add_do_method(animation.ptr(), "track_insert_key", selected_track, time, new_point); undo_redo->add_undo_method(this, "_clear_selection_for_anim", animation); undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", selected_track, time); + AnimationPlayerEditor *ape = AnimationPlayerEditor::get_singleton(); + if (ape) { + undo_redo->add_do_method(ape, "_animation_update_key_frame"); + undo_redo->add_undo_method(ape, "_animation_update_key_frame"); + } undo_redo->commit_action(); queue_redraw(); } @@ -1773,6 +1789,11 @@ void AnimationBezierTrackEdit::duplicate_selected_keys(real_t p_ofs, bool p_ofs_ i++; } + AnimationPlayerEditor *ape = AnimationPlayerEditor::get_singleton(); + if (ape) { + undo_redo->add_do_method(ape, "_animation_update_key_frame"); + undo_redo->add_undo_method(ape, "_animation_update_key_frame"); + } undo_redo->add_do_method(this, "queue_redraw"); undo_redo->add_undo_method(this, "queue_redraw"); undo_redo->commit_action(); @@ -1822,6 +1843,15 @@ void AnimationBezierTrackEdit::copy_selected_keys(bool p_cut) { undo_redo->add_undo_method(this, "_select_at_anim", animation, E->key().track, E->value().pos, i == 0); i++; } + + AnimationPlayerEditor *ape = AnimationPlayerEditor::get_singleton(); + if (ape) { + undo_redo->add_do_method(ape, "_animation_update_key_frame"); + undo_redo->add_undo_method(ape, "_animation_update_key_frame"); + } + undo_redo->add_do_method(this, "queue_redraw"); + undo_redo->add_undo_method(this, "queue_redraw"); + undo_redo->commit_action(); } } @@ -1900,9 +1930,15 @@ void AnimationBezierTrackEdit::paste_keys(real_t p_ofs, bool p_ofs_valid) { i++; } - undo_redo->commit_action(); + AnimationPlayerEditor *ape = AnimationPlayerEditor::get_singleton(); + if (ape) { + undo_redo->add_do_method(ape, "_animation_update_key_frame"); + undo_redo->add_undo_method(ape, "_animation_update_key_frame"); + } + undo_redo->add_do_method(this, "queue_redraw"); + undo_redo->add_undo_method(this, "queue_redraw"); - queue_redraw(); + undo_redo->commit_action(); } } @@ -1917,6 +1953,11 @@ void AnimationBezierTrackEdit::delete_selection() { } undo_redo->add_do_method(this, "_clear_selection_for_anim", animation); undo_redo->add_undo_method(this, "_clear_selection_for_anim", animation); + AnimationPlayerEditor *ape = AnimationPlayerEditor::get_singleton(); + if (ape) { + undo_redo->add_do_method(ape, "_animation_update_key_frame"); + undo_redo->add_undo_method(ape, "_animation_update_key_frame"); + } undo_redo->commit_action(); //selection.clear(); @@ -1926,6 +1967,15 @@ void AnimationBezierTrackEdit::delete_selection() { void AnimationBezierTrackEdit::_bezier_track_insert_key_at_anim(const Ref<Animation> &p_anim, int p_track, double p_time, real_t p_value, const Vector2 &p_in_handle, const Vector2 &p_out_handle, const Animation::HandleMode p_handle_mode) { int idx = p_anim->bezier_track_insert_key(p_track, p_time, p_value, p_in_handle, p_out_handle); p_anim->bezier_track_set_key_handle_mode(p_track, idx, p_handle_mode); + + EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); + undo_redo->create_action(TTR("Animation Bezier Curve Change Call")); + AnimationPlayerEditor *ape = AnimationPlayerEditor::get_singleton(); + if (ape) { + undo_redo->add_do_method(ape, "_animation_update_key_frame"); + undo_redo->add_undo_method(ape, "_animation_update_key_frame"); + } + undo_redo->commit_action(); } void AnimationBezierTrackEdit::_bind_methods() { diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp index 95ba301282..4ec097bdda 100644 --- a/editor/animation_track_editor.cpp +++ b/editor/animation_track_editor.cpp @@ -279,6 +279,11 @@ bool AnimationTrackKeyEdit::_set(const StringName &p_name, const Variant &p_valu undo_redo->add_undo_method(animation.ptr(), "bezier_track_set_key_value", track, key, prev); undo_redo->add_do_method(this, "_update_obj", animation); undo_redo->add_undo_method(this, "_update_obj", animation); + AnimationPlayerEditor *ape = AnimationPlayerEditor::get_singleton(); + if (ape) { + undo_redo->add_do_method(ape, "_animation_update_key_frame"); + undo_redo->add_undo_method(ape, "_animation_update_key_frame"); + } undo_redo->commit_action(); setting = false; @@ -295,6 +300,11 @@ bool AnimationTrackKeyEdit::_set(const StringName &p_name, const Variant &p_valu undo_redo->add_undo_method(animation.ptr(), "bezier_track_set_key_in_handle", track, key, prev); undo_redo->add_do_method(this, "_update_obj", animation); undo_redo->add_undo_method(this, "_update_obj", animation); + AnimationPlayerEditor *ape = AnimationPlayerEditor::get_singleton(); + if (ape) { + undo_redo->add_do_method(ape, "_animation_update_key_frame"); + undo_redo->add_undo_method(ape, "_animation_update_key_frame"); + } undo_redo->commit_action(); setting = false; @@ -311,6 +321,11 @@ bool AnimationTrackKeyEdit::_set(const StringName &p_name, const Variant &p_valu undo_redo->add_undo_method(animation.ptr(), "bezier_track_set_key_out_handle", track, key, prev); undo_redo->add_do_method(this, "_update_obj", animation); undo_redo->add_undo_method(this, "_update_obj", animation); + AnimationPlayerEditor *ape = AnimationPlayerEditor::get_singleton(); + if (ape) { + undo_redo->add_do_method(ape, "_animation_update_key_frame"); + undo_redo->add_undo_method(ape, "_animation_update_key_frame"); + } undo_redo->commit_action(); setting = false; @@ -331,6 +346,11 @@ bool AnimationTrackKeyEdit::_set(const StringName &p_name, const Variant &p_valu undo_redo->add_undo_method(animation.ptr(), "bezier_track_set_key_in_handle", track, key, prev_in_handle); undo_redo->add_undo_method(animation.ptr(), "bezier_track_set_key_out_handle", track, key, prev_out_handle); undo_redo->add_undo_method(this, "_update_obj", animation); + AnimationPlayerEditor *ape = AnimationPlayerEditor::get_singleton(); + if (ape) { + undo_redo->add_do_method(ape, "_animation_update_key_frame"); + undo_redo->add_undo_method(ape, "_animation_update_key_frame"); + } undo_redo->commit_action(); setting = false; @@ -4345,6 +4365,25 @@ PropertyInfo AnimationTrackEditor::_find_hint_for_track(int p_idx, NodePath &r_b property_info_base = property_info_base.get_named(leftover_path[i], valid); } + // Hack for the fact that bezier tracks leftover paths can reference + // the individual components for types like vectors. + if (property_info_base.is_null()) { + if (res.is_valid()) { + property_info_base = res; + } else if (node) { + property_info_base = node; + } + + if (leftover_path.size()) { + leftover_path.remove_at(leftover_path.size() - 1); + } + + for (int i = 0; i < leftover_path.size() - 1; i++) { + bool valid; + property_info_base = property_info_base.get_named(leftover_path[i], valid); + } + } + if (property_info_base.is_null()) { WARN_PRINT(vformat("Could not determine track hint for '%s:%s' because its base property is null.", String(path.get_concatenated_names()), String(path.get_concatenated_subnames()))); @@ -4472,7 +4511,11 @@ AnimationTrackEditor::TrackIndices AnimationTrackEditor::_confirm_insert(InsertD } break; case Animation::TYPE_BEZIER: { - int existing = animation->track_find_key(p_id.track_idx, time, Animation::FIND_MODE_APPROX); + int existing = -1; + if (p_id.track_idx < animation->get_track_count()) { + existing = animation->track_find_key(p_id.track_idx, time, Animation::FIND_MODE_APPROX); + } + if (existing != -1) { Array arr = animation->track_get_key_value(p_id.track_idx, existing); arr[0] = p_id.value; diff --git a/editor/debugger/debug_adapter/debug_adapter_server.cpp b/editor/debugger/debug_adapter/debug_adapter_server.cpp index 6041fec06c..0f4be147cf 100644 --- a/editor/debugger/debug_adapter/debug_adapter_server.cpp +++ b/editor/debugger/debug_adapter/debug_adapter_server.cpp @@ -38,6 +38,7 @@ int DebugAdapterServer::port_override = -1; DebugAdapterServer::DebugAdapterServer() { + // TODO: Move to editor_settings.cpp _EDITOR_DEF("network/debug_adapter/remote_port", remote_port); _EDITOR_DEF("network/debug_adapter/request_timeout", protocol._request_timeout); _EDITOR_DEF("network/debug_adapter/sync_breakpoints", protocol._sync_breakpoints); diff --git a/editor/debugger/editor_file_server.cpp b/editor/debugger/editor_file_server.cpp index 1092b07054..116e6681ae 100644 --- a/editor/debugger/editor_file_server.cpp +++ b/editor/debugger/editor_file_server.cpp @@ -271,9 +271,6 @@ void EditorFileServer::stop() { EditorFileServer::EditorFileServer() { server.instantiate(); - - EDITOR_DEF("filesystem/file_server/port", 6010); - EDITOR_DEF("filesystem/file_server/password", ""); } EditorFileServer::~EditorFileServer() { diff --git a/editor/debugger/editor_profiler.cpp b/editor/debugger/editor_profiler.cpp index 24bb694860..d244b6b4cd 100644 --- a/editor/debugger/editor_profiler.cpp +++ b/editor/debugger/editor_profiler.cpp @@ -35,6 +35,7 @@ #include "editor/editor_string_names.h" #include "editor/themes/editor_scale.h" #include "editor/themes/editor_theme_manager.h" +#include "scene/gui/check_box.h" #include "scene/resources/image_texture.h" void EditorProfiler::_make_metric_ptrs(Metric &m) { @@ -177,8 +178,8 @@ void EditorProfiler::_item_edited() { } void EditorProfiler::_update_plot() { - const int w = graph->get_size().width; - const int h = graph->get_size().height; + const int w = MAX(1, graph->get_size().width); // Clamp to 1 to prevent from crashing when profiler is autostarted. + const int h = MAX(1, graph->get_size().height); bool reset_texture = false; const int desired_len = w * h * 4; @@ -416,6 +417,10 @@ void EditorProfiler::_internal_profiles_pressed() { _combo_changed(0); } +void EditorProfiler::_autostart_toggled(bool p_toggled_on) { + EditorSettings::get_singleton()->set_project_metadata("debug_options", "autostart_profiler", p_toggled_on); +} + void EditorProfiler::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: @@ -539,9 +544,10 @@ void EditorProfiler::set_enabled(bool p_enable, bool p_clear) { } } -void EditorProfiler::set_pressed(bool p_pressed) { +void EditorProfiler::set_profiling(bool p_pressed) { activate->set_pressed(p_pressed); _update_button_text(); + emit_signal(SNAME("enable_profiling"), activate->is_pressed()); } bool EditorProfiler::is_profiling() { @@ -633,6 +639,12 @@ EditorProfiler::EditorProfiler() { clear_button->set_disabled(true); hb->add_child(clear_button); + CheckBox *autostart_checkbox = memnew(CheckBox); + autostart_checkbox->set_text(TTR("Autostart")); + autostart_checkbox->set_pressed(EditorSettings::get_singleton()->get_project_metadata("debug_options", "autostart_profiler", false)); + autostart_checkbox->connect(SceneStringName(toggled), callable_mp(this, &EditorProfiler::_autostart_toggled)); + hb->add_child(autostart_checkbox); + hb->add_child(memnew(Label(TTR("Measure:")))); display_mode = memnew(OptionButton); diff --git a/editor/debugger/editor_profiler.h b/editor/debugger/editor_profiler.h index 64253070b1..8de276be43 100644 --- a/editor/debugger/editor_profiler.h +++ b/editor/debugger/editor_profiler.h @@ -138,6 +138,7 @@ private: void _activate_pressed(); void _clear_pressed(); + void _autostart_toggled(bool p_toggled_on); void _internal_profiles_pressed(); @@ -168,7 +169,7 @@ protected: public: void add_frame_metric(const Metric &p_metric, bool p_final = false); void set_enabled(bool p_enable, bool p_clear = true); - void set_pressed(bool p_pressed); + void set_profiling(bool p_pressed); bool is_profiling(); bool is_seeking() { return seeking; } void disable_seeking(); diff --git a/editor/debugger/editor_visual_profiler.cpp b/editor/debugger/editor_visual_profiler.cpp index 56c9db44cd..d4859fbe4d 100644 --- a/editor/debugger/editor_visual_profiler.cpp +++ b/editor/debugger/editor_visual_profiler.cpp @@ -148,8 +148,8 @@ void EditorVisualProfiler::_item_selected() { } void EditorVisualProfiler::_update_plot() { - const int w = graph->get_size().width; - const int h = graph->get_size().height; + const int w = graph->get_size().width + 1; // `+1` is to prevent from crashing when visual profiler is auto started. + const int h = graph->get_size().height + 1; bool reset_texture = false; @@ -427,6 +427,10 @@ void EditorVisualProfiler::_clear_pressed() { _update_plot(); } +void EditorVisualProfiler::_autostart_toggled(bool p_toggled_on) { + EditorSettings::get_singleton()->set_project_metadata("debug_options", "autostart_visual_profiler", p_toggled_on); +} + void EditorVisualProfiler::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: @@ -668,9 +672,10 @@ void EditorVisualProfiler::set_enabled(bool p_enable) { activate->set_disabled(!p_enable); } -void EditorVisualProfiler::set_pressed(bool p_pressed) { - activate->set_pressed(p_pressed); +void EditorVisualProfiler::set_profiling(bool p_profiling) { + activate->set_pressed(p_profiling); _update_button_text(); + emit_signal(SNAME("enable_profiling"), activate->is_pressed()); } bool EditorVisualProfiler::is_profiling() { @@ -747,6 +752,12 @@ EditorVisualProfiler::EditorVisualProfiler() { clear_button->connect(SceneStringName(pressed), callable_mp(this, &EditorVisualProfiler::_clear_pressed)); hb->add_child(clear_button); + CheckBox *autostart_checkbox = memnew(CheckBox); + autostart_checkbox->set_text(TTR("Autostart")); + autostart_checkbox->set_pressed(EditorSettings::get_singleton()->get_project_metadata("debug_options", "autostart_visual_profiler", false)); + autostart_checkbox->connect(SceneStringName(toggled), callable_mp(this, &EditorVisualProfiler::_autostart_toggled)); + hb->add_child(autostart_checkbox); + hb->add_child(memnew(Label(TTR("Measure:")))); display_mode = memnew(OptionButton); diff --git a/editor/debugger/editor_visual_profiler.h b/editor/debugger/editor_visual_profiler.h index 492985506a..a8ed58132e 100644 --- a/editor/debugger/editor_visual_profiler.h +++ b/editor/debugger/editor_visual_profiler.h @@ -109,6 +109,7 @@ private: void _activate_pressed(); void _clear_pressed(); + void _autostart_toggled(bool p_toggled_on); String _get_time_as_text(float p_time); @@ -137,7 +138,7 @@ protected: public: void add_frame_metric(const Metric &p_metric); void set_enabled(bool p_enable); - void set_pressed(bool p_pressed); + void set_profiling(bool p_profiling); bool is_profiling(); bool is_seeking() { return seeking; } void disable_seeking(); diff --git a/editor/debugger/script_editor_debugger.cpp b/editor/debugger/script_editor_debugger.cpp index 5e96daf69c..b798bdf9c1 100644 --- a/editor/debugger/script_editor_debugger.cpp +++ b/editor/debugger/script_editor_debugger.cpp @@ -1012,6 +1012,14 @@ void ScriptEditorDebugger::start(Ref<RemoteDebuggerPeer> p_peer) { _set_reason_text(TTR("Debug session started."), MESSAGE_SUCCESS); _update_buttons_state(); emit_signal(SNAME("started")); + + if (EditorSettings::get_singleton()->get_project_metadata("debug_options", "autostart_profiler", false)) { + profiler->set_profiling(true); + } + + if (EditorSettings::get_singleton()->get_project_metadata("debug_options", "autostart_visual_profiler", false)) { + visual_profiler->set_profiling(true); + } } void ScriptEditorDebugger::_update_buttons_state() { @@ -1076,10 +1084,10 @@ void ScriptEditorDebugger::stop() { profiler_signature.clear(); profiler->set_enabled(false, false); - profiler->set_pressed(false); + profiler->set_profiling(false); visual_profiler->set_enabled(false); - visual_profiler->set_pressed(false); + visual_profiler->set_profiling(false); inspector->edit(nullptr); _update_buttons_state(); diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp index f5f7b8f51c..cfe257fcfc 100644 --- a/editor/editor_help.cpp +++ b/editor/editor_help.cpp @@ -3134,8 +3134,6 @@ void EditorHelp::init_gdext_pointers() { EditorHelp::EditorHelp() { set_custom_minimum_size(Size2(150 * EDSCALE, 0)); - EDITOR_DEF("text_editor/help/sort_functions_alphabetically", true); - class_desc = memnew(RichTextLabel); class_desc->set_tab_size(8); add_child(class_desc); diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index 4a2c41bc9c..da50ffc510 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -2246,7 +2246,7 @@ void EditorInspectorArray::_setup() { } move_vbox->add_child(ae.move_texture_rect); - if (element_position < _get_array_count() - 1) { + if (element_position < count - 1) { ae.move_down = memnew(Button); ae.move_down->set_icon(get_editor_theme_icon(SNAME("MoveDown"))); ae.move_down->connect(SceneStringName(pressed), callable_mp(this, &EditorInspectorArray::_move_element).bind(element_position, element_position + 2)); diff --git a/editor/editor_native_shader_source_visualizer.cpp b/editor/editor_native_shader_source_visualizer.cpp index 3d7d37c94e..9ebf7d680e 100644 --- a/editor/editor_native_shader_source_visualizer.cpp +++ b/editor/editor_native_shader_source_visualizer.cpp @@ -98,7 +98,7 @@ void EditorNativeShaderSourceVisualizer::_inspect_shader(RID p_shader) { code_edit->set_syntax_highlighter(syntax_highlighter); code_edit->add_theme_font_override(SceneStringName(font), get_theme_font("source", EditorStringName(EditorFonts))); code_edit->add_theme_font_size_override(SceneStringName(font_size), get_theme_font_size("source_size", EditorStringName(EditorFonts))); - code_edit->add_theme_constant_override("line_spacing", EDITOR_DEF("text_editor/theme/line_spacing", 6)); + code_edit->add_theme_constant_override("line_spacing", EDITOR_GET("text_editor/theme/line_spacing")); // Appearance: Caret code_edit->set_caret_type((TextEdit::CaretType)EDITOR_GET("text_editor/appearance/caret/type").operator int()); diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 363d07008a..66fd2cf904 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -3866,7 +3866,7 @@ Error EditorNode::load_scene(const String &p_scene, bool p_ignore_broken_deps, b } } - if (p_clear_errors) { + if (p_clear_errors && !load_errors_queued_to_display) { load_errors->clear(); } @@ -4547,14 +4547,26 @@ void EditorNode::add_io_error(const String &p_error) { DEV_ASSERT(Thread::get_caller_id() == Thread::get_main_id()); singleton->load_errors->add_image(singleton->theme->get_icon(SNAME("Error"), EditorStringName(EditorIcons))); singleton->load_errors->add_text(p_error + "\n"); - EditorInterface::get_singleton()->popup_dialog_centered_ratio(singleton->load_error_dialog, 0.5); + // When a progress dialog is displayed, we will wait for it ot close before displaying + // the io errors to prevent the io popup to set it's parent to the progress dialog. + if (singleton->progress_dialog->is_visible()) { + singleton->load_errors_queued_to_display = true; + } else { + EditorInterface::get_singleton()->popup_dialog_centered_ratio(singleton->load_error_dialog, 0.5); + } } void EditorNode::add_io_warning(const String &p_warning) { DEV_ASSERT(Thread::get_caller_id() == Thread::get_main_id()); singleton->load_errors->add_image(singleton->theme->get_icon(SNAME("Warning"), EditorStringName(EditorIcons))); singleton->load_errors->add_text(p_warning + "\n"); - EditorInterface::get_singleton()->popup_dialog_centered_ratio(singleton->load_error_dialog, 0.5); + // When a progress dialog is displayed, we will wait for it ot close before displaying + // the io errors to prevent the io popup to set it's parent to the progress dialog. + if (singleton->progress_dialog->is_visible()) { + singleton->load_errors_queued_to_display = true; + } else { + EditorInterface::get_singleton()->popup_dialog_centered_ratio(singleton->load_error_dialog, 0.5); + } } bool EditorNode::_find_scene_in_use(Node *p_node, const String &p_path) const { @@ -4825,6 +4837,14 @@ void EditorNode::progress_end_task_bg(const String &p_task) { singleton->progress_hb->end_task(p_task); } +void EditorNode::_progress_dialog_visibility_changed() { + // Open the io errors after the progress dialog is closed. + if (load_errors_queued_to_display && !progress_dialog->is_visible()) { + EditorInterface::get_singleton()->popup_dialog_centered_ratio(singleton->load_error_dialog, 0.5); + load_errors_queued_to_display = false; + } +} + String EditorNode::_get_system_info() const { String distribution_name = OS::get_singleton()->get_distribution_name(); if (distribution_name.is_empty()) { @@ -6821,6 +6841,7 @@ EditorNode::EditorNode() { add_child(resource_preview); progress_dialog = memnew(ProgressDialog); progress_dialog->set_unparent_when_invisible(true); + progress_dialog->connect(SceneStringName(visibility_changed), callable_mp(this, &EditorNode::_progress_dialog_visibility_changed)); gui_base = memnew(Panel); add_child(gui_base); diff --git a/editor/editor_node.h b/editor/editor_node.h index 63d7d58d50..4127dd1539 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -338,6 +338,7 @@ private: RichTextLabel *load_errors = nullptr; AcceptDialog *load_error_dialog = nullptr; + bool load_errors_queued_to_display = false; RichTextLabel *execute_outputs = nullptr; AcceptDialog *execute_output_dialog = nullptr; @@ -658,6 +659,8 @@ private: void _remove_all_not_owned_children(Node *p_node, Node *p_owner); + void _progress_dialog_visibility_changed(); + protected: friend class FileSystemDock; diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp index 2e46068e07..0fb57ce40e 100644 --- a/editor/editor_properties.cpp +++ b/editor/editor_properties.cpp @@ -687,16 +687,21 @@ void EditorPropertyEnum::update_property() { void EditorPropertyEnum::setup(const Vector<String> &p_options) { options->clear(); + HashMap<int64_t, Vector<String>> items; int64_t current_val = 0; - for (int i = 0; i < p_options.size(); i++) { - Vector<String> text_split = p_options[i].split(":"); + for (const String &option : p_options) { + Vector<String> text_split = option.split(":"); if (text_split.size() != 1) { current_val = text_split[1].to_int(); } - options->add_item(text_split[0]); - options->set_item_metadata(i, current_val); + items[current_val].push_back(text_split[0]); current_val += 1; } + + for (const KeyValue<int64_t, Vector<String>> &K : items) { + options->add_item(String(", ").join(K.value)); + options->set_item_metadata(-1, K.key); + } } void EditorPropertyEnum::set_option_button_clip(bool p_enable) { diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index d0c89c49c4..96a7700c34 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -400,6 +400,9 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { EDITOR_SETTING_USAGE(Variant::STRING, PROPERTY_HINT_ENUM, "interface/editor/editor_language", best, lang_hint, PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED); } + // Asset library + _initial_set("asset_library/use_threads", true); + /* Interface */ // Editor @@ -481,6 +484,10 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "interface/editor/vsync_mode", 1, "Disabled,Enabled,Adaptive,Mailbox") EDITOR_SETTING(Variant::BOOL, PROPERTY_HINT_NONE, "interface/editor/update_continuously", false, "") + _initial_set("interface/editors/show_scene_tree_root_selection", true); + _initial_set("interface/editors/derive_script_globals_by_name", true); + _initial_set("docks/scene_tree/ask_before_revoking_unique_name", true); + // Inspector EDITOR_SETTING(Variant::INT, PROPERTY_HINT_RANGE, "interface/inspector/max_array_dictionary_items_per_page", 20, "10,100,1") EDITOR_SETTING(Variant::BOOL, PROPERTY_HINT_NONE, "interface/inspector/show_low_level_opentype_features", false, "") @@ -571,6 +578,10 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { _initial_set("filesystem/on_save/compress_binary_resources", true); _initial_set("filesystem/on_save/safe_save_on_backup_then_rename", true); + // EditorFileServer + _initial_set("filesystem/file_server/port", 6010); + _initial_set("filesystem/file_server/password", ""); + // File dialog _initial_set("filesystem/file_dialog/show_hidden_files", false); EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "filesystem/file_dialog/display_mode", 0, "Thumbnails,List") @@ -588,6 +599,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { /* Docks */ // SceneTree + _initial_set("docks/scene_tree/ask_before_deleting_related_animation_tracks", true); _initial_set("docks/scene_tree/start_create_dialog_fully_expanded", false); _initial_set("docks/scene_tree/auto_expand_to_selected", true); _initial_set("docks/scene_tree/center_node_on_reparent", false); @@ -605,6 +617,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { /* Text editor */ // Theme + _initial_set("text_editor/theme/line_spacing", 6); EDITOR_SETTING(Variant::STRING, PROPERTY_HINT_ENUM, "text_editor/theme/color_theme", "Default", "Default,Godot 2,Custom") // Theme: Highlighting @@ -669,10 +682,19 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { _initial_set("text_editor/behavior/files/restore_scripts_on_load", true); _initial_set("text_editor/behavior/files/convert_indent_on_save", true); _initial_set("text_editor/behavior/files/auto_reload_scripts_on_external_change", false); + _initial_set("text_editor/behavior/files/auto_reload_and_parse_scripts_on_save", true); + _initial_set("text_editor/behavior/files/open_dominant_script_on_scene_change", false); // Script list _initial_set("text_editor/script_list/show_members_overview", true); _initial_set("text_editor/script_list/sort_members_outline_alphabetically", false); + _initial_set("text_editor/script_list/script_temperature_enabled", true); + _initial_set("text_editor/script_list/script_temperature_history_size", 15); + _initial_set("text_editor/script_list/group_help_pages", true); + EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "text_editor/script_list/sort_scripts_by", 0, "Name,Path,None"); + EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "text_editor/script_list/list_script_names_as", 0, "Name,Parent Directory And Name,Full Path"); + EDITOR_SETTING(Variant::STRING, PROPERTY_HINT_GLOBAL_FILE, "text_editor/external/exec_path", "", ""); + EDITOR_SETTING(Variant::STRING, PROPERTY_HINT_PLACEHOLDER_TEXT, "text_editor/external/exec_flags", "{file}", "Call flags with placeholders: {project}, {file}, {col}, {line}."); // Completion EDITOR_SETTING(Variant::FLOAT, PROPERTY_HINT_RANGE, "text_editor/completion/idle_parse_delay", 1.5, "0.1,10,0.01") @@ -687,17 +709,28 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { _initial_set("text_editor/completion/use_single_quotes", false); _initial_set("text_editor/completion/colorize_suggestions", true); + // External editor (ScriptEditorPlugin) + _initial_set("text_editor/external/use_external_editor", false); + _initial_set("text_editor/external/exec_path", ""); + // Help _initial_set("text_editor/help/show_help_index", true); EDITOR_SETTING(Variant::INT, PROPERTY_HINT_RANGE, "text_editor/help/help_font_size", 16, "8,48,1") EDITOR_SETTING(Variant::INT, PROPERTY_HINT_RANGE, "text_editor/help/help_source_font_size", 15, "8,48,1") EDITOR_SETTING(Variant::INT, PROPERTY_HINT_RANGE, "text_editor/help/help_title_font_size", 23, "8,64,1") EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "text_editor/help/class_reference_examples", 0, "GDScript,C#,GDScript and C#") + _initial_set("text_editor/help/sort_functions_alphabetically", true); /* Editors */ // GridMap + // GridMapEditor _initial_set("editors/grid_map/pick_distance", 5000.0); + _initial_set("editors/grid_map/palette_min_width", 230); + set_restart_if_changed("editors/grid_map/palette_min_width", true); + _initial_set("editors/grid_map/preview_size", 64); + // GridMapEditorPlugin + EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "editors/grid_map/editor_side", 1, "Left,Right"); // 3D EDITOR_SETTING(Variant::COLOR, PROPERTY_HINT_NONE, "editors/3d/primary_grid_color", Color(0.56, 0.56, 0.56, 0.5), "") @@ -708,6 +741,28 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { EDITOR_SETTING_USAGE(Variant::COLOR, PROPERTY_HINT_NONE, "editors/3d_gizmos/gizmo_colors/instantiated", Color(0.7, 0.7, 0.7, 0.6), "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED) EDITOR_SETTING_USAGE(Variant::COLOR, PROPERTY_HINT_NONE, "editors/3d_gizmos/gizmo_colors/joint", Color(0.5, 0.8, 1), "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED) EDITOR_SETTING_USAGE(Variant::COLOR, PROPERTY_HINT_NONE, "editors/3d_gizmos/gizmo_colors/aabb", Color(0.28, 0.8, 0.82), "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED) + EDITOR_SETTING_USAGE(Variant::COLOR, PROPERTY_HINT_NONE, "editors/3d_gizmos/gizmo_colors/stream_player_3d", Color(0.4, 0.8, 1), "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED) + EDITOR_SETTING_USAGE(Variant::COLOR, PROPERTY_HINT_NONE, "editors/3d_gizmos/gizmo_colors/camera", Color(0.8, 0.4, 0.8), "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED) + EDITOR_SETTING_USAGE(Variant::COLOR, PROPERTY_HINT_NONE, "editors/3d_gizmos/gizmo_colors/decal", Color(0.6, 0.5, 1.0), "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED) + EDITOR_SETTING_USAGE(Variant::COLOR, PROPERTY_HINT_NONE, "editors/3d_gizmos/gizmo_colors/fog_volume", Color(0.5, 0.7, 1), "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED) + EDITOR_SETTING_USAGE(Variant::COLOR, PROPERTY_HINT_NONE, "editors/3d_gizmos/gizmo_colors/particles", Color(0.8, 0.7, 0.4), "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED) + EDITOR_SETTING_USAGE(Variant::COLOR, PROPERTY_HINT_NONE, "editors/3d_gizmos/gizmo_colors/particle_attractor", Color(1, 0.7, 0.5), "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED) + EDITOR_SETTING_USAGE(Variant::COLOR, PROPERTY_HINT_NONE, "editors/3d_gizmos/gizmo_colors/particle_collision", Color(0.5, 0.7, 1), "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED) + EDITOR_SETTING_USAGE(Variant::COLOR, PROPERTY_HINT_NONE, "editors/3d_gizmos/gizmo_colors/joint_body_a", Color(0.6, 0.8, 1), "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED) + EDITOR_SETTING_USAGE(Variant::COLOR, PROPERTY_HINT_NONE, "editors/3d_gizmos/gizmo_colors/joint_body_b", Color(0.6, 0.9, 1), "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED) + EDITOR_SETTING_USAGE(Variant::COLOR, PROPERTY_HINT_NONE, "editors/3d_gizmos/gizmo_colors/lightmap_lines", Color(0.5, 0.6, 1), "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED) + EDITOR_SETTING_USAGE(Variant::COLOR, PROPERTY_HINT_NONE, "editors/3d_gizmos/gizmo_colors/lightprobe_lines", Color(0.5, 0.6, 1), "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED) + EDITOR_SETTING_USAGE(Variant::COLOR, PROPERTY_HINT_NONE, "editors/3d_gizmos/gizmo_colors/occluder", Color(0.8, 0.5, 1), "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED) + EDITOR_SETTING_USAGE(Variant::COLOR, PROPERTY_HINT_NONE, "editors/3d_gizmos/gizmo_colors/reflection_probe", Color(0.6, 1, 0.5), "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED) + EDITOR_SETTING_USAGE(Variant::COLOR, PROPERTY_HINT_NONE, "editors/3d_gizmos/gizmo_colors/visibility_notifier", Color(0.8, 0.5, 0.7), "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED) + EDITOR_SETTING_USAGE(Variant::COLOR, PROPERTY_HINT_NONE, "editors/3d_gizmos/gizmo_colors/voxel_gi", Color(0.5, 1, 0.6), "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED) + EDITOR_SETTING_USAGE(Variant::COLOR, PROPERTY_HINT_NONE, "editors/3d_gizmos/gizmo_colors/path_tilt", Color(1.0, 1.0, 0.4, 0.9), "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED) + EDITOR_SETTING_USAGE(Variant::COLOR, PROPERTY_HINT_NONE, "editors/3d_gizmos/gizmo_colors/skeleton", Color(1, 0.8, 0.4), "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED) + EDITOR_SETTING_USAGE(Variant::COLOR, PROPERTY_HINT_NONE, "editors/3d_gizmos/gizmo_colors/selected_bone", Color(0.8, 0.3, 0.0), "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED) + EDITOR_SETTING_USAGE(Variant::COLOR, PROPERTY_HINT_NONE, "editors/3d_gizmos/gizmo_colors/csg", Color(0.0, 0.4, 1, 0.15), "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED) + _initial_set("editors/3d_gizmos/gizmo_settings/bone_axis_length", (float)0.1); + EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "editors/3d_gizmos/gizmo_settings/bone_shape", 1, "Wire,Octahedron"); + EDITOR_SETTING_USAGE(Variant::FLOAT, PROPERTY_HINT_NONE, "editors/3d_gizmos/gizmo_settings/path3d_tilt_disk_size", 0.8, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED) // If a line is a multiple of this, it uses the primary grid color. // Use a power of 2 value by default as it's more common to use powers of 2 in level design. @@ -750,6 +805,8 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { EDITOR_SETTING(Variant::FLOAT, PROPERTY_HINT_RANGE, "editors/3d/navigation_feel/orbit_inertia", 0.0, "0,1,0.001") EDITOR_SETTING(Variant::FLOAT, PROPERTY_HINT_RANGE, "editors/3d/navigation_feel/translation_inertia", 0.05, "0,1,0.001") EDITOR_SETTING(Variant::FLOAT, PROPERTY_HINT_RANGE, "editors/3d/navigation_feel/zoom_inertia", 0.05, "0,1,0.001") + _initial_set("editors/3d/navigation/show_viewport_rotation_gizmo", true); + _initial_set("editors/3d/navigation/show_viewport_navigation_gizmo", DisplayServer::get_singleton()->is_touchscreen_available()); // 3D: Freelook EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "editors/3d/freelook/freelook_navigation_scheme", 0, "Default,Partially Axis-Locked (id Tech),Fully Axis-Locked (Minecraft)") @@ -759,6 +816,10 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "editors/3d/freelook/freelook_activation_modifier", 0, "None,Shift,Alt,Meta,Ctrl") _initial_set("editors/3d/freelook/freelook_speed_zoom_link", false); + // 3D: Manipulator + EDITOR_SETTING(Variant::INT, PROPERTY_HINT_RANGE, "editors/3d/manipulator_gizmo_size", 80, "16,160,1"); + EDITOR_SETTING_USAGE(Variant::FLOAT, PROPERTY_HINT_RANGE, "editors/3d/manipulator_gizmo_opacity", 0.9, "0,1,0.01", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED) + // 2D _initial_set("editors/2d/grid_color", Color(1.0, 1.0, 1.0, 0.07)); _initial_set("editors/2d/guides_color", Color(0.6, 0.0, 0.8)); @@ -774,6 +835,12 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { _initial_set("editors/2d/use_integer_zoom_by_default", false); EDITOR_SETTING(Variant::FLOAT, PROPERTY_HINT_RANGE, "editors/2d/zoom_speed_factor", 1.1, "1.01,2,0.01") + // Bone mapper (BoneMapEditorPlugin) + _initial_set("editors/bone_mapper/handle_colors/unset", Color(0.3, 0.3, 0.3)); + _initial_set("editors/bone_mapper/handle_colors/set", Color(0.1, 0.6, 0.25)); + _initial_set("editors/bone_mapper/handle_colors/missing", Color(0.8, 0.2, 0.8)); + _initial_set("editors/bone_mapper/handle_colors/error", Color(0.8, 0.2, 0.2)); + // Panning // Enum should be in sync with ControlScheme in ViewPanner. EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "editors/panning/2d_editor_panning_scheme", 0, "Scroll Zooms,Scroll Pans"); @@ -814,6 +881,10 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "editors/visual_editors/grid_pattern", 1, "Lines,Dots") EDITOR_SETTING(Variant::INT, PROPERTY_HINT_RANGE, "editors/visual_editors/visual_shader/port_preview_size", 160, "100,400,0.01") + // Export (EditorExportPlugin) + _initial_set("export/ssh/ssh", ""); + _initial_set("export/ssh/scp", ""); + /* Run */ // Window placement @@ -880,6 +951,11 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { EDITOR_SETTING(Variant::FLOAT, PROPERTY_HINT_RANGE, "debugger/remote_inspect_refresh_interval", 0.2, "0.02,10,0.01,or_greater") EDITOR_SETTING(Variant::BOOL, PROPERTY_HINT_NONE, "debugger/profile_native_calls", false, "") + // Version control (VersionControlEditorPlugin) + _initial_set("version_control/username", ""); + _initial_set("version_control/ssh_public_key_path", ""); + _initial_set("version_control/ssh_private_key_path", ""); + /* Extra config */ EDITOR_SETTING_USAGE(Variant::BOOL, PROPERTY_HINT_NONE, "input/buffering/agile_event_flushing", false, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED) diff --git a/editor/export/editor_export_plugin.cpp b/editor/export/editor_export_plugin.cpp index 6bb21d7fd4..3f1b8aa863 100644 --- a/editor/export/editor_export_plugin.cpp +++ b/editor/export/editor_export_plugin.cpp @@ -364,8 +364,3 @@ void EditorExportPlugin::_bind_methods() { GDVIRTUAL_BIND(_get_android_manifest_application_element_contents, "platform", "debug"); GDVIRTUAL_BIND(_get_android_manifest_element_contents, "platform", "debug"); } - -EditorExportPlugin::EditorExportPlugin() { - EDITOR_DEF("export/ssh/ssh", ""); - EDITOR_DEF("export/ssh/scp", ""); -} diff --git a/editor/export/editor_export_plugin.h b/editor/export/editor_export_plugin.h index 7a355614c7..ae186d4425 100644 --- a/editor/export/editor_export_plugin.h +++ b/editor/export/editor_export_plugin.h @@ -183,8 +183,6 @@ public: String get_ios_cpp_code() const; const Vector<String> &get_macos_plugin_files() const; Variant get_option(const StringName &p_name) const; - - EditorExportPlugin(); }; #endif // EDITOR_EXPORT_PLUGIN_H diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp index 68d74236bb..25725635e3 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -216,6 +216,7 @@ bool FileSystemDock::_create_tree(TreeItem *p_parent, EditorFileSystemDirectory // Set custom folder color (if applicable). bool has_custom_color = assigned_folder_colors.has(lpath); Color custom_color = has_custom_color ? folder_colors[assigned_folder_colors[lpath]] : Color(); + Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES); if (has_custom_color) { subdirectory_item->set_icon_modulate(0, editor_is_dark_theme ? custom_color : custom_color * ITEM_COLOR_SCALE); @@ -237,6 +238,10 @@ bool FileSystemDock::_create_tree(TreeItem *p_parent, EditorFileSystemDirectory subdirectory_item->set_text(0, dname); subdirectory_item->set_structured_text_bidi_override(0, TextServer::STRUCTURED_TEXT_FILE); subdirectory_item->set_icon(0, get_editor_theme_icon(SNAME("Folder"))); + if (da->is_link(lpath)) { + subdirectory_item->set_icon_overlay(0, get_editor_theme_icon(SNAME("LinkOverlay"))); + subdirectory_item->set_tooltip_text(0, vformat(TTR("Link to: %s"), da->read_link(lpath))); + } subdirectory_item->set_selectable(0, true); subdirectory_item->set_metadata(0, lpath); if (!p_select_in_favorites && (current_path == lpath || ((display_mode != DISPLAY_MODE_TREE_ONLY) && current_path.get_base_dir() == lpath))) { @@ -309,6 +314,10 @@ bool FileSystemDock::_create_tree(TreeItem *p_parent, EditorFileSystemDirectory file_item->set_text(0, fi.name); file_item->set_structured_text_bidi_override(0, TextServer::STRUCTURED_TEXT_FILE); file_item->set_icon(0, _get_tree_item_icon(!fi.import_broken, fi.type, fi.icon_path)); + if (da->is_link(file_metadata)) { + file_item->set_icon_overlay(0, get_editor_theme_icon(SNAME("LinkOverlay"))); + file_item->set_tooltip_text(0, vformat(TTR("Link to: %s"), da->read_link(file_metadata))); + } file_item->set_icon_max_width(0, icon_size); Color parent_bg_color = subdirectory_item->get_custom_bg_color(0); if (has_custom_color) { diff --git a/editor/icons/LinkOverlay.svg b/editor/icons/LinkOverlay.svg new file mode 100644 index 0000000000..b263f94c0b --- /dev/null +++ b/editor/icons/LinkOverlay.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path stroke="#fff" stroke-width=".6" d="M2.08 8.2v1.907h2.465l-4.02 4.02 1.347 1.349 4.021-4.021v2.465H7.8V9.153a.953.953 0 0 0-.953-.953z"/></svg>
\ No newline at end of file diff --git a/editor/import/resource_importer_wav.cpp b/editor/import/resource_importer_wav.cpp index 77b3629b07..7a6f39906c 100644 --- a/editor/import/resource_importer_wav.cpp +++ b/editor/import/resource_importer_wav.cpp @@ -429,10 +429,10 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s loop_end = p_options["edit/loop_end"]; // Wrap around to max frames, so `-1` can be used to select the end, etc. if (loop_begin < 0) { - loop_begin = CLAMP(loop_begin + frames + 1, 0, frames); + loop_begin = CLAMP(loop_begin + frames, 0, frames - 1); } if (loop_end < 0) { - loop_end = CLAMP(loop_end + frames + 1, 0, frames); + loop_end = CLAMP(loop_end + frames, 0, frames - 1); } } diff --git a/editor/plugins/abstract_polygon_2d_editor.cpp b/editor/plugins/abstract_polygon_2d_editor.cpp index 60d808952e..804f9c607e 100644 --- a/editor/plugins/abstract_polygon_2d_editor.cpp +++ b/editor/plugins/abstract_polygon_2d_editor.cpp @@ -748,7 +748,9 @@ AbstractPolygon2DEditor::AbstractPolygon2DEditor(bool p_wip_destructive) { } void AbstractPolygon2DEditorPlugin::edit(Object *p_object) { - polygon_editor->edit(Object::cast_to<Node>(p_object)); + Node *polygon_node = Object::cast_to<Node>(p_object); + polygon_editor->edit(polygon_node); + make_visible(polygon_node != nullptr); } bool AbstractPolygon2DEditorPlugin::handles(Object *p_object) const { diff --git a/editor/plugins/asset_library_editor_plugin.cpp b/editor/plugins/asset_library_editor_plugin.cpp index f31346fe67..af4b3a1643 100644 --- a/editor/plugins/asset_library_editor_plugin.cpp +++ b/editor/plugins/asset_library_editor_plugin.cpp @@ -47,7 +47,7 @@ #include "scene/resources/image_texture.h" static inline void setup_http_request(HTTPRequest *request) { - request->set_use_threads(EDITOR_DEF("asset_library/use_threads", true)); + request->set_use_threads(EDITOR_GET("asset_library/use_threads")); const String proxy_host = EDITOR_GET("network/http_proxy/host"); const int proxy_port = EDITOR_GET("network/http_proxy/port"); @@ -703,6 +703,7 @@ void EditorAssetLibrary::_notification(int p_what) { } void EditorAssetLibrary::_update_repository_options() { + // TODO: Move to editor_settings.cpp Dictionary default_urls; default_urls["godotengine.org (Official)"] = "https://godotengine.org/asset-library/api"; Dictionary available_urls = _EDITOR_DEF("asset_library/available_urls", default_urls, true); diff --git a/editor/plugins/bone_map_editor_plugin.cpp b/editor/plugins/bone_map_editor_plugin.cpp index 32ff478c33..d81ec21705 100644 --- a/editor/plugins/bone_map_editor_plugin.cpp +++ b/editor/plugins/bone_map_editor_plugin.cpp @@ -1477,12 +1477,6 @@ void EditorInspectorPluginBoneMap::parse_begin(Object *p_object) { } BoneMapEditorPlugin::BoneMapEditorPlugin() { - // Register properties in editor settings. - EDITOR_DEF("editors/bone_mapper/handle_colors/unset", Color(0.3, 0.3, 0.3)); - EDITOR_DEF("editors/bone_mapper/handle_colors/set", Color(0.1, 0.6, 0.25)); - EDITOR_DEF("editors/bone_mapper/handle_colors/missing", Color(0.8, 0.2, 0.8)); - EDITOR_DEF("editors/bone_mapper/handle_colors/error", Color(0.8, 0.2, 0.2)); - Ref<EditorInspectorPluginBoneMap> inspector_plugin; inspector_plugin.instantiate(); add_inspector_plugin(inspector_plugin); diff --git a/editor/plugins/gizmos/audio_stream_player_3d_gizmo_plugin.cpp b/editor/plugins/gizmos/audio_stream_player_3d_gizmo_plugin.cpp index 9d4b5e9d70..577f2b6b8e 100644 --- a/editor/plugins/gizmos/audio_stream_player_3d_gizmo_plugin.cpp +++ b/editor/plugins/gizmos/audio_stream_player_3d_gizmo_plugin.cpp @@ -38,7 +38,7 @@ #include "scene/3d/audio_stream_player_3d.h" AudioStreamPlayer3DGizmoPlugin::AudioStreamPlayer3DGizmoPlugin() { - Color gizmo_color = EDITOR_DEF_RST("editors/3d_gizmos/gizmo_colors/stream_player_3d", Color(0.4, 0.8, 1)); + Color gizmo_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/stream_player_3d"); create_icon_material("stream_player_3d_icon", EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("Gizmo3DSamplePlayer"), EditorStringName(EditorIcons))); create_material("stream_player_3d_material_primary", gizmo_color); diff --git a/editor/plugins/gizmos/camera_3d_gizmo_plugin.cpp b/editor/plugins/gizmos/camera_3d_gizmo_plugin.cpp index 8d0222215c..63a48d4165 100644 --- a/editor/plugins/gizmos/camera_3d_gizmo_plugin.cpp +++ b/editor/plugins/gizmos/camera_3d_gizmo_plugin.cpp @@ -39,7 +39,7 @@ #include "scene/3d/camera_3d.h" Camera3DGizmoPlugin::Camera3DGizmoPlugin() { - Color gizmo_color = EDITOR_DEF_RST("editors/3d_gizmos/gizmo_colors/camera", Color(0.8, 0.4, 0.8)); + Color gizmo_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/camera"); create_material("camera_material", gizmo_color); create_icon_material("camera_icon", EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("GizmoCamera3D"), EditorStringName(EditorIcons))); diff --git a/editor/plugins/gizmos/cpu_particles_3d_gizmo_plugin.cpp b/editor/plugins/gizmos/cpu_particles_3d_gizmo_plugin.cpp index fe5d8e92d1..2a5d8cb2a9 100644 --- a/editor/plugins/gizmos/cpu_particles_3d_gizmo_plugin.cpp +++ b/editor/plugins/gizmos/cpu_particles_3d_gizmo_plugin.cpp @@ -37,7 +37,7 @@ #include "scene/3d/cpu_particles_3d.h" CPUParticles3DGizmoPlugin::CPUParticles3DGizmoPlugin() { - Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/particles", Color(0.8, 0.7, 0.4)); + Color gizmo_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/particles"); create_material("particles_material", gizmo_color); gizmo_color.a = MAX((gizmo_color.a - 0.2) * 0.02, 0.0); create_material("particles_solid_material", gizmo_color); diff --git a/editor/plugins/gizmos/decal_gizmo_plugin.cpp b/editor/plugins/gizmos/decal_gizmo_plugin.cpp index f2b44790ee..b619c3a1dd 100644 --- a/editor/plugins/gizmos/decal_gizmo_plugin.cpp +++ b/editor/plugins/gizmos/decal_gizmo_plugin.cpp @@ -40,7 +40,7 @@ DecalGizmoPlugin::DecalGizmoPlugin() { helper.instantiate(); - Color gizmo_color = EDITOR_DEF_RST("editors/3d_gizmos/gizmo_colors/decal", Color(0.6, 0.5, 1.0)); + Color gizmo_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/decal"); create_icon_material("decal_icon", EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("GizmoDecal"), EditorStringName(EditorIcons))); diff --git a/editor/plugins/gizmos/fog_volume_gizmo_plugin.cpp b/editor/plugins/gizmos/fog_volume_gizmo_plugin.cpp index dd91d7dfe3..5c2f942519 100644 --- a/editor/plugins/gizmos/fog_volume_gizmo_plugin.cpp +++ b/editor/plugins/gizmos/fog_volume_gizmo_plugin.cpp @@ -40,7 +40,7 @@ FogVolumeGizmoPlugin::FogVolumeGizmoPlugin() { helper.instantiate(); - Color gizmo_color = EDITOR_DEF_RST("editors/3d_gizmos/gizmo_colors/fog_volume", Color(0.5, 0.7, 1)); + Color gizmo_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/fog_volume"); create_material("shape_material", gizmo_color); gizmo_color.a = 0.15; create_material("shape_material_internal", gizmo_color); diff --git a/editor/plugins/gizmos/gpu_particles_3d_gizmo_plugin.cpp b/editor/plugins/gizmos/gpu_particles_3d_gizmo_plugin.cpp index 39bfed93b0..d2a90ae57c 100644 --- a/editor/plugins/gizmos/gpu_particles_3d_gizmo_plugin.cpp +++ b/editor/plugins/gizmos/gpu_particles_3d_gizmo_plugin.cpp @@ -38,7 +38,7 @@ #include "scene/3d/gpu_particles_3d.h" GPUParticles3DGizmoPlugin::GPUParticles3DGizmoPlugin() { - Color gizmo_color = EDITOR_DEF_RST("editors/3d_gizmos/gizmo_colors/particles", Color(0.8, 0.7, 0.4)); + Color gizmo_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/particles"); create_material("particles_material", gizmo_color); gizmo_color.a = MAX((gizmo_color.a - 0.2) * 0.02, 0.0); create_icon_material("particles_icon", EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("GizmoGPUParticles3D"), EditorStringName(EditorIcons))); diff --git a/editor/plugins/gizmos/gpu_particles_collision_3d_gizmo_plugin.cpp b/editor/plugins/gizmos/gpu_particles_collision_3d_gizmo_plugin.cpp index 6f20a53459..78acbc4d23 100644 --- a/editor/plugins/gizmos/gpu_particles_collision_3d_gizmo_plugin.cpp +++ b/editor/plugins/gizmos/gpu_particles_collision_3d_gizmo_plugin.cpp @@ -39,12 +39,12 @@ GPUParticlesCollision3DGizmoPlugin::GPUParticlesCollision3DGizmoPlugin() { helper.instantiate(); - Color gizmo_color_attractor = EDITOR_DEF_RST("editors/3d_gizmos/gizmo_colors/particle_attractor", Color(1, 0.7, 0.5)); + Color gizmo_color_attractor = EDITOR_GET("editors/3d_gizmos/gizmo_colors/particle_attractor"); create_material("shape_material_attractor", gizmo_color_attractor); gizmo_color_attractor.a = 0.15; create_material("shape_material_attractor_internal", gizmo_color_attractor); - Color gizmo_color_collision = EDITOR_DEF_RST("editors/3d_gizmos/gizmo_colors/particle_collision", Color(0.5, 0.7, 1)); + Color gizmo_color_collision = EDITOR_GET("editors/3d_gizmos/gizmo_colors/particle_collision"); create_material("shape_material_collision", gizmo_color_collision); gizmo_color_collision.a = 0.15; create_material("shape_material_collision_internal", gizmo_color_collision); diff --git a/editor/plugins/gizmos/joint_3d_gizmo_plugin.cpp b/editor/plugins/gizmos/joint_3d_gizmo_plugin.cpp index c277ec8cd3..8c0192a961 100644 --- a/editor/plugins/gizmos/joint_3d_gizmo_plugin.cpp +++ b/editor/plugins/gizmos/joint_3d_gizmo_plugin.cpp @@ -280,8 +280,8 @@ void JointGizmosDrawer::draw_cone(const Transform3D &p_offset, const Basis &p_ba Joint3DGizmoPlugin::Joint3DGizmoPlugin() { create_material("joint_material", EDITOR_GET("editors/3d_gizmos/gizmo_colors/joint")); - create_material("joint_body_a_material", EDITOR_DEF_RST("editors/3d_gizmos/gizmo_colors/joint_body_a", Color(0.6, 0.8, 1))); - create_material("joint_body_b_material", EDITOR_DEF_RST("editors/3d_gizmos/gizmo_colors/joint_body_b", Color(0.6, 0.9, 1))); + create_material("joint_body_a_material", EDITOR_GET("editors/3d_gizmos/gizmo_colors/joint_body_a")); + create_material("joint_body_b_material", EDITOR_GET("editors/3d_gizmos/gizmo_colors/joint_body_b")); update_timer = memnew(Timer); update_timer->set_name("JointGizmoUpdateTimer"); diff --git a/editor/plugins/gizmos/lightmap_gi_gizmo_plugin.cpp b/editor/plugins/gizmos/lightmap_gi_gizmo_plugin.cpp index 64913dc779..748f770d4d 100644 --- a/editor/plugins/gizmos/lightmap_gi_gizmo_plugin.cpp +++ b/editor/plugins/gizmos/lightmap_gi_gizmo_plugin.cpp @@ -37,7 +37,7 @@ #include "scene/3d/lightmap_gi.h" LightmapGIGizmoPlugin::LightmapGIGizmoPlugin() { - Color gizmo_color = EDITOR_DEF_RST("editors/3d_gizmos/gizmo_colors/lightmap_lines", Color(0.5, 0.6, 1)); + Color gizmo_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/lightmap_lines"); gizmo_color.a = 0.1; create_material("lightmap_lines", gizmo_color); diff --git a/editor/plugins/gizmos/lightmap_probe_gizmo_plugin.cpp b/editor/plugins/gizmos/lightmap_probe_gizmo_plugin.cpp index 5fd8ad2235..cc8649d2d1 100644 --- a/editor/plugins/gizmos/lightmap_probe_gizmo_plugin.cpp +++ b/editor/plugins/gizmos/lightmap_probe_gizmo_plugin.cpp @@ -39,7 +39,7 @@ LightmapProbeGizmoPlugin::LightmapProbeGizmoPlugin() { create_icon_material("lightmap_probe_icon", EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("GizmoLightmapProbe"), EditorStringName(EditorIcons))); - Color gizmo_color = EDITOR_DEF_RST("editors/3d_gizmos/gizmo_colors/lightprobe_lines", Color(0.5, 0.6, 1)); + Color gizmo_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/lightprobe_lines"); gizmo_color.a = 0.3; create_material("lightprobe_lines", gizmo_color); diff --git a/editor/plugins/gizmos/mesh_instance_3d_gizmo_plugin.cpp b/editor/plugins/gizmos/mesh_instance_3d_gizmo_plugin.cpp index e54f429b8d..251fb6892e 100644 --- a/editor/plugins/gizmos/mesh_instance_3d_gizmo_plugin.cpp +++ b/editor/plugins/gizmos/mesh_instance_3d_gizmo_plugin.cpp @@ -33,6 +33,7 @@ #include "editor/plugins/node_3d_editor_plugin.h" #include "scene/3d/mesh_instance_3d.h" #include "scene/3d/soft_body_3d.h" +#include "scene/resources/3d/primitive_meshes.h" MeshInstance3DGizmoPlugin::MeshInstance3DGizmoPlugin() { } @@ -64,7 +65,22 @@ void MeshInstance3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { return; //none } - Ref<TriangleMesh> tm = m->generate_triangle_mesh(); + Ref<TriangleMesh> tm; + + Ref<PlaneMesh> plane_mesh = mesh->get_mesh(); + if (plane_mesh.is_valid() && (plane_mesh->get_subdivide_depth() > 0 || plane_mesh->get_subdivide_width() > 0)) { + // PlaneMesh subdiv makes gizmo redraw very slow due to TriangleMesh BVH calculation for every face. + // For gizmo collision this is very much unnecessary since a PlaneMesh is always flat, 2 faces is enough. + Ref<PlaneMesh> simple_plane_mesh; + simple_plane_mesh.instantiate(); + simple_plane_mesh->set_orientation(plane_mesh->get_orientation()); + simple_plane_mesh->set_size(plane_mesh->get_size()); + simple_plane_mesh->set_center_offset(plane_mesh->get_center_offset()); + tm = simple_plane_mesh->generate_triangle_mesh(); + } else { + tm = m->generate_triangle_mesh(); + } + if (tm.is_valid()) { p_gizmo->add_collision_triangles(tm); } diff --git a/editor/plugins/gizmos/occluder_instance_3d_gizmo_plugin.cpp b/editor/plugins/gizmos/occluder_instance_3d_gizmo_plugin.cpp index d6f649ab9c..29f060d795 100644 --- a/editor/plugins/gizmos/occluder_instance_3d_gizmo_plugin.cpp +++ b/editor/plugins/gizmos/occluder_instance_3d_gizmo_plugin.cpp @@ -36,7 +36,7 @@ #include "scene/3d/occluder_instance_3d.h" OccluderInstance3DGizmoPlugin::OccluderInstance3DGizmoPlugin() { - create_material("line_material", EDITOR_DEF_RST("editors/3d_gizmos/gizmo_colors/occluder", Color(0.8, 0.5, 1))); + create_material("line_material", EDITOR_GET("editors/3d_gizmos/gizmo_colors/occluder")); create_handle_material("handles"); } diff --git a/editor/plugins/gizmos/reflection_probe_gizmo_plugin.cpp b/editor/plugins/gizmos/reflection_probe_gizmo_plugin.cpp index eb7e668f41..7b91dac2eb 100644 --- a/editor/plugins/gizmos/reflection_probe_gizmo_plugin.cpp +++ b/editor/plugins/gizmos/reflection_probe_gizmo_plugin.cpp @@ -40,7 +40,7 @@ ReflectionProbeGizmoPlugin::ReflectionProbeGizmoPlugin() { helper.instantiate(); - Color gizmo_color = EDITOR_DEF_RST("editors/3d_gizmos/gizmo_colors/reflection_probe", Color(0.6, 1, 0.5)); + Color gizmo_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/reflection_probe"); create_material("reflection_probe_material", gizmo_color); diff --git a/editor/plugins/gizmos/visible_on_screen_notifier_3d_gizmo_plugin.cpp b/editor/plugins/gizmos/visible_on_screen_notifier_3d_gizmo_plugin.cpp index d3ae823fdc..2d40e66811 100644 --- a/editor/plugins/gizmos/visible_on_screen_notifier_3d_gizmo_plugin.cpp +++ b/editor/plugins/gizmos/visible_on_screen_notifier_3d_gizmo_plugin.cpp @@ -36,7 +36,7 @@ #include "scene/3d/visible_on_screen_notifier_3d.h" VisibleOnScreenNotifier3DGizmoPlugin::VisibleOnScreenNotifier3DGizmoPlugin() { - Color gizmo_color = EDITOR_DEF_RST("editors/3d_gizmos/gizmo_colors/visibility_notifier", Color(0.8, 0.5, 0.7)); + Color gizmo_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/visibility_notifier"); create_material("visibility_notifier_material", gizmo_color); gizmo_color.a = 0.1; create_material("visibility_notifier_solid_material", gizmo_color); diff --git a/editor/plugins/gizmos/voxel_gi_gizmo_plugin.cpp b/editor/plugins/gizmos/voxel_gi_gizmo_plugin.cpp index 8d09400f78..0123b9d84a 100644 --- a/editor/plugins/gizmos/voxel_gi_gizmo_plugin.cpp +++ b/editor/plugins/gizmos/voxel_gi_gizmo_plugin.cpp @@ -41,7 +41,7 @@ VoxelGIGizmoPlugin::VoxelGIGizmoPlugin() { helper.instantiate(); - Color gizmo_color = EDITOR_DEF_RST("editors/3d_gizmos/gizmo_colors/voxel_gi", Color(0.5, 1, 0.6)); + Color gizmo_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/voxel_gi"); create_material("voxel_gi_material", gizmo_color); diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp index f58dfbb5a5..d0d4f6098c 100644 --- a/editor/plugins/node_3d_editor_plugin.cpp +++ b/editor/plugins/node_3d_editor_plugin.cpp @@ -8999,13 +8999,6 @@ Node3DEditor::Node3DEditor() { set_process_shortcut_input(true); add_to_group(SceneStringName(_spatial_editor_group)); - EDITOR_DEF("editors/3d/manipulator_gizmo_size", 80); - EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::INT, "editors/3d/manipulator_gizmo_size", PROPERTY_HINT_RANGE, "16,160,1")); - EDITOR_DEF("editors/3d/manipulator_gizmo_opacity", 0.9); - EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::FLOAT, "editors/3d/manipulator_gizmo_opacity", PROPERTY_HINT_RANGE, "0,1,0.01")); - EDITOR_DEF("editors/3d/navigation/show_viewport_rotation_gizmo", true); - EDITOR_DEF("editors/3d/navigation/show_viewport_navigation_gizmo", DisplayServer::get_singleton()->is_touchscreen_available()); - current_hover_gizmo_handle = -1; current_hover_gizmo_handle_secondary = false; { diff --git a/editor/plugins/path_3d_editor_plugin.cpp b/editor/plugins/path_3d_editor_plugin.cpp index 890035e6a6..4fdcb79696 100644 --- a/editor/plugins/path_3d_editor_plugin.cpp +++ b/editor/plugins/path_3d_editor_plugin.cpp @@ -817,7 +817,10 @@ void Path3DEditorPlugin::_bind_methods() { Path3DEditorPlugin::Path3DEditorPlugin() { singleton = this; - disk_size = EDITOR_DEF_RST("editors/3d_gizmos/gizmo_settings/path3d_tilt_disk_size", 0.8); + mirror_handle_angle = true; + mirror_handle_length = true; + + disk_size = EDITOR_GET("editors/3d_gizmos/gizmo_settings/path3d_tilt_disk_size"); Ref<Path3DGizmoPlugin> gizmo_plugin = memnew(Path3DGizmoPlugin(disk_size)); Node3DEditor::get_singleton()->add_gizmo_plugin(gizmo_plugin); @@ -1061,7 +1064,7 @@ int Path3DGizmoPlugin::get_priority() const { Path3DGizmoPlugin::Path3DGizmoPlugin(float p_disk_size) { Color path_color = SceneTree::get_singleton()->get_debug_paths_color(); - Color path_tilt_color = EDITOR_DEF_RST("editors/3d_gizmos/gizmo_colors/path_tilt", Color(1.0, 1.0, 0.4, 0.9)); + Color path_tilt_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/path_tilt"); disk_size = p_disk_size; create_material("path_material", path_color); diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index 4996964976..25198e00bf 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -4058,6 +4058,7 @@ void ScriptEditor::_bind_methods() { ClassDB::bind_method(D_METHOD("open_script_create_dialog", "base_name", "base_path"), &ScriptEditor::open_script_create_dialog); ClassDB::bind_method(D_METHOD("goto_help", "topic"), &ScriptEditor::goto_help); + ClassDB::bind_method(D_METHOD("update_docs_from_script", "script"), &ScriptEditor::update_docs_from_script); ADD_SIGNAL(MethodInfo("editor_script_changed", PropertyInfo(Variant::OBJECT, "script", PROPERTY_HINT_RESOURCE_TYPE, "Script"))); ADD_SIGNAL(MethodInfo("script_close", PropertyInfo(Variant::OBJECT, "script", PROPERTY_HINT_RESOURCE_TYPE, "Script"))); @@ -4623,21 +4624,7 @@ ScriptEditorPlugin::ScriptEditorPlugin() { window_wrapper->hide(); window_wrapper->connect("window_visibility_changed", callable_mp(this, &ScriptEditorPlugin::_window_visibility_changed)); - EDITOR_GET("text_editor/behavior/files/auto_reload_scripts_on_external_change"); - ScriptServer::set_reload_scripts_on_save(EDITOR_DEF("text_editor/behavior/files/auto_reload_and_parse_scripts_on_save", true)); - EDITOR_DEF("text_editor/behavior/files/open_dominant_script_on_scene_change", false); - EDITOR_DEF("text_editor/external/use_external_editor", false); - EDITOR_DEF("text_editor/external/exec_path", ""); - EDITOR_DEF("text_editor/script_list/script_temperature_enabled", true); - EDITOR_DEF("text_editor/script_list/script_temperature_history_size", 15); - EDITOR_DEF("text_editor/script_list/group_help_pages", true); - EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::INT, "text_editor/script_list/sort_scripts_by", PROPERTY_HINT_ENUM, "Name,Path,None")); - EDITOR_DEF("text_editor/script_list/sort_scripts_by", 0); - EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::INT, "text_editor/script_list/list_script_names_as", PROPERTY_HINT_ENUM, "Name,Parent Directory And Name,Full Path")); - EDITOR_DEF("text_editor/script_list/list_script_names_as", 0); - EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "text_editor/external/exec_path", PROPERTY_HINT_GLOBAL_FILE)); - EDITOR_DEF("text_editor/external/exec_flags", "{file}"); - EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "text_editor/external/exec_flags", PROPERTY_HINT_PLACEHOLDER_TEXT, "Call flags with placeholders: {project}, {file}, {col}, {line}.")); + ScriptServer::set_reload_scripts_on_save(EDITOR_GET("text_editor/behavior/files/auto_reload_and_parse_scripts_on_save")); } ScriptEditorPlugin::~ScriptEditorPlugin() { diff --git a/editor/plugins/skeleton_3d_editor_plugin.cpp b/editor/plugins/skeleton_3d_editor_plugin.cpp index 9fc88b3a6a..99cb03cdcd 100644 --- a/editor/plugins/skeleton_3d_editor_plugin.cpp +++ b/editor/plugins/skeleton_3d_editor_plugin.cpp @@ -1379,13 +1379,6 @@ void fragment() { } )"); selection_materials.selected_mat->set_shader(selected_sh); - - // Register properties in editor settings. - EDITOR_DEF_RST("editors/3d_gizmos/gizmo_colors/skeleton", Color(1, 0.8, 0.4)); - EDITOR_DEF_RST("editors/3d_gizmos/gizmo_colors/selected_bone", Color(0.8, 0.3, 0.0)); - EDITOR_DEF("editors/3d_gizmos/gizmo_settings/bone_axis_length", (float)0.1); - EDITOR_DEF("editors/3d_gizmos/gizmo_settings/bone_shape", 1); - EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::INT, "editors/3d_gizmos/gizmo_settings/bone_shape", PROPERTY_HINT_ENUM, "Wire,Octahedron")); } Skeleton3DGizmoPlugin::~Skeleton3DGizmoPlugin() { diff --git a/editor/plugins/version_control_editor_plugin.cpp b/editor/plugins/version_control_editor_plugin.cpp index 1df84cb0a7..4f0df1d5fc 100644 --- a/editor/plugins/version_control_editor_plugin.cpp +++ b/editor/plugins/version_control_editor_plugin.cpp @@ -1032,7 +1032,7 @@ VersionControlEditorPlugin::VersionControlEditorPlugin() { set_up_username = memnew(LineEdit); set_up_username->set_h_size_flags(Control::SIZE_EXPAND_FILL); - set_up_username->set_text(EDITOR_DEF("version_control/username", "")); + set_up_username->set_text(EDITOR_GET("version_control/username")); set_up_username->connect(SceneStringName(text_changed), callable_mp(this, &VersionControlEditorPlugin::_update_set_up_warning)); set_up_username_input->add_child(set_up_username); @@ -1068,7 +1068,7 @@ VersionControlEditorPlugin::VersionControlEditorPlugin() { set_up_ssh_public_key_path = memnew(LineEdit); set_up_ssh_public_key_path->set_h_size_flags(Control::SIZE_EXPAND_FILL); - set_up_ssh_public_key_path->set_text(EDITOR_DEF("version_control/ssh_public_key_path", "")); + set_up_ssh_public_key_path->set_text(EDITOR_GET("version_control/ssh_public_key_path")); set_up_ssh_public_key_path->connect(SceneStringName(text_changed), callable_mp(this, &VersionControlEditorPlugin::_update_set_up_warning)); set_up_ssh_public_key_input_hbc->add_child(set_up_ssh_public_key_path); @@ -1101,7 +1101,7 @@ VersionControlEditorPlugin::VersionControlEditorPlugin() { set_up_ssh_private_key_path = memnew(LineEdit); set_up_ssh_private_key_path->set_h_size_flags(Control::SIZE_EXPAND_FILL); - set_up_ssh_private_key_path->set_text(EDITOR_DEF("version_control/ssh_private_key_path", "")); + set_up_ssh_private_key_path->set_text(EDITOR_GET("version_control/ssh_private_key_path")); set_up_ssh_private_key_path->connect(SceneStringName(text_changed), callable_mp(this, &VersionControlEditorPlugin::_update_set_up_warning)); set_up_ssh_private_key_input_hbc->add_child(set_up_ssh_private_key_path); diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index 9f56c586a2..62706a6ae8 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -4683,10 +4683,6 @@ SceneTreeDock::SceneTreeDock(Node *p_scene_root, EditorSelection *p_editor_selec set_process_input(true); set_process(true); - EDITOR_DEF("interface/editors/show_scene_tree_root_selection", true); - EDITOR_DEF("interface/editors/derive_script_globals_by_name", true); - EDITOR_DEF("docks/scene_tree/ask_before_deleting_related_animation_tracks", true); - EDITOR_DEF("docks/scene_tree/ask_before_revoking_unique_name", true); EDITOR_DEF("_use_favorites_root_selection", false); Resource::_update_configuration_warning = _update_configuration_warning; diff --git a/main/main.cpp b/main/main.cpp index af0d7b5804..9c9542325e 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -2550,7 +2550,6 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph // XR project settings. GLOBAL_DEF_RST_BASIC("xr/openxr/enabled", false); - GLOBAL_DEF_RST_BASIC("xr/openxr/enabled.editor", false); GLOBAL_DEF_BASIC(PropertyInfo(Variant::STRING, "xr/openxr/default_action_map", PROPERTY_HINT_FILE, "*.tres"), "res://openxr_action_map.tres"); GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "xr/openxr/form_factor", PROPERTY_HINT_ENUM, "Head Mounted,Handheld"), "0"); GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "xr/openxr/view_configuration", PROPERTY_HINT_ENUM, "Mono,Stereo"), "1"); // "Mono,Stereo,Quad,Observer" diff --git a/modules/csg/editor/csg_gizmos.cpp b/modules/csg/editor/csg_gizmos.cpp index 3712441e51..95ffeed6c3 100644 --- a/modules/csg/editor/csg_gizmos.cpp +++ b/modules/csg/editor/csg_gizmos.cpp @@ -173,7 +173,7 @@ CSGShapeEditor::CSGShapeEditor() { CSGShape3DGizmoPlugin::CSGShape3DGizmoPlugin() { helper.instantiate(); - Color gizmo_color = EDITOR_DEF_RST("editors/3d_gizmos/gizmo_colors/csg", Color(0.0, 0.4, 1, 0.15)); + Color gizmo_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/csg"); create_material("shape_union_material", gizmo_color); create_material("shape_union_solid_material", gizmo_color); gizmo_color.invert(); diff --git a/modules/fbx/fbx_document.cpp b/modules/fbx/fbx_document.cpp index 8d4d0234da..ce097092fb 100644 --- a/modules/fbx/fbx_document.cpp +++ b/modules/fbx/fbx_document.cpp @@ -288,14 +288,8 @@ String FBXDocument::_gen_unique_name(HashSet<String> &unique_names, const String } String FBXDocument::_sanitize_animation_name(const String &p_name) { - // Animations disallow the normal node invalid characters as well as "," and "[" - // (See animation/animation_player.cpp::add_animation) - - // TODO: Consider adding invalid_characters or a validate_animation_name to animation_player to mirror Node. String anim_name = p_name.validate_node_name(); - anim_name = anim_name.replace(",", ""); - anim_name = anim_name.replace("[", ""); - return anim_name; + return AnimationLibrary::validate_library_name(anim_name); } String FBXDocument::_gen_unique_animation_name(Ref<FBXState> p_state, const String &p_name) { diff --git a/modules/gdscript/editor/gdscript_highlighter.cpp b/modules/gdscript/editor/gdscript_highlighter.cpp index eecce70202..d765cfa1ea 100644 --- a/modules/gdscript/editor/gdscript_highlighter.cpp +++ b/modules/gdscript/editor/gdscript_highlighter.cpp @@ -852,6 +852,7 @@ void GDScriptSyntaxHighlighter::_update_cache() { comment_marker_colors[COMMENT_MARKER_NOTICE] = Color(0.24, 0.54, 0.09); } + // TODO: Move to editor_settings.cpp EDITOR_DEF("text_editor/theme/highlighting/gdscript/function_definition_color", function_definition_color); EDITOR_DEF("text_editor/theme/highlighting/gdscript/global_function_color", global_function_color); EDITOR_DEF("text_editor/theme/highlighting/gdscript/node_path_color", node_path_color); diff --git a/modules/gdscript/language_server/gdscript_language_server.cpp b/modules/gdscript/language_server/gdscript_language_server.cpp index 3df26ea576..731988148d 100644 --- a/modules/gdscript/language_server/gdscript_language_server.cpp +++ b/modules/gdscript/language_server/gdscript_language_server.cpp @@ -39,6 +39,7 @@ int GDScriptLanguageServer::port_override = -1; GDScriptLanguageServer::GDScriptLanguageServer() { + // TODO: Move to editor_settings.cpp _EDITOR_DEF("network/language_server/remote_host", host); _EDITOR_DEF("network/language_server/remote_port", port); _EDITOR_DEF("network/language_server/enable_smart_resolve", true); diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp index 4653df7afe..cf1a1ea4b3 100644 --- a/modules/gltf/gltf_document.cpp +++ b/modules/gltf/gltf_document.cpp @@ -491,14 +491,8 @@ String GLTFDocument::_gen_unique_name(Ref<GLTFState> p_state, const String &p_na } String GLTFDocument::_sanitize_animation_name(const String &p_name) { - // Animations disallow the normal node invalid characters as well as "," and "[" - // (See animation/animation_player.cpp::add_animation) - - // TODO: Consider adding invalid_characters or a validate_animation_name to animation_player to mirror Node. String anim_name = p_name.validate_node_name(); - anim_name = anim_name.replace(",", ""); - anim_name = anim_name.replace("[", ""); - return anim_name; + return AnimationLibrary::validate_library_name(anim_name); } String GLTFDocument::_gen_unique_animation_name(Ref<GLTFState> p_state, const String &p_name) { diff --git a/modules/gridmap/editor/grid_map_editor_plugin.cpp b/modules/gridmap/editor/grid_map_editor_plugin.cpp index 6b010335e6..1c658b9e9b 100644 --- a/modules/gridmap/editor/grid_map_editor_plugin.cpp +++ b/modules/gridmap/editor/grid_map_editor_plugin.cpp @@ -1199,7 +1199,7 @@ GridMapEditor::GridMapEditor() { ED_SHORTCUT("grid_map/clear_selection", TTR("Clear Selection"), Key::KEY_DELETE); ED_SHORTCUT("grid_map/fill_selection", TTR("Fill Selection"), KeyModifierMask::CTRL + Key::F); - int mw = EDITOR_DEF("editors/grid_map/palette_min_width", 230); + int mw = EDITOR_GET("editors/grid_map/palette_min_width"); Control *ec = memnew(Control); ec->set_custom_minimum_size(Size2(mw, 0) * EDSCALE); add_child(ec); @@ -1309,8 +1309,6 @@ GridMapEditor::GridMapEditor() { size_slider->connect(SceneStringName(value_changed), callable_mp(this, &GridMapEditor::_icon_size_changed)); add_child(size_slider); - EDITOR_DEF("editors/grid_map/preview_size", 64); - mesh_library_palette = memnew(ItemList); mesh_library_palette->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED); add_child(mesh_library_palette); @@ -1533,9 +1531,6 @@ void GridMapEditorPlugin::make_visible(bool p_visible) { } GridMapEditorPlugin::GridMapEditorPlugin() { - EDITOR_DEF("editors/grid_map/editor_side", 1); - EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::INT, "editors/grid_map/editor_side", PROPERTY_HINT_ENUM, "Left,Right")); - grid_map_editor = memnew(GridMapEditor); switch ((int)EDITOR_GET("editors/grid_map/editor_side")) { case 0: { // Left. diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index 177859f270..5d59c33636 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -1524,9 +1524,10 @@ void CSharpInstance::get_property_list(List<PropertyInfo> *p_properties) const { } } + props.reverse(); for (PropertyInfo &prop : props) { validate_property(prop); - p_properties->push_back(prop); + p_properties->push_front(prop); } } diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp index 2ec073e4fa..3222c58c4e 100644 --- a/modules/mono/editor/bindings_generator.cpp +++ b/modules/mono/editor/bindings_generator.cpp @@ -3448,6 +3448,12 @@ StringName BindingsGenerator::_get_int_type_name_from_meta(GodotTypeInfo::Metada case GodotTypeInfo::METADATA_INT_IS_UINT64: return "ulong"; break; + case GodotTypeInfo::METADATA_INT_IS_CHAR16: + return "char"; + break; + case GodotTypeInfo::METADATA_INT_IS_CHAR32: + // To prevent breaking compatibility, C# bindings need to keep using `long`. + return "long"; default: // Assume INT64 return "long"; diff --git a/modules/mono/godotsharp_dirs.cpp b/modules/mono/godotsharp_dirs.cpp index 3935854a29..039263b405 100644 --- a/modules/mono/godotsharp_dirs.cpp +++ b/modules/mono/godotsharp_dirs.cpp @@ -192,8 +192,14 @@ private: } } if (!has_data) { - // 3. Extract the data to a temporary location to load from there. - Ref<DirAccess> da = DirAccess::create_for_path(packed_path); + // 3. Extract the data to a temporary location to load from there, delete old data if it exists but is not up-to-date. + Ref<DirAccess> da; + if (DirAccess::exists(data_dir_root)) { + da = DirAccess::open(data_dir_root); + ERR_FAIL_COND(da.is_null()); + ERR_FAIL_COND(da->erase_contents_recursive() != OK); + } + da = DirAccess::create_for_path(packed_path); ERR_FAIL_COND(da.is_null()); ERR_FAIL_COND(da->copy_dir(packed_path, data_dir_root) != OK); } diff --git a/modules/multiplayer/editor/editor_network_profiler.cpp b/modules/multiplayer/editor/editor_network_profiler.cpp index 212fd1ef6b..3a51712c70 100644 --- a/modules/multiplayer/editor/editor_network_profiler.cpp +++ b/modules/multiplayer/editor/editor_network_profiler.cpp @@ -34,6 +34,7 @@ #include "editor/editor_settings.h" #include "editor/editor_string_names.h" #include "editor/themes/editor_scale.h" +#include "scene/gui/check_box.h" void EditorNetworkProfiler::_bind_methods() { ADD_SIGNAL(MethodInfo("enable_profiling", PropertyInfo(Variant::BOOL, "enable"))); @@ -170,15 +171,42 @@ void EditorNetworkProfiler::add_node_data(const NodeInfo &p_info) { } void EditorNetworkProfiler::_activate_pressed() { + _update_button_text(); + if (activate->is_pressed()) { refresh_timer->start(); + } else { + refresh_timer->stop(); + } + + emit_signal(SNAME("enable_profiling"), activate->is_pressed()); +} + +void EditorNetworkProfiler::_update_button_text() { + if (activate->is_pressed()) { activate->set_icon(theme_cache.stop_icon); activate->set_text(TTR("Stop")); } else { - refresh_timer->stop(); activate->set_icon(theme_cache.play_icon); activate->set_text(TTR("Start")); } +} + +void EditorNetworkProfiler::started() { + if (EditorSettings::get_singleton()->get_project_metadata("debug_options", "autostart_network_profiler", false)) { + set_profiling(true); + refresh_timer->start(); + } +} + +void EditorNetworkProfiler::stopped() { + set_profiling(false); + refresh_timer->stop(); +} + +void EditorNetworkProfiler::set_profiling(bool p_pressed) { + activate->set_pressed(p_pressed); + _update_button_text(); emit_signal(SNAME("enable_profiling"), activate->is_pressed()); } @@ -192,6 +220,10 @@ void EditorNetworkProfiler::_clear_pressed() { refresh_replication_data(); } +void EditorNetworkProfiler::_autostart_toggled(bool p_toggled_on) { + EditorSettings::get_singleton()->set_project_metadata("debug_options", "autostart_network_profiler", p_toggled_on); +} + void EditorNetworkProfiler::_replication_button_clicked(TreeItem *p_item, int p_column, int p_idx, MouseButton p_button) { if (!p_item) { return; @@ -268,6 +300,12 @@ EditorNetworkProfiler::EditorNetworkProfiler() { clear_button->connect(SceneStringName(pressed), callable_mp(this, &EditorNetworkProfiler::_clear_pressed)); hb->add_child(clear_button); + CheckBox *autostart_checkbox = memnew(CheckBox); + autostart_checkbox->set_text(TTR("Autostart")); + autostart_checkbox->set_pressed(EditorSettings::get_singleton()->get_project_metadata("debug_options", "autostart_network_profiler", false)); + autostart_checkbox->connect(SceneStringName(toggled), callable_mp(this, &EditorNetworkProfiler::_autostart_toggled)); + hb->add_child(autostart_checkbox); + hb->add_spacer(); Label *lb = memnew(Label); diff --git a/modules/multiplayer/editor/editor_network_profiler.h b/modules/multiplayer/editor/editor_network_profiler.h index b4f8ffa724..46931c9fc9 100644 --- a/modules/multiplayer/editor/editor_network_profiler.h +++ b/modules/multiplayer/editor/editor_network_profiler.h @@ -92,7 +92,9 @@ private: void _activate_pressed(); void _clear_pressed(); + void _autostart_toggled(bool p_toggled_on); void _refresh(); + void _update_button_text(); void _replication_button_clicked(TreeItem *p_item, int p_column, int p_idx, MouseButton p_button); protected: @@ -112,6 +114,10 @@ public: void set_bandwidth(int p_incoming, int p_outgoing); bool is_profiling(); + void set_profiling(bool p_pressed); + void started(); + void stopped(); + EditorNetworkProfiler(); }; diff --git a/modules/multiplayer/editor/multiplayer_editor_plugin.cpp b/modules/multiplayer/editor/multiplayer_editor_plugin.cpp index a496f5dfa2..817d503aec 100644 --- a/modules/multiplayer/editor/multiplayer_editor_plugin.cpp +++ b/modules/multiplayer/editor/multiplayer_editor_plugin.cpp @@ -106,6 +106,8 @@ void MultiplayerEditorDebugger::setup_session(int p_session_id) { profiler->connect("enable_profiling", callable_mp(this, &MultiplayerEditorDebugger::_profiler_activate).bind(p_session_id)); profiler->connect("open_request", callable_mp(this, &MultiplayerEditorDebugger::_open_request)); profiler->set_name(TTR("Network Profiler")); + session->connect("started", callable_mp(profiler, &EditorNetworkProfiler::started)); + session->connect("stopped", callable_mp(profiler, &EditorNetworkProfiler::stopped)); session->add_session_tab(profiler); profilers[p_session_id] = profiler; } diff --git a/modules/multiplayer/scene_rpc_interface.cpp b/modules/multiplayer/scene_rpc_interface.cpp index 592bb18a71..69bb19c01c 100644 --- a/modules/multiplayer/scene_rpc_interface.cpp +++ b/modules/multiplayer/scene_rpc_interface.cpp @@ -73,6 +73,16 @@ int get_packet_len(uint32_t p_node_target, int p_packet_len) { } } +bool SceneRPCInterface::_sort_rpc_names(const Variant &p_l, const Variant &p_r) { + if (likely(p_l.is_string() && p_r.is_string())) { + return p_l.operator String() < p_r.operator String(); + } + bool valid = false; + Variant res; + Variant::evaluate(Variant::OP_LESS, p_l, p_r, res, valid); + return valid ? res.operator bool() : false; +} + void SceneRPCInterface::_parse_rpc_config(const Variant &p_config, bool p_for_node, RPCConfigCache &r_cache) { if (p_config.get_type() == Variant::NIL) { return; @@ -80,7 +90,7 @@ void SceneRPCInterface::_parse_rpc_config(const Variant &p_config, bool p_for_no ERR_FAIL_COND(p_config.get_type() != Variant::DICTIONARY); const Dictionary config = p_config; Array names = config.keys(); - names.sort(); // Ensure ID order + names.sort_custom(callable_mp_static(&SceneRPCInterface::_sort_rpc_names)); // Ensure ID order for (int i = 0; i < names.size(); i++) { ERR_CONTINUE(!names[i].is_string()); String name = names[i].operator String(); diff --git a/modules/multiplayer/scene_rpc_interface.h b/modules/multiplayer/scene_rpc_interface.h index 5c9b66d5f5..852cef7830 100644 --- a/modules/multiplayer/scene_rpc_interface.h +++ b/modules/multiplayer/scene_rpc_interface.h @@ -91,6 +91,8 @@ private: #endif protected: + static bool _sort_rpc_names(const Variant &p_l, const Variant &p_r); + void _process_rpc(Node *p_node, const uint16_t p_rpc_method_id, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset); void _send_rpc(Node *p_from, int p_to, uint16_t p_rpc_id, const RPCConfig &p_config, const StringName &p_name, const Variant **p_arg, int p_argcount); diff --git a/modules/openxr/doc_classes/OpenXRCompositionLayer.xml b/modules/openxr/doc_classes/OpenXRCompositionLayer.xml index 6f4ac00f3e..341b50065c 100644 --- a/modules/openxr/doc_classes/OpenXRCompositionLayer.xml +++ b/modules/openxr/doc_classes/OpenXRCompositionLayer.xml @@ -10,6 +10,13 @@ <tutorials> </tutorials> <methods> + <method name="get_android_surface"> + <return type="JavaObject" /> + <description> + Returns a [JavaObject] representing an [code]android.view.Surface[/code] if [member use_android_surface] is enabled and OpenXR has created the surface. Otherwise, this will return [code]null[/code]. + [b]Note:[/b] The surface can only be created during an active OpenXR session. So, if [member use_android_surface] is enabled outside of an OpenXR session, it won't be created until a new session fully starts. + </description> + </method> <method name="intersects_ray" qualifiers="const"> <return type="Vector2" /> <param index="0" name="origin" type="Vector3" /> @@ -32,6 +39,9 @@ Enables the blending the layer using its alpha channel. Can be combined with [member Viewport.transparent_bg] to give the layer a transparent background. </member> + <member name="android_surface_size" type="Vector2i" setter="set_android_surface_size" getter="get_android_surface_size" default="Vector2i(1024, 1024)"> + The size of the Android surface to create if [member use_android_surface] is enabled. + </member> <member name="enable_hole_punch" type="bool" setter="set_enable_hole_punch" getter="get_enable_hole_punch" default="false"> Enables a technique called "hole punching", which allows putting the composition layer behind the main projection layer (i.e. setting [member sort_order] to a negative value) while "punching a hole" through everything rendered by Godot so that the layer is still visible. This can be used to create the illusion that the composition layer exists in the same 3D space as everything rendered by Godot, allowing objects to appear to pass both behind or in front of the composition layer. @@ -43,5 +53,10 @@ The sort order for this composition layer. Higher numbers will be shown in front of lower numbers. [b]Note:[/b] This will have no effect if a fallback mesh is being used. </member> + <member name="use_android_surface" type="bool" setter="set_use_android_surface" getter="get_use_android_surface" default="false"> + If enabled, an Android surface will be created (with the dimensions from [member android_surface_size]) which will provide the 2D content for the composition layer, rather than using [member layer_viewport]. + See [method get_android_surface] for information about how to get the surface so that your application can draw to it. + [b]Note:[/b] This will only work in Android builds. + </member> </members> </class> diff --git a/modules/openxr/editor/openxr_select_runtime.cpp b/modules/openxr/editor/openxr_select_runtime.cpp index 4d95b079e2..4a2a87cb88 100644 --- a/modules/openxr/editor/openxr_select_runtime.cpp +++ b/modules/openxr/editor/openxr_select_runtime.cpp @@ -119,6 +119,7 @@ OpenXRSelectRuntime::OpenXRSelectRuntime() { default_runtimes["SteamVR"] = "~/.steam/steam/steamapps/common/SteamVR/steamxr_linux64.json"; #endif + // TODO: Move to editor_settings.cpp EDITOR_DEF_RST("xr/openxr/runtime_paths", default_runtimes); set_flat(true); diff --git a/modules/openxr/extensions/openxr_composition_layer_extension.cpp b/modules/openxr/extensions/openxr_composition_layer_extension.cpp index 8a448afc08..83e45ffe7f 100644 --- a/modules/openxr/extensions/openxr_composition_layer_extension.cpp +++ b/modules/openxr/extensions/openxr_composition_layer_extension.cpp @@ -30,6 +30,12 @@ #include "openxr_composition_layer_extension.h" +#ifdef ANDROID_ENABLED +#include <openxr/openxr.h> +#include <openxr/openxr_platform.h> +#endif + +#include "platform/android/api/java_class_wrapper.h" #include "servers/rendering/rendering_server_globals.h" //////////////////////////////////////////////////////////////////////////// @@ -55,18 +61,37 @@ HashMap<String, bool *> OpenXRCompositionLayerExtension::get_requested_extension request_extensions[XR_KHR_COMPOSITION_LAYER_CYLINDER_EXTENSION_NAME] = &cylinder_ext_available; request_extensions[XR_KHR_COMPOSITION_LAYER_EQUIRECT2_EXTENSION_NAME] = &equirect_ext_available; +#ifdef ANDROID_ENABLED + request_extensions[XR_KHR_ANDROID_SURFACE_SWAPCHAIN_EXTENSION_NAME] = &android_surface_ext_available; +#endif + return request_extensions; } +void OpenXRCompositionLayerExtension::on_instance_created(const XrInstance p_instance) { +#ifdef ANDROID_ENABLED + EXT_INIT_XR_FUNC(xrDestroySwapchain); + EXT_INIT_XR_FUNC(xrCreateSwapchainAndroidSurfaceKHR); +#endif +} + void OpenXRCompositionLayerExtension::on_session_created(const XrSession p_session) { OpenXRAPI::get_singleton()->register_composition_layer_provider(this); } void OpenXRCompositionLayerExtension::on_session_destroyed() { OpenXRAPI::get_singleton()->unregister_composition_layer_provider(this); + +#ifdef ANDROID_ENABLED + free_queued_android_surface_swapchains(); +#endif } void OpenXRCompositionLayerExtension::on_pre_render() { +#ifdef ANDROID_ENABLED + free_queued_android_surface_swapchains(); +#endif + for (OpenXRViewportCompositionLayerProvider *composition_layer : composition_layers) { composition_layer->on_pre_render(); } @@ -113,6 +138,37 @@ bool OpenXRCompositionLayerExtension::is_available(XrStructureType p_which) { } } +#ifdef ANDROID_ENABLED +bool OpenXRCompositionLayerExtension::create_android_surface_swapchain(XrSwapchainCreateInfo *p_info, XrSwapchain *r_swapchain, jobject *r_surface) { + if (android_surface_ext_available) { + OpenXRAPI *openxr_api = OpenXRAPI::get_singleton(); + ERR_FAIL_NULL_V(openxr_api, false); + + // @todo We need a way to add to the next pointer chain. + XrResult result = xrCreateSwapchainAndroidSurfaceKHR(openxr_api->get_session(), p_info, r_swapchain, r_surface); + if (XR_FAILED(result)) { + print_line("OpenXR: Failed to create Android surface swapchain [", openxr_api->get_error_string(result), "]"); + return false; + } + + return true; + } + + return false; +} + +void OpenXRCompositionLayerExtension::free_android_surface_swapchain(XrSwapchain p_swapchain) { + android_surface_swapchain_free_queue.push_back(p_swapchain); +} + +void OpenXRCompositionLayerExtension::free_queued_android_surface_swapchains() { + for (XrSwapchain swapchain : android_surface_swapchain_free_queue) { + xrDestroySwapchain(swapchain); + } + android_surface_swapchain_free_queue.clear(); +} +#endif + //////////////////////////////////////////////////////////////////////////// // OpenXRViewportCompositionLayerProvider @@ -127,8 +183,12 @@ OpenXRViewportCompositionLayerProvider::~OpenXRViewportCompositionLayerProvider( extension->on_viewport_composition_layer_destroyed(composition_layer); } - // This will reset the viewport and free the swapchain too. - set_viewport(RID(), Size2i()); + if (use_android_surface) { + free_swapchain(); + } else { + // This will reset the viewport and free the swapchain too. + set_viewport(RID(), Size2i()); + } } void OpenXRViewportCompositionLayerProvider::set_alpha_blend(bool p_alpha_blend) { @@ -143,24 +203,92 @@ void OpenXRViewportCompositionLayerProvider::set_alpha_blend(bool p_alpha_blend) } void OpenXRViewportCompositionLayerProvider::set_viewport(RID p_viewport, Size2i p_size) { + ERR_FAIL_COND(use_android_surface); + RenderingServer *rs = RenderingServer::get_singleton(); ERR_FAIL_NULL(rs); - if (viewport != p_viewport) { - if (viewport.is_valid()) { - RID rt = rs->viewport_get_render_target(viewport); + if (subviewport.viewport != p_viewport) { + if (subviewport.viewport.is_valid()) { + RID rt = rs->viewport_get_render_target(subviewport.viewport); RSG::texture_storage->render_target_set_override(rt, RID(), RID(), RID()); } - viewport = p_viewport; + subviewport.viewport = p_viewport; - if (viewport.is_valid()) { - viewport_size = p_size; + if (subviewport.viewport.is_valid()) { + subviewport.viewport_size = p_size; } else { free_swapchain(); - viewport_size = Size2i(); + subviewport.viewport_size = Size2i(); + } + } +} + +void OpenXRViewportCompositionLayerProvider::set_use_android_surface(bool p_use_android_surface, Size2i p_size) { +#ifdef ANDROID_ENABLED + if (p_use_android_surface == use_android_surface) { + return; + } + + use_android_surface = p_use_android_surface; + + if (use_android_surface) { + if (!composition_layer_extension->is_android_surface_swapchain_available()) { + ERR_PRINT_ONCE("OpenXR: Cannot use Android surface for composition layer because the extension isn't available"); } + + if (subviewport.viewport.is_valid()) { + set_viewport(RID(), Size2i()); + } + + swapchain_size = p_size; + } else { + free_swapchain(); + } +#endif +} + +#ifdef ANDROID_ENABLED +void OpenXRViewportCompositionLayerProvider::create_android_surface() { + ERR_FAIL_COND(android_surface.swapchain != XR_NULL_HANDLE || android_surface.surface.is_valid()); + ERR_FAIL_COND(!openxr_api || !openxr_api->is_running()); + + // The XR_FB_android_surface_swapchain_create extension mandates that format, sampleCount, + // faceCount, arraySize, and mipCount must be zero. + XrSwapchainCreateInfo info = { + XR_TYPE_SWAPCHAIN_CREATE_INFO, // type + nullptr, // next + 0, // createFlags + XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT | XR_SWAPCHAIN_USAGE_MUTABLE_FORMAT_BIT, // usageFlags + 0, // format + 0, // sampleCount + (uint32_t)swapchain_size.x, // width + (uint32_t)swapchain_size.y, // height + 0, // faceCount + 0, // arraySize + 0, // mipCount + }; + + jobject surface; + composition_layer_extension->create_android_surface_swapchain(&info, &android_surface.swapchain, &surface); + + if (surface) { + android_surface.surface = Ref<JavaObject>(memnew(JavaObject(JavaClassWrapper::get_singleton()->wrap("android.view.Surface"), surface))); + } +} +#endif + +Ref<JavaObject> OpenXRViewportCompositionLayerProvider::get_android_surface() { +#ifdef ANDROID_ENABLED + if (use_android_surface) { + if (android_surface.surface.is_null()) { + create_android_surface(); + } + return android_surface.surface; } +#endif + return Ref<JavaObject>(); } void OpenXRViewportCompositionLayerProvider::set_extension_property_values(const Dictionary &p_extension_property_values) { @@ -169,16 +297,25 @@ void OpenXRViewportCompositionLayerProvider::set_extension_property_values(const } void OpenXRViewportCompositionLayerProvider::on_pre_render() { +#ifdef ANDROID_ENABLED + if (use_android_surface) { + if (android_surface.surface.is_null()) { + create_android_surface(); + } + return; + } +#endif + RenderingServer *rs = RenderingServer::get_singleton(); ERR_FAIL_NULL(rs); - if (viewport.is_valid() && openxr_api && openxr_api->is_running()) { - RS::ViewportUpdateMode update_mode = rs->viewport_get_update_mode(viewport); + if (subviewport.viewport.is_valid() && openxr_api && openxr_api->is_running()) { + RS::ViewportUpdateMode update_mode = rs->viewport_get_update_mode(subviewport.viewport); if (update_mode == RS::VIEWPORT_UPDATE_ONCE || update_mode == RS::VIEWPORT_UPDATE_ALWAYS) { // Update our XR swapchain if (update_and_acquire_swapchain(update_mode == RS::VIEWPORT_UPDATE_ONCE)) { // Render to our XR swapchain image. - RID rt = rs->viewport_get_render_target(viewport); + RID rt = rs->viewport_get_render_target(subviewport.viewport); RSG::texture_storage->render_target_set_override(rt, get_current_swapchain_texture(), RID(), RID()); } } @@ -196,48 +333,36 @@ XrCompositionLayerBaseHeader *OpenXRViewportCompositionLayerProvider::get_compos return nullptr; } - if (swapchain_info.get_swapchain() == XR_NULL_HANDLE) { + XrSwapchainSubImage subimage = { + 0, // swapchain + { { 0, 0 }, { 0, 0 } }, // imageRect + 0, // imageArrayIndex + }; + update_swapchain_sub_image(subimage); + + if (subimage.swapchain == XR_NULL_HANDLE) { // Don't have a swapchain to display? Ignore our layer. return nullptr; } - if (swapchain_info.is_image_acquired()) { - swapchain_info.release(); - } - // Update the layer struct for the swapchain. switch (composition_layer->type) { case XR_TYPE_COMPOSITION_LAYER_QUAD: { XrCompositionLayerQuad *quad_layer = (XrCompositionLayerQuad *)composition_layer; quad_layer->space = openxr_api->get_play_space(); - quad_layer->subImage.swapchain = swapchain_info.get_swapchain(); - quad_layer->subImage.imageArrayIndex = 0; - quad_layer->subImage.imageRect.offset.x = 0; - quad_layer->subImage.imageRect.offset.y = 0; - quad_layer->subImage.imageRect.extent.width = swapchain_size.width; - quad_layer->subImage.imageRect.extent.height = swapchain_size.height; + quad_layer->subImage = subimage; } break; case XR_TYPE_COMPOSITION_LAYER_CYLINDER_KHR: { XrCompositionLayerCylinderKHR *cylinder_layer = (XrCompositionLayerCylinderKHR *)composition_layer; cylinder_layer->space = openxr_api->get_play_space(); - cylinder_layer->subImage.swapchain = swapchain_info.get_swapchain(); - cylinder_layer->subImage.imageArrayIndex = 0; - cylinder_layer->subImage.imageRect.offset.x = 0; - cylinder_layer->subImage.imageRect.offset.y = 0; - cylinder_layer->subImage.imageRect.extent.width = swapchain_size.width; - cylinder_layer->subImage.imageRect.extent.height = swapchain_size.height; + cylinder_layer->subImage = subimage; } break; case XR_TYPE_COMPOSITION_LAYER_EQUIRECT2_KHR: { XrCompositionLayerEquirect2KHR *equirect_layer = (XrCompositionLayerEquirect2KHR *)composition_layer; equirect_layer->space = openxr_api->get_play_space(); - equirect_layer->subImage.swapchain = swapchain_info.get_swapchain(); - equirect_layer->subImage.imageArrayIndex = 0; - equirect_layer->subImage.imageRect.offset.x = 0; - equirect_layer->subImage.imageRect.offset.y = 0; - equirect_layer->subImage.imageRect.extent.width = swapchain_size.width; - equirect_layer->subImage.imageRect.extent.height = swapchain_size.height; + equirect_layer->subImage = subimage; } break; default: { @@ -261,27 +386,49 @@ XrCompositionLayerBaseHeader *OpenXRViewportCompositionLayerProvider::get_compos return composition_layer; } +void OpenXRViewportCompositionLayerProvider::update_swapchain_sub_image(XrSwapchainSubImage &r_subimage) { +#ifdef ANDROID_ENABLED + if (use_android_surface) { + r_subimage.swapchain = android_surface.swapchain; + } else +#endif + { + XrSwapchain swapchain = subviewport.swapchain_info.get_swapchain(); + + if (swapchain && subviewport.swapchain_info.is_image_acquired()) { + subviewport.swapchain_info.release(); + } + + r_subimage.swapchain = swapchain; + } + + r_subimage.imageRect.extent.width = swapchain_size.width; + r_subimage.imageRect.extent.height = swapchain_size.height; +} + bool OpenXRViewportCompositionLayerProvider::update_and_acquire_swapchain(bool p_static_image) { + ERR_FAIL_COND_V(use_android_surface, false); + if (openxr_api == nullptr || composition_layer_extension == nullptr) { // OpenXR not initialized or we're in the editor? return false; } - if (!composition_layer_extension->is_available(composition_layer->type)) { + if (!composition_layer_extension->is_available(get_openxr_type())) { // Selected type is not supported? return false; } // See if our current swapchain is outdated. - if (swapchain_info.get_swapchain() != XR_NULL_HANDLE) { + if (subviewport.swapchain_info.get_swapchain() != XR_NULL_HANDLE) { // If this swap chain, or the previous one, were static, then we can't reuse it. - if (swapchain_size == viewport_size && !p_static_image && !static_image) { + if (swapchain_size == subviewport.viewport_size && !p_static_image && !subviewport.static_image) { // We're all good! Just acquire it. // We can ignore should_render here, return will be false. bool should_render = true; - return swapchain_info.acquire(should_render); + return subviewport.swapchain_info.acquire(should_render); } - swapchain_info.queue_free(); + subviewport.swapchain_info.queue_free(); } // Create our new swap chain @@ -292,7 +439,7 @@ bool OpenXRViewportCompositionLayerProvider::update_and_acquire_swapchain(bool p if (p_static_image) { create_flags |= XR_SWAPCHAIN_CREATE_STATIC_IMAGE_BIT; } - if (!swapchain_info.create(create_flags, XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT | XR_SWAPCHAIN_USAGE_MUTABLE_FORMAT_BIT, swapchain_format, viewport_size.width, viewport_size.height, sample_count, array_size)) { + if (!subviewport.swapchain_info.create(create_flags, XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT | XR_SWAPCHAIN_USAGE_MUTABLE_FORMAT_BIT, swapchain_format, subviewport.viewport_size.width, subviewport.viewport_size.height, sample_count, array_size)) { swapchain_size = Size2i(); return false; } @@ -300,26 +447,40 @@ bool OpenXRViewportCompositionLayerProvider::update_and_acquire_swapchain(bool p // Acquire our image so we can start rendering into it, // we can ignore should_render here, ret will be false. bool should_render = true; - bool ret = swapchain_info.acquire(should_render); + bool ret = subviewport.swapchain_info.acquire(should_render); - swapchain_size = viewport_size; - static_image = p_static_image; + swapchain_size = subviewport.viewport_size; + subviewport.static_image = p_static_image; return ret; } void OpenXRViewportCompositionLayerProvider::free_swapchain() { - if (swapchain_info.get_swapchain() != XR_NULL_HANDLE) { - swapchain_info.queue_free(); +#ifdef ANDROID_ENABLED + if (use_android_surface) { + if (android_surface.swapchain != XR_NULL_HANDLE) { + composition_layer_extension->free_android_surface_swapchain(android_surface.swapchain); + + android_surface.swapchain = XR_NULL_HANDLE; + android_surface.surface.unref(); + } + } else +#endif + { + if (subviewport.swapchain_info.get_swapchain() != XR_NULL_HANDLE) { + subviewport.swapchain_info.queue_free(); + } + subviewport.static_image = false; } swapchain_size = Size2i(); - static_image = false; } RID OpenXRViewportCompositionLayerProvider::get_current_swapchain_texture() { + ERR_FAIL_COND_V(use_android_surface, RID()); + if (openxr_api == nullptr) { return RID(); } - return swapchain_info.get_image(); + return subviewport.swapchain_info.get_image(); } diff --git a/modules/openxr/extensions/openxr_composition_layer_extension.h b/modules/openxr/extensions/openxr_composition_layer_extension.h index 34e330a60a..bce34f098c 100644 --- a/modules/openxr/extensions/openxr_composition_layer_extension.h +++ b/modules/openxr/extensions/openxr_composition_layer_extension.h @@ -36,6 +36,15 @@ #include "../openxr_api.h" +#ifdef ANDROID_ENABLED +#include <jni.h> + +// Copied here from openxr_platform.h, in order to avoid including that whole header, +// which can cause compilation issues on some platforms. +typedef XrResult(XRAPI_PTR *PFN_xrCreateSwapchainAndroidSurfaceKHR)(XrSession session, const XrSwapchainCreateInfo *info, XrSwapchain *swapchain, jobject *surface); +#endif + +class JavaObject; class OpenXRViewportCompositionLayerProvider; // This extension provides access to composition layers for displaying 2D content through the XR compositor. @@ -49,6 +58,7 @@ public: virtual ~OpenXRCompositionLayerExtension() override; virtual HashMap<String, bool *> get_requested_extensions() override; + virtual void on_instance_created(const XrInstance p_instance) override; virtual void on_session_created(const XrSession p_session) override; virtual void on_session_destroyed() override; virtual void on_pre_render() override; @@ -61,6 +71,12 @@ public: void unregister_viewport_composition_layer_provider(OpenXRViewportCompositionLayerProvider *p_composition_layer); bool is_available(XrStructureType p_which); + bool is_android_surface_swapchain_available() { return android_surface_ext_available; } + +#ifdef ANDROID_ENABLED + bool create_android_surface_swapchain(XrSwapchainCreateInfo *p_info, XrSwapchain *r_swapchain, jobject *r_surface); + void free_android_surface_swapchain(XrSwapchain p_swapchain); +#endif private: static OpenXRCompositionLayerExtension *singleton; @@ -69,6 +85,15 @@ private: bool cylinder_ext_available = false; bool equirect_ext_available = false; + bool android_surface_ext_available = false; + +#ifdef ANDROID_ENABLED + Vector<XrSwapchain> android_surface_swapchain_free_queue; + void free_queued_android_surface_swapchains(); + + EXT_PROTO_XRRESULT_FUNC1(xrDestroySwapchain, (XrSwapchain), swapchain) + EXT_PROTO_XRRESULT_FUNC4(xrCreateSwapchainAndroidSurfaceKHR, (XrSession), session, (const XrSwapchainCreateInfo *), info, (XrSwapchain *), swapchain, (jobject *), surface) +#endif }; class OpenXRViewportCompositionLayerProvider { @@ -78,20 +103,37 @@ class OpenXRViewportCompositionLayerProvider { Dictionary extension_property_values; bool extension_property_values_changed = true; - RID viewport; - Size2i viewport_size; - - OpenXRAPI::OpenXRSwapChainInfo swapchain_info; + struct { + RID viewport; + Size2i viewport_size; + OpenXRAPI::OpenXRSwapChainInfo swapchain_info; + bool static_image = false; + } subviewport; + +#ifdef ANDROID_ENABLED + struct { + XrSwapchain swapchain = XR_NULL_HANDLE; + Ref<JavaObject> surface; + } android_surface; +#endif + + bool use_android_surface = false; Size2i swapchain_size; - bool static_image = false; OpenXRAPI *openxr_api = nullptr; OpenXRCompositionLayerExtension *composition_layer_extension = nullptr; + // Only for SubViewports. bool update_and_acquire_swapchain(bool p_static_image); - void free_swapchain(); RID get_current_swapchain_texture(); + void update_swapchain_sub_image(XrSwapchainSubImage &r_swapchain_sub_image); + void free_swapchain(); + +#ifdef ANDROID_ENABLED + void create_android_surface(); +#endif + public: XrStructureType get_openxr_type() { return composition_layer->type; } @@ -102,7 +144,12 @@ public: bool get_alpha_blend() const { return alpha_blend; } void set_viewport(RID p_viewport, Size2i p_size); - RID get_viewport() const { return viewport; } + RID get_viewport() const { return subviewport.viewport; } + + void set_use_android_surface(bool p_enable, Size2i p_size); + bool get_use_android_surface() const { return use_android_surface; } + + Ref<JavaObject> get_android_surface(); void set_extension_property_values(const Dictionary &p_property_values); diff --git a/modules/openxr/openxr_api.cpp b/modules/openxr/openxr_api.cpp index a19a75e722..73b6f6c1c9 100644 --- a/modules/openxr/openxr_api.cpp +++ b/modules/openxr/openxr_api.cpp @@ -273,7 +273,9 @@ Vector<OpenXRExtensionWrapper *> OpenXRAPI::registered_extension_wrappers; bool OpenXRAPI::openxr_is_enabled(bool p_check_run_in_editor) { if (XRServer::get_xr_mode() == XRServer::XRMODE_DEFAULT) { if (Engine::get_singleton()->is_editor_hint() && p_check_run_in_editor) { - return GLOBAL_GET("xr/openxr/enabled.editor"); + // For now, don't start OpenXR when the editor starts up. In the future, this may change + // if we want to integrate more XR features into the editor experience. + return false; } else { return GLOBAL_GET("xr/openxr/enabled"); } diff --git a/modules/openxr/scene/openxr_composition_layer.cpp b/modules/openxr/scene/openxr_composition_layer.cpp index f69a907be9..697369d516 100644 --- a/modules/openxr/scene/openxr_composition_layer.cpp +++ b/modules/openxr/scene/openxr_composition_layer.cpp @@ -38,6 +38,8 @@ #include "scene/3d/xr_nodes.h" #include "scene/main/viewport.h" +#include "platform/android/api/java_class_wrapper.h" + Vector<OpenXRCompositionLayer *> OpenXRCompositionLayer::composition_layer_nodes; static const char *HOLE_PUNCH_SHADER_CODE = @@ -47,7 +49,10 @@ static const char *HOLE_PUNCH_SHADER_CODE = "\tALBEDO = vec3(0.0, 0.0, 0.0);\n" "}\n"; -OpenXRCompositionLayer::OpenXRCompositionLayer() { +OpenXRCompositionLayer::OpenXRCompositionLayer(XrCompositionLayerBaseHeader *p_composition_layer) { + composition_layer_base_header = p_composition_layer; + openxr_layer_provider = memnew(OpenXRViewportCompositionLayerProvider(composition_layer_base_header)); + openxr_api = OpenXRAPI::get_singleton(); composition_layer_extension = OpenXRCompositionLayerExtension::get_singleton(); @@ -85,6 +90,12 @@ void OpenXRCompositionLayer::_bind_methods() { ClassDB::bind_method(D_METHOD("set_layer_viewport", "viewport"), &OpenXRCompositionLayer::set_layer_viewport); ClassDB::bind_method(D_METHOD("get_layer_viewport"), &OpenXRCompositionLayer::get_layer_viewport); + ClassDB::bind_method(D_METHOD("set_use_android_surface", "enable"), &OpenXRCompositionLayer::set_use_android_surface); + ClassDB::bind_method(D_METHOD("get_use_android_surface"), &OpenXRCompositionLayer::get_use_android_surface); + + ClassDB::bind_method(D_METHOD("set_android_surface_size", "size"), &OpenXRCompositionLayer::set_android_surface_size); + ClassDB::bind_method(D_METHOD("get_android_surface_size"), &OpenXRCompositionLayer::get_android_surface_size); + ClassDB::bind_method(D_METHOD("set_enable_hole_punch", "enable"), &OpenXRCompositionLayer::set_enable_hole_punch); ClassDB::bind_method(D_METHOD("get_enable_hole_punch"), &OpenXRCompositionLayer::get_enable_hole_punch); @@ -94,11 +105,14 @@ void OpenXRCompositionLayer::_bind_methods() { ClassDB::bind_method(D_METHOD("set_alpha_blend", "enabled"), &OpenXRCompositionLayer::set_alpha_blend); ClassDB::bind_method(D_METHOD("get_alpha_blend"), &OpenXRCompositionLayer::get_alpha_blend); + ClassDB::bind_method(D_METHOD("get_android_surface"), &OpenXRCompositionLayer::get_android_surface); ClassDB::bind_method(D_METHOD("is_natively_supported"), &OpenXRCompositionLayer::is_natively_supported); ClassDB::bind_method(D_METHOD("intersects_ray", "origin", "direction"), &OpenXRCompositionLayer::intersects_ray); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "layer_viewport", PROPERTY_HINT_NODE_TYPE, "SubViewport"), "set_layer_viewport", "get_layer_viewport"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_android_surface", PROPERTY_HINT_NONE, ""), "set_use_android_surface", "get_use_android_surface"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "android_surface_size", PROPERTY_HINT_NONE, ""), "set_android_surface_size", "get_android_surface_size"); ADD_PROPERTY(PropertyInfo(Variant::INT, "sort_order", PROPERTY_HINT_NONE, ""), "set_sort_order", "get_sort_order"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "alpha_blend", PROPERTY_HINT_NONE, ""), "set_alpha_blend", "get_alpha_blend"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enable_hole_punch", PROPERTY_HINT_NONE, ""), "set_enable_hole_punch", "get_enable_hole_punch"); @@ -108,7 +122,7 @@ bool OpenXRCompositionLayer::_should_use_fallback_node() { if (Engine::get_singleton()->is_editor_hint()) { return true; } else if (openxr_session_running) { - return enable_hole_punch || !is_natively_supported(); + return enable_hole_punch || (!is_natively_supported() && !use_android_surface); } return false; } @@ -128,10 +142,36 @@ void OpenXRCompositionLayer::_remove_fallback_node() { fallback = nullptr; } +void OpenXRCompositionLayer::_setup_composition_layer_provider() { + if (use_android_surface || layer_viewport) { + if (composition_layer_extension) { + composition_layer_extension->register_viewport_composition_layer_provider(openxr_layer_provider); + } + + // NOTE: We don't setup/clear when using Android surfaces, so we don't destroy the surface unexpectedly. + if (layer_viewport) { + // Set our properties on the layer provider, which will create all the necessary resources (ex swap chains). + openxr_layer_provider->set_viewport(layer_viewport->get_viewport_rid(), layer_viewport->get_size()); + } + } +} + +void OpenXRCompositionLayer::_clear_composition_layer_provider() { + if (composition_layer_extension) { + composition_layer_extension->unregister_viewport_composition_layer_provider(openxr_layer_provider); + } + + // NOTE: We don't setup/clear when using Android surfaces, so we don't destroy the surface unexpectedly. + if (!use_android_surface) { + // This will reset the viewport and free all the resources (ex swap chains) used by the layer. + openxr_layer_provider->set_viewport(RID(), Size2i()); + } +} + void OpenXRCompositionLayer::_on_openxr_session_begun() { openxr_session_running = true; - if (layer_viewport && is_natively_supported() && is_visible() && is_inside_tree()) { - openxr_layer_provider->set_viewport(layer_viewport->get_viewport_rid(), layer_viewport->get_size()); + if (is_natively_supported() && is_visible() && is_inside_tree()) { + _setup_composition_layer_provider(); } if (!fallback && _should_use_fallback_node()) { _create_fallback_node(); @@ -142,9 +182,8 @@ void OpenXRCompositionLayer::_on_openxr_session_stopping() { openxr_session_running = false; if (fallback && !_should_use_fallback_node()) { _remove_fallback_node(); - } else { - openxr_layer_provider->set_viewport(RID(), Size2i()); } + _clear_composition_layer_provider(); } void OpenXRCompositionLayer::update_fallback_mesh() { @@ -162,6 +201,7 @@ XrPosef OpenXRCompositionLayer::get_openxr_pose() { } bool OpenXRCompositionLayer::is_viewport_in_use(SubViewport *p_viewport) { + ERR_FAIL_NULL_V(p_viewport, false); for (const OpenXRCompositionLayer *other_composition_layer : composition_layer_nodes) { if (other_composition_layer != this && other_composition_layer->is_inside_tree() && other_composition_layer->get_layer_viewport() == p_viewport) { return true; @@ -178,6 +218,9 @@ void OpenXRCompositionLayer::set_layer_viewport(SubViewport *p_viewport) { if (p_viewport != nullptr) { ERR_FAIL_COND_EDMSG(is_viewport_in_use(p_viewport), RTR("Cannot use the same SubViewport with multiple OpenXR composition layers. Clear it from its current layer first.")); } + if (use_android_surface) { + ERR_FAIL_COND_MSG(p_viewport != nullptr, RTR("Cannot set SubViewport on an OpenXR composition layer when using an Android surface.")); + } layer_viewport = p_viewport; @@ -200,6 +243,41 @@ void OpenXRCompositionLayer::set_layer_viewport(SubViewport *p_viewport) { } } +void OpenXRCompositionLayer::set_use_android_surface(bool p_use_android_surface) { + if (use_android_surface == p_use_android_surface) { + return; + } + + use_android_surface = p_use_android_surface; + if (use_android_surface) { + set_layer_viewport(nullptr); + openxr_layer_provider->set_use_android_surface(true, android_surface_size); + } else { + openxr_layer_provider->set_use_android_surface(false, Size2i()); + } + + notify_property_list_changed(); +} + +bool OpenXRCompositionLayer::get_use_android_surface() const { + return use_android_surface; +} + +void OpenXRCompositionLayer::set_android_surface_size(Size2i p_size) { + if (android_surface_size == p_size) { + return; + } + + android_surface_size = p_size; + if (use_android_surface) { + openxr_layer_provider->set_use_android_surface(true, android_surface_size); + } +} + +Size2i OpenXRCompositionLayer::get_android_surface_size() const { + return android_surface_size; +} + SubViewport *OpenXRCompositionLayer::get_layer_viewport() const { return layer_viewport; } @@ -228,33 +306,23 @@ bool OpenXRCompositionLayer::get_enable_hole_punch() const { } void OpenXRCompositionLayer::set_sort_order(int p_order) { - if (openxr_layer_provider) { - openxr_layer_provider->set_sort_order(p_order); - update_configuration_warnings(); - } + openxr_layer_provider->set_sort_order(p_order); + update_configuration_warnings(); } int OpenXRCompositionLayer::get_sort_order() const { - if (openxr_layer_provider) { - return openxr_layer_provider->get_sort_order(); - } - return 1; + return openxr_layer_provider->get_sort_order(); } void OpenXRCompositionLayer::set_alpha_blend(bool p_alpha_blend) { - if (openxr_layer_provider) { - openxr_layer_provider->set_alpha_blend(p_alpha_blend); - if (fallback) { - _reset_fallback_material(); - } + openxr_layer_provider->set_alpha_blend(p_alpha_blend); + if (fallback) { + _reset_fallback_material(); } } bool OpenXRCompositionLayer::get_alpha_blend() const { - if (openxr_layer_provider) { - return openxr_layer_provider->get_alpha_blend(); - } - return false; + return openxr_layer_provider->get_alpha_blend(); } bool OpenXRCompositionLayer::is_natively_supported() const { @@ -264,6 +332,10 @@ bool OpenXRCompositionLayer::is_natively_supported() const { return false; } +Ref<JavaObject> OpenXRCompositionLayer::get_android_surface() { + return openxr_layer_provider->get_android_surface(); +} + Vector2 OpenXRCompositionLayer::intersects_ray(const Vector3 &p_origin, const Vector3 &p_direction) const { return Vector2(-1.0, -1.0); } @@ -301,10 +373,7 @@ void OpenXRCompositionLayer::_reset_fallback_material() { Ref<ViewportTexture> texture = material->get_texture(StandardMaterial3D::TEXTURE_ALBEDO); if (texture.is_null()) { - texture.instantiate(); - // ViewportTexture can't be configured without a local scene, so use this hack to set it. - HashMap<Ref<Resource>, Ref<Resource>> remap_cache; - texture->configure_for_local_scene(this, remap_cache); + texture = layer_viewport->get_texture(); } Node *loc_scene = texture->get_local_scene(); @@ -321,12 +390,10 @@ void OpenXRCompositionLayer::_notification(int p_what) { case NOTIFICATION_POSTINITIALIZE: { composition_layer_nodes.push_back(this); - if (openxr_layer_provider) { - for (OpenXRExtensionWrapper *extension : OpenXRAPI::get_registered_extension_wrappers()) { - extension_property_values.merge(extension->get_viewport_composition_layer_extension_property_defaults()); - } - openxr_layer_provider->set_extension_property_values(extension_property_values); + for (OpenXRExtensionWrapper *extension : OpenXRAPI::get_registered_extension_wrappers()) { + extension_property_values.merge(extension->get_viewport_composition_layer_extension_property_defaults()); } + openxr_layer_provider->set_extension_property_values(extension_property_values); } break; case NOTIFICATION_INTERNAL_PROCESS: { if (fallback) { @@ -339,10 +406,10 @@ void OpenXRCompositionLayer::_notification(int p_what) { } break; case NOTIFICATION_VISIBILITY_CHANGED: { if (!fallback && openxr_session_running && is_inside_tree()) { - if (layer_viewport && is_visible()) { - openxr_layer_provider->set_viewport(layer_viewport->get_viewport_rid(), layer_viewport->get_size()); + if (is_visible()) { + _setup_composition_layer_provider(); } else { - openxr_layer_provider->set_viewport(RID(), Size2i()); + _clear_composition_layer_provider(); } } update_configuration_warnings(); @@ -351,25 +418,15 @@ void OpenXRCompositionLayer::_notification(int p_what) { update_configuration_warnings(); } break; case NOTIFICATION_ENTER_TREE: { - if (composition_layer_extension) { - composition_layer_extension->register_viewport_composition_layer_provider(openxr_layer_provider); - } - - if (is_viewport_in_use(layer_viewport)) { - set_layer_viewport(nullptr); - } else if (!fallback && layer_viewport && openxr_session_running && is_visible()) { - openxr_layer_provider->set_viewport(layer_viewport->get_viewport_rid(), layer_viewport->get_size()); + if (layer_viewport && is_viewport_in_use(layer_viewport)) { + _clear_composition_layer_provider(); + } else if (openxr_session_running && is_visible()) { + _setup_composition_layer_provider(); } } break; case NOTIFICATION_EXIT_TREE: { - if (composition_layer_extension) { - composition_layer_extension->unregister_viewport_composition_layer_provider(openxr_layer_provider); - } - - if (!fallback) { - // This will clean up existing resources. - openxr_layer_provider->set_viewport(RID(), Size2i()); - } + // This will clean up existing resources. + _clear_composition_layer_provider(); } break; } } @@ -401,13 +458,27 @@ bool OpenXRCompositionLayer::_get(const StringName &p_property, Variant &r_value bool OpenXRCompositionLayer::_set(const StringName &p_property, const Variant &p_value) { extension_property_values[p_property] = p_value; - if (openxr_layer_provider) { - openxr_layer_provider->set_extension_property_values(extension_property_values); - } + openxr_layer_provider->set_extension_property_values(extension_property_values); return true; } +void OpenXRCompositionLayer::_validate_property(PropertyInfo &p_property) const { + if (p_property.name == "layer_viewport") { + if (use_android_surface) { + p_property.usage &= ~PROPERTY_USAGE_EDITOR; + } else { + p_property.usage |= PROPERTY_USAGE_EDITOR; + } + } else if (p_property.name == "android_surface_size") { + if (use_android_surface) { + p_property.usage |= PROPERTY_USAGE_EDITOR; + } else { + p_property.usage &= ~PROPERTY_USAGE_EDITOR; + } + } +} + PackedStringArray OpenXRCompositionLayer::get_configuration_warnings() const { PackedStringArray warnings = Node3D::get_configuration_warnings(); diff --git a/modules/openxr/scene/openxr_composition_layer.h b/modules/openxr/scene/openxr_composition_layer.h index 55cae27d23..26b40236d2 100644 --- a/modules/openxr/scene/openxr_composition_layer.h +++ b/modules/openxr/scene/openxr_composition_layer.h @@ -35,6 +35,7 @@ #include "scene/3d/node_3d.h" +class JavaObject; class MeshInstance3D; class Mesh; class OpenXRAPI; @@ -45,7 +46,12 @@ class SubViewport; class OpenXRCompositionLayer : public Node3D { GDCLASS(OpenXRCompositionLayer, Node3D); + XrCompositionLayerBaseHeader *composition_layer_base_header = nullptr; + OpenXRViewportCompositionLayerProvider *openxr_layer_provider = nullptr; + SubViewport *layer_viewport = nullptr; + bool use_android_surface = false; + Size2i android_surface_size = Size2i(1024, 1024); bool enable_hole_punch = false; MeshInstance3D *fallback = nullptr; bool should_update_fallback_mesh = false; @@ -58,10 +64,12 @@ class OpenXRCompositionLayer : public Node3D { void _reset_fallback_material(); void _remove_fallback_node(); + void _setup_composition_layer_provider(); + void _clear_composition_layer_provider(); + protected: OpenXRAPI *openxr_api = nullptr; OpenXRCompositionLayerExtension *composition_layer_extension = nullptr; - OpenXRViewportCompositionLayerProvider *openxr_layer_provider = nullptr; static void _bind_methods(); @@ -69,6 +77,7 @@ protected: void _get_property_list(List<PropertyInfo> *p_property_list) const; bool _get(const StringName &p_property, Variant &r_value) const; bool _set(const StringName &p_property, const Variant &p_value); + void _validate_property(PropertyInfo &p_property) const; virtual void _on_openxr_session_begun(); virtual void _on_openxr_session_stopping(); @@ -82,10 +91,18 @@ protected: static Vector<OpenXRCompositionLayer *> composition_layer_nodes; bool is_viewport_in_use(SubViewport *p_viewport); + OpenXRCompositionLayer(XrCompositionLayerBaseHeader *p_composition_layer); + public: void set_layer_viewport(SubViewport *p_viewport); SubViewport *get_layer_viewport() const; + void set_use_android_surface(bool p_use_android_surface); + bool get_use_android_surface() const; + + void set_android_surface_size(Size2i p_size); + Size2i get_android_surface_size() const; + void set_enable_hole_punch(bool p_enable); bool get_enable_hole_punch() const; @@ -95,13 +112,13 @@ public: void set_alpha_blend(bool p_alpha_blend); bool get_alpha_blend() const; + Ref<JavaObject> get_android_surface(); bool is_natively_supported() const; virtual PackedStringArray get_configuration_warnings() const override; virtual Vector2 intersects_ray(const Vector3 &p_origin, const Vector3 &p_direction) const; - OpenXRCompositionLayer(); ~OpenXRCompositionLayer(); }; diff --git a/modules/openxr/scene/openxr_composition_layer_cylinder.cpp b/modules/openxr/scene/openxr_composition_layer_cylinder.cpp index 6c8d2fc885..727586467a 100644 --- a/modules/openxr/scene/openxr_composition_layer_cylinder.cpp +++ b/modules/openxr/scene/openxr_composition_layer_cylinder.cpp @@ -38,20 +38,8 @@ #include "scene/main/viewport.h" #include "scene/resources/mesh.h" -OpenXRCompositionLayerCylinder::OpenXRCompositionLayerCylinder() { - composition_layer = { - XR_TYPE_COMPOSITION_LAYER_CYLINDER_KHR, // type - nullptr, // next - 0, // layerFlags - XR_NULL_HANDLE, // space - XR_EYE_VISIBILITY_BOTH, // eyeVisibility - {}, // subImage - { { 0, 0, 0, 0 }, { 0, 0, 0 } }, // pose - radius, // radius - central_angle, // centralAngle - aspect_ratio, // aspectRatio - }; - openxr_layer_provider = memnew(OpenXRViewportCompositionLayerProvider((XrCompositionLayerBaseHeader *)&composition_layer)); +OpenXRCompositionLayerCylinder::OpenXRCompositionLayerCylinder() : + OpenXRCompositionLayer((XrCompositionLayerBaseHeader *)&composition_layer) { XRServer::get_singleton()->connect("reference_frame_changed", callable_mp(this, &OpenXRCompositionLayerCylinder::update_transform)); } diff --git a/modules/openxr/scene/openxr_composition_layer_cylinder.h b/modules/openxr/scene/openxr_composition_layer_cylinder.h index 9bd5a42d36..a701575972 100644 --- a/modules/openxr/scene/openxr_composition_layer_cylinder.h +++ b/modules/openxr/scene/openxr_composition_layer_cylinder.h @@ -38,7 +38,18 @@ class OpenXRCompositionLayerCylinder : public OpenXRCompositionLayer { GDCLASS(OpenXRCompositionLayerCylinder, OpenXRCompositionLayer); - XrCompositionLayerCylinderKHR composition_layer; + XrCompositionLayerCylinderKHR composition_layer = { + XR_TYPE_COMPOSITION_LAYER_CYLINDER_KHR, // type + nullptr, // next + 0, // layerFlags + XR_NULL_HANDLE, // space + XR_EYE_VISIBILITY_BOTH, // eyeVisibility + {}, // subImage + { { 0, 0, 0, 0 }, { 0, 0, 0 } }, // pose + 1.0, // radius + Math_PI / 2.0, // centralAngle + 1.0, // aspectRatio + }; float radius = 1.0; float aspect_ratio = 1.0; diff --git a/modules/openxr/scene/openxr_composition_layer_equirect.cpp b/modules/openxr/scene/openxr_composition_layer_equirect.cpp index b6f5d27ffe..2fce26c965 100644 --- a/modules/openxr/scene/openxr_composition_layer_equirect.cpp +++ b/modules/openxr/scene/openxr_composition_layer_equirect.cpp @@ -38,21 +38,8 @@ #include "scene/main/viewport.h" #include "scene/resources/mesh.h" -OpenXRCompositionLayerEquirect::OpenXRCompositionLayerEquirect() { - composition_layer = { - XR_TYPE_COMPOSITION_LAYER_EQUIRECT2_KHR, // type - nullptr, // next - 0, // layerFlags - XR_NULL_HANDLE, // space - XR_EYE_VISIBILITY_BOTH, // eyeVisibility - {}, // subImage - { { 0, 0, 0, 0 }, { 0, 0, 0 } }, // pose - radius, // radius - central_horizontal_angle, // centralHorizontalAngle - upper_vertical_angle, // upperVerticalAngle - -lower_vertical_angle, // lowerVerticalAngle - }; - openxr_layer_provider = memnew(OpenXRViewportCompositionLayerProvider((XrCompositionLayerBaseHeader *)&composition_layer)); +OpenXRCompositionLayerEquirect::OpenXRCompositionLayerEquirect() : + OpenXRCompositionLayer((XrCompositionLayerBaseHeader *)&composition_layer) { XRServer::get_singleton()->connect("reference_frame_changed", callable_mp(this, &OpenXRCompositionLayerEquirect::update_transform)); } diff --git a/modules/openxr/scene/openxr_composition_layer_equirect.h b/modules/openxr/scene/openxr_composition_layer_equirect.h index af6cd92cbe..45a65fe5aa 100644 --- a/modules/openxr/scene/openxr_composition_layer_equirect.h +++ b/modules/openxr/scene/openxr_composition_layer_equirect.h @@ -38,7 +38,19 @@ class OpenXRCompositionLayerEquirect : public OpenXRCompositionLayer { GDCLASS(OpenXRCompositionLayerEquirect, OpenXRCompositionLayer); - XrCompositionLayerEquirect2KHR composition_layer; + XrCompositionLayerEquirect2KHR composition_layer = { + XR_TYPE_COMPOSITION_LAYER_EQUIRECT2_KHR, // type + nullptr, // next + 0, // layerFlags + XR_NULL_HANDLE, // space + XR_EYE_VISIBILITY_BOTH, // eyeVisibility + {}, // subImage + { { 0, 0, 0, 0 }, { 0, 0, 0 } }, // pose + 1.0, // radius + Math_PI / 2.0, // centralHorizontalAngle + Math_PI / 4.0, // upperVerticalAngle + -Math_PI / 4.0, // lowerVerticalAngle + }; float radius = 1.0; float central_horizontal_angle = Math_PI / 2.0; diff --git a/modules/openxr/scene/openxr_composition_layer_quad.cpp b/modules/openxr/scene/openxr_composition_layer_quad.cpp index 21919496d6..4a00fd371e 100644 --- a/modules/openxr/scene/openxr_composition_layer_quad.cpp +++ b/modules/openxr/scene/openxr_composition_layer_quad.cpp @@ -38,18 +38,8 @@ #include "scene/main/viewport.h" #include "scene/resources/3d/primitive_meshes.h" -OpenXRCompositionLayerQuad::OpenXRCompositionLayerQuad() { - composition_layer = { - XR_TYPE_COMPOSITION_LAYER_QUAD, // type - nullptr, // next - 0, // layerFlags - XR_NULL_HANDLE, // space - XR_EYE_VISIBILITY_BOTH, // eyeVisibility - {}, // subImage - { { 0, 0, 0, 0 }, { 0, 0, 0 } }, // pose - { (float)quad_size.x, (float)quad_size.y }, // size - }; - openxr_layer_provider = memnew(OpenXRViewportCompositionLayerProvider((XrCompositionLayerBaseHeader *)&composition_layer)); +OpenXRCompositionLayerQuad::OpenXRCompositionLayerQuad() : + OpenXRCompositionLayer((XrCompositionLayerBaseHeader *)&composition_layer) { XRServer::get_singleton()->connect("reference_frame_changed", callable_mp(this, &OpenXRCompositionLayerQuad::update_transform)); } diff --git a/modules/openxr/scene/openxr_composition_layer_quad.h b/modules/openxr/scene/openxr_composition_layer_quad.h index 0c3172dbb2..a4ccfc6d8e 100644 --- a/modules/openxr/scene/openxr_composition_layer_quad.h +++ b/modules/openxr/scene/openxr_composition_layer_quad.h @@ -38,7 +38,16 @@ class OpenXRCompositionLayerQuad : public OpenXRCompositionLayer { GDCLASS(OpenXRCompositionLayerQuad, OpenXRCompositionLayer); - XrCompositionLayerQuad composition_layer; + XrCompositionLayerQuad composition_layer = { + XR_TYPE_COMPOSITION_LAYER_QUAD, // type + nullptr, // next + 0, // layerFlags + XR_NULL_HANDLE, // space + XR_EYE_VISIBILITY_BOTH, // eyeVisibility + {}, // subImage + { { 0, 0, 0, 0 }, { 0, 0, 0 } }, // pose + { 1.0, 1.0 }, // size + }; Size2 quad_size = Size2(1.0, 1.0); diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp index 3f4624d09c..1bc600c56f 100644 --- a/platform/android/export/export.cpp +++ b/platform/android/export/export.cpp @@ -42,6 +42,7 @@ void register_android_exporter_types() { } void register_android_exporter() { + // TODO: Move to editor_settings.cpp EDITOR_DEF("export/android/debug_keystore", EditorPaths::get_singleton()->get_debug_keystore_path()); EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/android/debug_keystore", PROPERTY_HINT_GLOBAL_FILE, "*.keystore,*.jks")); EDITOR_DEF("export/android/debug_keystore_user", DEFAULT_ANDROID_KEYSTORE_DEBUG_USER); diff --git a/platform/android/java/editor/src/meta/java/org/godotengine/editor/GodotEditor.kt b/platform/android/java/editor/src/meta/java/org/godotengine/editor/GodotEditor.kt index 9f0440e87d..328ff9a3bd 100644 --- a/platform/android/java/editor/src/meta/java/org/godotengine/editor/GodotEditor.kt +++ b/platform/android/java/editor/src/meta/java/org/godotengine/editor/GodotEditor.kt @@ -45,15 +45,13 @@ open class GodotEditor : BaseGodotEditor() { internal val XR_RUN_GAME_INFO = EditorWindowInfo(GodotXRGame::class.java, 1667, ":GodotXRGame") - internal const val USE_ANCHOR_API_PERMISSION = "com.oculus.permission.USE_ANCHOR_API" internal const val USE_SCENE_PERMISSION = "com.oculus.permission.USE_SCENE" } override fun getExcludedPermissions(): MutableSet<String> { val excludedPermissions = super.getExcludedPermissions() - // The USE_ANCHOR_API and USE_SCENE permissions are requested when the "xr/openxr/enabled" - // project setting is enabled. - excludedPermissions.add(USE_ANCHOR_API_PERMISSION) + // The USE_SCENE permission is requested when the "xr/openxr/enabled" project setting + // is enabled. excludedPermissions.add(USE_SCENE_PERMISSION) return excludedPermissions } diff --git a/platform/android/java/editor/src/meta/java/org/godotengine/editor/GodotXRGame.kt b/platform/android/java/editor/src/meta/java/org/godotengine/editor/GodotXRGame.kt index d71fbb53f2..5db2879aad 100644 --- a/platform/android/java/editor/src/meta/java/org/godotengine/editor/GodotXRGame.kt +++ b/platform/android/java/editor/src/meta/java/org/godotengine/editor/GodotXRGame.kt @@ -31,7 +31,6 @@ package org.godotengine.editor import org.godotengine.godot.GodotLib -import org.godotengine.godot.utils.PermissionsUtil import org.godotengine.godot.xr.XRMode /** @@ -62,8 +61,16 @@ open class GodotXRGame: GodotGame() { val openxrEnabled = GodotLib.getGlobal("xr/openxr/enabled").toBoolean() if (openxrEnabled) { - permissionsToEnable.add(USE_ANCHOR_API_PERMISSION) - permissionsToEnable.add(USE_SCENE_PERMISSION) + // We only request permissions when the `automatically_request_runtime_permissions` + // project setting is enabled. + // If the project setting is not defined, we fall-back to the default behavior which is + // to automatically request permissions. + val automaticallyRequestPermissionsSetting = GodotLib.getGlobal("xr/openxr/extensions/automatically_request_runtime_permissions") + val automaticPermissionsRequestEnabled = automaticallyRequestPermissionsSetting.isNullOrEmpty() || + automaticallyRequestPermissionsSetting.toBoolean() + if (automaticPermissionsRequestEnabled) { + permissionsToEnable.add(USE_SCENE_PERMISSION) + } } return permissionsToEnable diff --git a/platform/android/java_godot_lib_jni.cpp b/platform/android/java_godot_lib_jni.cpp index 390677df22..6086f67a1e 100644 --- a/platform/android/java_godot_lib_jni.cpp +++ b/platform/android/java_godot_lib_jni.cpp @@ -472,19 +472,22 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_focusout(JNIEnv *env, JNIEXPORT jstring JNICALL Java_org_godotengine_godot_GodotLib_getGlobal(JNIEnv *env, jclass clazz, jstring path) { String js = jstring_to_string(path, env); - return env->NewStringUTF(GLOBAL_GET(js).operator String().utf8().get_data()); + Variant setting_with_override = GLOBAL_GET(js); + String setting_value = (setting_with_override.get_type() == Variant::NIL) ? "" : setting_with_override; + return env->NewStringUTF(setting_value.utf8().get_data()); } JNIEXPORT jstring JNICALL Java_org_godotengine_godot_GodotLib_getEditorSetting(JNIEnv *env, jclass clazz, jstring p_setting_key) { - String editor_setting = ""; + String editor_setting_value = ""; #ifdef TOOLS_ENABLED String godot_setting_key = jstring_to_string(p_setting_key, env); - editor_setting = EDITOR_GET(godot_setting_key).operator String(); + Variant editor_setting = EDITOR_GET(godot_setting_key); + editor_setting_value = (editor_setting.get_type() == Variant::NIL) ? "" : editor_setting; #else WARN_PRINT("Access to the Editor Settings in only available on Editor builds"); #endif - return env->NewStringUTF(editor_setting.utf8().get_data()); + return env->NewStringUTF(editor_setting_value.utf8().get_data()); } JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_callobject(JNIEnv *env, jclass clazz, jlong ID, jstring method, jobjectArray params) { diff --git a/platform/ios/export/export.cpp b/platform/ios/export/export.cpp index 98cc80e4a0..5e9ca3e025 100644 --- a/platform/ios/export/export.cpp +++ b/platform/ios/export/export.cpp @@ -39,6 +39,7 @@ void register_ios_exporter_types() { } void register_ios_exporter() { + // TODO: Move to editor_settings.cpp #ifdef MACOS_ENABLED EDITOR_DEF("export/ios/ios_deploy", ""); EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/ios/ios_deploy", PROPERTY_HINT_GLOBAL_FILE, "*")); diff --git a/platform/linuxbsd/x11/display_server_x11.cpp b/platform/linuxbsd/x11/display_server_x11.cpp index 840cadace3..499df55bef 100644 --- a/platform/linuxbsd/x11/display_server_x11.cpp +++ b/platform/linuxbsd/x11/display_server_x11.cpp @@ -1787,12 +1787,6 @@ void DisplayServerX11::delete_sub_window(WindowID p_id) { _send_window_event(windows[p_id], WINDOW_EVENT_MOUSE_EXIT); } - window_set_rect_changed_callback(Callable(), p_id); - window_set_window_event_callback(Callable(), p_id); - window_set_input_event_callback(Callable(), p_id); - window_set_input_text_callback(Callable(), p_id); - window_set_drop_files_callback(Callable(), p_id); - while (wd.transient_children.size()) { window_set_transient(*wd.transient_children.begin(), INVALID_WINDOW_ID); } @@ -1836,6 +1830,12 @@ void DisplayServerX11::delete_sub_window(WindowID p_id) { XUnmapWindow(x11_display, wd.x11_window); XDestroyWindow(x11_display, wd.x11_window); + window_set_rect_changed_callback(Callable(), p_id); + window_set_window_event_callback(Callable(), p_id); + window_set_input_event_callback(Callable(), p_id); + window_set_input_text_callback(Callable(), p_id); + window_set_drop_files_callback(Callable(), p_id); + windows.erase(p_id); } diff --git a/platform/macos/export/export.cpp b/platform/macos/export/export.cpp index 8930974df9..df31ccfb56 100644 --- a/platform/macos/export/export.cpp +++ b/platform/macos/export/export.cpp @@ -37,6 +37,7 @@ void register_macos_exporter_types() { } void register_macos_exporter() { + // TODO: Move to editor_settings.cpp #ifndef ANDROID_ENABLED EDITOR_DEF("export/macos/rcodesign", ""); #ifdef WINDOWS_ENABLED diff --git a/platform/web/export/export.cpp b/platform/web/export/export.cpp index 306ec624a0..ee6d02e68f 100644 --- a/platform/web/export/export.cpp +++ b/platform/web/export/export.cpp @@ -40,6 +40,7 @@ void register_web_exporter_types() { } void register_web_exporter() { + // TODO: Move to editor_settings.cpp EDITOR_DEF("export/web/http_host", "localhost"); EDITOR_DEF("export/web/http_port", 8060); EDITOR_DEF("export/web/use_tls", false); diff --git a/platform/windows/export/export.cpp b/platform/windows/export/export.cpp index 7a65f74f0d..a062963c2e 100644 --- a/platform/windows/export/export.cpp +++ b/platform/windows/export/export.cpp @@ -39,6 +39,7 @@ void register_windows_exporter_types() { } void register_windows_exporter() { + // TODO: Move to editor_settings.cpp #ifndef ANDROID_ENABLED EDITOR_DEF("export/windows/rcedit", ""); EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/windows/rcedit", PROPERTY_HINT_GLOBAL_FILE, "*.exe")); diff --git a/scene/3d/label_3d.cpp b/scene/3d/label_3d.cpp index 9330cd8b65..6b3510a72a 100644 --- a/scene/3d/label_3d.cpp +++ b/scene/3d/label_3d.cpp @@ -30,7 +30,7 @@ #include "label_3d.h" -#include "scene/main/viewport.h" +#include "scene/main/window.h" #include "scene/resources/theme.h" #include "scene/theme/theme_db.h" @@ -197,14 +197,14 @@ void Label3D::_notification(int p_what) { if (!pending_update) { _im_update(); } - Viewport *viewport = get_viewport(); - ERR_FAIL_NULL(viewport); - viewport->connect("size_changed", callable_mp(this, &Label3D::_font_changed)); + Window *window = get_window(); + ERR_FAIL_NULL(window); + window->connect("size_changed", callable_mp(this, &Label3D::_font_changed)); } break; case NOTIFICATION_EXIT_TREE: { - Viewport *viewport = get_viewport(); - ERR_FAIL_NULL(viewport); - viewport->disconnect("size_changed", callable_mp(this, &Label3D::_font_changed)); + Window *window = get_window(); + ERR_FAIL_NULL(window); + window->disconnect("size_changed", callable_mp(this, &Label3D::_font_changed)); } break; case NOTIFICATION_TRANSLATION_CHANGED: { String new_text = atr(text); diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp index 373488b0fc..1fc8586448 100644 --- a/scene/gui/file_dialog.cpp +++ b/scene/gui/file_dialog.cpp @@ -76,6 +76,7 @@ void FileDialog::popup(const Rect2i &p_rect) { #ifdef TOOLS_ENABLED if (is_part_of_edited_scene()) { ConfirmationDialog::popup(p_rect); + return; } #endif @@ -1380,7 +1381,7 @@ void FileDialog::set_use_native_dialog(bool p_native) { #endif // Replace the built-in dialog with the native one if it's currently visible. - if (is_visible() && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_NATIVE_DIALOG_FILE) && (use_native_dialog || OS::get_singleton()->is_sandboxed())) { + if (is_inside_tree() && is_visible() && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_NATIVE_DIALOG_FILE) && (use_native_dialog || OS::get_singleton()->is_sandboxed())) { ConfirmationDialog::set_visible(false); _native_popup(); } diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp index 60b3e371a0..11a6411e65 100644 --- a/scene/gui/graph_edit.cpp +++ b/scene/gui/graph_edit.cpp @@ -1646,24 +1646,34 @@ void GraphEdit::_draw_grid() { Color transparent_grid_minor = theme_cache.grid_minor; transparent_grid_minor.a *= CLAMP(1.0 * (zoom - 0.4), 0, 1); - for (int i = from_pos.x; i < from_pos.x + len.x; i++) { - for (int j = from_pos.y; j < from_pos.y + len.y; j++) { - Color color = transparent_grid_minor; + // Minor dots. + if (transparent_grid_minor.a != 0) { + for (int i = from_pos.x; i < from_pos.x + len.x; i++) { + for (int j = from_pos.y; j < from_pos.y + len.y; j++) { + if (ABS(i) % GRID_MINOR_STEPS_PER_MAJOR_DOT == 0 && ABS(j) % GRID_MINOR_STEPS_PER_MAJOR_DOT == 0) { + continue; + } - if (ABS(i) % GRID_MINOR_STEPS_PER_MAJOR_DOT == 0 && ABS(j) % GRID_MINOR_STEPS_PER_MAJOR_DOT == 0) { - color = theme_cache.grid_major; - } + float base_offset_x = i * snapping_distance * zoom - offset.x * zoom; + float base_offset_y = j * snapping_distance * zoom - offset.y * zoom; - if (color.a == 0) { - continue; + draw_rect(Rect2(base_offset_x - 1, base_offset_y - 1, 3, 3), transparent_grid_minor); } + } + } - float base_offset_x = i * snapping_distance * zoom - offset.x * zoom; - float base_offset_y = j * snapping_distance * zoom - offset.y * zoom; + // Major dots. + if (theme_cache.grid_major.a != 0) { + for (int i = from_pos.x - from_pos.x % GRID_MINOR_STEPS_PER_MAJOR_DOT; i < from_pos.x + len.x; i += GRID_MINOR_STEPS_PER_MAJOR_DOT) { + for (int j = from_pos.y - from_pos.y % GRID_MINOR_STEPS_PER_MAJOR_DOT; j < from_pos.y + len.y; j += GRID_MINOR_STEPS_PER_MAJOR_DOT) { + float base_offset_x = i * snapping_distance * zoom - offset.x * zoom; + float base_offset_y = j * snapping_distance * zoom - offset.y * zoom; - draw_rect(Rect2(base_offset_x - 1, base_offset_y - 1, 3, 3), color); + draw_rect(Rect2(base_offset_x - 1, base_offset_y - 1, 3, 3), theme_cache.grid_major); + } } } + } break; } } diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index 3b5d4fc33e..1a066b0728 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -61,16 +61,6 @@ void LineEdit::_edit() { editing = true; _validate_caret_can_draw(); - DisplayServer::WindowID wid = get_window() ? get_window()->get_window_id() : DisplayServer::INVALID_WINDOW_ID; - if (wid != DisplayServer::INVALID_WINDOW_ID && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_IME)) { - DisplayServer::get_singleton()->window_set_ime_active(true, wid); - Point2 pos = Point2(get_caret_pixel_pos().x, (get_size().y + theme_cache.font->get_height(theme_cache.font_size)) / 2) + get_global_position(); - if (get_window()->get_embedder()) { - pos += get_viewport()->get_popup_base_transform().get_origin(); - } - DisplayServer::get_singleton()->window_set_ime_position(pos, wid); - } - show_virtual_keyboard(); queue_redraw(); emit_signal(SNAME("editing_toggled"), true); @@ -84,14 +74,7 @@ void LineEdit::_unedit() { editing = false; _validate_caret_can_draw(); - DisplayServer::WindowID wid = get_window() ? get_window()->get_window_id() : DisplayServer::INVALID_WINDOW_ID; - if (wid != DisplayServer::INVALID_WINDOW_ID && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_IME)) { - DisplayServer::get_singleton()->window_set_ime_position(Point2(), wid); - DisplayServer::get_singleton()->window_set_ime_active(false, wid); - } - ime_text = ""; - ime_selection = Point2(); - _shape(); + apply_ime(); set_caret_column(caret_column); // Update scroll_offset. if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_VIRTUAL_KEYBOARD) && virtual_keyboard_enabled) { @@ -109,6 +92,64 @@ bool LineEdit::is_editing() const { return editing; } +void LineEdit::_close_ime_window() { + DisplayServer::WindowID wid = get_window() ? get_window()->get_window_id() : DisplayServer::INVALID_WINDOW_ID; + if (wid == DisplayServer::INVALID_WINDOW_ID || !DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_IME)) { + return; + } + DisplayServer::get_singleton()->window_set_ime_position(Point2(), wid); + DisplayServer::get_singleton()->window_set_ime_active(false, wid); +} + +void LineEdit::_update_ime_window_position() { + DisplayServer::WindowID wid = get_window() ? get_window()->get_window_id() : DisplayServer::INVALID_WINDOW_ID; + if (wid == DisplayServer::INVALID_WINDOW_ID || !DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_IME)) { + return; + } + DisplayServer::get_singleton()->window_set_ime_active(true, wid); + Point2 pos = Point2(get_caret_pixel_pos().x, (get_size().y + theme_cache.font->get_height(theme_cache.font_size)) / 2) + get_global_position(); + if (get_window()->get_embedder()) { + pos += get_viewport()->get_popup_base_transform().get_origin(); + } + // The window will move to the updated position the next time the IME is updated, not immediately. + DisplayServer::get_singleton()->window_set_ime_position(pos, wid); +} + +bool LineEdit::has_ime_text() const { + return !ime_text.is_empty(); +} + +void LineEdit::cancel_ime() { + if (!has_ime_text()) { + return; + } + ime_text = String(); + ime_selection = Vector2i(); + alt_start = false; + alt_start_no_hold = false; + _close_ime_window(); + _shape(); +} + +void LineEdit::apply_ime() { + if (!has_ime_text()) { + return; + } + + // Force apply the current IME text. + if (alt_start || alt_start_no_hold) { + cancel_ime(); + if ((alt_code > 0x31 && alt_code < 0xd800) || (alt_code > 0xdfff && alt_code <= 0x10ffff)) { + char32_t ucodestr[2] = { (char32_t)alt_code, 0 }; + insert_text_at_caret(ucodestr); + } + } else { + String insert_ime_text = ime_text; + cancel_ime(); + insert_text_at_caret(insert_ime_text); + } +} + void LineEdit::_swap_current_input_direction() { if (input_direction == TEXT_DIRECTION_LTR) { input_direction = TEXT_DIRECTION_RTL; @@ -333,9 +374,10 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) { Ref<InputEventMouseButton> b = p_event; - // Ignore mouse clicks in IME input mode. - if (b.is_valid() && ime_text.is_empty()) { + if (b.is_valid()) { if (b->is_pressed() && b->get_button_index() == MouseButton::RIGHT) { + apply_ime(); + if (editable && !selection.enabled) { set_caret_at_pixel_pos(b->get_position().x); } @@ -356,6 +398,8 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) { } if (editable && is_middle_mouse_paste_enabled() && b->is_pressed() && b->get_button_index() == MouseButton::MIDDLE && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CLIPBOARD_PRIMARY)) { + apply_ime(); + String paste_buffer = DisplayServer::get_singleton()->clipboard_get_primary().strip_escapes(); deselect(); @@ -388,6 +432,8 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) { } if (b->is_pressed()) { + apply_ime(); + accept_event(); // Don't pass event further when clicked on text field. if (editable && !text.is_empty() && _is_over_clear_button(b->get_position())) { clear_button_status.press_attempt = true; @@ -561,46 +607,111 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) { return; } - if (!k->is_pressed()) { - if (alt_start && k->get_keycode() == Key::ALT) { - alt_start = false; - if ((alt_code > 0x31 && alt_code < 0xd800) || (alt_code > 0xdfff && alt_code <= 0x10ffff)) { - char32_t ucodestr[2] = { (char32_t)alt_code, 0 }; - insert_text_at_caret(ucodestr); - } - accept_event(); - return; + // Start Unicode input (hold). + if (k->is_alt_pressed() && k->get_keycode() == Key::KP_ADD && !alt_start && !alt_start_no_hold) { + if (selection.enabled) { + selection_delete(); } + alt_start = true; + alt_code = 0; + ime_text = "u"; + ime_selection = Vector2i(0, -1); + _shape(); + queue_redraw(); + accept_event(); return; } - // Alt + Unicode input: - if (k->is_alt_pressed()) { - if (!alt_start) { - if (k->get_keycode() == Key::KP_ADD) { - alt_start = true; - alt_code = 0; - accept_event(); - return; - } + // Start Unicode input (press). + if (k->is_action("ui_unicode_start", true) && !alt_start && !alt_start_no_hold) { + if (selection.enabled) { + selection_delete(); + } + alt_start_no_hold = true; + alt_code = 0; + ime_text = "u"; + ime_selection = Vector2i(0, -1); + _shape(); + queue_redraw(); + accept_event(); + return; + } + + // Update Unicode input. + if (k->is_pressed() && ((k->is_alt_pressed() && alt_start) || alt_start_no_hold)) { + if (k->get_keycode() >= Key::KEY_0 && k->get_keycode() <= Key::KEY_9) { + alt_code = alt_code << 4; + alt_code += (uint32_t)(k->get_keycode() - Key::KEY_0); + } else if (k->get_keycode() >= Key::KP_0 && k->get_keycode() <= Key::KP_9) { + alt_code = alt_code << 4; + alt_code += (uint32_t)(k->get_keycode() - Key::KP_0); + } else if (k->get_keycode() >= Key::A && k->get_keycode() <= Key::F) { + alt_code = alt_code << 4; + alt_code += (uint32_t)(k->get_keycode() - Key::A) + 10; + } else if ((Key)k->get_unicode() >= Key::KEY_0 && (Key)k->get_unicode() <= Key::KEY_9) { + alt_code = alt_code << 4; + alt_code += (uint32_t)((Key)k->get_unicode() - Key::KEY_0); + } else if ((Key)k->get_unicode() >= Key::A && (Key)k->get_unicode() <= Key::F) { + alt_code = alt_code << 4; + alt_code += (uint32_t)((Key)k->get_unicode() - Key::A) + 10; + } else if (k->get_physical_keycode() >= Key::KEY_0 && k->get_physical_keycode() <= Key::KEY_9) { + alt_code = alt_code << 4; + alt_code += (uint32_t)(k->get_physical_keycode() - Key::KEY_0); + } + if (k->get_keycode() == Key::BACKSPACE) { + alt_code = alt_code >> 4; + } + if (alt_code > 0x10ffff) { + alt_code = 0x10ffff; + } + if (alt_code > 0) { + ime_text = vformat("u%s", String::num_int64(alt_code, 16, true)); } else { - if (k->get_keycode() >= Key::KEY_0 && k->get_keycode() <= Key::KEY_9) { - alt_code = alt_code << 4; - alt_code += (uint32_t)(k->get_keycode() - Key::KEY_0); - } - if (k->get_keycode() >= Key::KP_0 && k->get_keycode() <= Key::KP_9) { - alt_code = alt_code << 4; - alt_code += (uint32_t)(k->get_keycode() - Key::KP_0); - } - if (k->get_keycode() >= Key::A && k->get_keycode() <= Key::F) { - alt_code = alt_code << 4; - alt_code += (uint32_t)(k->get_keycode() - Key::A) + 10; - } - accept_event(); - return; + ime_text = "u"; + } + ime_selection = Vector2i(0, -1); + _shape(); + queue_redraw(); + accept_event(); + return; + } + + // Submit Unicode input. + if ((!k->is_pressed() && alt_start && k->get_keycode() == Key::ALT) || (alt_start_no_hold && (k->is_action("ui_text_submit", true) || k->is_action("ui_accept", true)))) { + alt_start = false; + alt_start_no_hold = false; + if ((alt_code > 0x31 && alt_code < 0xd800) || (alt_code > 0xdfff && alt_code <= 0x10ffff)) { + ime_text = String(); + ime_selection = Vector2i(); + char32_t ucodestr[2] = { (char32_t)alt_code, 0 }; + insert_text_at_caret(ucodestr); + } else { + ime_text = String(); + ime_selection = Vector2i(); + _shape(); } + queue_redraw(); + accept_event(); + return; } + // Cancel Unicode input. + if (alt_start_no_hold && k->is_action("ui_cancel", true)) { + alt_start = false; + alt_start_no_hold = false; + ime_text = String(); + ime_selection = Vector2i(); + _shape(); + queue_redraw(); + accept_event(); + return; + } + + if (!k->is_pressed()) { + return; + } + + // Open context menu. if (context_menu_enabled) { if (k->is_action("ui_menu", true)) { _update_context_menu(); @@ -830,6 +941,8 @@ void LineEdit::drop_data(const Point2 &p_point, const Variant &p_data) { Control::drop_data(p_point, p_data); if (p_data.is_string() && is_editable()) { + apply_ime(); + set_caret_at_pixel_pos(p_point.x); int caret_column_tmp = caret_column; bool is_inside_sel = selection.enabled && caret_column >= selection.begin && caret_column <= selection.end; @@ -1213,15 +1326,7 @@ void LineEdit::_notification(int p_what) { } if (editing) { - DisplayServer::WindowID wid = get_window() ? get_window()->get_window_id() : DisplayServer::INVALID_WINDOW_ID; - if (wid != DisplayServer::INVALID_WINDOW_ID && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_IME)) { - DisplayServer::get_singleton()->window_set_ime_active(true, wid); - Point2 pos = Point2(get_caret_pixel_pos().x, (get_size().y + theme_cache.font->get_height(theme_cache.font_size)) / 2) + get_global_position(); - if (get_window()->get_embedder()) { - pos += get_viewport()->get_popup_base_transform().get_origin(); - } - DisplayServer::get_singleton()->window_set_ime_position(pos, wid); - } + _update_ime_window_position(); } } break; @@ -1745,6 +1850,8 @@ void LineEdit::clear() { } void LineEdit::show_virtual_keyboard() { + _update_ime_window_position(); + if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_VIRTUAL_KEYBOARD) && virtual_keyboard_enabled) { if (selection.enabled) { DisplayServer::get_singleton()->virtual_keyboard_show(text, get_global_rect(), DisplayServer::VirtualKeyboardType(virtual_keyboard_type), max_length, selection.begin, selection.end); @@ -2644,6 +2751,10 @@ void LineEdit::_validate_property(PropertyInfo &p_property) const { } void LineEdit::_bind_methods() { + ClassDB::bind_method(D_METHOD("has_ime_text"), &LineEdit::has_ime_text); + ClassDB::bind_method(D_METHOD("cancel_ime"), &LineEdit::cancel_ime); + ClassDB::bind_method(D_METHOD("apply_ime"), &LineEdit::apply_ime); + ClassDB::bind_method(D_METHOD("set_horizontal_alignment", "alignment"), &LineEdit::set_horizontal_alignment); ClassDB::bind_method(D_METHOD("get_horizontal_alignment"), &LineEdit::get_horizontal_alignment); diff --git a/scene/gui/line_edit.h b/scene/gui/line_edit.h index 984512745a..ac7436646b 100644 --- a/scene/gui/line_edit.h +++ b/scene/gui/line_edit.h @@ -92,6 +92,7 @@ private: bool text_changed_dirty = false; bool alt_start = false; + bool alt_start_no_hold = false; uint32_t alt_code = 0; String undo_text; @@ -209,6 +210,9 @@ private: void _edit(); void _unedit(); + void _close_ime_window(); + void _update_ime_window_position(); + void _clear_undo_stack(); void _clear_redo(); void _create_undo_state(); @@ -263,6 +267,10 @@ protected: public: bool is_editing() const; + bool has_ime_text() const; + void cancel_ime(); + void apply_ime(); + void set_horizontal_alignment(HorizontalAlignment p_alignment); HorizontalAlignment get_horizontal_alignment() const; diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index 552458245c..0c3c90d070 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -333,6 +333,8 @@ void RichTextLabel::_update_line_font(ItemFrame *p_frame, int p_line, const Ref< font_size = font_size_it->font_size; } TS->shaped_set_span_update_font(t, i, font->get_rids(), font_size, font->get_opentype_features()); + } else { + TS->shaped_set_span_update_font(t, i, p_base_font->get_rids(), p_base_font_size, p_base_font->get_opentype_features()); } } diff --git a/scene/gui/scroll_container.cpp b/scene/gui/scroll_container.cpp index d96809b67a..f1902bade4 100644 --- a/scene/gui/scroll_container.cpp +++ b/scene/gui/scroll_container.cpp @@ -35,8 +35,6 @@ #include "scene/theme/theme_db.h" Size2 ScrollContainer::get_minimum_size() const { - Size2 min_size; - // Calculated in this function, as it needs to traverse all child controls once to calculate; // and needs to be calculated before being used by update_scrollbars(). largest_child_min_size = Size2(); @@ -55,21 +53,23 @@ Size2 ScrollContainer::get_minimum_size() const { largest_child_min_size = largest_child_min_size.max(child_min_size); } + Size2 min_size; + const Size2 size = get_size(); + if (horizontal_scroll_mode == SCROLL_MODE_DISABLED) { - min_size.x = MAX(min_size.x, largest_child_min_size.x); - } - if (vertical_scroll_mode == SCROLL_MODE_DISABLED) { - min_size.y = MAX(min_size.y, largest_child_min_size.y); + min_size.x = largest_child_min_size.x; + bool v_scroll_show = vertical_scroll_mode == SCROLL_MODE_SHOW_ALWAYS || (vertical_scroll_mode == SCROLL_MODE_AUTO && largest_child_min_size.y > size.y); + if (v_scroll_show && v_scroll->get_parent() == this) { + min_size.x += v_scroll->get_minimum_size().x; + } } - bool h_scroll_show = horizontal_scroll_mode == SCROLL_MODE_SHOW_ALWAYS || (horizontal_scroll_mode == SCROLL_MODE_AUTO && largest_child_min_size.x > min_size.x); - bool v_scroll_show = vertical_scroll_mode == SCROLL_MODE_SHOW_ALWAYS || (vertical_scroll_mode == SCROLL_MODE_AUTO && largest_child_min_size.y > min_size.y); - - if (h_scroll_show && h_scroll->get_parent() == this) { - min_size.y += h_scroll->get_minimum_size().y; - } - if (v_scroll_show && v_scroll->get_parent() == this) { - min_size.x += v_scroll->get_minimum_size().x; + if (vertical_scroll_mode == SCROLL_MODE_DISABLED) { + min_size.y = largest_child_min_size.y; + bool h_scroll_show = horizontal_scroll_mode == SCROLL_MODE_SHOW_ALWAYS || (horizontal_scroll_mode == SCROLL_MODE_AUTO && largest_child_min_size.x > size.x); + if (h_scroll_show && h_scroll->get_parent() == this) { + min_size.y += h_scroll->get_minimum_size().y; + } } min_size += theme_cache.panel_style->get_minimum_size(); diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index ab0ad2f4b7..d7799588ea 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -1557,7 +1557,7 @@ void TextEdit::_notification(int p_what) { carets.write[c].draw_pos.x = rect.position.x; } } - { + if (ime_selection.y > 0) { // IME caret. const Vector<Vector2> sel = TS->shaped_text_get_selection(rid, get_caret_column(c) + ime_selection.x, get_caret_column(c) + ime_selection.x + ime_selection.y); for (int j = 0; j < sel.size(); j++) { @@ -1688,39 +1688,93 @@ void TextEdit::unhandled_key_input(const Ref<InputEvent> &p_event) { bool TextEdit::alt_input(const Ref<InputEvent> &p_gui_input) { Ref<InputEventKey> k = p_gui_input; if (k.is_valid()) { - if (!k->is_pressed()) { - if (alt_start && k->get_keycode() == Key::ALT) { - alt_start = false; - if ((alt_code > 0x31 && alt_code < 0xd800) || (alt_code > 0xdfff && alt_code <= 0x10ffff)) { - handle_unicode_input(alt_code); - } - return true; + // Start Unicode input (hold). + if (k->is_alt_pressed() && k->get_keycode() == Key::KP_ADD && !alt_start && !alt_start_no_hold) { + if (has_selection()) { + delete_selection(); } - return false; + alt_start = true; + alt_code = 0; + ime_text = "u"; + ime_selection = Vector2i(0, -1); + _update_ime_text(); + return true; } - if (k->is_alt_pressed()) { - if (!alt_start) { - if (k->get_keycode() == Key::KP_ADD) { - alt_start = true; - alt_code = 0; - return true; - } + // Start Unicode input (press). + if (k->is_action("ui_unicode_start", true) && !alt_start && !alt_start_no_hold) { + if (has_selection()) { + delete_selection(); + } + alt_start_no_hold = true; + alt_code = 0; + ime_text = "u"; + ime_selection = Vector2i(0, -1); + _update_ime_text(); + return true; + } + + // Update Unicode input. + if (k->is_pressed() && ((k->is_alt_pressed() && alt_start) || alt_start_no_hold)) { + if (k->get_keycode() >= Key::KEY_0 && k->get_keycode() <= Key::KEY_9) { + alt_code = alt_code << 4; + alt_code += (uint32_t)(k->get_keycode() - Key::KEY_0); + } else if (k->get_keycode() >= Key::KP_0 && k->get_keycode() <= Key::KP_9) { + alt_code = alt_code << 4; + alt_code += (uint32_t)(k->get_keycode() - Key::KP_0); + } else if (k->get_keycode() >= Key::A && k->get_keycode() <= Key::F) { + alt_code = alt_code << 4; + alt_code += (uint32_t)(k->get_keycode() - Key::A) + 10; + } else if ((Key)k->get_unicode() >= Key::KEY_0 && (Key)k->get_unicode() <= Key::KEY_9) { + alt_code = alt_code << 4; + alt_code += (uint32_t)((Key)k->get_unicode() - Key::KEY_0); + } else if ((Key)k->get_unicode() >= Key::A && (Key)k->get_unicode() <= Key::F) { + alt_code = alt_code << 4; + alt_code += (uint32_t)((Key)k->get_unicode() - Key::A) + 10; + } else if (k->get_physical_keycode() >= Key::KEY_0 && k->get_physical_keycode() <= Key::KEY_9) { + alt_code = alt_code << 4; + alt_code += (uint32_t)(k->get_physical_keycode() - Key::KEY_0); + } + if (k->get_keycode() == Key::BACKSPACE) { + alt_code = alt_code >> 4; + } + if (alt_code > 0x10ffff) { + alt_code = 0x10ffff; + } + if (alt_code > 0) { + ime_text = vformat("u%s", String::num_int64(alt_code, 16, true)); } else { - if (k->get_keycode() >= Key::KEY_0 && k->get_keycode() <= Key::KEY_9) { - alt_code = alt_code << 4; - alt_code += (uint32_t)(k->get_keycode() - Key::KEY_0); - } - if (k->get_keycode() >= Key::KP_0 && k->get_keycode() <= Key::KP_9) { - alt_code = alt_code << 4; - alt_code += (uint32_t)(k->get_keycode() - Key::KP_0); - } - if (k->get_keycode() >= Key::A && k->get_keycode() <= Key::F) { - alt_code = alt_code << 4; - alt_code += (uint32_t)(k->get_keycode() - Key::A) + 10; - } - return true; + ime_text = "u"; } + ime_selection = Vector2i(0, -1); + _update_ime_text(); + return true; + } + + // Submit Unicode input. + if ((!k->is_pressed() && alt_start && k->get_keycode() == Key::ALT) || (alt_start_no_hold && (k->is_action("ui_text_submit", true) || k->is_action("ui_accept", true)))) { + alt_start = false; + alt_start_no_hold = false; + if ((alt_code > 0x31 && alt_code < 0xd800) || (alt_code > 0xdfff && alt_code <= 0x10ffff)) { + ime_text = String(); + ime_selection = Vector2i(); + handle_unicode_input(alt_code); + } else { + ime_text = String(); + ime_selection = Vector2i(); + } + _update_ime_text(); + return true; + } + + // Cancel Unicode input. + if (alt_start_no_hold && k->is_action("ui_cancel", true)) { + alt_start = false; + alt_start_no_hold = false; + ime_text = String(); + ime_selection = Vector2i(); + _update_ime_text(); + return true; } } return false; @@ -3117,7 +3171,9 @@ void TextEdit::cancel_ime() { return; } ime_text = String(); - ime_selection = Point2(); + ime_selection = Vector2i(); + alt_start = false; + alt_start_no_hold = false; _close_ime_window(); _update_ime_text(); } @@ -3126,10 +3182,18 @@ void TextEdit::apply_ime() { if (!has_ime_text()) { return; } + // Force apply the current IME text. - String insert_ime_text = ime_text; - cancel_ime(); - insert_text_at_caret(insert_ime_text); + if (alt_start || alt_start_no_hold) { + cancel_ime(); + if ((alt_code > 0x31 && alt_code < 0xd800) || (alt_code > 0xdfff && alt_code <= 0x10ffff)) { + handle_unicode_input(alt_code); + } + } else { + String insert_ime_text = ime_text; + cancel_ime(); + insert_text_at_caret(insert_ime_text); + } } void TextEdit::set_editable(bool p_editable) { @@ -5966,7 +6030,7 @@ void TextEdit::adjust_viewport_to_caret(int p_caret) { // Get position of the end of caret. if (has_ime_text()) { - if (ime_selection.y != 0) { + if (ime_selection.y > 0) { caret_pos.y = _get_column_x_offset_for_line(get_caret_column(p_caret) + ime_selection.x + ime_selection.y, get_caret_line(p_caret), get_caret_column(p_caret)); } else { caret_pos.y = _get_column_x_offset_for_line(get_caret_column(p_caret) + ime_text.size(), get_caret_line(p_caret), get_caret_column(p_caret)); @@ -6018,7 +6082,7 @@ void TextEdit::center_viewport_to_caret(int p_caret) { // Get position of the end of caret. if (has_ime_text()) { - if (ime_selection.y != 0) { + if (ime_selection.y > 0) { caret_pos.y = _get_column_x_offset_for_line(get_caret_column(p_caret) + ime_selection.x + ime_selection.y, get_caret_line(p_caret), get_caret_column(p_caret)); } else { caret_pos.y = _get_column_x_offset_for_line(get_caret_column(p_caret) + ime_text.size(), get_caret_line(p_caret), get_caret_column(p_caret)); diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h index a448e185b1..c5f838020b 100644 --- a/scene/gui/text_edit.h +++ b/scene/gui/text_edit.h @@ -276,6 +276,7 @@ private: bool setting_text = false; bool alt_start = false; + bool alt_start_no_hold = false; uint32_t alt_code = 0; // Text properties. diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index 8871af23cb..e4f52ee8ee 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -62,8 +62,15 @@ void TreeItem::Cell::draw_icon(const RID &p_where, const Point2 &p_pos, const Si if (icon_region == Rect2i()) { icon->draw_rect_region(p_where, Rect2(p_pos, dsize), Rect2(Point2(), icon->get_size()), p_color); + if (icon_overlay.is_valid()) { + Vector2 offset = icon->get_size() - icon_overlay->get_size(); + icon_overlay->draw_rect_region(p_where, Rect2(p_pos + offset, dsize), Rect2(Point2(), icon_overlay->get_size()), p_color); + } } else { icon->draw_rect_region(p_where, Rect2(p_pos, dsize), icon_region, p_color); + if (icon_overlay.is_valid()) { + icon_overlay->draw_rect_region(p_where, Rect2(p_pos, dsize), icon_region, p_color); + } } } @@ -477,6 +484,24 @@ Ref<Texture2D> TreeItem::get_icon(int p_column) const { return cells[p_column].icon; } +void TreeItem::set_icon_overlay(int p_column, const Ref<Texture2D> &p_icon_overlay) { + ERR_FAIL_INDEX(p_column, cells.size()); + + if (cells[p_column].icon_overlay == p_icon_overlay) { + return; + } + + cells.write[p_column].icon_overlay = p_icon_overlay; + cells.write[p_column].cached_minimum_size_dirty = true; + + _changed_notify(p_column); +} + +Ref<Texture2D> TreeItem::get_icon_overlay(int p_column) const { + ERR_FAIL_INDEX_V(p_column, cells.size(), Ref<Texture2D>()); + return cells[p_column].icon_overlay; +} + void TreeItem::set_icon_region(int p_column, const Rect2 &p_icon_region) { ERR_FAIL_INDEX(p_column, cells.size()); @@ -1633,6 +1658,9 @@ void TreeItem::_bind_methods() { ClassDB::bind_method(D_METHOD("set_icon", "column", "texture"), &TreeItem::set_icon); ClassDB::bind_method(D_METHOD("get_icon", "column"), &TreeItem::get_icon); + ClassDB::bind_method(D_METHOD("set_icon_overlay", "column", "texture"), &TreeItem::set_icon_overlay); + ClassDB::bind_method(D_METHOD("get_icon_overlay", "column"), &TreeItem::get_icon_overlay); + ClassDB::bind_method(D_METHOD("set_icon_region", "column", "region"), &TreeItem::set_icon_region); ClassDB::bind_method(D_METHOD("get_icon_region", "column"), &TreeItem::get_icon_region); diff --git a/scene/gui/tree.h b/scene/gui/tree.h index 4518708685..17ea31a733 100644 --- a/scene/gui/tree.h +++ b/scene/gui/tree.h @@ -60,6 +60,7 @@ private: TreeCellMode mode = TreeItem::CELL_MODE_STRING; Ref<Texture2D> icon; + Ref<Texture2D> icon_overlay; Rect2i icon_region; String text; String xl_text; @@ -257,6 +258,9 @@ public: void set_icon(int p_column, const Ref<Texture2D> &p_icon); Ref<Texture2D> get_icon(int p_column) const; + void set_icon_overlay(int p_column, const Ref<Texture2D> &p_icon_overlay); + Ref<Texture2D> get_icon_overlay(int p_column) const; + void set_icon_region(int p_column, const Rect2 &p_icon_region); Rect2 get_icon_region(int p_column) const; diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index de2332125f..44463ef063 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -1235,14 +1235,16 @@ Ref<World2D> Viewport::find_world_2d() const { } } -void Viewport::_propagate_viewport_notification(Node *p_node, int p_what) { +void Viewport::_propagate_drag_notification(Node *p_node, int p_what) { + // Send notification to p_node and all children and descendant nodes of p_node, except to SubViewports which are not children of a SubViewportContainer. p_node->notification(p_what); + bool is_svc = Object::cast_to<SubViewportContainer>(p_node); for (int i = 0; i < p_node->get_child_count(); i++) { Node *c = p_node->get_child(i); - if (Object::cast_to<Viewport>(c)) { + if (!is_svc && Object::cast_to<SubViewport>(c)) { continue; } - _propagate_viewport_notification(c, p_what); + Viewport::_propagate_drag_notification(c, p_what); } } @@ -1345,7 +1347,7 @@ Ref<InputEvent> Viewport::_make_input_local(const Ref<InputEvent> &ev) { Vector2 Viewport::get_mouse_position() const { ERR_READ_THREAD_GUARD_V(Vector2()); - if (!is_directly_attached_to_screen()) { + if (get_section_root_viewport() != SceneTree::get_singleton()->get_root()) { // Rely on the most recent mouse coordinate from an InputEventMouse in push_input. // In this case get_screen_transform is not applicable, because it is ambiguous. return gui.last_mouse_pos; @@ -1701,14 +1703,15 @@ Control *Viewport::_gui_find_control_at_pos(CanvasItem *p_node, const Point2 &p_ } bool Viewport::_gui_drop(Control *p_at_control, Point2 p_at_pos, bool p_just_check) { - // Attempt grab, try parent controls too. + // Attempt drop, try parent controls too. CanvasItem *ci = p_at_control; + Viewport *section_root = get_section_root_viewport(); while (ci) { Control *control = Object::cast_to<Control>(ci); if (control) { - if (control->can_drop_data(p_at_pos, gui.drag_data)) { + if (control->can_drop_data(p_at_pos, section_root->gui.drag_data)) { if (!p_just_check) { - control->drop_data(p_at_pos, gui.drag_data); + control->drop_data(p_at_pos, section_root->gui.drag_data); } return true; @@ -1806,13 +1809,13 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { if (gui.dragging && mb->get_button_index() == MouseButton::LEFT) { // Alternate drop use (when using force_drag(), as proposed by #5342). - _perform_drop(gui.mouse_focus, pos); + _perform_drop(gui.mouse_focus); } _gui_cancel_tooltip(); } else { if (gui.dragging && mb->get_button_index() == MouseButton::LEFT) { - _perform_drop(gui.drag_mouse_over, gui.drag_mouse_over_pos); + _perform_drop(gui.drag_mouse_over); } gui.mouse_focus_mask.clear_flag(mouse_button_to_mask(mb->get_button_index())); // Remove from mask. @@ -1848,7 +1851,8 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { Point2 mpos = mm->get_position(); // Drag & drop. - if (!gui.drag_attempted && gui.mouse_focus && (mm->get_button_mask().has_flag(MouseButtonMask::LEFT))) { + Viewport *section_root = get_section_root_viewport(); + if (!gui.drag_attempted && gui.mouse_focus && section_root && !section_root->gui.global_dragging && (mm->get_button_mask().has_flag(MouseButtonMask::LEFT))) { gui.drag_accum += mm->get_relative(); float len = gui.drag_accum.length(); if (len > 10) { @@ -1857,11 +1861,12 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { while (ci) { Control *control = Object::cast_to<Control>(ci); if (control) { - gui.dragging = true; - gui.drag_data = control->get_drag_data(control->get_global_transform_with_canvas().affine_inverse().xform(mpos - gui.drag_accum)); - if (gui.drag_data.get_type() != Variant::NIL) { + section_root->gui.global_dragging = true; + section_root->gui.drag_data = control->get_drag_data(control->get_global_transform_with_canvas().affine_inverse().xform(mpos - gui.drag_accum)); + if (section_root->gui.drag_data.get_type() != Variant::NIL) { gui.mouse_focus = nullptr; gui.mouse_focus_mask.clear(); + gui.dragging = true; break; } else { Control *drag_preview = _gui_get_drag_preview(); @@ -1870,7 +1875,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { memdelete(drag_preview); gui.drag_preview_id = ObjectID(); } - gui.dragging = false; + section_root->gui.global_dragging = false; } if (control->data.mouse_filter == Control::MOUSE_FILTER_STOP) { @@ -1888,7 +1893,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { gui.drag_attempted = true; if (gui.dragging) { - _propagate_viewport_notification(this, NOTIFICATION_DRAG_BEGIN); + Viewport::_propagate_drag_notification(section_root, NOTIFICATION_DRAG_BEGIN); } } } @@ -1986,105 +1991,29 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { } if (gui.dragging) { - // Handle drag & drop. + // Handle drag & drop. This happens in the viewport where dragging started. Control *drag_preview = _gui_get_drag_preview(); if (drag_preview) { drag_preview->set_position(mpos); } - gui.drag_mouse_over = over; - gui.drag_mouse_over_pos = Vector2(); - - // Find the window this is above of. - // See if there is an embedder. - Viewport *embedder = nullptr; - Vector2 viewport_pos; - - if (is_embedding_subwindows()) { - embedder = this; - viewport_pos = mpos; - } else { - // Not an embedder, but may be a subwindow of an embedder. - Window *w = Object::cast_to<Window>(this); - if (w) { - if (w->is_embedded()) { - embedder = w->get_embedder(); - - viewport_pos = get_final_transform().xform(mpos) + w->get_position(); // To parent coords. - } - } - } - - Viewport *viewport_under = nullptr; - - if (embedder) { - // Use embedder logic. - - for (int i = embedder->gui.sub_windows.size() - 1; i >= 0; i--) { - Window *sw = embedder->gui.sub_windows[i].window; - Rect2 swrect = Rect2i(sw->get_position(), sw->get_size()); - if (!sw->get_flag(Window::FLAG_BORDERLESS)) { - int title_height = sw->theme_cache.title_height; - swrect.position.y -= title_height; - swrect.size.y += title_height; - } - - if (swrect.has_point(viewport_pos)) { - viewport_under = sw; - viewport_pos -= sw->get_position(); - } - } - - if (!viewport_under) { - // Not in a subwindow, likely in embedder. - viewport_under = embedder; - } - } else { - // Use DisplayServer logic. - Vector2i screen_mouse_pos = DisplayServer::get_singleton()->mouse_get_position(); - - DisplayServer::WindowID window_id = DisplayServer::get_singleton()->get_window_at_screen_position(screen_mouse_pos); - - if (window_id != DisplayServer::INVALID_WINDOW_ID) { - ObjectID object_under = DisplayServer::get_singleton()->window_get_attached_instance_id(window_id); - - if (object_under != ObjectID()) { // Fetch window. - Window *w = Object::cast_to<Window>(ObjectDB::get_instance(object_under)); - if (w) { - viewport_under = w; - viewport_pos = w->get_final_transform().affine_inverse().xform(screen_mouse_pos - w->get_position()); - } - } + gui.drag_mouse_over = section_root->gui.target_control; + if (gui.drag_mouse_over) { + if (!_gui_drop(gui.drag_mouse_over, gui.drag_mouse_over->get_local_mouse_position(), true)) { + gui.drag_mouse_over = nullptr; } - } - - if (viewport_under) { - if (viewport_under != this) { - Transform2D ai = viewport_under->get_final_transform().affine_inverse(); - viewport_pos = ai.xform(viewport_pos); - } - // Find control under at position. - gui.drag_mouse_over = viewport_under->gui_find_control(viewport_pos); if (gui.drag_mouse_over) { - Transform2D localizer = gui.drag_mouse_over->get_global_transform_with_canvas().affine_inverse(); - gui.drag_mouse_over_pos = localizer.xform(viewport_pos); - - bool can_drop = _gui_drop(gui.drag_mouse_over, gui.drag_mouse_over_pos, true); - - if (!can_drop) { - ds_cursor_shape = DisplayServer::CURSOR_FORBIDDEN; - } else { - ds_cursor_shape = DisplayServer::CURSOR_CAN_DROP; - } + ds_cursor_shape = DisplayServer::CURSOR_CAN_DROP; + } else { + ds_cursor_shape = DisplayServer::CURSOR_FORBIDDEN; } - - } else { - gui.drag_mouse_over = nullptr; } } - if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CURSOR_SHAPE) && !Object::cast_to<SubViewportContainer>(over)) { + if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CURSOR_SHAPE) && (gui.dragging || (!section_root->gui.global_dragging && !Object::cast_to<SubViewportContainer>(over)))) { + // If dragging is active, then set the cursor shape only from the Viewport where dragging started. + // If dragging is inactive, then set the cursor shape only when not over a SubViewportContainer. DisplayServer::get_singleton()->cursor_set_shape(ds_cursor_shape); } } @@ -2284,10 +2213,10 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { } } -void Viewport::_perform_drop(Control *p_control, Point2 p_pos) { +void Viewport::_perform_drop(Control *p_control) { // Without any arguments, simply cancel Drag and Drop. if (p_control) { - gui.drag_successful = _gui_drop(p_control, p_pos, false); + gui.drag_successful = _gui_drop(p_control, p_control->get_local_mouse_position(), false); } else { gui.drag_successful = false; } @@ -2298,10 +2227,12 @@ void Viewport::_perform_drop(Control *p_control, Point2 p_pos) { gui.drag_preview_id = ObjectID(); } - gui.drag_data = Variant(); + Viewport *section_root = get_section_root_viewport(); + section_root->gui.drag_data = Variant(); gui.dragging = false; + section_root->gui.global_dragging = false; gui.drag_mouse_over = nullptr; - _propagate_viewport_notification(this, NOTIFICATION_DRAG_END); + Viewport::_propagate_drag_notification(section_root, NOTIFICATION_DRAG_END); // Display the new cursor shape instantly. update_mouse_cursor_state(); } @@ -2331,14 +2262,16 @@ void Viewport::_gui_force_drag(Control *p_base, const Variant &p_data, Control * ERR_FAIL_COND_MSG(p_data.get_type() == Variant::NIL, "Drag data must be a value."); gui.dragging = true; - gui.drag_data = p_data; + Viewport *section_root = get_section_root_viewport(); + section_root->gui.global_dragging = true; + section_root->gui.drag_data = p_data; gui.mouse_focus = nullptr; gui.mouse_focus_mask.clear(); if (p_control) { _gui_set_drag_preview(p_base, p_control); } - _propagate_viewport_notification(this, NOTIFICATION_DRAG_BEGIN); + Viewport::_propagate_drag_notification(section_root, NOTIFICATION_DRAG_BEGIN); } void Viewport::_gui_set_drag_preview(Control *p_base, Control *p_control) { @@ -3022,6 +2955,7 @@ void Viewport::_update_mouse_over() { } void Viewport::_update_mouse_over(Vector2 p_pos) { + gui.last_mouse_pos = p_pos; // Necessary, because mouse cursor can be over Viewports that are not reached by the InputEvent. // Look for embedded windows at mouse position. if (is_embedding_subwindows()) { for (int i = gui.sub_windows.size() - 1; i >= 0; i--) { @@ -3073,6 +3007,7 @@ void Viewport::_update_mouse_over(Vector2 p_pos) { // Look for Controls at mouse position. Control *over = gui_find_control(p_pos); + get_section_root_viewport()->gui.target_control = over; bool notify_embedded_viewports = false; if (over != gui.mouse_over || (!over && !gui.mouse_over_hierarchy.is_empty())) { // Find the common ancestor of `gui.mouse_over` and `over`. @@ -3195,6 +3130,10 @@ void Viewport::_drop_mouse_over(Control *p_until_control) { if (gui.mouse_over && gui.mouse_over->is_inside_tree()) { gui.mouse_over->notification(Control::NOTIFICATION_MOUSE_EXIT_SELF); } + Viewport *section_root = get_section_root_viewport(); + if (section_root && section_root->gui.target_control == gui.mouse_over) { + section_root->gui.target_control = nullptr; + } gui.mouse_over = nullptr; // Send Mouse Exit notifications to children first. Don't send to p_until_control or above. @@ -3403,7 +3342,7 @@ bool Viewport::is_input_disabled() const { Variant Viewport::gui_get_drag_data() const { ERR_READ_THREAD_GUARD_V(Variant()); - return gui.drag_data; + return get_section_root_viewport()->gui.drag_data; } PackedStringArray Viewport::get_configuration_warnings() const { @@ -3597,7 +3536,7 @@ bool Viewport::is_snap_2d_vertices_to_pixel_enabled() const { bool Viewport::gui_is_dragging() const { ERR_READ_THREAD_GUARD_V(false); - return gui.dragging; + return get_section_root_viewport()->gui.global_dragging; } bool Viewport::gui_is_drag_successful() const { @@ -5156,9 +5095,12 @@ Transform2D SubViewport::get_popup_base_transform() const { return c->get_screen_transform() * container_transform * get_final_transform(); } -bool SubViewport::is_directly_attached_to_screen() const { - // SubViewports, that are used as Textures are not considered to be directly attached to screen. - return Object::cast_to<SubViewportContainer>(get_parent()) && get_parent()->get_viewport() && get_parent()->get_viewport()->is_directly_attached_to_screen(); +Viewport *SubViewport::get_section_root_viewport() const { + if (Object::cast_to<SubViewportContainer>(get_parent()) && get_parent()->get_viewport()) { + return get_parent()->get_viewport()->get_section_root_viewport(); + } + SubViewport *vp = const_cast<SubViewport *>(this); + return vp; } bool SubViewport::is_attached_in_viewport() const { diff --git a/scene/main/viewport.h b/scene/main/viewport.h index faa36851e9..f6a461d54e 100644 --- a/scene/main/viewport.h +++ b/scene/main/viewport.h @@ -281,7 +281,7 @@ private: bool disable_3d = false; - void _propagate_viewport_notification(Node *p_node, int p_what); + static void _propagate_drag_notification(Node *p_node, int p_what); void _update_global_transform(); @@ -362,7 +362,6 @@ private: Window *subwindow_over = nullptr; // mouse_over and subwindow_over are mutually exclusive. At all times at least one of them is nullptr. Window *windowmanager_window_over = nullptr; // Only used in root Viewport. Control *drag_mouse_over = nullptr; - Vector2 drag_mouse_over_pos; Control *tooltip_control = nullptr; Window *tooltip_popup = nullptr; Label *tooltip_label = nullptr; @@ -371,7 +370,7 @@ private: Point2 last_mouse_pos; Point2 drag_accum; bool drag_attempted = false; - Variant drag_data; + Variant drag_data; // Only used in root-Viewport and SubViewports, that are not children of a SubViewportContainer. ObjectID drag_preview_id; Ref<SceneTreeTimer> tooltip_timer; double tooltip_delay = 0.0; @@ -379,8 +378,10 @@ private: List<Control *> roots; HashSet<ObjectID> canvas_parents_with_dirty_order; int canvas_sort_index = 0; //for sorting items with canvas as root - bool dragging = false; + bool dragging = false; // Is true in the viewport in which dragging started while dragging is active. + bool global_dragging = false; // Is true while dragging is active. Only used in root-Viewport and SubViewports that are not children of a SubViewportContainer. bool drag_successful = false; + Control *target_control = nullptr; // Control that the mouse is over in the innermost nested Viewport. Only used in root-Viewport and SubViewports, that are not children of a SubViewportContainer. bool embed_subwindows_hint = false; Window *subwindow_focused = nullptr; @@ -408,7 +409,7 @@ private: Control *_gui_find_control_at_pos(CanvasItem *p_node, const Point2 &p_global, const Transform2D &p_xform); void _gui_input_event(Ref<InputEvent> p_event); - void _perform_drop(Control *p_control = nullptr, Point2 p_pos = Point2()); + void _perform_drop(Control *p_control = nullptr); void _gui_cleanup_internal_state(Ref<InputEvent> p_event); void _push_unhandled_input_internal(const Ref<InputEvent> &p_event); @@ -672,9 +673,9 @@ public: Transform2D get_screen_transform() const; virtual Transform2D get_screen_transform_internal(bool p_absolute_position = false) const; virtual Transform2D get_popup_base_transform() const { return Transform2D(); } - virtual bool is_directly_attached_to_screen() const { return false; }; - virtual bool is_attached_in_viewport() const { return false; }; - virtual bool is_sub_viewport() const { return false; }; + virtual Viewport *get_section_root_viewport() const { return nullptr; } + virtual bool is_attached_in_viewport() const { return false; } + virtual bool is_sub_viewport() const { return false; } private: // 2D audio, camera, and physics. (don't put World2D here because World2D is needed for Control nodes). @@ -836,9 +837,9 @@ public: virtual Transform2D get_screen_transform_internal(bool p_absolute_position = false) const override; virtual Transform2D get_popup_base_transform() const override; - virtual bool is_directly_attached_to_screen() const override; + virtual Viewport *get_section_root_viewport() const override; virtual bool is_attached_in_viewport() const override; - virtual bool is_sub_viewport() const override { return true; }; + virtual bool is_sub_viewport() const override { return true; } void _validate_property(PropertyInfo &p_property) const; SubViewport(); diff --git a/scene/main/window.cpp b/scene/main/window.cpp index dafbbb867b..803ce89bc9 100644 --- a/scene/main/window.cpp +++ b/scene/main/window.cpp @@ -1223,6 +1223,7 @@ void Window::_update_viewport_size() { TS->font_set_global_oversampling(font_oversampling); if (!ci_updated) { update_canvas_items(); + emit_signal(SNAME("size_changed")); } } } @@ -2754,12 +2755,16 @@ Transform2D Window::get_popup_base_transform() const { return popup_base_transform; } -bool Window::is_directly_attached_to_screen() const { +Viewport *Window::get_section_root_viewport() const { if (get_embedder()) { - return get_embedder()->is_directly_attached_to_screen(); + return get_embedder()->get_section_root_viewport(); } - // Distinguish between the case that this is a native Window and not inside the tree. - return is_inside_tree(); + if (is_inside_tree()) { + // Native window. + return SceneTree::get_singleton()->get_root(); + } + Window *vp = const_cast<Window *>(this); + return vp; } bool Window::is_attached_in_viewport() const { diff --git a/scene/main/window.h b/scene/main/window.h index 84d2febe51..47aaf73728 100644 --- a/scene/main/window.h +++ b/scene/main/window.h @@ -469,7 +469,7 @@ public: virtual Transform2D get_final_transform() const override; virtual Transform2D get_screen_transform_internal(bool p_absolute_position = false) const override; virtual Transform2D get_popup_base_transform() const override; - virtual bool is_directly_attached_to_screen() const override; + virtual Viewport *get_section_root_viewport() const override; virtual bool is_attached_in_viewport() const override; Rect2i get_parent_rect() const; diff --git a/scene/resources/3d/importer_mesh.cpp b/scene/resources/3d/importer_mesh.cpp index 4f4c485db3..47cd64f19a 100644 --- a/scene/resources/3d/importer_mesh.cpp +++ b/scene/resources/3d/importer_mesh.cpp @@ -34,6 +34,7 @@ #include "core/math/convex_hull.h" #include "core/math/random_pcg.h" #include "core/math/static_raycaster.h" +#include "scene/resources/animation_library.h" #include "scene/resources/surface_tool.h" #include <cstdint> @@ -134,9 +135,18 @@ void ImporterMesh::Surface::_split_normals(Array &r_arrays, const LocalVector<in } } +String ImporterMesh::validate_blend_shape_name(const String &p_name) { + String name = p_name; + const char *characters = ":"; + for (const char *p = characters; *p; p++) { + name = name.replace(String::chr(*p), "_"); + } + return name; +} + void ImporterMesh::add_blend_shape(const String &p_name) { ERR_FAIL_COND(surfaces.size() > 0); - blend_shapes.push_back(p_name); + blend_shapes.push_back(validate_blend_shape_name(p_name)); } int ImporterMesh::get_blend_shape_count() const { @@ -849,7 +859,7 @@ void ImporterMesh::create_shadow_mesh() { index_wptr[j] = vertex_remap[index]; } - if (SurfaceTool::optimize_vertex_cache_func) { + if (SurfaceTool::optimize_vertex_cache_func && surfaces[i].primitive == Mesh::PRIMITIVE_TRIANGLES) { SurfaceTool::optimize_vertex_cache_func((unsigned int *)index_wptr, (const unsigned int *)index_wptr, index_count, new_vertices.size()); } @@ -871,7 +881,7 @@ void ImporterMesh::create_shadow_mesh() { index_wptr[k] = vertex_remap[index]; } - if (SurfaceTool::optimize_vertex_cache_func) { + if (SurfaceTool::optimize_vertex_cache_func && surfaces[i].primitive == Mesh::PRIMITIVE_TRIANGLES) { SurfaceTool::optimize_vertex_cache_func((unsigned int *)index_wptr, (const unsigned int *)index_wptr, index_count, new_vertices.size()); } diff --git a/scene/resources/3d/importer_mesh.h b/scene/resources/3d/importer_mesh.h index 5eb4ee884e..c7e3a059d6 100644 --- a/scene/resources/3d/importer_mesh.h +++ b/scene/resources/3d/importer_mesh.h @@ -95,6 +95,8 @@ public: int get_blend_shape_count() const; String get_blend_shape_name(int p_blend_shape) const; + static String validate_blend_shape_name(const String &p_name); + void add_surface(Mesh::PrimitiveType p_primitive, const Array &p_arrays, const TypedArray<Array> &p_blend_shapes = Array(), const Dictionary &p_lods = Dictionary(), const Ref<Material> &p_material = Ref<Material>(), const String &p_name = String(), const uint64_t p_flags = 0); int get_surface_count() const; diff --git a/scene/resources/audio_stream_wav.cpp b/scene/resources/audio_stream_wav.cpp index de67a93bd1..f9787dde2e 100644 --- a/scene/resources/audio_stream_wav.cpp +++ b/scene/resources/audio_stream_wav.cpp @@ -297,7 +297,7 @@ int AudioStreamPlaybackWAV::mix(AudioFrame *p_buffer, float p_rate_scale, int p_ int64_t loop_end_fp = ((int64_t)base->loop_end << MIX_FRAC_BITS); int64_t length_fp = ((int64_t)len << MIX_FRAC_BITS); int64_t begin_limit = (base->loop_mode != AudioStreamWAV::LOOP_DISABLED) ? loop_begin_fp : 0; - int64_t end_limit = (base->loop_mode != AudioStreamWAV::LOOP_DISABLED) ? loop_end_fp : length_fp; + int64_t end_limit = (base->loop_mode != AudioStreamWAV::LOOP_DISABLED) ? loop_end_fp : length_fp - MIX_FRAC_LEN; bool is_stereo = base->stereo; int32_t todo = p_frames; diff --git a/scene/resources/bit_map.cpp b/scene/resources/bit_map.cpp index 653a4f4949..53f97fefc9 100644 --- a/scene/resources/bit_map.cpp +++ b/scene/resources/bit_map.cpp @@ -559,6 +559,7 @@ void BitMap::grow_mask(int p_pixels, const Rect2i &p_rect) { bool bit_value = p_pixels > 0; p_pixels = Math::abs(p_pixels); + const int pixels2 = p_pixels * p_pixels; Rect2i r = Rect2i(0, 0, width, height).intersection(p_rect); @@ -588,8 +589,8 @@ void BitMap::grow_mask(int p_pixels, const Rect2i &p_rect) { } } - float d = Point2(j, i).distance_to(Point2(x, y)) - CMP_EPSILON; - if (d > p_pixels) { + float d = Point2(j, i).distance_squared_to(Point2(x, y)) - CMP_EPSILON2; + if (d > pixels2) { continue; } diff --git a/servers/audio_server.cpp b/servers/audio_server.cpp index e06079efe8..5835ecfed0 100644 --- a/servers/audio_server.cpp +++ b/servers/audio_server.cpp @@ -1487,7 +1487,7 @@ void AudioServer::init() { void AudioServer::update() { #ifdef DEBUG_ENABLED - if (EngineDebugger::is_profiling("servers")) { + if (EngineDebugger::is_profiling(SNAME("servers"))) { // Driver time includes server time + effects times // Server time includes effects times uint64_t driver_time = AudioDriver::get_singleton()->get_profiling_time(); diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp index f51b4ae8d0..88a22c6fc3 100644 --- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp @@ -351,710 +351,12 @@ void RendererCanvasRenderRD::free_polygon(PolygonID p_polygon) { //////////////////// -void RendererCanvasRenderRD::_bind_canvas_texture(RD::DrawListID p_draw_list, RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, RID &r_last_texture, PushConstant &push_constant, Size2 &r_texpixel_size, bool p_texture_is_data) { - if (p_texture == RID()) { - p_texture = default_canvas_texture; - } - - if (r_last_texture == p_texture) { - return; //nothing to do, its the same - } - - RID uniform_set; - Color specular_shininess; - Size2i size; - bool use_normal; - bool use_specular; - - bool success = RendererRD::TextureStorage::get_singleton()->canvas_texture_get_uniform_set(p_texture, p_base_filter, p_base_repeat, shader.default_version_rd_shader, CANVAS_TEXTURE_UNIFORM_SET, bool(push_constant.flags & FLAGS_CONVERT_ATTRIBUTES_TO_LINEAR), uniform_set, size, specular_shininess, use_normal, use_specular, p_texture_is_data); - //something odd happened - if (!success) { - _bind_canvas_texture(p_draw_list, default_canvas_texture, p_base_filter, p_base_repeat, r_last_texture, push_constant, r_texpixel_size); - return; - } - - RD::get_singleton()->draw_list_bind_uniform_set(p_draw_list, uniform_set, CANVAS_TEXTURE_UNIFORM_SET); - - if (specular_shininess.a < 0.999) { - push_constant.flags |= FLAGS_DEFAULT_SPECULAR_MAP_USED; - } else { - push_constant.flags &= ~FLAGS_DEFAULT_SPECULAR_MAP_USED; - } - - if (use_normal) { - push_constant.flags |= FLAGS_DEFAULT_NORMAL_MAP_USED; - } else { - push_constant.flags &= ~FLAGS_DEFAULT_NORMAL_MAP_USED; - } - - push_constant.specular_shininess = uint32_t(CLAMP(specular_shininess.a * 255.0, 0, 255)) << 24; - push_constant.specular_shininess |= uint32_t(CLAMP(specular_shininess.b * 255.0, 0, 255)) << 16; - push_constant.specular_shininess |= uint32_t(CLAMP(specular_shininess.g * 255.0, 0, 255)) << 8; - push_constant.specular_shininess |= uint32_t(CLAMP(specular_shininess.r * 255.0, 0, 255)); - - r_texpixel_size.x = 1.0 / float(size.x); - r_texpixel_size.y = 1.0 / float(size.y); - - push_constant.color_texture_pixel_size[0] = r_texpixel_size.x; - push_constant.color_texture_pixel_size[1] = r_texpixel_size.y; - - r_last_texture = p_texture; -} - _FORCE_INLINE_ static uint32_t _indices_to_primitives(RS::PrimitiveType p_primitive, uint32_t p_indices) { static const uint32_t divisor[RS::PRIMITIVE_MAX] = { 1, 2, 1, 3, 1 }; static const uint32_t subtractor[RS::PRIMITIVE_MAX] = { 0, 0, 1, 0, 1 }; return (p_indices - subtractor[p_primitive]) / divisor[p_primitive]; } -void RendererCanvasRenderRD::_render_item(RD::DrawListID p_draw_list, RID p_render_target, const Item *p_item, RD::FramebufferFormatID p_framebuffer_format, const Transform2D &p_canvas_transform_inverse, Item *¤t_clip, Light *p_lights, PipelineVariants *p_pipeline_variants, bool &r_sdf_used, const Point2 &p_repeat_offset, RenderingMethod::RenderInfo *r_render_info) { - //create an empty push constant - RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton(); - RendererRD::MeshStorage *mesh_storage = RendererRD::MeshStorage::get_singleton(); - RendererRD::ParticlesStorage *particles_storage = RendererRD::ParticlesStorage::get_singleton(); - - RS::CanvasItemTextureFilter current_filter = default_filter; - RS::CanvasItemTextureRepeat current_repeat = default_repeat; - - if (p_item->texture_filter != RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT) { - current_filter = p_item->texture_filter; - } - - if (p_item->texture_repeat != RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT) { - current_repeat = p_item->texture_repeat; - } - - PushConstant push_constant; - Transform2D base_transform = p_item->final_transform; - if (p_item->repeat_source_item && (p_repeat_offset.x || p_repeat_offset.y)) { - base_transform.columns[2] += p_item->repeat_source_item->final_transform.basis_xform(p_repeat_offset); - } - base_transform = p_canvas_transform_inverse * base_transform; - - Transform2D draw_transform; - _update_transform_2d_to_mat2x3(base_transform, push_constant.world); - - Color base_color = p_item->final_modulate; - bool use_linear_colors = texture_storage->render_target_is_using_hdr(p_render_target); - - for (int i = 0; i < 4; i++) { - push_constant.modulation[i] = 0; - push_constant.ninepatch_margins[i] = 0; - push_constant.src_rect[i] = 0; - push_constant.dst_rect[i] = 0; - } - push_constant.flags = 0; - push_constant.color_texture_pixel_size[0] = 0; - push_constant.color_texture_pixel_size[1] = 0; - - push_constant.pad[0] = 0; - push_constant.pad[1] = 0; - - push_constant.lights[0] = 0; - push_constant.lights[1] = 0; - push_constant.lights[2] = 0; - push_constant.lights[3] = 0; - - uint32_t base_flags = 0; - base_flags |= use_linear_colors ? FLAGS_CONVERT_ATTRIBUTES_TO_LINEAR : 0; - - uint16_t light_count = 0; - PipelineLightMode light_mode; - - { - Light *light = p_lights; - - while (light) { - if (light->render_index_cache >= 0 && p_item->light_mask & light->item_mask && p_item->z_final >= light->z_min && p_item->z_final <= light->z_max && p_item->global_rect_cache.intersects_transformed(light->xform_cache, light->rect_cache)) { - uint32_t light_index = light->render_index_cache; - push_constant.lights[light_count >> 2] |= light_index << ((light_count & 3) * 8); - - light_count++; - - if (light_count == MAX_LIGHTS_PER_ITEM - 1) { - break; - } - } - light = light->next_ptr; - } - - base_flags |= light_count << FLAGS_LIGHT_COUNT_SHIFT; - } - - light_mode = (light_count > 0 || using_directional_lights) ? PIPELINE_LIGHT_MODE_ENABLED : PIPELINE_LIGHT_MODE_DISABLED; - - PipelineVariants *pipeline_variants = p_pipeline_variants; - - bool reclip = false; - - RID last_texture; - Size2 texpixel_size; - - bool skipping = false; - - const Item::Command *c = p_item->commands; - while (c) { - if (skipping && c->type != Item::Command::TYPE_ANIMATION_SLICE) { - c = c->next; - continue; - } - - push_constant.flags = base_flags | (push_constant.flags & (FLAGS_DEFAULT_NORMAL_MAP_USED | FLAGS_DEFAULT_SPECULAR_MAP_USED)); // Reset on each command for safety, keep canvastexture binding config. - - switch (c->type) { - case Item::Command::TYPE_RECT: { - const Item::CommandRect *rect = static_cast<const Item::CommandRect *>(c); - - if (rect->flags & CANVAS_RECT_TILE) { - current_repeat = RenderingServer::CanvasItemTextureRepeat::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED; - } - - Color modulated = rect->modulate * base_color; - if (use_linear_colors) { - modulated = modulated.srgb_to_linear(); - } - - //bind pipeline - if (rect->flags & CANVAS_RECT_LCD) { - RID pipeline = pipeline_variants->variants[light_mode][PIPELINE_VARIANT_QUAD_LCD_BLEND].get_render_pipeline(RD::INVALID_ID, p_framebuffer_format); - RD::get_singleton()->draw_list_bind_render_pipeline(p_draw_list, pipeline); - RD::get_singleton()->draw_list_set_blend_constants(p_draw_list, modulated); - } else { - RID pipeline = pipeline_variants->variants[light_mode][PIPELINE_VARIANT_QUAD].get_render_pipeline(RD::INVALID_ID, p_framebuffer_format); - RD::get_singleton()->draw_list_bind_render_pipeline(p_draw_list, pipeline); - } - - //bind textures - - _bind_canvas_texture(p_draw_list, rect->texture, current_filter, current_repeat, last_texture, push_constant, texpixel_size, bool(rect->flags & CANVAS_RECT_MSDF)); - - Rect2 src_rect; - Rect2 dst_rect; - - if (rect->texture != RID()) { - src_rect = (rect->flags & CANVAS_RECT_REGION) ? Rect2(rect->source.position * texpixel_size, rect->source.size * texpixel_size) : Rect2(0, 0, 1, 1); - dst_rect = Rect2(rect->rect.position, rect->rect.size); - - if (dst_rect.size.width < 0) { - dst_rect.position.x += dst_rect.size.width; - dst_rect.size.width *= -1; - } - if (dst_rect.size.height < 0) { - dst_rect.position.y += dst_rect.size.height; - dst_rect.size.height *= -1; - } - - if (rect->flags & CANVAS_RECT_FLIP_H) { - src_rect.size.x *= -1; - push_constant.flags |= FLAGS_FLIP_H; - } - - if (rect->flags & CANVAS_RECT_FLIP_V) { - src_rect.size.y *= -1; - push_constant.flags |= FLAGS_FLIP_V; - } - - if (rect->flags & CANVAS_RECT_TRANSPOSE) { - push_constant.flags |= FLAGS_TRANSPOSE_RECT; - } - - if (rect->flags & CANVAS_RECT_CLIP_UV) { - push_constant.flags |= FLAGS_CLIP_RECT_UV; - } - - } else { - dst_rect = Rect2(rect->rect.position, rect->rect.size); - - if (dst_rect.size.width < 0) { - dst_rect.position.x += dst_rect.size.width; - dst_rect.size.width *= -1; - } - if (dst_rect.size.height < 0) { - dst_rect.position.y += dst_rect.size.height; - dst_rect.size.height *= -1; - } - - src_rect = Rect2(0, 0, 1, 1); - } - - if (rect->flags & CANVAS_RECT_MSDF) { - push_constant.flags |= FLAGS_USE_MSDF; - push_constant.msdf[0] = rect->px_range; // Pixel range. - push_constant.msdf[1] = rect->outline; // Outline size. - push_constant.msdf[2] = 0.f; // Reserved. - push_constant.msdf[3] = 0.f; // Reserved. - } else if (rect->flags & CANVAS_RECT_LCD) { - push_constant.flags |= FLAGS_USE_LCD; - } - - push_constant.modulation[0] = modulated.r; - push_constant.modulation[1] = modulated.g; - push_constant.modulation[2] = modulated.b; - push_constant.modulation[3] = modulated.a; - - push_constant.src_rect[0] = src_rect.position.x; - push_constant.src_rect[1] = src_rect.position.y; - push_constant.src_rect[2] = src_rect.size.width; - push_constant.src_rect[3] = src_rect.size.height; - - push_constant.dst_rect[0] = dst_rect.position.x; - push_constant.dst_rect[1] = dst_rect.position.y; - push_constant.dst_rect[2] = dst_rect.size.width; - push_constant.dst_rect[3] = dst_rect.size.height; - - RD::get_singleton()->draw_list_set_push_constant(p_draw_list, &push_constant, sizeof(PushConstant)); - RD::get_singleton()->draw_list_bind_index_array(p_draw_list, shader.quad_index_array); - RD::get_singleton()->draw_list_draw(p_draw_list, true); - - if (r_render_info) { - r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_OBJECTS_IN_FRAME]++; - r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_PRIMITIVES_IN_FRAME] += 2; - r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_DRAW_CALLS_IN_FRAME]++; - } - - } break; - - case Item::Command::TYPE_NINEPATCH: { - const Item::CommandNinePatch *np = static_cast<const Item::CommandNinePatch *>(c); - - //bind pipeline - { - RID pipeline = pipeline_variants->variants[light_mode][PIPELINE_VARIANT_NINEPATCH].get_render_pipeline(RD::INVALID_ID, p_framebuffer_format); - RD::get_singleton()->draw_list_bind_render_pipeline(p_draw_list, pipeline); - } - - //bind textures - - _bind_canvas_texture(p_draw_list, np->texture, current_filter, current_repeat, last_texture, push_constant, texpixel_size); - - Rect2 src_rect; - Rect2 dst_rect(np->rect.position.x, np->rect.position.y, np->rect.size.x, np->rect.size.y); - - if (np->texture == RID()) { - texpixel_size = Size2(1, 1); - src_rect = Rect2(0, 0, 1, 1); - - } else { - if (np->source != Rect2()) { - src_rect = Rect2(np->source.position.x * texpixel_size.width, np->source.position.y * texpixel_size.height, np->source.size.x * texpixel_size.width, np->source.size.y * texpixel_size.height); - push_constant.color_texture_pixel_size[0] = 1.0 / np->source.size.width; - push_constant.color_texture_pixel_size[1] = 1.0 / np->source.size.height; - - } else { - src_rect = Rect2(0, 0, 1, 1); - } - } - - Color modulated = np->color * base_color; - if (use_linear_colors) { - modulated = modulated.srgb_to_linear(); - } - - push_constant.modulation[0] = modulated.r; - push_constant.modulation[1] = modulated.g; - push_constant.modulation[2] = modulated.b; - push_constant.modulation[3] = modulated.a; - - push_constant.src_rect[0] = src_rect.position.x; - push_constant.src_rect[1] = src_rect.position.y; - push_constant.src_rect[2] = src_rect.size.width; - push_constant.src_rect[3] = src_rect.size.height; - - push_constant.dst_rect[0] = dst_rect.position.x; - push_constant.dst_rect[1] = dst_rect.position.y; - push_constant.dst_rect[2] = dst_rect.size.width; - push_constant.dst_rect[3] = dst_rect.size.height; - - push_constant.flags |= int(np->axis_x) << FLAGS_NINEPATCH_H_MODE_SHIFT; - push_constant.flags |= int(np->axis_y) << FLAGS_NINEPATCH_V_MODE_SHIFT; - - if (np->draw_center) { - push_constant.flags |= FLAGS_NINEPACH_DRAW_CENTER; - } - - push_constant.ninepatch_margins[0] = np->margin[SIDE_LEFT]; - push_constant.ninepatch_margins[1] = np->margin[SIDE_TOP]; - push_constant.ninepatch_margins[2] = np->margin[SIDE_RIGHT]; - push_constant.ninepatch_margins[3] = np->margin[SIDE_BOTTOM]; - - RD::get_singleton()->draw_list_set_push_constant(p_draw_list, &push_constant, sizeof(PushConstant)); - RD::get_singleton()->draw_list_bind_index_array(p_draw_list, shader.quad_index_array); - RD::get_singleton()->draw_list_draw(p_draw_list, true); - - if (r_render_info) { - r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_OBJECTS_IN_FRAME]++; - r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_PRIMITIVES_IN_FRAME] += 2; - r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_DRAW_CALLS_IN_FRAME]++; - } - - // Restore if overridden. - push_constant.color_texture_pixel_size[0] = texpixel_size.x; - push_constant.color_texture_pixel_size[1] = texpixel_size.y; - - } break; - case Item::Command::TYPE_POLYGON: { - const Item::CommandPolygon *polygon = static_cast<const Item::CommandPolygon *>(c); - - PolygonBuffers *pb = polygon_buffers.polygons.getptr(polygon->polygon.polygon_id); - ERR_CONTINUE(!pb); - //bind pipeline - { - static const PipelineVariant variant[RS::PRIMITIVE_MAX] = { PIPELINE_VARIANT_ATTRIBUTE_POINTS, PIPELINE_VARIANT_ATTRIBUTE_LINES, PIPELINE_VARIANT_ATTRIBUTE_LINES_STRIP, PIPELINE_VARIANT_ATTRIBUTE_TRIANGLES, PIPELINE_VARIANT_ATTRIBUTE_TRIANGLE_STRIP }; - ERR_CONTINUE(polygon->primitive < 0 || polygon->primitive >= RS::PRIMITIVE_MAX); - RID pipeline = pipeline_variants->variants[light_mode][variant[polygon->primitive]].get_render_pipeline(pb->vertex_format_id, p_framebuffer_format); - RD::get_singleton()->draw_list_bind_render_pipeline(p_draw_list, pipeline); - } - - if (polygon->primitive == RS::PRIMITIVE_LINES) { - //not supported in most hardware, so pointless - //RD::get_singleton()->draw_list_set_line_width(p_draw_list, polygon->line_width); - } - - //bind textures - - _bind_canvas_texture(p_draw_list, polygon->texture, current_filter, current_repeat, last_texture, push_constant, texpixel_size); - - Color color = base_color; - if (use_linear_colors) { - color = color.srgb_to_linear(); - } - - push_constant.modulation[0] = color.r; - push_constant.modulation[1] = color.g; - push_constant.modulation[2] = color.b; - push_constant.modulation[3] = color.a; - - for (int j = 0; j < 4; j++) { - push_constant.src_rect[j] = 0; - push_constant.dst_rect[j] = 0; - push_constant.ninepatch_margins[j] = 0; - } - - RD::get_singleton()->draw_list_set_push_constant(p_draw_list, &push_constant, sizeof(PushConstant)); - RD::get_singleton()->draw_list_bind_vertex_array(p_draw_list, pb->vertex_array); - if (pb->indices.is_valid()) { - RD::get_singleton()->draw_list_bind_index_array(p_draw_list, pb->indices); - } - RD::get_singleton()->draw_list_draw(p_draw_list, pb->indices.is_valid()); - - if (r_render_info) { - r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_OBJECTS_IN_FRAME]++; - r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_PRIMITIVES_IN_FRAME] += _indices_to_primitives(polygon->primitive, pb->primitive_count); - r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_DRAW_CALLS_IN_FRAME]++; - } - - } break; - case Item::Command::TYPE_PRIMITIVE: { - const Item::CommandPrimitive *primitive = static_cast<const Item::CommandPrimitive *>(c); - - //bind pipeline - { - static const PipelineVariant variant[4] = { PIPELINE_VARIANT_PRIMITIVE_POINTS, PIPELINE_VARIANT_PRIMITIVE_LINES, PIPELINE_VARIANT_PRIMITIVE_TRIANGLES, PIPELINE_VARIANT_PRIMITIVE_TRIANGLES }; - ERR_CONTINUE(primitive->point_count == 0 || primitive->point_count > 4); - RID pipeline = pipeline_variants->variants[light_mode][variant[primitive->point_count - 1]].get_render_pipeline(RD::INVALID_ID, p_framebuffer_format); - RD::get_singleton()->draw_list_bind_render_pipeline(p_draw_list, pipeline); - } - - //bind textures - - _bind_canvas_texture(p_draw_list, primitive->texture, current_filter, current_repeat, last_texture, push_constant, texpixel_size); - - RD::get_singleton()->draw_list_bind_index_array(p_draw_list, primitive_arrays.index_array[MIN(3u, primitive->point_count) - 1]); - - for (uint32_t j = 0; j < MIN(3u, primitive->point_count); j++) { - push_constant.points[j * 2 + 0] = primitive->points[j].x; - push_constant.points[j * 2 + 1] = primitive->points[j].y; - push_constant.uvs[j * 2 + 0] = primitive->uvs[j].x; - push_constant.uvs[j * 2 + 1] = primitive->uvs[j].y; - Color col = primitive->colors[j] * base_color; - if (use_linear_colors) { - col = col.srgb_to_linear(); - } - push_constant.colors[j * 2 + 0] = (uint32_t(Math::make_half_float(col.g)) << 16) | Math::make_half_float(col.r); - push_constant.colors[j * 2 + 1] = (uint32_t(Math::make_half_float(col.a)) << 16) | Math::make_half_float(col.b); - } - RD::get_singleton()->draw_list_set_push_constant(p_draw_list, &push_constant, sizeof(PushConstant)); - RD::get_singleton()->draw_list_draw(p_draw_list, true); - - if (r_render_info) { - r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_OBJECTS_IN_FRAME]++; - r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_PRIMITIVES_IN_FRAME]++; - r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_DRAW_CALLS_IN_FRAME]++; - } - - if (primitive->point_count == 4) { - for (uint32_t j = 1; j < 3; j++) { - //second half of triangle - push_constant.points[j * 2 + 0] = primitive->points[j + 1].x; - push_constant.points[j * 2 + 1] = primitive->points[j + 1].y; - push_constant.uvs[j * 2 + 0] = primitive->uvs[j + 1].x; - push_constant.uvs[j * 2 + 1] = primitive->uvs[j + 1].y; - Color col = primitive->colors[j + 1] * base_color; - if (use_linear_colors) { - col = col.srgb_to_linear(); - } - push_constant.colors[j * 2 + 0] = (uint32_t(Math::make_half_float(col.g)) << 16) | Math::make_half_float(col.r); - push_constant.colors[j * 2 + 1] = (uint32_t(Math::make_half_float(col.a)) << 16) | Math::make_half_float(col.b); - } - - RD::get_singleton()->draw_list_set_push_constant(p_draw_list, &push_constant, sizeof(PushConstant)); - RD::get_singleton()->draw_list_draw(p_draw_list, true); - - if (r_render_info) { - r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_OBJECTS_IN_FRAME]++; - r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_PRIMITIVES_IN_FRAME]++; - r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_DRAW_CALLS_IN_FRAME]++; - } - } - - } break; - case Item::Command::TYPE_MESH: - case Item::Command::TYPE_MULTIMESH: - case Item::Command::TYPE_PARTICLES: { - RID mesh; - RID mesh_instance; - RID texture; - Color modulate(1, 1, 1, 1); - float world_backup[6]; - int instance_count = 1; - - for (int j = 0; j < 6; j++) { - world_backup[j] = push_constant.world[j]; - } - - if (c->type == Item::Command::TYPE_MESH) { - const Item::CommandMesh *m = static_cast<const Item::CommandMesh *>(c); - mesh = m->mesh; - mesh_instance = m->mesh_instance; - texture = m->texture; - modulate = m->modulate; - _update_transform_2d_to_mat2x3(base_transform * draw_transform * m->transform, push_constant.world); - } else if (c->type == Item::Command::TYPE_MULTIMESH) { - const Item::CommandMultiMesh *mm = static_cast<const Item::CommandMultiMesh *>(c); - RID multimesh = mm->multimesh; - mesh = mesh_storage->multimesh_get_mesh(multimesh); - texture = mm->texture; - - if (mesh_storage->multimesh_get_transform_format(multimesh) != RS::MULTIMESH_TRANSFORM_2D) { - break; - } - - instance_count = mesh_storage->multimesh_get_instances_to_draw(multimesh); - - if (instance_count == 0) { - break; - } - - RID uniform_set = mesh_storage->multimesh_get_2d_uniform_set(multimesh, shader.default_version_rd_shader, TRANSFORMS_UNIFORM_SET); - RD::get_singleton()->draw_list_bind_uniform_set(p_draw_list, uniform_set, TRANSFORMS_UNIFORM_SET); - push_constant.flags |= 1; //multimesh, trails disabled - if (mesh_storage->multimesh_uses_colors(multimesh)) { - push_constant.flags |= FLAGS_INSTANCING_HAS_COLORS; - } - if (mesh_storage->multimesh_uses_custom_data(multimesh)) { - push_constant.flags |= FLAGS_INSTANCING_HAS_CUSTOM_DATA; - } - } else if (c->type == Item::Command::TYPE_PARTICLES) { - const Item::CommandParticles *pt = static_cast<const Item::CommandParticles *>(c); - ERR_BREAK(particles_storage->particles_get_mode(pt->particles) != RS::PARTICLES_MODE_2D); - particles_storage->particles_request_process(pt->particles); - - if (particles_storage->particles_is_inactive(pt->particles) || particles_storage->particles_get_frame_counter(pt->particles) == 0) { - break; - } - - RenderingServerDefault::redraw_request(); // active particles means redraw request - - int dpc = particles_storage->particles_get_draw_passes(pt->particles); - if (dpc == 0) { - break; //nothing to draw - } - uint32_t divisor = 1; - instance_count = particles_storage->particles_get_amount(pt->particles, divisor); - - RID uniform_set = particles_storage->particles_get_instance_buffer_uniform_set(pt->particles, shader.default_version_rd_shader, TRANSFORMS_UNIFORM_SET); - RD::get_singleton()->draw_list_bind_uniform_set(p_draw_list, uniform_set, TRANSFORMS_UNIFORM_SET); - - push_constant.flags |= divisor; - instance_count /= divisor; - - push_constant.flags |= FLAGS_INSTANCING_HAS_COLORS; - push_constant.flags |= FLAGS_INSTANCING_HAS_CUSTOM_DATA; - - mesh = particles_storage->particles_get_draw_pass_mesh(pt->particles, 0); //higher ones are ignored - texture = pt->texture; - - if (particles_storage->particles_has_collision(pt->particles) && texture_storage->render_target_is_sdf_enabled(p_render_target)) { - //pass collision information - Transform2D xform = p_item->final_transform; - - RID sdf_texture = texture_storage->render_target_get_sdf_texture(p_render_target); - - Rect2 to_screen; - { - Rect2 sdf_rect = texture_storage->render_target_get_sdf_rect(p_render_target); - - to_screen.size = Vector2(1.0 / sdf_rect.size.width, 1.0 / sdf_rect.size.height); - to_screen.position = -sdf_rect.position * to_screen.size; - } - - particles_storage->particles_set_canvas_sdf_collision(pt->particles, true, xform, to_screen, sdf_texture); - } else { - particles_storage->particles_set_canvas_sdf_collision(pt->particles, false, Transform2D(), Rect2(), RID()); - } - - // Signal that SDF texture needs to be updated. - r_sdf_used |= particles_storage->particles_has_collision(pt->particles); - } - - if (mesh.is_null()) { - break; - } - - _bind_canvas_texture(p_draw_list, texture, current_filter, current_repeat, last_texture, push_constant, texpixel_size); - - uint32_t surf_count = mesh_storage->mesh_get_surface_count(mesh); - static const PipelineVariant variant[RS::PRIMITIVE_MAX] = { PIPELINE_VARIANT_ATTRIBUTE_POINTS, PIPELINE_VARIANT_ATTRIBUTE_LINES, PIPELINE_VARIANT_ATTRIBUTE_LINES_STRIP, PIPELINE_VARIANT_ATTRIBUTE_TRIANGLES, PIPELINE_VARIANT_ATTRIBUTE_TRIANGLE_STRIP }; - - Color modulated = modulate * base_color; - if (use_linear_colors) { - modulated = modulated.srgb_to_linear(); - } - - push_constant.modulation[0] = modulated.r; - push_constant.modulation[1] = modulated.g; - push_constant.modulation[2] = modulated.b; - push_constant.modulation[3] = modulated.a; - - for (int j = 0; j < 4; j++) { - push_constant.src_rect[j] = 0; - push_constant.dst_rect[j] = 0; - push_constant.ninepatch_margins[j] = 0; - } - - for (uint32_t j = 0; j < surf_count; j++) { - void *surface = mesh_storage->mesh_get_surface(mesh, j); - - RS::PrimitiveType primitive = mesh_storage->mesh_surface_get_primitive(surface); - ERR_CONTINUE(primitive < 0 || primitive >= RS::PRIMITIVE_MAX); - - uint64_t input_mask = pipeline_variants->variants[light_mode][variant[primitive]].get_vertex_input_mask(); - - RID vertex_array; - RD::VertexFormatID vertex_format = RD::INVALID_FORMAT_ID; - - if (mesh_instance.is_valid()) { - mesh_storage->mesh_instance_surface_get_vertex_arrays_and_format(mesh_instance, j, input_mask, false, vertex_array, vertex_format); - } else { - mesh_storage->mesh_surface_get_vertex_arrays_and_format(surface, input_mask, false, vertex_array, vertex_format); - } - - RID pipeline = pipeline_variants->variants[light_mode][variant[primitive]].get_render_pipeline(vertex_format, p_framebuffer_format); - RD::get_singleton()->draw_list_bind_render_pipeline(p_draw_list, pipeline); - - RID index_array = mesh_storage->mesh_surface_get_index_array(surface, 0); - - if (index_array.is_valid()) { - RD::get_singleton()->draw_list_bind_index_array(p_draw_list, index_array); - } - - RD::get_singleton()->draw_list_bind_vertex_array(p_draw_list, vertex_array); - RD::get_singleton()->draw_list_set_push_constant(p_draw_list, &push_constant, sizeof(PushConstant)); - - RD::get_singleton()->draw_list_draw(p_draw_list, index_array.is_valid(), instance_count); - - if (r_render_info) { - r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_OBJECTS_IN_FRAME]++; - r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_PRIMITIVES_IN_FRAME] += _indices_to_primitives(primitive, mesh_storage->mesh_surface_get_vertices_drawn_count(surface)) * instance_count; - r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_DRAW_CALLS_IN_FRAME]++; - } - } - - for (int j = 0; j < 6; j++) { - push_constant.world[j] = world_backup[j]; - } - } break; - case Item::Command::TYPE_TRANSFORM: { - const Item::CommandTransform *transform = static_cast<const Item::CommandTransform *>(c); - draw_transform = transform->xform; - _update_transform_2d_to_mat2x3(base_transform * transform->xform, push_constant.world); - - } break; - case Item::Command::TYPE_CLIP_IGNORE: { - const Item::CommandClipIgnore *ci = static_cast<const Item::CommandClipIgnore *>(c); - if (current_clip) { - if (ci->ignore != reclip) { - if (ci->ignore) { - RD::get_singleton()->draw_list_disable_scissor(p_draw_list); - reclip = true; - } else { - RD::get_singleton()->draw_list_enable_scissor(p_draw_list, current_clip->final_clip_rect); - reclip = false; - } - } - } - - } break; - case Item::Command::TYPE_ANIMATION_SLICE: { - const Item::CommandAnimationSlice *as = static_cast<const Item::CommandAnimationSlice *>(c); - double current_time = RendererCompositorRD::get_singleton()->get_total_time(); - double local_time = Math::fposmod(current_time - as->offset, as->animation_length); - skipping = !(local_time >= as->slice_begin && local_time < as->slice_end); - - RenderingServerDefault::redraw_request(); // animation visible means redraw request - } break; - } - - c = c->next; - } -#ifdef DEBUG_ENABLED - if (debug_redraw && p_item->debug_redraw_time > 0.0) { - Color dc = debug_redraw_color; - dc.a *= p_item->debug_redraw_time / debug_redraw_time; - - RID pipeline = pipeline_variants->variants[PIPELINE_LIGHT_MODE_DISABLED][PIPELINE_VARIANT_QUAD].get_render_pipeline(RD::INVALID_ID, p_framebuffer_format); - RD::get_singleton()->draw_list_bind_render_pipeline(p_draw_list, pipeline); - - //bind textures - - _bind_canvas_texture(p_draw_list, RID(), current_filter, current_repeat, last_texture, push_constant, texpixel_size); - - Rect2 src_rect; - Rect2 dst_rect; - - dst_rect = Rect2(Vector2(), p_item->rect.size); - src_rect = Rect2(0, 0, 1, 1); - - push_constant.modulation[0] = dc.r; - push_constant.modulation[1] = dc.g; - push_constant.modulation[2] = dc.b; - push_constant.modulation[3] = dc.a; - - push_constant.src_rect[0] = src_rect.position.x; - push_constant.src_rect[1] = src_rect.position.y; - push_constant.src_rect[2] = src_rect.size.width; - push_constant.src_rect[3] = src_rect.size.height; - - push_constant.dst_rect[0] = dst_rect.position.x; - push_constant.dst_rect[1] = dst_rect.position.y; - push_constant.dst_rect[2] = dst_rect.size.width; - push_constant.dst_rect[3] = dst_rect.size.height; - - RD::get_singleton()->draw_list_set_push_constant(p_draw_list, &push_constant, sizeof(PushConstant)); - RD::get_singleton()->draw_list_bind_index_array(p_draw_list, shader.quad_index_array); - RD::get_singleton()->draw_list_draw(p_draw_list, true); - - p_item->debug_redraw_time -= RSG::rasterizer->get_frame_delta_time(); - - RenderingServerDefault::redraw_request(); - } -#endif - if (current_clip && reclip) { - //will make it re-enable clipping if needed afterwards - current_clip = nullptr; - } -} - RID RendererCanvasRenderRD::_create_base_uniform_set(RID p_to_render_target, bool p_backbuffer) { RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton(); RendererRD::MaterialStorage *material_storage = RendererRD::MaterialStorage::get_singleton(); @@ -1148,127 +450,6 @@ RID RendererCanvasRenderRD::_create_base_uniform_set(RID p_to_render_target, boo return uniform_set; } -void RendererCanvasRenderRD::_render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, bool &r_sdf_used, bool p_to_backbuffer, RenderingMethod::RenderInfo *r_render_info) { - RendererRD::MaterialStorage *material_storage = RendererRD::MaterialStorage::get_singleton(); - RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton(); - - Item *current_clip = nullptr; - - Transform2D canvas_transform_inverse = p_canvas_transform_inverse; - - RID framebuffer; - RID fb_uniform_set; - bool clear = false; - Vector<Color> clear_colors; - - if (p_to_backbuffer) { - framebuffer = texture_storage->render_target_get_rd_backbuffer_framebuffer(p_to_render_target); - fb_uniform_set = texture_storage->render_target_get_backbuffer_uniform_set(p_to_render_target); - } else { - framebuffer = texture_storage->render_target_get_rd_framebuffer(p_to_render_target); - texture_storage->render_target_set_msaa_needs_resolve(p_to_render_target, false); // If MSAA is enabled, our framebuffer will be resolved! - - if (texture_storage->render_target_is_clear_requested(p_to_render_target)) { - clear = true; - clear_colors.push_back(texture_storage->render_target_get_clear_request_color(p_to_render_target)); - texture_storage->render_target_disable_clear_request(p_to_render_target); - } - // TODO: Obtain from framebuffer format eventually when this is implemented. - fb_uniform_set = texture_storage->render_target_get_framebuffer_uniform_set(p_to_render_target); - } - - if (fb_uniform_set.is_null() || !RD::get_singleton()->uniform_set_is_valid(fb_uniform_set)) { - fb_uniform_set = _create_base_uniform_set(p_to_render_target, p_to_backbuffer); - } - - RD::FramebufferFormatID fb_format = RD::get_singleton()->framebuffer_get_format(framebuffer); - - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, clear ? RD::INITIAL_ACTION_CLEAR : RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD, clear_colors, 1, 0, Rect2(), RDD::BreadcrumbMarker::UI_PASS); - - RD::get_singleton()->draw_list_bind_uniform_set(draw_list, fb_uniform_set, BASE_UNIFORM_SET); - RD::get_singleton()->draw_list_bind_uniform_set(draw_list, state.default_transforms_uniform_set, TRANSFORMS_UNIFORM_SET); - - RID prev_material; - - PipelineVariants *pipeline_variants = &shader.pipeline_variants; - - for (int i = 0; i < p_item_count; i++) { - Item *ci = items[i]; - - if (current_clip != ci->final_clip_owner) { - current_clip = ci->final_clip_owner; - - //setup clip - if (current_clip) { - RD::get_singleton()->draw_list_enable_scissor(draw_list, current_clip->final_clip_rect); - - } else { - RD::get_singleton()->draw_list_disable_scissor(draw_list); - } - } - - RID material = ci->material_owner == nullptr ? ci->material : ci->material_owner->material; - - if (ci->use_canvas_group) { - if (ci->canvas_group->mode == RS::CANVAS_GROUP_MODE_CLIP_AND_DRAW) { - material = default_clip_children_material; - } else { - if (material.is_null()) { - if (ci->canvas_group->mode == RS::CANVAS_GROUP_MODE_CLIP_ONLY) { - material = default_clip_children_material; - } else { - material = default_canvas_group_material; - } - } - } - } - - if (material != prev_material) { - CanvasMaterialData *material_data = nullptr; - if (material.is_valid()) { - material_data = static_cast<CanvasMaterialData *>(material_storage->material_get_data(material, RendererRD::MaterialStorage::SHADER_TYPE_2D)); - } - - if (material_data) { - if (material_data->shader_data->version.is_valid() && material_data->shader_data->valid) { - pipeline_variants = &material_data->shader_data->pipeline_variants; - // Update uniform set. - RID uniform_set = texture_storage->render_target_is_using_hdr(p_to_render_target) ? material_data->uniform_set : material_data->uniform_set_srgb; - if (uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(uniform_set)) { // Material may not have a uniform set. - RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set, MATERIAL_UNIFORM_SET); - material_data->set_as_used(); - } - } else { - pipeline_variants = &shader.pipeline_variants; - } - } else { - pipeline_variants = &shader.pipeline_variants; - } - } - - if (!ci->repeat_size.x && !ci->repeat_size.y) { - _render_item(draw_list, p_to_render_target, ci, fb_format, canvas_transform_inverse, current_clip, p_lights, pipeline_variants, r_sdf_used, Point2(), r_render_info); - } else { - Point2 start_pos = ci->repeat_size * -(ci->repeat_times / 2); - Point2 offset; - - int repeat_times_x = ci->repeat_size.x ? ci->repeat_times : 0; - int repeat_times_y = ci->repeat_size.y ? ci->repeat_times : 0; - for (int ry = 0; ry <= repeat_times_y; ry++) { - offset.y = start_pos.y + ry * ci->repeat_size.y; - for (int rx = 0; rx <= repeat_times_x; rx++) { - offset.x = start_pos.x + rx * ci->repeat_size.x; - _render_item(draw_list, p_to_render_target, ci, fb_format, canvas_transform_inverse, current_clip, p_lights, pipeline_variants, r_sdf_used, offset, r_render_info); - } - } - } - - prev_material = material; - } - - RD::get_singleton()->draw_list_end(); -} - void RendererCanvasRenderRD::canvas_render_items(RID p_to_render_target, Item *p_item_list, const Color &p_modulate, Light *p_light_list, Light *p_directional_light_list, const Transform2D &p_canvas_transform, RenderingServer::CanvasItemTextureFilter p_default_filter, RenderingServer::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel, bool &r_sdf_used, RenderingMethod::RenderInfo *r_render_info) { RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton(); RendererRD::MaterialStorage *material_storage = RendererRD::MaterialStorage::get_singleton(); @@ -1509,11 +690,18 @@ void RendererCanvasRenderRD::canvas_render_items(RID p_to_render_target, Item *p Item *canvas_group_owner = nullptr; bool skip_item = false; + state.last_instance_index = 0; + bool update_skeletons = false; bool time_used = false; bool backbuffer_cleared = false; + RenderTarget to_render_target; + to_render_target.render_target = p_to_render_target; + bool use_linear_colors = texture_storage->render_target_is_using_hdr(p_to_render_target); + to_render_target.base_flags = use_linear_colors ? FLAGS_CONVERT_ATTRIBUTES_TO_LINEAR : 0; + while (ci) { if (ci->copy_back_buffer && canvas_group_owner == nullptr) { backbuffer_copy = true; @@ -1572,8 +760,7 @@ void RendererCanvasRenderRD::canvas_render_items(RID p_to_render_target, Item *p mesh_storage->update_mesh_instances(); update_skeletons = false; } - - _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used, false, r_render_info); + _render_batch_items(to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used, false, r_render_info); item_count = 0; if (ci->canvas_group_owner->canvas_group->mode != RS::CANVAS_GROUP_MODE_TRANSPARENT) { @@ -1605,7 +792,7 @@ void RendererCanvasRenderRD::canvas_render_items(RID p_to_render_target, Item *p update_skeletons = false; } - _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used, true, r_render_info); + _render_batch_items(to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used, true, r_render_info); item_count = 0; if (ci->canvas_group->blur_mipmaps) { @@ -1629,7 +816,7 @@ void RendererCanvasRenderRD::canvas_render_items(RID p_to_render_target, Item *p update_skeletons = false; } - _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used, false, r_render_info); + _render_batch_items(to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used, false, r_render_info); item_count = 0; texture_storage->render_target_copy_to_back_buffer(p_to_render_target, back_buffer_rect, backbuffer_gen_mipmaps); @@ -1659,7 +846,7 @@ void RendererCanvasRenderRD::canvas_render_items(RID p_to_render_target, Item *p update_skeletons = false; } - _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used, canvas_group_owner != nullptr, r_render_info); + _render_batch_items(to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used, canvas_group_owner != nullptr, r_render_info); //then reset item_count = 0; } @@ -1670,6 +857,9 @@ void RendererCanvasRenderRD::canvas_render_items(RID p_to_render_target, Item *p if (time_used) { RenderingServerDefault::redraw_request(); } + + state.current_data_buffer_index = (state.current_data_buffer_index + 1) % state.canvas_instance_data_buffers.size(); + state.current_instance_buffer_index = 0; } RID RendererCanvasRenderRD::light_create() { @@ -2635,7 +1825,7 @@ RendererCanvasRenderRD::RendererCanvasRenderRD() { actions.base_uniform_string = "material."; actions.default_filter = ShaderLanguage::FILTER_LINEAR; actions.default_repeat = ShaderLanguage::REPEAT_DISABLE; - actions.base_varying_index = 4; + actions.base_varying_index = 5; actions.global_buffer_array_variable = "global_shader_uniforms.data"; @@ -2843,7 +2033,19 @@ void fragment() { material_storage->material_set_shader(default_clip_children_material, default_clip_children_shader); } - static_assert(sizeof(PushConstant) == 128); + { + state.max_instances_per_buffer = uint32_t(GLOBAL_GET("rendering/2d/batching/item_buffer_size")); + state.max_instance_buffer_size = state.max_instances_per_buffer * sizeof(InstanceData); + state.canvas_instance_data_buffers.resize(3); + state.canvas_instance_batches.reserve(200); + + for (int i = 0; i < 3; i++) { + DataBuffer db; + db.instance_buffers.push_back(RD::get_singleton()->storage_buffer_create(state.max_instance_buffer_size)); + state.canvas_instance_data_buffers[i] = db; + } + state.instance_data_array = memnew_arr(InstanceData, state.max_instances_per_buffer); + } } bool RendererCanvasRenderRD::free(RID p_rid) { @@ -2893,6 +2095,1008 @@ void RendererCanvasRenderRD::set_debug_redraw(bool p_enabled, double p_time, con debug_redraw_color = p_color; } +void RendererCanvasRenderRD::_render_batch_items(RenderTarget p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, bool &r_sdf_used, bool p_to_backbuffer, RenderingMethod::RenderInfo *r_render_info) { + // Record batches + uint32_t instance_index = 0; + { + RendererRD::MaterialStorage *material_storage = RendererRD::MaterialStorage::get_singleton(); + Item *current_clip = nullptr; + + // Record Batches. + // First item always forms its own batch. + bool batch_broken = false; + Batch *current_batch = _new_batch(batch_broken); + // Override the start position and index as we want to start from where we finished off last time. + current_batch->start = state.last_instance_index; + + for (int i = 0; i < p_item_count; i++) { + Item *ci = items[i]; + + if (ci->final_clip_owner != current_batch->clip) { + current_batch = _new_batch(batch_broken); + current_batch->clip = ci->final_clip_owner; + current_clip = ci->final_clip_owner; + } + + RID material = ci->material_owner == nullptr ? ci->material : ci->material_owner->material; + + if (ci->use_canvas_group) { + if (ci->canvas_group->mode == RS::CANVAS_GROUP_MODE_CLIP_AND_DRAW) { + material = default_clip_children_material; + } else { + if (material.is_null()) { + if (ci->canvas_group->mode == RS::CANVAS_GROUP_MODE_CLIP_ONLY) { + material = default_clip_children_material; + } else { + material = default_canvas_group_material; + } + } + } + } + + if (material != current_batch->material) { + current_batch = _new_batch(batch_broken); + + CanvasMaterialData *material_data = nullptr; + if (material.is_valid()) { + material_data = static_cast<CanvasMaterialData *>(material_storage->material_get_data(material, RendererRD::MaterialStorage::SHADER_TYPE_2D)); + } + + current_batch->material = material; + current_batch->material_data = material_data; + } + + if (ci->repeat_source_item == nullptr || ci->repeat_size == Vector2()) { + Transform2D base_transform = p_canvas_transform_inverse * ci->final_transform; + _record_item_commands(ci, p_to_render_target, base_transform, current_clip, p_lights, instance_index, batch_broken, r_sdf_used, current_batch); + } else { + Point2 start_pos = ci->repeat_size * -(ci->repeat_times / 2); + Point2 offset; + int repeat_times_x = ci->repeat_size.x ? ci->repeat_times : 0; + int repeat_times_y = ci->repeat_size.y ? ci->repeat_times : 0; + for (int ry = 0; ry <= repeat_times_y; ry++) { + offset.y = start_pos.y + ry * ci->repeat_size.y; + for (int rx = 0; rx <= repeat_times_x; rx++) { + offset.x = start_pos.x + rx * ci->repeat_size.x; + Transform2D base_transform = ci->final_transform; + base_transform.columns[2] += ci->repeat_source_item->final_transform.basis_xform(offset); + base_transform = p_canvas_transform_inverse * base_transform; + _record_item_commands(ci, p_to_render_target, base_transform, current_clip, p_lights, instance_index, batch_broken, r_sdf_used, current_batch); + } + } + } + } + + // Copy over remaining data needed for rendering. + if (instance_index > 0) { + RD::get_singleton()->buffer_update( + state.canvas_instance_data_buffers[state.current_data_buffer_index].instance_buffers[state.current_instance_buffer_index], + state.last_instance_index * sizeof(InstanceData), + instance_index * sizeof(InstanceData), + state.instance_data_array); + } + } + + if (state.canvas_instance_batches.is_empty()) { + // Nothing to render, just return. + return; + } + + // Render batches + + RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton(); + + RID framebuffer; + RID fb_uniform_set; + bool clear = false; + Vector<Color> clear_colors; + + if (p_to_backbuffer) { + framebuffer = texture_storage->render_target_get_rd_backbuffer_framebuffer(p_to_render_target.render_target); + fb_uniform_set = texture_storage->render_target_get_backbuffer_uniform_set(p_to_render_target.render_target); + } else { + framebuffer = texture_storage->render_target_get_rd_framebuffer(p_to_render_target.render_target); + texture_storage->render_target_set_msaa_needs_resolve(p_to_render_target.render_target, false); // If MSAA is enabled, our framebuffer will be resolved! + + if (texture_storage->render_target_is_clear_requested(p_to_render_target.render_target)) { + clear = true; + clear_colors.push_back(texture_storage->render_target_get_clear_request_color(p_to_render_target.render_target)); + texture_storage->render_target_disable_clear_request(p_to_render_target.render_target); + } + // TODO: Obtain from framebuffer format eventually when this is implemented. + fb_uniform_set = texture_storage->render_target_get_framebuffer_uniform_set(p_to_render_target.render_target); + } + + if (fb_uniform_set.is_null() || !RD::get_singleton()->uniform_set_is_valid(fb_uniform_set)) { + fb_uniform_set = _create_base_uniform_set(p_to_render_target.render_target, p_to_backbuffer); + } + + RD::FramebufferFormatID fb_format = RD::get_singleton()->framebuffer_get_format(framebuffer); + + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, clear ? RD::INITIAL_ACTION_CLEAR : RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD, clear_colors); + + RD::get_singleton()->draw_list_bind_uniform_set(draw_list, fb_uniform_set, BASE_UNIFORM_SET); + RD::get_singleton()->draw_list_bind_uniform_set(draw_list, state.default_transforms_uniform_set, TRANSFORMS_UNIFORM_SET); + + Item *current_clip = nullptr; + state.current_tex_uniform_set = RID(); + + for (uint32_t i = 0; i <= state.current_batch_index; i++) { + Batch *current_batch = &state.canvas_instance_batches[i]; + // Skipping when there is no instances. + if (current_batch->instance_count == 0) { + continue; + } + + //setup clip + if (current_clip != current_batch->clip) { + current_clip = current_batch->clip; + if (current_clip) { + RD::get_singleton()->draw_list_enable_scissor(draw_list, current_clip->final_clip_rect); + } else { + RD::get_singleton()->draw_list_disable_scissor(draw_list); + } + } + + PipelineVariants *pipeline_variants = &shader.pipeline_variants; + + CanvasMaterialData *material_data = current_batch->material_data; + if (material_data) { + if (material_data->shader_data->version.is_valid() && material_data->shader_data->valid) { + pipeline_variants = &material_data->shader_data->pipeline_variants; + // Update uniform set. + RID uniform_set = texture_storage->render_target_is_using_hdr(p_to_render_target.render_target) ? material_data->uniform_set : material_data->uniform_set_srgb; + if (uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(uniform_set)) { // Material may not have a uniform set. + RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set, MATERIAL_UNIFORM_SET); + material_data->set_as_used(); + } + } + } + + _render_batch(draw_list, pipeline_variants, fb_format, p_lights, current_batch, r_render_info); + } + + RD::get_singleton()->draw_list_end(); + + state.current_batch_index = 0; + state.canvas_instance_batches.clear(); + state.last_instance_index += instance_index; +} + +void RendererCanvasRenderRD::_record_item_commands(const Item *p_item, RenderTarget p_render_target, const Transform2D &p_base_transform, Item *&r_current_clip, Light *p_lights, uint32_t &r_index, bool &r_batch_broken, bool &r_sdf_used, Batch *&r_current_batch) { + RenderingServer::CanvasItemTextureFilter texture_filter = p_item->texture_filter == RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT ? default_filter : p_item->texture_filter; + RenderingServer::CanvasItemTextureRepeat texture_repeat = p_item->texture_repeat == RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT ? default_repeat : p_item->texture_repeat; + + Transform2D base_transform = p_base_transform; + + float world[6]; + Transform2D draw_transform; // Used by transform command + _update_transform_2d_to_mat2x3(base_transform, world); + + Color base_color = p_item->final_modulate; + bool use_linear_colors = bool(p_render_target.base_flags & FLAGS_CONVERT_ATTRIBUTES_TO_LINEAR); + uint32_t base_flags = p_render_target.base_flags; + + bool reclip = false; + + bool skipping = false; + + // TODO: consider making lights a per-batch property and then baking light operations in the shader for better performance. + uint32_t lights[4] = { 0, 0, 0, 0 }; + + uint16_t light_count = 0; + PipelineLightMode light_mode; + + { + Light *light = p_lights; + + while (light) { + if (light->render_index_cache >= 0 && p_item->light_mask & light->item_mask && p_item->z_final >= light->z_min && p_item->z_final <= light->z_max && p_item->global_rect_cache.intersects_transformed(light->xform_cache, light->rect_cache)) { + uint32_t light_index = light->render_index_cache; + lights[light_count >> 2] |= light_index << ((light_count & 3) * 8); + + light_count++; + + if (light_count == state.max_lights_per_item - 1) { + break; + } + } + light = light->next_ptr; + } + + base_flags |= light_count << FLAGS_LIGHT_COUNT_SHIFT; + } + + light_mode = (light_count > 0 || using_directional_lights) ? PIPELINE_LIGHT_MODE_ENABLED : PIPELINE_LIGHT_MODE_DISABLED; + + if (light_mode != r_current_batch->light_mode) { + r_current_batch = _new_batch(r_batch_broken); + r_current_batch->light_mode = light_mode; + } + + // new_instance_data should be called after the current_batch is set. + auto new_instance_data = [&]() -> InstanceData * { + InstanceData *instance_data = &state.instance_data_array[r_index]; + // Zero out most fields. + for (int i = 0; i < 4; i++) { + instance_data->modulation[i] = 0.0; + instance_data->ninepatch_margins[i] = 0.0; + instance_data->src_rect[i] = 0.0; + instance_data->dst_rect[i] = 0.0; + } + + instance_data->pad[0] = 0.0; + instance_data->pad[1] = 0.0; + + instance_data->lights[0] = lights[0]; + instance_data->lights[1] = lights[1]; + instance_data->lights[2] = lights[2]; + instance_data->lights[3] = lights[3]; + + for (int i = 0; i < 6; i++) { + instance_data->world[i] = world[i]; + } + + instance_data->flags = base_flags | r_current_batch->tex_flags; // Reset on each command for safety, keep canvas texture binding config. + + instance_data->color_texture_pixel_size[0] = r_current_batch->tex_texpixel_size.width; + instance_data->color_texture_pixel_size[1] = r_current_batch->tex_texpixel_size.height; + instance_data->specular_shininess = r_current_batch->tex_specular_shininess; + + return instance_data; + }; + + const Item::Command *c = p_item->commands; + while (c) { + if (skipping && c->type != Item::Command::TYPE_ANIMATION_SLICE) { + c = c->next; + continue; + } + + switch (c->type) { + case Item::Command::TYPE_RECT: { + const Item::CommandRect *rect = static_cast<const Item::CommandRect *>(c); + + // 1: If commands are different, start a new batch. + if (r_current_batch->command_type != Item::Command::TYPE_RECT) { + r_current_batch = _new_batch(r_batch_broken); + r_current_batch->command_type = Item::Command::TYPE_RECT; + r_current_batch->command = c; + // default variant + r_current_batch->pipeline_variant = PIPELINE_VARIANT_QUAD; + } + + if (bool(rect->flags & CANVAS_RECT_TILE)) { + texture_repeat = RenderingServer::CanvasItemTextureRepeat::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED; + } + + bool has_msdf = bool(rect->flags & CANVAS_RECT_MSDF); + TextureState tex_state(rect->texture, texture_filter, texture_repeat, has_msdf, use_linear_colors); + + if (tex_state != r_current_batch->tex_state) { + r_current_batch = _new_batch(r_batch_broken); + r_current_batch->set_tex_state(tex_state); + _prepare_batch_texture(r_current_batch, rect->texture); + } + + Color modulated = rect->modulate * base_color; + if (use_linear_colors) { + modulated = modulated.srgb_to_linear(); + } + + bool has_blend = bool(rect->flags & CANVAS_RECT_LCD); + // Start a new batch if the blend mode has changed, + // or blend mode is enabled and the modulation has changed. + if (has_blend != r_current_batch->has_blend || (has_blend && modulated != r_current_batch->modulate)) { + r_current_batch = _new_batch(r_batch_broken); + r_current_batch->has_blend = has_blend; + r_current_batch->modulate = modulated; + r_current_batch->pipeline_variant = has_blend ? PIPELINE_VARIANT_QUAD_LCD_BLEND : PIPELINE_VARIANT_QUAD; + } + + InstanceData *instance_data = new_instance_data(); + Rect2 src_rect; + Rect2 dst_rect; + + if (rect->texture.is_valid()) { + src_rect = (rect->flags & CANVAS_RECT_REGION) ? Rect2(rect->source.position * r_current_batch->tex_texpixel_size, rect->source.size * r_current_batch->tex_texpixel_size) : Rect2(0, 0, 1, 1); + dst_rect = Rect2(rect->rect.position, rect->rect.size); + + if (dst_rect.size.width < 0) { + dst_rect.position.x += dst_rect.size.width; + dst_rect.size.width *= -1; + } + if (dst_rect.size.height < 0) { + dst_rect.position.y += dst_rect.size.height; + dst_rect.size.height *= -1; + } + + if (rect->flags & CANVAS_RECT_FLIP_H) { + src_rect.size.x *= -1; + instance_data->flags |= FLAGS_FLIP_H; + } + + if (rect->flags & CANVAS_RECT_FLIP_V) { + src_rect.size.y *= -1; + instance_data->flags |= FLAGS_FLIP_V; + } + + if (rect->flags & CANVAS_RECT_TRANSPOSE) { + instance_data->flags |= FLAGS_TRANSPOSE_RECT; + } + + if (rect->flags & CANVAS_RECT_CLIP_UV) { + instance_data->flags |= FLAGS_CLIP_RECT_UV; + } + + } else { + dst_rect = Rect2(rect->rect.position, rect->rect.size); + + if (dst_rect.size.width < 0) { + dst_rect.position.x += dst_rect.size.width; + dst_rect.size.width *= -1; + } + if (dst_rect.size.height < 0) { + dst_rect.position.y += dst_rect.size.height; + dst_rect.size.height *= -1; + } + + src_rect = Rect2(0, 0, 1, 1); + } + + if (has_msdf) { + instance_data->flags |= FLAGS_USE_MSDF; + instance_data->msdf[0] = rect->px_range; // Pixel range. + instance_data->msdf[1] = rect->outline; // Outline size. + instance_data->msdf[2] = 0.f; // Reserved. + instance_data->msdf[3] = 0.f; // Reserved. + } else if (rect->flags & CANVAS_RECT_LCD) { + instance_data->flags |= FLAGS_USE_LCD; + } + + instance_data->modulation[0] = modulated.r; + instance_data->modulation[1] = modulated.g; + instance_data->modulation[2] = modulated.b; + instance_data->modulation[3] = modulated.a; + + instance_data->src_rect[0] = src_rect.position.x; + instance_data->src_rect[1] = src_rect.position.y; + instance_data->src_rect[2] = src_rect.size.width; + instance_data->src_rect[3] = src_rect.size.height; + + instance_data->dst_rect[0] = dst_rect.position.x; + instance_data->dst_rect[1] = dst_rect.position.y; + instance_data->dst_rect[2] = dst_rect.size.width; + instance_data->dst_rect[3] = dst_rect.size.height; + + _add_to_batch(r_index, r_batch_broken, r_current_batch); + } break; + + case Item::Command::TYPE_NINEPATCH: { + const Item::CommandNinePatch *np = static_cast<const Item::CommandNinePatch *>(c); + + if (r_current_batch->command_type != Item::Command::TYPE_NINEPATCH) { + r_current_batch = _new_batch(r_batch_broken); + r_current_batch->command_type = Item::Command::TYPE_NINEPATCH; + r_current_batch->command = c; + r_current_batch->pipeline_variant = PipelineVariant::PIPELINE_VARIANT_NINEPATCH; + } + + TextureState tex_state(np->texture, texture_filter, texture_repeat, false, use_linear_colors); + if (tex_state != r_current_batch->tex_state) { + r_current_batch = _new_batch(r_batch_broken); + r_current_batch->set_tex_state(tex_state); + _prepare_batch_texture(r_current_batch, np->texture); + } + + InstanceData *instance_data = new_instance_data(); + + Rect2 src_rect; + Rect2 dst_rect(np->rect.position.x, np->rect.position.y, np->rect.size.x, np->rect.size.y); + + if (np->texture.is_null()) { + src_rect = Rect2(0, 0, 1, 1); + } else { + if (np->source != Rect2()) { + src_rect = Rect2(np->source.position.x * r_current_batch->tex_texpixel_size.width, np->source.position.y * r_current_batch->tex_texpixel_size.height, np->source.size.x * r_current_batch->tex_texpixel_size.width, np->source.size.y * r_current_batch->tex_texpixel_size.height); + instance_data->color_texture_pixel_size[0] = 1.0 / np->source.size.width; + instance_data->color_texture_pixel_size[1] = 1.0 / np->source.size.height; + } else { + src_rect = Rect2(0, 0, 1, 1); + } + } + + Color modulated = np->color * base_color; + if (use_linear_colors) { + modulated = modulated.srgb_to_linear(); + } + + instance_data->modulation[0] = modulated.r; + instance_data->modulation[1] = modulated.g; + instance_data->modulation[2] = modulated.b; + instance_data->modulation[3] = modulated.a; + + instance_data->src_rect[0] = src_rect.position.x; + instance_data->src_rect[1] = src_rect.position.y; + instance_data->src_rect[2] = src_rect.size.width; + instance_data->src_rect[3] = src_rect.size.height; + + instance_data->dst_rect[0] = dst_rect.position.x; + instance_data->dst_rect[1] = dst_rect.position.y; + instance_data->dst_rect[2] = dst_rect.size.width; + instance_data->dst_rect[3] = dst_rect.size.height; + + instance_data->flags |= int(np->axis_x) << FLAGS_NINEPATCH_H_MODE_SHIFT; + instance_data->flags |= int(np->axis_y) << FLAGS_NINEPATCH_V_MODE_SHIFT; + + if (np->draw_center) { + instance_data->flags |= FLAGS_NINEPACH_DRAW_CENTER; + } + + instance_data->ninepatch_margins[0] = np->margin[SIDE_LEFT]; + instance_data->ninepatch_margins[1] = np->margin[SIDE_TOP]; + instance_data->ninepatch_margins[2] = np->margin[SIDE_RIGHT]; + instance_data->ninepatch_margins[3] = np->margin[SIDE_BOTTOM]; + + _add_to_batch(r_index, r_batch_broken, r_current_batch); + } break; + + case Item::Command::TYPE_POLYGON: { + const Item::CommandPolygon *polygon = static_cast<const Item::CommandPolygon *>(c); + + // Polygon's can't be batched, so always create a new batch + r_current_batch = _new_batch(r_batch_broken); + + r_current_batch->command_type = Item::Command::TYPE_POLYGON; + r_current_batch->command = c; + + TextureState tex_state(polygon->texture, texture_filter, texture_repeat, false, use_linear_colors); + if (tex_state != r_current_batch->tex_state) { + r_current_batch = _new_batch(r_batch_broken); + r_current_batch->set_tex_state(tex_state); + _prepare_batch_texture(r_current_batch, polygon->texture); + } + + // pipeline variant + { + static const PipelineVariant variant[RS::PRIMITIVE_MAX] = { PIPELINE_VARIANT_ATTRIBUTE_POINTS, PIPELINE_VARIANT_ATTRIBUTE_LINES, PIPELINE_VARIANT_ATTRIBUTE_LINES_STRIP, PIPELINE_VARIANT_ATTRIBUTE_TRIANGLES, PIPELINE_VARIANT_ATTRIBUTE_TRIANGLE_STRIP }; + ERR_CONTINUE(polygon->primitive < 0 || polygon->primitive >= RS::PRIMITIVE_MAX); + r_current_batch->pipeline_variant = variant[polygon->primitive]; + } + + InstanceData *instance_data = new_instance_data(); + + Color color = base_color; + if (use_linear_colors) { + color = color.srgb_to_linear(); + } + + instance_data->modulation[0] = color.r; + instance_data->modulation[1] = color.g; + instance_data->modulation[2] = color.b; + instance_data->modulation[3] = color.a; + + _add_to_batch(r_index, r_batch_broken, r_current_batch); + } break; + + case Item::Command::TYPE_PRIMITIVE: { + const Item::CommandPrimitive *primitive = static_cast<const Item::CommandPrimitive *>(c); + + if (primitive->point_count != r_current_batch->primitive_points || r_current_batch->command_type != Item::Command::TYPE_PRIMITIVE) { + r_current_batch = _new_batch(r_batch_broken); + r_current_batch->command_type = Item::Command::TYPE_PRIMITIVE; + r_current_batch->command = c; + r_current_batch->primitive_points = primitive->point_count; + + static const PipelineVariant variant[4] = { PIPELINE_VARIANT_PRIMITIVE_POINTS, PIPELINE_VARIANT_PRIMITIVE_LINES, PIPELINE_VARIANT_PRIMITIVE_TRIANGLES, PIPELINE_VARIANT_PRIMITIVE_TRIANGLES }; + ERR_CONTINUE(primitive->point_count == 0 || primitive->point_count > 4); + r_current_batch->pipeline_variant = variant[primitive->point_count - 1]; + + TextureState tex_state(primitive->texture, texture_filter, texture_repeat, false, use_linear_colors); + if (tex_state != r_current_batch->tex_state) { + r_current_batch = _new_batch(r_batch_broken); + r_current_batch->set_tex_state(tex_state); + _prepare_batch_texture(r_current_batch, primitive->texture); + } + } + + InstanceData *instance_data = new_instance_data(); + + for (uint32_t j = 0; j < MIN(3u, primitive->point_count); j++) { + instance_data->points[j * 2 + 0] = primitive->points[j].x; + instance_data->points[j * 2 + 1] = primitive->points[j].y; + instance_data->uvs[j * 2 + 0] = primitive->uvs[j].x; + instance_data->uvs[j * 2 + 1] = primitive->uvs[j].y; + Color col = primitive->colors[j] * base_color; + if (use_linear_colors) { + col = col.srgb_to_linear(); + } + instance_data->colors[j * 2 + 0] = (uint32_t(Math::make_half_float(col.g)) << 16) | Math::make_half_float(col.r); + instance_data->colors[j * 2 + 1] = (uint32_t(Math::make_half_float(col.a)) << 16) | Math::make_half_float(col.b); + } + + _add_to_batch(r_index, r_batch_broken, r_current_batch); + + if (primitive->point_count == 4) { + instance_data = new_instance_data(); + + for (uint32_t j = 0; j < 3; j++) { + int offset = j == 0 ? 0 : 1; + // Second triangle in the quad. Uses vertices 0, 2, 3. + instance_data->points[j * 2 + 0] = primitive->points[j + offset].x; + instance_data->points[j * 2 + 1] = primitive->points[j + offset].y; + instance_data->uvs[j * 2 + 0] = primitive->uvs[j + offset].x; + instance_data->uvs[j * 2 + 1] = primitive->uvs[j + offset].y; + Color col = primitive->colors[j] * base_color; + if (use_linear_colors) { + col = col.srgb_to_linear(); + } + instance_data->colors[j * 2 + 0] = (uint32_t(Math::make_half_float(col.g)) << 16) | Math::make_half_float(col.r); + instance_data->colors[j * 2 + 1] = (uint32_t(Math::make_half_float(col.a)) << 16) | Math::make_half_float(col.b); + } + + _add_to_batch(r_index, r_batch_broken, r_current_batch); + } + } break; + + case Item::Command::TYPE_MESH: + case Item::Command::TYPE_MULTIMESH: + case Item::Command::TYPE_PARTICLES: { + // Mesh's can't be batched, so always create a new batch + r_current_batch = _new_batch(r_batch_broken); + r_current_batch->command = c; + r_current_batch->command_type = c->type; + + InstanceData *instance_data = nullptr; + + Color modulate(1, 1, 1, 1); + if (c->type == Item::Command::TYPE_MESH) { + const Item::CommandMesh *m = static_cast<const Item::CommandMesh *>(c); + TextureState tex_state(m->texture, texture_filter, texture_repeat, false, use_linear_colors); + r_current_batch->set_tex_state(tex_state); + _prepare_batch_texture(r_current_batch, m->texture); + instance_data = new_instance_data(); + + r_current_batch->mesh_instance_count = 1; + _update_transform_2d_to_mat2x3(base_transform * draw_transform * m->transform, instance_data->world); + modulate = m->modulate; + } else if (c->type == Item::Command::TYPE_MULTIMESH) { + RendererRD::MeshStorage *mesh_storage = RendererRD::MeshStorage::get_singleton(); + + const Item::CommandMultiMesh *mm = static_cast<const Item::CommandMultiMesh *>(c); + RID multimesh = mm->multimesh; + + if (mesh_storage->multimesh_get_transform_format(multimesh) != RS::MULTIMESH_TRANSFORM_2D) { + break; + } + + r_current_batch->mesh_instance_count = mesh_storage->multimesh_get_instances_to_draw(multimesh); + if (r_current_batch->mesh_instance_count == 0) { + break; + } + + TextureState tex_state(mm->texture, texture_filter, texture_repeat, false, use_linear_colors); + r_current_batch->set_tex_state(tex_state); + _prepare_batch_texture(r_current_batch, mm->texture); + instance_data = new_instance_data(); + + instance_data->flags |= 1; // multimesh, trails disabled + + if (mesh_storage->multimesh_uses_colors(mm->multimesh)) { + instance_data->flags |= FLAGS_INSTANCING_HAS_COLORS; + } + if (mesh_storage->multimesh_uses_custom_data(mm->multimesh)) { + instance_data->flags |= FLAGS_INSTANCING_HAS_CUSTOM_DATA; + } + } else if (c->type == Item::Command::TYPE_PARTICLES) { + RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton(); + RendererRD::ParticlesStorage *particles_storage = RendererRD::ParticlesStorage::get_singleton(); + + const Item::CommandParticles *pt = static_cast<const Item::CommandParticles *>(c); + TextureState tex_state(pt->texture, texture_filter, texture_repeat, false, use_linear_colors); + r_current_batch->set_tex_state(tex_state); + _prepare_batch_texture(r_current_batch, pt->texture); + + instance_data = new_instance_data(); + + uint32_t divisor = 1; + r_current_batch->mesh_instance_count = particles_storage->particles_get_amount(pt->particles, divisor); + instance_data->flags |= (divisor & FLAGS_INSTANCING_MASK); + r_current_batch->mesh_instance_count /= divisor; + + RID particles = pt->particles; + + instance_data->flags |= FLAGS_INSTANCING_HAS_COLORS; + instance_data->flags |= FLAGS_INSTANCING_HAS_CUSTOM_DATA; + + if (particles_storage->particles_has_collision(particles) && texture_storage->render_target_is_sdf_enabled(p_render_target.render_target)) { + // Pass collision information. + Transform2D xform = p_item->final_transform; + + RID sdf_texture = texture_storage->render_target_get_sdf_texture(p_render_target.render_target); + + Rect2 to_screen; + { + Rect2 sdf_rect = texture_storage->render_target_get_sdf_rect(p_render_target.render_target); + + to_screen.size = Vector2(1.0 / sdf_rect.size.width, 1.0 / sdf_rect.size.height); + to_screen.position = -sdf_rect.position * to_screen.size; + } + + particles_storage->particles_set_canvas_sdf_collision(pt->particles, true, xform, to_screen, sdf_texture); + } else { + particles_storage->particles_set_canvas_sdf_collision(pt->particles, false, Transform2D(), Rect2(), RID()); + } + r_sdf_used |= particles_storage->particles_has_collision(particles); + } + + Color modulated = modulate * base_color; + if (use_linear_colors) { + modulated = modulated.srgb_to_linear(); + } + + instance_data->modulation[0] = modulated.r; + instance_data->modulation[1] = modulated.g; + instance_data->modulation[2] = modulated.b; + instance_data->modulation[3] = modulated.a; + + _add_to_batch(r_index, r_batch_broken, r_current_batch); + } break; + + case Item::Command::TYPE_TRANSFORM: { + const Item::CommandTransform *transform = static_cast<const Item::CommandTransform *>(c); + draw_transform = transform->xform; + _update_transform_2d_to_mat2x3(base_transform * transform->xform, world); + } break; + + case Item::Command::TYPE_CLIP_IGNORE: { + const Item::CommandClipIgnore *ci = static_cast<const Item::CommandClipIgnore *>(c); + if (r_current_clip) { + if (ci->ignore != reclip) { + r_current_batch = _new_batch(r_batch_broken); + if (ci->ignore) { + r_current_batch->clip = nullptr; + reclip = true; + } else { + r_current_batch->clip = r_current_clip; + reclip = false; + } + } + } + } break; + + case Item::Command::TYPE_ANIMATION_SLICE: { + const Item::CommandAnimationSlice *as = static_cast<const Item::CommandAnimationSlice *>(c); + double current_time = RSG::rasterizer->get_total_time(); + double local_time = Math::fposmod(current_time - as->offset, as->animation_length); + skipping = !(local_time >= as->slice_begin && local_time < as->slice_end); + + RenderingServerDefault::redraw_request(); // animation visible means redraw request + } break; + } + + c = c->next; + r_batch_broken = false; + } + + if (r_current_clip && reclip) { + // will make it re-enable clipping if needed afterwards + r_current_clip = nullptr; + } +} + +void RendererCanvasRenderRD::_render_batch(RD::DrawListID p_draw_list, PipelineVariants *p_pipeline_variants, RenderingDevice::FramebufferFormatID p_framebuffer_format, Light *p_lights, Batch const *p_batch, RenderingMethod::RenderInfo *r_render_info) { + UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton(); + ERR_FAIL_NULL(uniform_set_cache); + + ERR_FAIL_NULL(p_batch->command); + + _bind_canvas_texture(p_draw_list, p_batch->tex_uniform_set); + + switch (p_batch->command_type) { + case Item::Command::TYPE_RECT: + case Item::Command::TYPE_NINEPATCH: { + RID pipeline = p_pipeline_variants->variants[p_batch->light_mode][p_batch->pipeline_variant].get_render_pipeline(RD::INVALID_ID, p_framebuffer_format); + RD::get_singleton()->draw_list_bind_render_pipeline(p_draw_list, pipeline); + if (p_batch->has_blend) { + RD::get_singleton()->draw_list_set_blend_constants(p_draw_list, p_batch->modulate); + } + + PushConstant push_constant; + push_constant.base_instance_index = p_batch->start; + RD::get_singleton()->draw_list_set_push_constant(p_draw_list, &push_constant, sizeof(PushConstant)); + + RD::Uniform u_instance_data(RD::UNIFORM_TYPE_STORAGE_BUFFER, 0, state.canvas_instance_data_buffers[state.current_data_buffer_index].instance_buffers[p_batch->instance_buffer_index]); + RD::get_singleton()->draw_list_bind_uniform_set( + p_draw_list, + uniform_set_cache->get_cache(shader.default_version_rd_shader, INSTANCE_DATA_UNIFORM_SET, u_instance_data), + INSTANCE_DATA_UNIFORM_SET); + + RD::get_singleton()->draw_list_bind_index_array(p_draw_list, shader.quad_index_array); + RD::get_singleton()->draw_list_draw(p_draw_list, true, p_batch->instance_count); + + if (r_render_info) { + r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_OBJECTS_IN_FRAME] += p_batch->instance_count; + r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_PRIMITIVES_IN_FRAME] += 2 * p_batch->instance_count; + r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_DRAW_CALLS_IN_FRAME]++; + } + } break; + + case Item::Command::TYPE_POLYGON: { + const Item::CommandPolygon *polygon = static_cast<const Item::CommandPolygon *>(p_batch->command); + + PolygonBuffers *pb = polygon_buffers.polygons.getptr(polygon->polygon.polygon_id); + ERR_FAIL_NULL(pb); + + RID pipeline = p_pipeline_variants->variants[p_batch->light_mode][p_batch->pipeline_variant].get_render_pipeline(pb->vertex_format_id, p_framebuffer_format); + RD::get_singleton()->draw_list_bind_render_pipeline(p_draw_list, pipeline); + + PushConstant push_constant; + push_constant.base_instance_index = p_batch->start; + RD::get_singleton()->draw_list_set_push_constant(p_draw_list, &push_constant, sizeof(PushConstant)); + + RD::Uniform u_instance_data(RD::UNIFORM_TYPE_STORAGE_BUFFER, 0, state.canvas_instance_data_buffers[state.current_data_buffer_index].instance_buffers[p_batch->instance_buffer_index]); + RD::get_singleton()->draw_list_bind_uniform_set( + p_draw_list, + uniform_set_cache->get_cache(shader.default_version_rd_shader, INSTANCE_DATA_UNIFORM_SET, u_instance_data), + INSTANCE_DATA_UNIFORM_SET); + + RD::get_singleton()->draw_list_bind_vertex_array(p_draw_list, pb->vertex_array); + if (pb->indices.is_valid()) { + RD::get_singleton()->draw_list_bind_index_array(p_draw_list, pb->indices); + } + + RD::get_singleton()->draw_list_draw(p_draw_list, pb->indices.is_valid()); + if (r_render_info) { + r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_OBJECTS_IN_FRAME]++; + r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_PRIMITIVES_IN_FRAME] += _indices_to_primitives(polygon->primitive, pb->primitive_count); + r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_DRAW_CALLS_IN_FRAME]++; + } + } break; + + case Item::Command::TYPE_PRIMITIVE: { + const Item::CommandPrimitive *primitive = static_cast<const Item::CommandPrimitive *>(p_batch->command); + + RID pipeline = p_pipeline_variants->variants[p_batch->light_mode][p_batch->pipeline_variant].get_render_pipeline(RD::INVALID_ID, p_framebuffer_format); + RD::get_singleton()->draw_list_bind_render_pipeline(p_draw_list, pipeline); + + PushConstant push_constant; + push_constant.base_instance_index = p_batch->start; + RD::get_singleton()->draw_list_set_push_constant(p_draw_list, &push_constant, sizeof(PushConstant)); + + RD::Uniform u_instance_data(RD::UNIFORM_TYPE_STORAGE_BUFFER, 0, state.canvas_instance_data_buffers[state.current_data_buffer_index].instance_buffers[p_batch->instance_buffer_index]); + RD::get_singleton()->draw_list_bind_uniform_set( + p_draw_list, + uniform_set_cache->get_cache(shader.default_version_rd_shader, INSTANCE_DATA_UNIFORM_SET, u_instance_data), + INSTANCE_DATA_UNIFORM_SET); + + RD::get_singleton()->draw_list_bind_index_array(p_draw_list, primitive_arrays.index_array[MIN(3u, primitive->point_count) - 1]); + uint32_t instance_count = p_batch->instance_count; + RD::get_singleton()->draw_list_draw(p_draw_list, true, instance_count); + + if (r_render_info) { + const RenderingServer::PrimitiveType rs_primitive[5] = { RS::PRIMITIVE_POINTS, RS::PRIMITIVE_POINTS, RS::PRIMITIVE_LINES, RS::PRIMITIVE_TRIANGLES, RS::PRIMITIVE_TRIANGLES }; + r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_OBJECTS_IN_FRAME] += instance_count; + r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_PRIMITIVES_IN_FRAME] += _indices_to_primitives(rs_primitive[p_batch->primitive_points], p_batch->primitive_points) * instance_count; + r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_DRAW_CALLS_IN_FRAME]++; + } + } break; + + case Item::Command::TYPE_MESH: + case Item::Command::TYPE_MULTIMESH: + case Item::Command::TYPE_PARTICLES: { + RendererRD::MeshStorage *mesh_storage = RendererRD::MeshStorage::get_singleton(); + RendererRD::ParticlesStorage *particles_storage = RendererRD::ParticlesStorage::get_singleton(); + + RID mesh; + RID mesh_instance; + + if (p_batch->command_type == Item::Command::TYPE_MESH) { + const Item::CommandMesh *m = static_cast<const Item::CommandMesh *>(p_batch->command); + mesh = m->mesh; + mesh_instance = m->mesh_instance; + } else if (p_batch->command_type == Item::Command::TYPE_MULTIMESH) { + const Item::CommandMultiMesh *mm = static_cast<const Item::CommandMultiMesh *>(p_batch->command); + RID multimesh = mm->multimesh; + mesh = mesh_storage->multimesh_get_mesh(multimesh); + + RID uniform_set = mesh_storage->multimesh_get_2d_uniform_set(multimesh, shader.default_version_rd_shader, TRANSFORMS_UNIFORM_SET); + RD::get_singleton()->draw_list_bind_uniform_set(p_draw_list, uniform_set, TRANSFORMS_UNIFORM_SET); + } else if (p_batch->command_type == Item::Command::TYPE_PARTICLES) { + const Item::CommandParticles *pt = static_cast<const Item::CommandParticles *>(p_batch->command); + RID particles = pt->particles; + mesh = particles_storage->particles_get_draw_pass_mesh(particles, 0); + + ERR_BREAK(particles_storage->particles_get_mode(particles) != RS::PARTICLES_MODE_2D); + particles_storage->particles_request_process(particles); + + if (particles_storage->particles_is_inactive(particles)) { + break; + } + + RenderingServerDefault::redraw_request(); // Active particles means redraw request. + + int dpc = particles_storage->particles_get_draw_passes(particles); + if (dpc == 0) { + break; // Nothing to draw. + } + + RID uniform_set = particles_storage->particles_get_instance_buffer_uniform_set(pt->particles, shader.default_version_rd_shader, TRANSFORMS_UNIFORM_SET); + RD::get_singleton()->draw_list_bind_uniform_set(p_draw_list, uniform_set, TRANSFORMS_UNIFORM_SET); + } + + if (mesh.is_null()) { + break; + } + + RD::Uniform u_instance_data(RD::UNIFORM_TYPE_STORAGE_BUFFER, 0, state.canvas_instance_data_buffers[state.current_data_buffer_index].instance_buffers[p_batch->instance_buffer_index]); + RD::get_singleton()->draw_list_bind_uniform_set( + p_draw_list, + uniform_set_cache->get_cache(shader.default_version_rd_shader, INSTANCE_DATA_UNIFORM_SET, u_instance_data), + INSTANCE_DATA_UNIFORM_SET); + + uint32_t surf_count = mesh_storage->mesh_get_surface_count(mesh); + static const PipelineVariant variant[RS::PRIMITIVE_MAX] = { PIPELINE_VARIANT_ATTRIBUTE_POINTS, PIPELINE_VARIANT_ATTRIBUTE_LINES, PIPELINE_VARIANT_ATTRIBUTE_LINES_STRIP, PIPELINE_VARIANT_ATTRIBUTE_TRIANGLES, PIPELINE_VARIANT_ATTRIBUTE_TRIANGLE_STRIP }; + + for (uint32_t j = 0; j < surf_count; j++) { + void *surface = mesh_storage->mesh_get_surface(mesh, j); + + RS::PrimitiveType primitive = mesh_storage->mesh_surface_get_primitive(surface); + ERR_CONTINUE(primitive < 0 || primitive >= RS::PRIMITIVE_MAX); + + uint64_t input_mask = p_pipeline_variants->variants[p_batch->light_mode][variant[primitive]].get_vertex_input_mask(); + + RID vertex_array; + RD::VertexFormatID vertex_format = RD::INVALID_FORMAT_ID; + + if (mesh_instance.is_valid()) { + mesh_storage->mesh_instance_surface_get_vertex_arrays_and_format(mesh_instance, j, input_mask, false, vertex_array, vertex_format); + } else { + mesh_storage->mesh_surface_get_vertex_arrays_and_format(surface, input_mask, false, vertex_array, vertex_format); + } + + RID pipeline = p_pipeline_variants->variants[p_batch->light_mode][variant[primitive]].get_render_pipeline(vertex_format, p_framebuffer_format); + RD::get_singleton()->draw_list_bind_render_pipeline(p_draw_list, pipeline); + + PushConstant push_constant; + push_constant.base_instance_index = p_batch->start; + RD::get_singleton()->draw_list_set_push_constant(p_draw_list, &push_constant, sizeof(PushConstant)); + + RID index_array = mesh_storage->mesh_surface_get_index_array(surface, 0); + + if (index_array.is_valid()) { + RD::get_singleton()->draw_list_bind_index_array(p_draw_list, index_array); + } + + RD::get_singleton()->draw_list_bind_vertex_array(p_draw_list, vertex_array); + RD::get_singleton()->draw_list_draw(p_draw_list, index_array.is_valid(), p_batch->mesh_instance_count); + + if (r_render_info) { + r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_OBJECTS_IN_FRAME]++; + r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_PRIMITIVES_IN_FRAME] += _indices_to_primitives(primitive, mesh_storage->mesh_surface_get_vertices_drawn_count(surface)) * p_batch->mesh_instance_count; + r_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_DRAW_CALLS_IN_FRAME]++; + } + } + } break; + case Item::Command::TYPE_TRANSFORM: + case Item::Command::TYPE_CLIP_IGNORE: + case Item::Command::TYPE_ANIMATION_SLICE: { + // Can ignore these as they only impact batch creation. + } break; + } +} + +RendererCanvasRenderRD::Batch *RendererCanvasRenderRD::_new_batch(bool &r_batch_broken) { + if (state.canvas_instance_batches.size() == 0) { + state.canvas_instance_batches.push_back(Batch()); + return state.canvas_instance_batches.ptr(); + } + + if (r_batch_broken || state.canvas_instance_batches[state.current_batch_index].instance_count == 0) { + return &state.canvas_instance_batches[state.current_batch_index]; + } + + r_batch_broken = true; + + // Copy the properties of the current batch, we will manually update the things that changed. + Batch new_batch = state.canvas_instance_batches[state.current_batch_index]; + new_batch.instance_count = 0; + new_batch.start = state.canvas_instance_batches[state.current_batch_index].start + state.canvas_instance_batches[state.current_batch_index].instance_count; + new_batch.instance_buffer_index = state.current_instance_buffer_index; + state.current_batch_index++; + state.canvas_instance_batches.push_back(new_batch); + return &state.canvas_instance_batches[state.current_batch_index]; +} + +void RendererCanvasRenderRD::_add_to_batch(uint32_t &r_index, bool &r_batch_broken, Batch *&r_current_batch) { + r_current_batch->instance_count++; + r_index++; + if (r_index + state.last_instance_index >= state.max_instances_per_buffer) { + // Copy over all data needed for rendering right away + // then go back to recording item commands. + RD::get_singleton()->buffer_update( + state.canvas_instance_data_buffers[state.current_data_buffer_index].instance_buffers[state.current_instance_buffer_index], + state.last_instance_index * sizeof(InstanceData), + r_index * sizeof(InstanceData), + state.instance_data_array); + _allocate_instance_buffer(); + r_index = 0; + state.last_instance_index = 0; + r_batch_broken = false; // Force a new batch to be created + r_current_batch = _new_batch(r_batch_broken); + r_current_batch->start = 0; + } +} + +void RendererCanvasRenderRD::_allocate_instance_buffer() { + state.current_instance_buffer_index++; + + if (state.current_instance_buffer_index < state.canvas_instance_data_buffers[state.current_data_buffer_index].instance_buffers.size()) { + // We already allocated another buffer in a previous frame, so we can just use it. + return; + } + + // Allocate a new buffer. + RID buf = RD::get_singleton()->storage_buffer_create(state.max_instance_buffer_size); + state.canvas_instance_data_buffers[state.current_data_buffer_index].instance_buffers.push_back(buf); +} + +void RendererCanvasRenderRD::_prepare_batch_texture(Batch *p_current_batch, RID p_texture) const { + if (p_texture.is_null()) { + p_texture = default_canvas_texture; + } + + Color specular_shininess; + bool use_normal; + bool use_specular; + Size2i size; + bool success = RendererRD::TextureStorage::get_singleton()->canvas_texture_get_uniform_set( + p_texture, + p_current_batch->tex_state.texture_filter(), + p_current_batch->tex_state.texture_repeat(), + shader.default_version_rd_shader, + CANVAS_TEXTURE_UNIFORM_SET, + p_current_batch->tex_state.linear_colors(), + p_current_batch->tex_uniform_set, + size, + specular_shininess, + use_normal, + use_specular, + p_current_batch->tex_state.texture_is_data()); + // something odd happened + if (!success) { + _prepare_batch_texture(p_current_batch, default_canvas_texture); + return; + } + + // cache values to be copied to instance data + if (specular_shininess.a < 0.999) { + p_current_batch->tex_flags |= FLAGS_DEFAULT_SPECULAR_MAP_USED; + } + + if (use_normal) { + p_current_batch->tex_flags |= FLAGS_DEFAULT_NORMAL_MAP_USED; + } + + uint8_t a = uint8_t(CLAMP(specular_shininess.a * 255.0, 0.0, 255.0)); + uint8_t b = uint8_t(CLAMP(specular_shininess.b * 255.0, 0.0, 255.0)); + uint8_t g = uint8_t(CLAMP(specular_shininess.g * 255.0, 0.0, 255.0)); + uint8_t r = uint8_t(CLAMP(specular_shininess.r * 255.0, 0.0, 255.0)); + p_current_batch->tex_specular_shininess = uint32_t(a) << 24 | uint32_t(b) << 16 | uint32_t(g) << 8 | uint32_t(r); + + p_current_batch->tex_texpixel_size = Vector2(1.0 / float(size.width), 1.0 / float(size.height)); +} + +void RendererCanvasRenderRD::_bind_canvas_texture(RD::DrawListID p_draw_list, RID p_uniform_set) { + if (state.current_tex_uniform_set == p_uniform_set) { + return; + } + + state.current_tex_uniform_set = p_uniform_set; + + RD::get_singleton()->draw_list_bind_uniform_set(p_draw_list, p_uniform_set, CANVAS_TEXTURE_UNIFORM_SET); +} + RendererCanvasRenderRD::~RendererCanvasRenderRD() { RendererRD::MaterialStorage *material_storage = RendererRD::MaterialStorage::get_singleton(); //canvas state @@ -2936,6 +3140,13 @@ RendererCanvasRenderRD::~RendererCanvasRenderRD() { } RD::get_singleton()->free(state.shadow_texture); + memdelete_arr(state.instance_data_array); + for (uint32_t i = 0; i < state.canvas_instance_data_buffers.size(); i++) { + for (uint32_t j = 0; j < state.canvas_instance_data_buffers[i].instance_buffers.size(); j++) { + RD::get_singleton()->free(state.canvas_instance_data_buffers[i].instance_buffers[j]); + } + } + RendererRD::TextureStorage::get_singleton()->canvas_texture_free(default_canvas_texture); //pipelines don't need freeing, they are all gone after shaders are gone } diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.h b/servers/rendering/renderer_rd/renderer_canvas_render_rd.h index 9deb4814c7..8d90cd23ce 100644 --- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.h +++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.h @@ -46,6 +46,7 @@ class RendererCanvasRenderRD : public RendererCanvasRender { MATERIAL_UNIFORM_SET = 1, TRANSFORMS_UNIFORM_SET = 2, CANVAS_TEXTURE_UNIFORM_SET = 3, + INSTANCE_DATA_UNIFORM_SET = 4, }; const int SAMPLERS_BINDING_FIRST_INDEX = 10; @@ -335,6 +336,146 @@ class RendererCanvasRenderRD : public RendererCanvasRender { //state that does not vary across rendering all items + struct InstanceData { + float world[6]; + uint32_t flags; + uint32_t specular_shininess; + union { + //rect + struct { + float modulation[4]; + union { + float msdf[4]; + float ninepatch_margins[4]; + }; + float dst_rect[4]; + float src_rect[4]; + float pad[2]; + }; + //primitive + struct { + float points[6]; // vec2 points[3] + float uvs[6]; // vec2 points[3] + uint32_t colors[6]; // colors encoded as half + }; + }; + float color_texture_pixel_size[2]; + uint32_t lights[4]; + }; + + struct PushConstant { + uint32_t base_instance_index; + uint32_t pad1; + uint32_t pad2; + uint32_t pad3; + }; + + // TextureState is used to determine when a new batch is required due to a change of texture state. + struct TextureState { + static const uint32_t FILTER_SHIFT = 0; + static const uint32_t FILTER_BITS = 3; + static const uint32_t FILTER_MASK = (1 << FILTER_BITS) - 1; + static const uint32_t REPEAT_SHIFT = FILTER_BITS; + static const uint32_t REPEAT_BITS = 2; + static const uint32_t REPEAT_MASK = (1 << REPEAT_BITS) - 1; + static const uint32_t TEXTURE_IS_DATA_SHIFT = REPEAT_SHIFT + REPEAT_BITS; + static const uint32_t TEXTURE_IS_DATA_BITS = 1; + static const uint32_t TEXTURE_IS_DATA_MASK = (1 << TEXTURE_IS_DATA_BITS) - 1; + static const uint32_t LINEAR_COLORS_SHIFT = TEXTURE_IS_DATA_SHIFT + TEXTURE_IS_DATA_BITS; + static const uint32_t LINEAR_COLORS_BITS = 1; + static const uint32_t LINEAR_COLORS_MASK = (1 << LINEAR_COLORS_BITS) - 1; + + RID texture; + uint32_t other = 0; + + TextureState() {} + + TextureState(RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, bool p_texture_is_data, bool p_use_linear_colors) { + texture = p_texture; + other = (((uint32_t)p_base_filter & FILTER_MASK) << FILTER_SHIFT) | + (((uint32_t)p_base_repeat & REPEAT_MASK) << REPEAT_SHIFT) | + (((uint32_t)p_texture_is_data & TEXTURE_IS_DATA_MASK) << TEXTURE_IS_DATA_SHIFT) | + (((uint32_t)p_use_linear_colors & LINEAR_COLORS_MASK) << LINEAR_COLORS_SHIFT); + } + + _FORCE_INLINE_ RS::CanvasItemTextureFilter texture_filter() const { + return (RS::CanvasItemTextureFilter)((other >> FILTER_SHIFT) & FILTER_MASK); + } + + _FORCE_INLINE_ RS::CanvasItemTextureRepeat texture_repeat() const { + return (RS::CanvasItemTextureRepeat)((other >> REPEAT_SHIFT) & REPEAT_MASK); + } + + _FORCE_INLINE_ bool linear_colors() const { + return (other >> LINEAR_COLORS_SHIFT) & LINEAR_COLORS_MASK; + } + + _FORCE_INLINE_ bool texture_is_data() const { + return (other >> TEXTURE_IS_DATA_SHIFT) & TEXTURE_IS_DATA_MASK; + } + + bool operator==(const TextureState &p_val) const { + return (texture == p_val.texture) && (other == p_val.other); + } + + bool operator!=(const TextureState &p_val) const { + return (texture != p_val.texture) || (other != p_val.other); + } + }; + + struct Batch { + // Position in the UBO measured in bytes + uint32_t start = 0; + uint32_t instance_count = 0; + uint32_t instance_buffer_index = 0; + + TextureState tex_state; + RID tex_uniform_set; + + // The following tex_ prefixed fields are used to cache the texture data for the current batch. + // These values are applied to new InstanceData for the batch + + // The cached specular shininess derived from the current texture. + uint32_t tex_specular_shininess = 0; + // The cached texture flags, such as FLAGS_DEFAULT_SPECULAR_MAP_USED and FLAGS_DEFAULT_NORMAL_MAP_USED + uint32_t tex_flags = 0; + // The cached texture pixel size. + Vector2 tex_texpixel_size; + + Color modulate = Color(1.0, 1.0, 1.0, 1.0); + + Item *clip = nullptr; + + RID material; + CanvasMaterialData *material_data = nullptr; + PipelineLightMode light_mode = PipelineLightMode::PIPELINE_LIGHT_MODE_DISABLED; + PipelineVariant pipeline_variant = PipelineVariant::PIPELINE_VARIANT_QUAD; + + const Item::Command *command = nullptr; + Item::Command::Type command_type = Item::Command::TYPE_ANIMATION_SLICE; // Can default to any type that doesn't form a batch. + + // batch-specific data + union { + // TYPE_PRIMITIVE + uint32_t primitive_points = 0; + // TYPE_PARTICLES + uint32_t mesh_instance_count; + }; + bool has_blend = false; + + void set_tex_state(TextureState &p_tex_state) { + tex_state = p_tex_state; + tex_uniform_set = RID(); + tex_texpixel_size = Size2(); + tex_specular_shininess = 0; + tex_flags = 0; + } + }; + + struct DataBuffer { + LocalVector<RID> instance_buffers; + }; + struct State { //state buffer struct Buffer { @@ -357,6 +498,19 @@ class RendererCanvasRenderRD : public RendererCanvasRender { uint32_t pad2; }; + LocalVector<DataBuffer> canvas_instance_data_buffers; + LocalVector<Batch> canvas_instance_batches; + uint32_t current_data_buffer_index = 0; + uint32_t current_instance_buffer_index = 0; + uint32_t current_batch_index = 0; + uint32_t last_instance_index = 0; + InstanceData *instance_data_array = nullptr; + + uint32_t max_instances_per_buffer = 16384; + uint32_t max_instance_buffer_size = 16384 * sizeof(InstanceData); + + RID current_tex_uniform_set; + LightUniform *light_uniforms = nullptr; RID lights_uniform_buffer; @@ -376,33 +530,6 @@ class RendererCanvasRenderRD : public RendererCanvasRender { } state; - struct PushConstant { - float world[6]; - uint32_t flags; - uint32_t specular_shininess; - union { - //rect - struct { - float modulation[4]; - union { - float msdf[4]; - float ninepatch_margins[4]; - }; - float dst_rect[4]; - float src_rect[4]; - float pad[2]; - }; - //primitive - struct { - float points[6]; // vec2 points[3] - float uvs[6]; // vec2 points[3] - uint32_t colors[6]; // colors encoded as half - }; - }; - float color_texture_pixel_size[2]; - uint32_t lights[4]; - }; - Item *items[MAX_RENDER_ITEMS]; bool using_directional_lights = false; @@ -422,9 +549,23 @@ class RendererCanvasRenderRD : public RendererCanvasRender { Color debug_redraw_color; double debug_redraw_time = 1.0; - inline void _bind_canvas_texture(RD::DrawListID p_draw_list, RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, RID &r_last_texture, PushConstant &push_constant, Size2 &r_texpixel_size, bool p_texture_is_data = false); //recursive, so regular inline used instead. - void _render_item(RenderingDevice::DrawListID p_draw_list, RID p_render_target, const Item *p_item, RenderingDevice::FramebufferFormatID p_framebuffer_format, const Transform2D &p_canvas_transform_inverse, Item *¤t_clip, Light *p_lights, PipelineVariants *p_pipeline_variants, bool &r_sdf_used, const Point2 &p_repeat_offset, RenderingMethod::RenderInfo *r_render_info = nullptr); - void _render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, bool &r_sdf_used, bool p_to_backbuffer = false, RenderingMethod::RenderInfo *r_render_info = nullptr); + // A structure to store cached render target information + struct RenderTarget { + // Current render target for the canvas. + RID render_target; + // The base flags for each InstanceData, derived from the render target. + // Either FLAGS_CONVERT_ATTRIBUTES_TO_LINEAR or 0 + uint32_t base_flags = 0; + }; + + void _render_batch_items(RenderTarget p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, bool &r_sdf_used, bool p_to_backbuffer = false, RenderingMethod::RenderInfo *r_render_info = nullptr); + void _record_item_commands(const Item *p_item, RenderTarget p_render_target, const Transform2D &p_base_transform, Item *&r_current_clip, Light *p_lights, uint32_t &r_index, bool &r_batch_broken, bool &r_sdf_used, Batch *&r_current_batch); + void _render_batch(RD::DrawListID p_draw_list, PipelineVariants *p_pipeline_variants, RenderingDevice::FramebufferFormatID p_framebuffer_format, Light *p_lights, Batch const *p_batch, RenderingMethod::RenderInfo *r_render_info = nullptr); + void _prepare_batch_texture(Batch *p_current_batch, RID p_texture) const; + void _bind_canvas_texture(RD::DrawListID p_draw_list, RID p_uniform_set); + [[nodiscard]] Batch *_new_batch(bool &r_batch_broken); + void _add_to_batch(uint32_t &r_index, bool &r_batch_broken, Batch *&r_current_batch); + void _allocate_instance_buffer(); _FORCE_INLINE_ void _update_transform_2d_to_mat2x4(const Transform2D &p_transform, float *p_mat2x4); _FORCE_INLINE_ void _update_transform_2d_to_mat2x3(const Transform2D &p_transform, float *p_mat2x3); diff --git a/servers/rendering/renderer_rd/shaders/canvas.glsl b/servers/rendering/renderer_rd/shaders/canvas.glsl index 4426d9eb66..2154d56faf 100644 --- a/servers/rendering/renderer_rd/shaders/canvas.glsl +++ b/servers/rendering/renderer_rd/shaders/canvas.glsl @@ -24,6 +24,12 @@ layout(location = 11) in vec4 weight_attrib; #include "canvas_uniforms_inc.glsl" +#ifndef USE_ATTRIBUTES + +layout(location = 4) out flat uint instance_index_interp; + +#endif // USE_ATTRIBUTES + layout(location = 0) out vec2 uv_interp; layout(location = 1) out vec4 color_interp; layout(location = 2) out vec2 vertex_interp; @@ -59,6 +65,14 @@ void main() { vec4 custom1 = vec4(0.0); #endif +#ifdef USE_ATTRIBUTES + uint instance_index = params.base_instance_index; +#else + uint instance_index = gl_InstanceIndex + params.base_instance_index; + instance_index_interp = instance_index; +#endif // USE_ATTRIBUTES + const InstanceData draw_data = instances.data[instance_index]; + #ifdef USE_PRIMITIVE //weird bug, @@ -117,13 +131,10 @@ void main() { mat4 model_matrix = mat4(vec4(draw_data.world_x, 0.0, 0.0), vec4(draw_data.world_y, 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0), vec4(draw_data.world_ofs, 0.0, 1.0)); -#define FLAGS_INSTANCING_MASK 0x7F -#define FLAGS_INSTANCING_HAS_COLORS (1 << 7) -#define FLAGS_INSTANCING_HAS_CUSTOM_DATA (1 << 8) +#ifdef USE_ATTRIBUTES uint instancing = draw_data.flags & FLAGS_INSTANCING_MASK; -#ifdef USE_ATTRIBUTES if (instancing > 1) { // trails @@ -160,38 +171,27 @@ void main() { vertex = new_vertex; color *= pcolor; - } else -#endif // USE_ATTRIBUTES - { - if (instancing == 1) { - uint stride = 2; - { - if (bool(draw_data.flags & FLAGS_INSTANCING_HAS_COLORS)) { - stride += 1; - } - if (bool(draw_data.flags & FLAGS_INSTANCING_HAS_CUSTOM_DATA)) { - stride += 1; - } - } - - uint offset = stride * gl_InstanceIndex; + } else if (instancing == 1) { + uint stride = 2 + bitfieldExtract(draw_data.flags, FLAGS_INSTANCING_HAS_COLORS_SHIFT, 1) + bitfieldExtract(draw_data.flags, FLAGS_INSTANCING_HAS_CUSTOM_DATA_SHIFT, 1); - mat4 matrix = mat4(transforms.data[offset + 0], transforms.data[offset + 1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0)); - offset += 2; + uint offset = stride * gl_InstanceIndex; - if (bool(draw_data.flags & FLAGS_INSTANCING_HAS_COLORS)) { - color *= transforms.data[offset]; - offset += 1; - } + mat4 matrix = mat4(transforms.data[offset + 0], transforms.data[offset + 1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0)); + offset += 2; - if (bool(draw_data.flags & FLAGS_INSTANCING_HAS_CUSTOM_DATA)) { - instance_custom = transforms.data[offset]; - } + if (bool(draw_data.flags & FLAGS_INSTANCING_HAS_COLORS)) { + color *= transforms.data[offset]; + offset += 1; + } - matrix = transpose(matrix); - model_matrix = model_matrix * matrix; + if (bool(draw_data.flags & FLAGS_INSTANCING_HAS_CUSTOM_DATA)) { + instance_custom = transforms.data[offset]; } + + matrix = transpose(matrix); + model_matrix = model_matrix * matrix; } +#endif // USE_ATTRIBUTES #ifdef USE_POINT_SIZE float point_size = 1.0; @@ -241,6 +241,10 @@ void main() { #include "canvas_uniforms_inc.glsl" +#ifndef USE_ATTRIBUTES +layout(location = 4) in flat uint instance_index; +#endif // USE_ATTRIBUTES + layout(location = 0) in vec2 uv_interp; layout(location = 1) in vec4 color_interp; layout(location = 2) in vec2 vertex_interp; @@ -320,6 +324,12 @@ vec4 light_compute( #ifdef USE_NINEPATCH float map_ninepatch_axis(float pixel, float draw_size, float tex_pixel_size, float margin_begin, float margin_end, int np_repeat, inout int draw_center) { +#ifdef USE_ATTRIBUTES + const InstanceData draw_data = instances.data[params.base_instance_index]; +#else + const InstanceData draw_data = instances.data[instance_index]; +#endif // USE_ATTRIBUTES + float tex_size = 1.0 / tex_pixel_size; if (pixel < margin_begin) { @@ -327,9 +337,7 @@ float map_ninepatch_axis(float pixel, float draw_size, float tex_pixel_size, flo } else if (pixel >= draw_size - margin_end) { return (tex_size - (draw_size - pixel)) * tex_pixel_size; } else { - if (!bool(draw_data.flags & FLAGS_NINEPACH_DRAW_CENTER)) { - draw_center--; - } + draw_center -= 1 - int(bitfieldExtract(draw_data.flags, FLAGS_NINEPACH_DRAW_CENTER_SHIFT, 1)); // np_repeat is passed as uniform using NinePatchRect::AxisStretchMode enum. if (np_repeat == 0) { // Stretch. @@ -462,14 +470,20 @@ void main() { vec2 uv = uv_interp; vec2 vertex = vertex_interp; +#ifdef USE_ATTRIBUTES + const InstanceData draw_data = instances.data[params.base_instance_index]; +#else + const InstanceData draw_data = instances.data[instance_index]; +#endif // USE_ATTRIBUTES + #if !defined(USE_ATTRIBUTES) && !defined(USE_PRIMITIVE) #ifdef USE_NINEPATCH int draw_center = 2; uv = vec2( - map_ninepatch_axis(pixel_size_interp.x, abs(draw_data.dst_rect.z), draw_data.color_texture_pixel_size.x, draw_data.ninepatch_margins.x, draw_data.ninepatch_margins.z, int(draw_data.flags >> FLAGS_NINEPATCH_H_MODE_SHIFT) & 0x3, draw_center), - map_ninepatch_axis(pixel_size_interp.y, abs(draw_data.dst_rect.w), draw_data.color_texture_pixel_size.y, draw_data.ninepatch_margins.y, draw_data.ninepatch_margins.w, int(draw_data.flags >> FLAGS_NINEPATCH_V_MODE_SHIFT) & 0x3, draw_center)); + map_ninepatch_axis(pixel_size_interp.x, abs(draw_data.dst_rect.z), draw_data.color_texture_pixel_size.x, draw_data.ninepatch_margins.x, draw_data.ninepatch_margins.z, int(bitfieldExtract(draw_data.flags, FLAGS_NINEPATCH_H_MODE_SHIFT, 2)), draw_center), + map_ninepatch_axis(pixel_size_interp.y, abs(draw_data.dst_rect.w), draw_data.color_texture_pixel_size.y, draw_data.ninepatch_margins.y, draw_data.ninepatch_margins.w, int(bitfieldExtract(draw_data.flags, FLAGS_NINEPATCH_V_MODE_SHIFT, 2)), draw_center)); if (draw_center == 0) { color.a = 0.0; @@ -519,8 +533,8 @@ void main() { color *= texture(sampler2D(color_texture, texture_sampler), uv); } - uint light_count = (draw_data.flags >> FLAGS_LIGHT_COUNT_SHIFT) & 0xF; //max 16 lights - bool using_light = light_count > 0 || canvas_data.directional_light_count > 0; + uint light_count = bitfieldExtract(draw_data.flags, FLAGS_LIGHT_COUNT_SHIFT, 4); //max 16 lights + bool using_light = (light_count + canvas_data.directional_light_count) > 0; vec3 normal; @@ -652,9 +666,7 @@ void main() { if (i >= light_count) { break; } - uint light_base = draw_data.lights[i >> 2]; - light_base >>= (i & 3) * 8; - light_base &= 0xFF; + uint light_base = bitfieldExtract(draw_data.lights[i >> 2], (int(i) & 0x3) * 8, 8); vec2 tex_uv = (vec4(vertex, 0.0, 1.0) * mat4(light_array.data[light_base].texture_matrix[0], light_array.data[light_base].texture_matrix[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy; //multiply inverse given its transposed. Optimizer removes useless operations. vec2 tex_uv_atlas = tex_uv * light_array.data[light_base].atlas_rect.zw + light_array.data[light_base].atlas_rect.xy; diff --git a/servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl b/servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl index 8649f4710b..7cf5b4576e 100644 --- a/servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl +++ b/servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl @@ -7,13 +7,16 @@ //1 means enabled, 2+ means trails in use #define FLAGS_INSTANCING_MASK 0x7F -#define FLAGS_INSTANCING_HAS_COLORS (1 << 7) -#define FLAGS_INSTANCING_HAS_CUSTOM_DATA (1 << 8) +#define FLAGS_INSTANCING_HAS_COLORS_SHIFT 7 +#define FLAGS_INSTANCING_HAS_COLORS (1 << FLAGS_INSTANCING_HAS_COLORS_SHIFT) +#define FLAGS_INSTANCING_HAS_CUSTOM_DATA_SHIFT 8 +#define FLAGS_INSTANCING_HAS_CUSTOM_DATA (1 << FLAGS_INSTANCING_HAS_CUSTOM_DATA_SHIFT) #define FLAGS_CLIP_RECT_UV (1 << 9) #define FLAGS_TRANSPOSE_RECT (1 << 10) #define FLAGS_CONVERT_ATTRIBUTES_TO_LINEAR (1 << 11) -#define FLAGS_NINEPACH_DRAW_CENTER (1 << 12) +#define FLAGS_NINEPACH_DRAW_CENTER_SHIFT 12 +#define FLAGS_NINEPACH_DRAW_CENTER (1 << FLAGS_NINEPACH_DRAW_CENTER_SHIFT) #define FLAGS_NINEPATCH_H_MODE_SHIFT 16 #define FLAGS_NINEPATCH_V_MODE_SHIFT 18 @@ -29,9 +32,7 @@ #define FLAGS_FLIP_H (1 << 30) #define FLAGS_FLIP_V (1 << 31) -// Push Constant - -layout(push_constant, std430) uniform DrawData { +struct InstanceData { vec2 world_x; vec2 world_y; vec2 world_ofs; @@ -51,8 +52,20 @@ layout(push_constant, std430) uniform DrawData { #endif vec2 color_texture_pixel_size; uint lights[4]; +}; + +layout(set = 4, binding = 0, std430) restrict readonly buffer DrawData { + InstanceData data[]; +} +instances; + +layout(push_constant, std430) uniform Params { + uint base_instance_index; // base index to instance data + uint pad1; + uint pad2; + uint pad3; } -draw_data; +params; // In vulkan, sets should always be ordered using the following logic: // Lower Sets: Sets that change format and layout less often diff --git a/servers/rendering/renderer_viewport.cpp b/servers/rendering/renderer_viewport.cpp index 7e45eba1de..781d29ffaa 100644 --- a/servers/rendering/renderer_viewport.cpp +++ b/servers/rendering/renderer_viewport.cpp @@ -818,8 +818,9 @@ void RendererViewport::draw_viewports(bool p_swap_buffers) { blit.dst_rect.size = vp->size; } - if (!blit_to_screen_list.has(vp->viewport_to_screen)) { - blit_to_screen_list[vp->viewport_to_screen] = Vector<BlitToScreen>(); + Vector<BlitToScreen> *blits = blit_to_screen_list.getptr(vp->viewport_to_screen); + if (blits == nullptr) { + blits = &blit_to_screen_list.insert(vp->viewport_to_screen, Vector<BlitToScreen>())->value; } if (OS::get_singleton()->get_current_rendering_driver_name().begins_with("opengl3")) { @@ -828,7 +829,7 @@ void RendererViewport::draw_viewports(bool p_swap_buffers) { RSG::rasterizer->blit_render_targets_to_screen(vp->viewport_to_screen, blit_to_screen_vec.ptr(), 1); RSG::rasterizer->gl_end_frame(p_swap_buffers); } else { - blit_to_screen_list[vp->viewport_to_screen].push_back(blit); + blits->push_back(blit); } } } diff --git a/servers/rendering/rendering_device.cpp b/servers/rendering/rendering_device.cpp index 276cb4b210..e322bba768 100644 --- a/servers/rendering/rendering_device.cpp +++ b/servers/rendering/rendering_device.cpp @@ -138,17 +138,17 @@ RenderingDevice::ShaderSPIRVGetCacheKeyFunction RenderingDevice::get_spirv_cache /***************************/ void RenderingDevice::_add_dependency(RID p_id, RID p_depends_on) { - if (!dependency_map.has(p_depends_on)) { - dependency_map[p_depends_on] = HashSet<RID>(); + HashSet<RID> *set = dependency_map.getptr(p_depends_on); + if (set == nullptr) { + set = &dependency_map.insert(p_depends_on, HashSet<RID>())->value; } + set->insert(p_id); - dependency_map[p_depends_on].insert(p_id); - - if (!reverse_dependency_map.has(p_id)) { - reverse_dependency_map[p_id] = HashSet<RID>(); + set = reverse_dependency_map.getptr(p_id); + if (set == nullptr) { + set = &reverse_dependency_map.insert(p_id, HashSet<RID>())->value; } - - reverse_dependency_map[p_id].insert(p_depends_on); + set->insert(p_depends_on); } void RenderingDevice::_free_dependencies(RID p_id) { diff --git a/servers/rendering/rendering_server_default.cpp b/servers/rendering/rendering_server_default.cpp index 86efccef9a..b994ebf337 100644 --- a/servers/rendering/rendering_server_default.cpp +++ b/servers/rendering/rendering_server_default.cpp @@ -150,12 +150,10 @@ void RenderingServerDefault::_draw(bool p_swap_buffers, double frame_step) { double time = frame_profile[i + 1].gpu_msec - frame_profile[i].gpu_msec; - if (name[0] != '<' && name[0] != '>') { - if (print_gpu_profile_task_time.has(name)) { - print_gpu_profile_task_time[name] += time; - } else { - print_gpu_profile_task_time[name] = time; - } + if (print_gpu_profile_task_time.has(name)) { + print_gpu_profile_task_time[name] += time; + } else { + print_gpu_profile_task_time[name] = time; } } diff --git a/servers/rendering/shader_compiler.cpp b/servers/rendering/shader_compiler.cpp index 29183eac6f..5f97fa0c9b 100644 --- a/servers/rendering/shader_compiler.cpp +++ b/servers/rendering/shader_compiler.cpp @@ -185,7 +185,7 @@ static String f2sp0(float p_float) { return num; } -static String get_constant_text(SL::DataType p_type, const Vector<SL::Scalar> &p_values, bool p_is_op) { +static String get_constant_text(SL::DataType p_type, const Vector<SL::Scalar> &p_values) { switch (p_type) { case SL::TYPE_BOOL: return p_values[0].boolean ? "true" : "false"; @@ -205,7 +205,7 @@ static String get_constant_text(SL::DataType p_type, const Vector<SL::Scalar> &p } case SL::TYPE_INT: - return itos(p_is_op ? Math::abs(p_values[0].sint) : p_values[0].sint); // To prevent writing unary minus twice in operator expression parsing. + return itos(p_values[0].sint); case SL::TYPE_IVEC2: case SL::TYPE_IVEC3: case SL::TYPE_IVEC4: { @@ -238,7 +238,7 @@ static String get_constant_text(SL::DataType p_type, const Vector<SL::Scalar> &p return text; } break; case SL::TYPE_FLOAT: - return f2sp0(p_is_op ? Math::abs(p_values[0].real) : p_values[0].real); // To prevent writing unary minus twice in operator expression parsing. + return f2sp0(p_values[0].real); case SL::TYPE_VEC2: case SL::TYPE_VEC3: case SL::TYPE_VEC4: { @@ -446,7 +446,7 @@ static String _get_global_shader_uniform_from_type_and_index(const String &p_buf } } -String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, GeneratedCode &r_gen_code, IdentifierActions &p_actions, const DefaultIdentifierActions &p_default_actions, bool p_assigning, bool p_use_scope, bool p_is_op) { +String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, GeneratedCode &r_gen_code, IdentifierActions &p_actions, const DefaultIdentifierActions &p_default_actions, bool p_assigning, bool p_use_scope) { String code; switch (p_node->type) { @@ -1090,7 +1090,7 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene SL::ConstantNode *cnode = (SL::ConstantNode *)p_node; if (cnode->array_size == 0) { - return get_constant_text(cnode->datatype, cnode->values, p_is_op); + return get_constant_text(cnode->datatype, cnode->values); } else { if (cnode->get_datatype() == SL::TYPE_STRUCT) { code += _mkid(cnode->struct_name); @@ -1128,18 +1128,25 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene case SL::OP_ASSIGN_BIT_AND: case SL::OP_ASSIGN_BIT_OR: case SL::OP_ASSIGN_BIT_XOR: - code = _dump_node_code(onode->arguments[0], p_level, r_gen_code, p_actions, p_default_actions, true, true, true) + _opstr(onode->op) + _dump_node_code(onode->arguments[1], p_level, r_gen_code, p_actions, p_default_actions, p_assigning, true, true); + code = _dump_node_code(onode->arguments[0], p_level, r_gen_code, p_actions, p_default_actions, true) + _opstr(onode->op) + _dump_node_code(onode->arguments[1], p_level, r_gen_code, p_actions, p_default_actions, p_assigning); break; case SL::OP_BIT_INVERT: case SL::OP_NEGATE: case SL::OP_NOT: case SL::OP_DECREMENT: - case SL::OP_INCREMENT: - code = _opstr(onode->op) + _dump_node_code(onode->arguments[0], p_level, r_gen_code, p_actions, p_default_actions, p_assigning, true, true); - break; + case SL::OP_INCREMENT: { + const String node_code = _dump_node_code(onode->arguments[0], p_level, r_gen_code, p_actions, p_default_actions, p_assigning); + + if (onode->op == SL::OP_NEGATE && node_code.begins_with("-")) { // To prevent writing unary minus twice. + code = node_code; + } else { + code = _opstr(onode->op) + node_code; + } + + } break; case SL::OP_POST_DECREMENT: case SL::OP_POST_INCREMENT: - code = _dump_node_code(onode->arguments[0], p_level, r_gen_code, p_actions, p_default_actions, p_assigning, true, true) + _opstr(onode->op); + code = _dump_node_code(onode->arguments[0], p_level, r_gen_code, p_actions, p_default_actions, p_assigning) + _opstr(onode->op); break; case SL::OP_CALL: case SL::OP_STRUCT: @@ -1235,7 +1242,7 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene } } - String node_code = _dump_node_code(onode->arguments[i], p_level, r_gen_code, p_actions, p_default_actions, p_assigning, true, true); + String node_code = _dump_node_code(onode->arguments[i], p_level, r_gen_code, p_actions, p_default_actions, p_assigning); if (is_texture_func && i == 1) { // If we're doing a texture lookup we need to check our texture argument StringName texture_uniform; @@ -1352,19 +1359,19 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene } } break; case SL::OP_INDEX: { - code += _dump_node_code(onode->arguments[0], p_level, r_gen_code, p_actions, p_default_actions, p_assigning, true, true); + code += _dump_node_code(onode->arguments[0], p_level, r_gen_code, p_actions, p_default_actions, p_assigning); code += "["; - code += _dump_node_code(onode->arguments[1], p_level, r_gen_code, p_actions, p_default_actions, p_assigning, true, true); + code += _dump_node_code(onode->arguments[1], p_level, r_gen_code, p_actions, p_default_actions, p_assigning); code += "]"; } break; case SL::OP_SELECT_IF: { code += "("; - code += _dump_node_code(onode->arguments[0], p_level, r_gen_code, p_actions, p_default_actions, p_assigning, true, true); + code += _dump_node_code(onode->arguments[0], p_level, r_gen_code, p_actions, p_default_actions, p_assigning); code += "?"; - code += _dump_node_code(onode->arguments[1], p_level, r_gen_code, p_actions, p_default_actions, p_assigning, true, true); + code += _dump_node_code(onode->arguments[1], p_level, r_gen_code, p_actions, p_default_actions, p_assigning); code += ":"; - code += _dump_node_code(onode->arguments[2], p_level, r_gen_code, p_actions, p_default_actions, p_assigning, true, true); + code += _dump_node_code(onode->arguments[2], p_level, r_gen_code, p_actions, p_default_actions, p_assigning); code += ")"; } break; @@ -1376,7 +1383,7 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene if (p_use_scope) { code += "("; } - code += _dump_node_code(onode->arguments[0], p_level, r_gen_code, p_actions, p_default_actions, p_assigning) + " " + _opstr(onode->op) + " " + _dump_node_code(onode->arguments[1], p_level, r_gen_code, p_actions, p_default_actions, p_assigning, true, true); + code += _dump_node_code(onode->arguments[0], p_level, r_gen_code, p_actions, p_default_actions, p_assigning) + " " + _opstr(onode->op) + " " + _dump_node_code(onode->arguments[1], p_level, r_gen_code, p_actions, p_default_actions, p_assigning); if (p_use_scope) { code += ")"; } diff --git a/servers/rendering/shader_compiler.h b/servers/rendering/shader_compiler.h index bf0cfd7032..66106d7eb7 100644 --- a/servers/rendering/shader_compiler.h +++ b/servers/rendering/shader_compiler.h @@ -109,7 +109,7 @@ private: String _get_sampler_name(ShaderLanguage::TextureFilter p_filter, ShaderLanguage::TextureRepeat p_repeat); void _dump_function_deps(const ShaderLanguage::ShaderNode *p_node, const StringName &p_for_func, const HashMap<StringName, String> &p_func_code, String &r_to_add, HashSet<StringName> &added); - String _dump_node_code(const ShaderLanguage::Node *p_node, int p_level, GeneratedCode &r_gen_code, IdentifierActions &p_actions, const DefaultIdentifierActions &p_default_actions, bool p_assigning, bool p_scope = true, bool p_is_op = false); + String _dump_node_code(const ShaderLanguage::Node *p_node, int p_level, GeneratedCode &r_gen_code, IdentifierActions &p_actions, const DefaultIdentifierActions &p_default_actions, bool p_assigning, bool p_scope = true); const ShaderLanguage::ShaderNode *shader = nullptr; const ShaderLanguage::FunctionNode *function = nullptr; diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp index 2249cd2010..5a3c5d2fd0 100644 --- a/servers/rendering/shader_language.cpp +++ b/servers/rendering/shader_language.cpp @@ -8071,12 +8071,10 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun if (!n) { return ERR_PARSE_ERROR; } - { - const ShaderLanguage::DataType switch_type = n->get_datatype(); - if (switch_type != TYPE_INT && switch_type != TYPE_UINT) { - _set_error(RTR("Expected an integer expression.")); - return ERR_PARSE_ERROR; - } + const ShaderLanguage::DataType data_type = n->get_datatype(); + if (data_type != TYPE_INT && data_type != TYPE_UINT) { + _set_error(RTR("Expected an integer or unsigned integer expression.")); + return ERR_PARSE_ERROR; } tk = _get_token(); if (tk.type != TK_PARENTHESIS_CLOSE) { @@ -8091,11 +8089,22 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun BlockNode *switch_block = alloc_node<BlockNode>(); switch_block->block_type = BlockNode::BLOCK_TYPE_SWITCH; switch_block->parent_block = p_block; + switch_block->expected_type = data_type; cf->expressions.push_back(n); cf->blocks.push_back(switch_block); p_block->statements.push_back(cf); - int prev_type = TK_CF_CASE; + pos = _get_tkpos(); + tk = _get_token(); + TokenType prev_type; + if (tk.type == TK_CF_CASE || tk.type == TK_CF_DEFAULT) { + prev_type = tk.type; + _set_tkpos(pos); + } else { + _set_expected_error("case", "default"); + return ERR_PARSE_ERROR; + } + while (true) { // Go-through multiple cases. if (_parse_block(switch_block, p_function_info, true, true, false) != OK) { @@ -8117,43 +8126,6 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun _set_tkpos(pos); continue; } else { - HashSet<int> constants; - for (ShaderLanguage::Node *statement : switch_block->statements) { // Checks for duplicates. - ControlFlowNode *flow = static_cast<ControlFlowNode *>(statement); - if (flow) { - if (flow->flow_op == FLOW_OP_CASE) { - if (flow->expressions[0]->type == Node::NODE_TYPE_CONSTANT) { - ConstantNode *cn = static_cast<ConstantNode *>(flow->expressions[0]); - if (!cn || cn->values.is_empty()) { - return ERR_PARSE_ERROR; - } - if (constants.has(cn->values[0].sint)) { - _set_error(vformat(RTR("Duplicated case label: %d."), cn->values[0].sint)); - return ERR_PARSE_ERROR; - } - constants.insert(cn->values[0].sint); - } else if (flow->expressions[0]->type == Node::NODE_TYPE_VARIABLE) { - VariableNode *vn = static_cast<VariableNode *>(flow->expressions[0]); - if (!vn) { - return ERR_PARSE_ERROR; - } - Vector<Scalar> v = { Scalar() }; - _find_identifier(p_block, false, p_function_info, vn->name, nullptr, nullptr, nullptr, nullptr, nullptr, &v); - if (constants.has(v[0].sint)) { - _set_error(vformat(RTR("Duplicated case label: %d."), v[0].sint)); - return ERR_PARSE_ERROR; - } - constants.insert(v[0].sint); - } - } else if (flow->flow_op == FLOW_OP_DEFAULT) { - continue; - } else { - return ERR_PARSE_ERROR; - } - } else { - return ERR_PARSE_ERROR; - } - } break; } } @@ -8184,36 +8156,77 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun if (!tk.is_integer_constant()) { bool correct_constant_expression = false; - DataType data_type; if (tk.type == TK_IDENTIFIER) { + DataType data_type; bool is_const; + _find_identifier(p_block, false, p_function_info, tk.text, &data_type, nullptr, &is_const); - if (is_const) { - if (data_type == TYPE_INT) { - correct_constant_expression = true; - } + if (is_const && data_type == p_block->expected_type) { + correct_constant_expression = true; } } + if (!correct_constant_expression) { - _set_error(RTR("Expected an integer constant.")); + if (p_block->expected_type == TYPE_UINT) { + _set_error(RTR("Expected an unsigned integer constant.")); + } else { + _set_error(RTR("Expected an integer constant.")); + } return ERR_PARSE_ERROR; } VariableNode *vn = alloc_node<VariableNode>(); vn->name = tk.text; + { + Vector<Scalar> v; + DataType data_type; + + _find_identifier(p_block, false, p_function_info, vn->name, &data_type, nullptr, nullptr, nullptr, nullptr, &v); + if (data_type == TYPE_INT) { + if (p_block->constants.has(v[0].sint)) { + _set_error(vformat(RTR("Duplicated case label: %d."), v[0].sint)); + return ERR_PARSE_ERROR; + } + p_block->constants.insert(v[0].sint); + } else { + if (p_block->constants.has(v[0].uint)) { + _set_error(vformat(RTR("Duplicated case label: %d."), v[0].uint)); + return ERR_PARSE_ERROR; + } + p_block->constants.insert(v[0].uint); + } + } n = vn; } else { + ConstantNode *cn = alloc_node<ConstantNode>(); Scalar v; - if (tk.type == TK_UINT_CONSTANT) { + if (p_block->expected_type == TYPE_UINT) { + if (tk.type != TK_UINT_CONSTANT) { + _set_error(RTR("Expected an unsigned integer constant.")); + return ERR_PARSE_ERROR; + } v.uint = (uint32_t)tk.constant; + if (p_block->constants.has(v.uint)) { + _set_error(vformat(RTR("Duplicated case label: %d."), v.uint)); + return ERR_PARSE_ERROR; + } + p_block->constants.insert(v.uint); + cn->datatype = TYPE_UINT; } else { - v.sint = (int)tk.constant * sign; + if (tk.type != TK_INT_CONSTANT) { + _set_error(RTR("Expected an integer constant.")); + return ERR_PARSE_ERROR; + } + v.sint = (int32_t)tk.constant * sign; + if (p_block->constants.has(v.sint)) { + _set_error(vformat(RTR("Duplicated case label: %d."), v.sint)); + return ERR_PARSE_ERROR; + } + p_block->constants.insert(v.sint); + cn->datatype = TYPE_INT; } - - ConstantNode *cn = alloc_node<ConstantNode>(); cn->values.push_back(v); - cn->datatype = (tk.type == TK_UINT_CONSTANT ? TYPE_UINT : TYPE_INT); n = cn; } diff --git a/servers/rendering/shader_language.h b/servers/rendering/shader_language.h index ba02e181b9..b0d579dfe7 100644 --- a/servers/rendering/shader_language.h +++ b/servers/rendering/shader_language.h @@ -544,6 +544,9 @@ public: bool use_comma_between_statements = false; bool use_op_eval = true; + DataType expected_type = TYPE_VOID; + HashSet<int> constants; + BlockNode() : Node(NODE_TYPE_BLOCK) {} }; diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp index 1848d5602e..f354e83893 100644 --- a/servers/rendering_server.cpp +++ b/servers/rendering_server.cpp @@ -3548,6 +3548,7 @@ void RenderingServer::init() { GLOBAL_DEF("rendering/lights_and_shadows/positional_shadow/soft_shadow_filter_quality.mobile", 0); GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/2d/shadow_atlas/size", PROPERTY_HINT_RANGE, "128,16384"), 2048); + GLOBAL_DEF_RST(PropertyInfo(Variant::INT, "rendering/2d/batching/item_buffer_size", PROPERTY_HINT_RANGE, "128,1048576,1"), 16384); // Number of commands that can be drawn per frame. GLOBAL_DEF_RST(PropertyInfo(Variant::INT, "rendering/gl_compatibility/item_buffer_size", PROPERTY_HINT_RANGE, "128,1048576,1"), 16384); diff --git a/tests/scene/test_viewport.h b/tests/scene/test_viewport.h index 1341cc0332..9d02c41719 100644 --- a/tests/scene/test_viewport.h +++ b/tests/scene/test_viewport.h @@ -119,8 +119,23 @@ public: class DragTarget : public NotificationControlViewport { GDCLASS(DragTarget, NotificationControlViewport); +protected: + void _notification(int p_what) { + switch (p_what) { + case NOTIFICATION_DRAG_BEGIN: { + during_drag = true; + } break; + + case NOTIFICATION_DRAG_END: { + during_drag = false; + } break; + } + } + public: Variant drag_data; + bool valid_drop = false; + bool during_drag = false; virtual bool can_drop_data(const Point2 &p_point, const Variant &p_data) const override { StringName string_data = p_data; // Verify drag data is compatible. @@ -136,6 +151,7 @@ public: virtual void drop_data(const Point2 &p_point, const Variant &p_data) override { drag_data = p_data; + valid_drop = true; } }; @@ -1107,12 +1123,10 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") { SUBCASE("[Viewport][GuiInputEvent] Drag and Drop") { // FIXME: Drag-Preview will likely change. Tests for this part would have to be rewritten anyway. // See https://github.com/godotengine/godot/pull/67531#issuecomment-1385353430 for details. - // FIXME: Testing Drag and Drop with non-embedded windows would require DisplayServerMock additions - // FIXME: Drag and Drop currently doesn't work with embedded Windows and SubViewports - not testing. - // See https://github.com/godotengine/godot/issues/28522 for example. + // Note: Testing Drag and Drop with non-embedded windows would require DisplayServerMock additions. int min_grab_movement = 11; - SUBCASE("[Viewport][GuiInputEvent] Drag from one Control to another in the same viewport.") { - SUBCASE("[Viewport][GuiInputEvent] Perform successful Drag and Drop on a different Control.") { + SUBCASE("[Viewport][GuiInputEvent][DnD] Drag from one Control to another in the same viewport.") { + SUBCASE("[Viewport][GuiInputEvent][DnD] Perform successful Drag and Drop on a different Control.") { SEND_GUI_MOUSE_BUTTON_EVENT(on_a, MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE); CHECK_FALSE(root->gui_is_dragging()); @@ -1131,7 +1145,7 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") { CHECK((StringName)node_d->drag_data == SNAME("Drag Data")); } - SUBCASE("[Viewport][GuiInputEvent] Perform unsuccessful drop on Control.") { + SUBCASE("[Viewport][GuiInputEvent][DnD] Perform unsuccessful drop on Control.") { SEND_GUI_MOUSE_BUTTON_EVENT(on_a, MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE); CHECK_FALSE(root->gui_is_dragging()); @@ -1157,7 +1171,7 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") { CHECK_FALSE(root->gui_is_drag_successful()); } - SUBCASE("[Viewport][GuiInputEvent] Perform unsuccessful drop on No-Control.") { + SUBCASE("[Viewport][GuiInputEvent][DnD] Perform unsuccessful drop on No-Control.") { SEND_GUI_MOUSE_BUTTON_EVENT(on_a, MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE); CHECK_FALSE(root->gui_is_dragging()); @@ -1171,7 +1185,7 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") { // Move away from Controls. SEND_GUI_MOUSE_MOTION_EVENT(on_background, MouseButtonMask::LEFT, Key::NONE); - CHECK(DS->get_cursor_shape() == DisplayServer::CURSOR_ARROW); // This could also be CURSOR_FORBIDDEN. + CHECK(DS->get_cursor_shape() == DisplayServer::CURSOR_ARROW); CHECK(root->gui_is_dragging()); SEND_GUI_MOUSE_BUTTON_RELEASED_EVENT(on_background, MouseButton::LEFT, MouseButtonMask::NONE, Key::NONE); @@ -1179,7 +1193,7 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") { CHECK_FALSE(root->gui_is_drag_successful()); } - SUBCASE("[Viewport][GuiInputEvent] Perform unsuccessful drop outside of window.") { + SUBCASE("[Viewport][GuiInputEvent][DnD] Perform unsuccessful drop outside of window.") { SEND_GUI_MOUSE_BUTTON_EVENT(on_a, MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE); CHECK_FALSE(root->gui_is_dragging()); @@ -1192,7 +1206,6 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") { // Move outside of window. SEND_GUI_MOUSE_MOTION_EVENT(on_outside, MouseButtonMask::LEFT, Key::NONE); - CHECK(DS->get_cursor_shape() == DisplayServer::CURSOR_ARROW); CHECK(root->gui_is_dragging()); SEND_GUI_MOUSE_BUTTON_RELEASED_EVENT(on_outside, MouseButton::LEFT, MouseButtonMask::NONE, Key::NONE); @@ -1200,7 +1213,7 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") { CHECK_FALSE(root->gui_is_drag_successful()); } - SUBCASE("[Viewport][GuiInputEvent] Drag and Drop doesn't work with other Mouse Buttons than LMB.") { + SUBCASE("[Viewport][GuiInputEvent][DnD] Drag and Drop doesn't work with other Mouse Buttons than LMB.") { SEND_GUI_MOUSE_BUTTON_EVENT(on_a, MouseButton::MIDDLE, MouseButtonMask::MIDDLE, Key::NONE); CHECK_FALSE(root->gui_is_dragging()); @@ -1209,7 +1222,7 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") { SEND_GUI_MOUSE_BUTTON_RELEASED_EVENT(on_a, MouseButton::MIDDLE, MouseButtonMask::NONE, Key::NONE); } - SUBCASE("[Viewport][GuiInputEvent] Drag and Drop parent propagation.") { + SUBCASE("[Viewport][GuiInputEvent][DnD] Drag and Drop parent propagation.") { Node2D *node_aa = memnew(Node2D); Control *node_aaa = memnew(Control); Node2D *node_dd = memnew(Node2D); @@ -1318,7 +1331,7 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") { memdelete(node_aa); } - SUBCASE("[Viewport][GuiInputEvent] Force Drag and Drop.") { + SUBCASE("[Viewport][GuiInputEvent][DnD] Force Drag and Drop.") { SEND_GUI_MOUSE_MOTION_EVENT(on_background, MouseButtonMask::NONE, Key::NONE); CHECK_FALSE(root->gui_is_dragging()); node_a->force_drag(SNAME("Drag Data"), nullptr); @@ -1339,6 +1352,111 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") { SEND_GUI_MOUSE_BUTTON_RELEASED_EVENT(on_d, MouseButton::LEFT, MouseButtonMask::NONE, Key::NONE); } } + + SUBCASE("[Viewport][GuiInputEvent][DnD] Drag to a different Viewport.") { + SubViewportContainer *svc = memnew(SubViewportContainer); + svc->set_size(Size2(100, 100)); + svc->set_position(Point2(200, 50)); + root->add_child(svc); + + SubViewport *sv = memnew(SubViewport); + sv->set_embedding_subwindows(true); + sv->set_size(Size2i(100, 100)); + svc->add_child(sv); + + DragStart *sv_a = memnew(DragStart); + sv_a->set_position(Point2(10, 10)); + sv_a->set_size(Size2(10, 10)); + sv->add_child(sv_a); + Point2i on_sva = Point2i(215, 65); + + DragTarget *sv_b = memnew(DragTarget); + sv_b->set_position(Point2(30, 30)); + sv_b->set_size(Size2(20, 20)); + sv->add_child(sv_b); + Point2i on_svb = Point2i(235, 85); + + Window *ew = memnew(Window); + ew->set_position(Point2(50, 200)); + ew->set_size(Size2(100, 100)); + root->add_child(ew); + + DragStart *ew_a = memnew(DragStart); + ew_a->set_position(Point2(10, 10)); + ew_a->set_size(Size2(10, 10)); + ew->add_child(ew_a); + Point2i on_ewa = Point2i(65, 215); + + DragTarget *ew_b = memnew(DragTarget); + ew_b->set_position(Point2(30, 30)); + ew_b->set_size(Size2(20, 20)); + ew->add_child(ew_b); + Point2i on_ewb = Point2i(85, 235); + + SUBCASE("[Viewport][GuiInputEvent][DnD] Drag to SubViewport") { + sv_b->valid_drop = false; + SEND_GUI_MOUSE_BUTTON_EVENT(on_a, MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE); + SEND_GUI_MOUSE_MOTION_EVENT(on_a + Point2i(min_grab_movement, 0), MouseButtonMask::LEFT, Key::NONE); + CHECK(root->gui_is_dragging()); + CHECK(sv_b->during_drag); + SEND_GUI_MOUSE_MOTION_EVENT(on_svb, MouseButtonMask::LEFT, Key::NONE); + CHECK(DS->get_cursor_shape() == DisplayServer::CURSOR_CAN_DROP); + + SEND_GUI_MOUSE_BUTTON_RELEASED_EVENT(on_svb, MouseButton::LEFT, MouseButtonMask::NONE, Key::NONE); + CHECK(sv_b->valid_drop); + CHECK(!sv_b->during_drag); + } + + SUBCASE("[Viewport][GuiInputEvent][DnD] Drag from SubViewport") { + node_d->valid_drop = false; + SEND_GUI_MOUSE_BUTTON_EVENT(on_sva, MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE); + SEND_GUI_MOUSE_MOTION_EVENT(on_sva + Point2i(min_grab_movement, 0), MouseButtonMask::LEFT, Key::NONE); + CHECK(sv->gui_is_dragging()); + CHECK(node_d->during_drag); + SEND_GUI_MOUSE_MOTION_EVENT(on_d, MouseButtonMask::LEFT, Key::NONE); + CHECK(DS->get_cursor_shape() == DisplayServer::CURSOR_CAN_DROP); + + SEND_GUI_MOUSE_BUTTON_RELEASED_EVENT(on_d, MouseButton::LEFT, MouseButtonMask::NONE, Key::NONE); + CHECK(node_d->valid_drop); + CHECK(!node_d->during_drag); + } + + SUBCASE("[Viewport][GuiInputEvent][DnD] Drag to embedded Window") { + ew_b->valid_drop = false; + SEND_GUI_MOUSE_BUTTON_EVENT(on_a, MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE); + SEND_GUI_MOUSE_MOTION_EVENT(on_a + Point2i(min_grab_movement, 0), MouseButtonMask::LEFT, Key::NONE); + CHECK(root->gui_is_dragging()); + CHECK(ew_b->during_drag); + SEND_GUI_MOUSE_MOTION_EVENT(on_ewb, MouseButtonMask::LEFT, Key::NONE); + CHECK(DS->get_cursor_shape() == DisplayServer::CURSOR_CAN_DROP); + + SEND_GUI_MOUSE_BUTTON_RELEASED_EVENT(on_ewb, MouseButton::LEFT, MouseButtonMask::NONE, Key::NONE); + CHECK(ew_b->valid_drop); + CHECK(!ew_b->during_drag); + } + + SUBCASE("[Viewport][GuiInputEvent][DnD] Drag from embedded Window") { + node_d->valid_drop = false; + SEND_GUI_MOUSE_BUTTON_EVENT(on_ewa, MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE); + SEND_GUI_MOUSE_MOTION_EVENT(on_ewa + Point2i(min_grab_movement, 0), MouseButtonMask::LEFT, Key::NONE); + CHECK(ew->gui_is_dragging()); + CHECK(node_d->during_drag); + SEND_GUI_MOUSE_MOTION_EVENT(on_d, MouseButtonMask::LEFT, Key::NONE); + CHECK(DS->get_cursor_shape() == DisplayServer::CURSOR_CAN_DROP); + + SEND_GUI_MOUSE_BUTTON_RELEASED_EVENT(on_d, MouseButton::LEFT, MouseButtonMask::NONE, Key::NONE); + CHECK(node_d->valid_drop); + CHECK(!node_d->during_drag); + } + + memdelete(ew_a); + memdelete(ew_b); + memdelete(ew); + memdelete(sv_a); + memdelete(sv_b); + memdelete(sv); + memdelete(svc); + } } memdelete(node_j); |
