diff options
278 files changed, 4505 insertions, 3369 deletions
diff --git a/.github/workflows/windows_builds.yml b/.github/workflows/windows_builds.yml index f9513af5e3..7a3347d49c 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 + SCONSFLAGS: verbose=yes warnings=extra werror=yes module_text_server_fb_enabled=yes d3d12=yes "angle_libs=${{github.workspace}}/" SCONS_CACHE_MSVC_CONFIG: true concurrency: @@ -54,6 +54,17 @@ jobs: - name: Download Direct3D 12 SDK components run: python ./misc/scripts/install_d3d12_sdk_windows.py + - name: Download pre-built ANGLE static libraries + uses: dsaltares/fetch-gh-release-asset@1.1.2 + with: + repo: godotengine/godot-angle-static + version: tags/chromium/6029 + file: Windows.6029-1.MSVC_17.x86_64.x86_32.zip + target: angle/angle.zip + + - name: Extract pre-built ANGLE static libraries + run: Expand-Archive -Force angle/angle.zip ${{github.workspace}}/ + - name: Setup MSVC problem matcher uses: ammaraskar/msvc-problem-matcher@master diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5a0e919c47..c9a7714023 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -17,12 +17,12 @@ repos: platform/android/java/lib/src/com/.* ) - - repo: https://github.com/psf/black-pre-commit-mirror - rev: 24.2.0 + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.4.4 hooks: - - id: black - files: (\.py$|SConstruct|SCsub) - types_or: [text] + - id: ruff + args: [--fix] + - id: ruff-format - repo: https://github.com/pre-commit/mirrors-mypy rev: v0.971 @@ -74,37 +74,6 @@ repos: # files: ^(doc/classes|.*/doc_classes)/.*\.xml$ # args: [--schema, doc/class.xsd] - - repo: https://github.com/pre-commit/mirrors-eslint - rev: v8.46.0 - hooks: - - id: eslint - name: eslint-engine - files: ^(platform/web/js/engine/|js/jsdoc2rst/).*\.js$ - args: [--fix, --no-eslintrc, --config, platform/web/.eslintrc.engine.js] - additional_dependencies: &eslint-deps - - eslint@8.46.0 - - eslint-config-airbnb-base@15.0.0 - - eslint-plugin-import@2.28.0 - - eslint-plugin-html@7.1.0 - - '@html-eslint/eslint-plugin@0.19.1' - - '@html-eslint/parser@0.19.1' - - id: eslint - name: eslint-libs - files: ^(platform/web/js/libs/|modules/).*\.js$ - args: [--fix, --no-eslintrc, --config, platform/web/.eslintrc.libs.js] - additional_dependencies: *eslint-deps - - id: eslint - name: eslint-sw - files: ^misc/dist/html/service-worker\.js$ - args: [--fix, --no-eslintrc, --config, platform/web/.eslintrc.sw.js] - additional_dependencies: *eslint-deps - - id: eslint - name: eslint-html - files: ^misc/dist/html/.*\.html$ - types: [html] - args: [--fix, --no-eslintrc, --config, platform/web/.eslintrc.html.js] - additional_dependencies: *eslint-deps - - repo: local hooks: - id: make-rst @@ -120,6 +89,22 @@ repos: entry: python3 doc/tools/doc_status.py files: ^(doc/classes|.*/doc_classes)/.*\.xml$ + - id: eslint + name: eslint + language: node + entry: eslint + files: ^(platform/web/js/|modules/|misc/dist/html/).*\.(js|html)$ + args: [--fix, --no-warn-ignored, --no-config-lookup, --config, platform/web/eslint.config.cjs] + additional_dependencies: + - '@eslint/js@^9.3.0' + - '@html-eslint/eslint-plugin@^0.24.1' + - '@html-eslint/parser@^0.24.1' + - '@stylistic/eslint-plugin@^2.1.0' + - 'eslint@^9.3.0' + - 'eslint-plugin-html@^8.1.1' + - 'globals@^15.3.0' + - 'espree@^10.0.1' + - id: jsdoc name: jsdoc language: node @@ -146,6 +131,7 @@ repos: exclude: | (?x)^( core/math/bvh_.*\.inc$| + platform/(?!android|ios|linuxbsd|macos|web|windows)\w+/.*| platform/android/java/lib/src/com/.*| platform/android/java/lib/src/org/godotengine/godot/gl/GLSurfaceView\.java$| platform/android/java/lib/src/org/godotengine/godot/gl/EGLLogWrapper\.java$| @@ -186,3 +172,15 @@ repos: language: python entry: python3 misc/scripts/dotnet_format.py types_or: [c#] + +# End of upstream Godot pre-commit hooks. +# +# Keep this separation to let downstream forks add their own hooks to this file, +# without running into merge conflicts when rebasing on latest upstream. +# +# Start of downstream pre-commit hooks. +# +# This is still the "repo: local" scope, so new local hooks can be defined directly at this indentation: +# - id: new-local-hook +# To add external repo hooks, bring the indentation back to: +# - repo: my-remote-hook diff --git a/SConstruct b/SConstruct index afa44fef4a..117528de37 100644 --- a/SConstruct +++ b/SConstruct @@ -10,11 +10,11 @@ import os import pickle import sys import time -from types import ModuleType from collections import OrderedDict -from importlib.util import spec_from_file_location, module_from_spec -from SCons import __version__ as scons_raw_version +from importlib.util import module_from_spec, spec_from_file_location +from types import ModuleType +from SCons import __version__ as scons_raw_version # Explicitly resolve the helper modules, this is done to avoid clash with # modules of the same name that might be randomly added (e.g. someone adding @@ -53,12 +53,12 @@ _helper_module("core.core_builders", "core/core_builders.py") _helper_module("main.main_builders", "main/main_builders.py") # Local -import methods -import glsl_builders import gles3_builders +import glsl_builders +import methods import scu_builders -from methods import print_warning, print_error -from platform_methods import architectures, architecture_aliases +from methods import print_error, print_warning +from platform_methods import architecture_aliases, architectures if ARGUMENTS.get("target", "editor") == "editor": _helper_module("editor.editor_builders", "editor/editor_builders.py") @@ -68,7 +68,7 @@ if ARGUMENTS.get("target", "editor") == "editor": # <https://github.com/python/cpython/issues/73245> if sys.stdout.isatty() and sys.platform == "win32": try: - from ctypes import windll, byref, WinError # type: ignore + from ctypes import WinError, byref, windll # type: ignore from ctypes.wintypes import DWORD # type: ignore stdout_handle = windll.kernel32.GetStdHandle(DWORD(-11)) @@ -122,6 +122,8 @@ for x in sorted(glob.glob("platform/*")): platform_list += [x] platform_opts[x] = detect.get_opts() platform_flags[x] = detect.get_flags() + if isinstance(platform_flags[x], list): # backwards compatibility + platform_flags[x] = {flag[0]: flag[1] for flag in platform_flags[x]} sys.path.remove(tmppath) sys.modules.pop("detect") @@ -216,7 +218,7 @@ opts.Add(BoolVariable("brotli", "Enable Brotli for decompresson and WOFF2 fonts opts.Add(BoolVariable("xaudio2", "Enable the XAudio2 audio driver", False)) opts.Add(BoolVariable("vulkan", "Enable the vulkan rendering driver", True)) opts.Add(BoolVariable("opengl3", "Enable the OpenGL/GLES3 rendering driver", True)) -opts.Add(BoolVariable("d3d12", "Enable the Direct3D 12 rendering driver (Windows only)", False)) +opts.Add(BoolVariable("d3d12", "Enable the Direct3D 12 rendering driver", False)) opts.Add(BoolVariable("openxr", "Enable the OpenXR driver", True)) opts.Add(BoolVariable("use_volk", "Use the volk library to load the Vulkan loader dynamically", True)) opts.Add(BoolVariable("disable_exceptions", "Force disabling exception handling code", True)) @@ -562,16 +564,16 @@ if env["build_profile"] != "": dbo = ft["disabled_build_options"] for c in dbo: env[c] = dbo[c] - except: + except json.JSONDecodeError: print_error('Failed to open feature build profile: "{}"'.format(env["build_profile"])) Exit(255) # Platform specific flags. # These can sometimes override default options. flag_list = platform_flags[env["platform"]] -for f in flag_list: - if not (f[0] in ARGUMENTS) or ARGUMENTS[f[0]] == "auto": # Allow command line to override platform flags - env[f[0]] = f[1] +for key, value in flag_list.items(): + if key not in ARGUMENTS or ARGUMENTS[key] == "auto": # Allow command line to override platform flags + env[key] = value # 'dev_mode' and 'production' are aliases to set default options if they haven't been # set manually by the user. @@ -591,7 +593,7 @@ if env["production"]: # Run SCU file generation script if in a SCU build. if env["scu_build"]: max_includes_per_scu = 8 - if env.dev_build == True: + if env.dev_build: max_includes_per_scu = 1024 read_scu_limit = int(env["scu_limit"]) @@ -984,7 +986,7 @@ GLSL_BUILDERS = { env.Append(BUILDERS=GLSL_BUILDERS) scons_cache_path = os.environ.get("SCONS_CACHE") -if scons_cache_path != None: +if scons_cache_path is not None: CacheDir(scons_cache_path) print("Scons cache enabled... (path: '" + scons_cache_path + "')") diff --git a/core/SCsub b/core/SCsub index a61c0b5fc2..52f3506416 100644 --- a/core/SCsub +++ b/core/SCsub @@ -2,9 +2,11 @@ Import("env") +import os + import core_builders + import methods -import os env.core_sources = [] @@ -188,9 +190,7 @@ def version_info_builder(target, source, env): #define VERSION_WEBSITE "{website}" #define VERSION_DOCS_BRANCH "{docs_branch}" #define VERSION_DOCS_URL "https://docs.godotengine.org/en/" VERSION_DOCS_BRANCH -""".format( - **env.version_info - ) +""".format(**env.version_info) ) @@ -206,9 +206,7 @@ def version_hash_builder(target, source, env): const char *const VERSION_HASH = "{git_hash}"; const uint64_t VERSION_TIMESTAMP = {git_timestamp}; -""".format( - **env.version_info - ) +""".format(**env.version_info) ) diff --git a/core/core_builders.py b/core/core_builders.py index a401f03693..a3dc935b79 100644 --- a/core/core_builders.py +++ b/core/core_builders.py @@ -180,7 +180,7 @@ def make_license_header(target, source, env): return line def next_tag(self): - if not ":" in self.current: + if ":" not in self.current: return ("", []) tag, line = self.current.split(":", 1) lines = [line.strip()] @@ -206,7 +206,7 @@ def make_license_header(target, source, env): if not tag or not reader.current: # end of a paragraph start a new part - if "License" in part and not "Files" in part: + if "License" in part and "Files" not in part: # no Files tag in this one, so assume standalone license license_list.append(part["License"]) part = {} @@ -298,13 +298,13 @@ def make_license_header(target, source, env): f.write("const int LICENSE_COUNT = " + str(len(license_list)) + ";\n") f.write("const char *const LICENSE_NAMES[] = {\n") - for l in license_list: - f.write('\t"' + escape_string(l[0]) + '",\n') + for license in license_list: + f.write('\t"' + escape_string(license[0]) + '",\n') f.write("};\n\n") f.write("const char *const LICENSE_BODIES[] = {\n\n") - for l in license_list: - for line in l[1:]: + for license in license_list: + for line in license[1:]: if line == ".": f.write('\t"\\n"\n') else: diff --git a/core/extension/SCsub b/core/extension/SCsub index 901ceec1e8..6ab2d2b0a6 100644 --- a/core/extension/SCsub +++ b/core/extension/SCsub @@ -2,8 +2,8 @@ Import("env") -import make_wrappers import make_interface_dumper +import make_wrappers env.CommandNoCache(["ext_wrappers.gen.inc"], "make_wrappers.py", env.Run(make_wrappers.run)) env.CommandNoCache( diff --git a/core/extension/gdextension.compat.inc b/core/extension/gdextension.compat.inc new file mode 100644 index 0000000000..9dea865dbd --- /dev/null +++ b/core/extension/gdextension.compat.inc @@ -0,0 +1,49 @@ +/**************************************************************************/ +/* gdextension.compat.inc */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef DISABLE_DEPRECATED + +Error GDExtension::_open_library_bind_compat_88418(const String &p_path, const String &p_entry_symbol) { + return ERR_UNAVAILABLE; +} + +void GDExtension::_close_library_bind_compat_88418() { +} + +void GDExtension::_initialize_library_bind_compat_88418(InitializationLevel p_level) { +} + +void GDExtension::_bind_compatibility_methods() { + ClassDB::bind_compatibility_method(D_METHOD("open_library", "path", "entry_symbol"), &GDExtension::_open_library_bind_compat_88418); + ClassDB::bind_compatibility_method(D_METHOD("close_library"), &GDExtension::_close_library_bind_compat_88418); + ClassDB::bind_compatibility_method(D_METHOD("initialize_library", "level"), &GDExtension::_initialize_library_bind_compat_88418); +} + +#endif diff --git a/core/extension/gdextension.cpp b/core/extension/gdextension.cpp index a26bb3e8f3..47628e4ea0 100644 --- a/core/extension/gdextension.cpp +++ b/core/extension/gdextension.cpp @@ -29,6 +29,8 @@ /**************************************************************************/ #include "gdextension.h" +#include "gdextension.compat.inc" + #include "core/config/project_settings.h" #include "core/io/dir_access.h" #include "core/object/class_db.h" diff --git a/core/extension/gdextension.h b/core/extension/gdextension.h index 3b15639890..9393e7399b 100644 --- a/core/extension/gdextension.h +++ b/core/extension/gdextension.h @@ -137,6 +137,15 @@ public: INITIALIZATION_LEVEL_EDITOR = GDEXTENSION_INITIALIZATION_EDITOR }; +protected: +#ifndef DISABLE_DEPRECATED + Error _open_library_bind_compat_88418(const String &p_path, const String &p_entry_symbol); + void _close_library_bind_compat_88418(); + void _initialize_library_bind_compat_88418(InitializationLevel p_level); + static void _bind_compatibility_methods(); +#endif + +public: bool is_library_open() const; #ifdef TOOLS_ENABLED diff --git a/core/extension/make_wrappers.py b/core/extension/make_wrappers.py index 655b90d2b1..54f4fd5579 100644 --- a/core/extension/make_wrappers.py +++ b/core/extension/make_wrappers.py @@ -10,7 +10,6 @@ _FORCE_INLINE_ virtual $RETVAL m_name($FUNCARGS) $CONST override { \\ def generate_mod_version(argcount, const=False, returns=False): s = proto_mod sproto = str(argcount) - method_info = "" if returns: sproto += "R" s = s.replace("$RETTYPE", "m_ret, ") @@ -68,7 +67,6 @@ virtual $RETVAL m_name($FUNCARGS) $CONST override { \\ def generate_ex_version(argcount, const=False, returns=False): s = proto_ex sproto = str(argcount) - method_info = "" if returns: sproto += "R" s = s.replace("$RETTYPE", "m_ret, ") diff --git a/core/input/SCsub b/core/input/SCsub index da29637135..d8e6f33156 100644 --- a/core/input/SCsub +++ b/core/input/SCsub @@ -4,7 +4,6 @@ Import("env") import input_builders - # Order matters here. Higher index controller database files write on top of lower index database files. controller_databases = [ "gamecontrollerdb.txt", diff --git a/core/io/resource_loader.h b/core/io/resource_loader.h index 5caf699d32..486f7bbf37 100644 --- a/core/io/resource_loader.h +++ b/core/io/resource_loader.h @@ -226,7 +226,7 @@ public: // Loaders can safely use this regardless which thread they are running on. static void notify_load_error(const String &p_err) { if (err_notify) { - callable_mp_static(err_notify).bind(p_err).call_deferred(); + callable_mp_static(err_notify).call_deferred(p_err); } } static void set_error_notify_func(ResourceLoadErrorNotify p_err_notify) { @@ -239,7 +239,7 @@ public: if (Thread::get_caller_id() == Thread::get_main_id()) { dep_err_notify(p_path, p_dependency, p_type); } else { - callable_mp_static(dep_err_notify).bind(p_path, p_dependency, p_type).call_deferred(); + callable_mp_static(dep_err_notify).call_deferred(p_path, p_dependency, p_type); } } } diff --git a/core/math/color.cpp b/core/math/color.cpp index d36306d968..1638acd74d 100644 --- a/core/math/color.cpp +++ b/core/math/color.cpp @@ -33,7 +33,7 @@ #include "color_names.inc" #include "core/math/math_funcs.h" #include "core/string/ustring.h" -#include "core/templates/rb_map.h" +#include "core/templates/hash_map.h" #include "thirdparty/misc/ok_color.h" @@ -414,7 +414,7 @@ Color Color::named(const String &p_name, const Color &p_default) { int Color::find_named_color(const String &p_name) { String name = p_name; - // Normalize name + // Normalize name. name = name.replace(" ", ""); name = name.replace("-", ""); name = name.replace("_", ""); @@ -422,23 +422,24 @@ int Color::find_named_color(const String &p_name) { name = name.replace(".", ""); name = name.to_upper(); - int idx = 0; - while (named_colors[idx].name != nullptr) { - if (name == String(named_colors[idx].name).replace("_", "")) { - return idx; + static HashMap<String, int> named_colors_hashmap; + if (unlikely(named_colors_hashmap.is_empty())) { + const int named_color_count = get_named_color_count(); + for (int i = 0; i < named_color_count; i++) { + named_colors_hashmap[String(named_colors[i].name).replace("_", "")] = i; } - idx++; + } + + const HashMap<String, int>::ConstIterator E = named_colors_hashmap.find(name); + if (E) { + return E->value; } return -1; } int Color::get_named_color_count() { - int idx = 0; - while (named_colors[idx].name != nullptr) { - idx++; - } - return idx; + return sizeof(named_colors) / sizeof(NamedColor); } String Color::get_named_color_name(int p_idx) { diff --git a/core/math/color_names.inc b/core/math/color_names.inc index eaa1b1087f..6c0d2a4bfd 100644 --- a/core/math/color_names.inc +++ b/core/math/color_names.inc @@ -189,5 +189,4 @@ static NamedColor named_colors[] = { { "WHITE_SMOKE", Color::hex(0xF5F5F5FF) }, { "YELLOW", Color::hex(0xFFFF00FF) }, { "YELLOW_GREEN", Color::hex(0x9ACD32FF) }, - { nullptr, Color() }, }; diff --git a/core/object/class_db.cpp b/core/object/class_db.cpp index e25703b93f..fe4345aa0d 100644 --- a/core/object/class_db.cpp +++ b/core/object/class_db.cpp @@ -505,7 +505,7 @@ Object *ClassDB::_instantiate_internal(const StringName &p_class, bool p_require ERR_FAIL_NULL_V_MSG(ti->creation_func, nullptr, "Class '" + String(p_class) + "' or its base class cannot be instantiated."); } #ifdef TOOLS_ENABLED - if (ti->api == API_EDITOR && !Engine::get_singleton()->is_editor_hint()) { + if ((ti->api == API_EDITOR || ti->api == API_EDITOR_EXTENSION) && !Engine::get_singleton()->is_editor_hint()) { ERR_PRINT("Class '" + String(p_class) + "' can only be instantiated by editor."); return nullptr; } @@ -522,7 +522,7 @@ Object *ClassDB::_instantiate_internal(const StringName &p_class, bool p_require #ifdef TOOLS_ENABLED if (!p_require_real_class && ti->is_runtime && Engine::get_singleton()->is_editor_hint()) { if (!ti->inherits_ptr || !ti->inherits_ptr->creation_func) { - ERR_PRINT_ONCE(vformat("Cannot make a placeholder instance of runtime class %s because its parent cannot be constructed.", ti->name)); + ERR_PRINT(vformat("Cannot make a placeholder instance of runtime class %s because its parent cannot be constructed.", ti->name)); } else { ObjectGDExtension *extension = get_placeholder_extension(ti->name); return (Object *)extension->create_instance(extension->class_userdata); @@ -664,7 +664,7 @@ bool ClassDB::can_instantiate(const StringName &p_class) { return scr.is_valid() && scr->is_valid() && !scr->is_abstract(); } #ifdef TOOLS_ENABLED - if (ti->api == API_EDITOR && !Engine::get_singleton()->is_editor_hint()) { + if ((ti->api == API_EDITOR || ti->api == API_EDITOR_EXTENSION) && !Engine::get_singleton()->is_editor_hint()) { return false; } #endif @@ -684,7 +684,7 @@ bool ClassDB::is_virtual(const StringName &p_class) { return scr.is_valid() && scr->is_valid() && scr->is_abstract(); } #ifdef TOOLS_ENABLED - if (ti->api == API_EDITOR && !Engine::get_singleton()->is_editor_hint()) { + if ((ti->api == API_EDITOR || ti->api == API_EDITOR_EXTENSION) && !Engine::get_singleton()->is_editor_hint()) { return false; } #endif diff --git a/core/object/undo_redo.cpp b/core/object/undo_redo.cpp index 4d67cd930e..0f7884305a 100644 --- a/core/object/undo_redo.cpp +++ b/core/object/undo_redo.cpp @@ -159,10 +159,11 @@ void UndoRedo::add_do_method(const Callable &p_callable) { do_op.ref = Ref<RefCounted>(Object::cast_to<RefCounted>(object)); } do_op.type = Operation::TYPE_METHOD; - do_op.name = p_callable.get_method(); - if (do_op.name == StringName()) { - // There's no `get_method()` for custom callables, so use `operator String()` instead. + // There's no `get_method()` for custom callables, so use `operator String()` instead. + if (p_callable.is_custom()) { do_op.name = static_cast<String>(p_callable); + } else { + do_op.name = p_callable.get_method(); } actions.write[current_action + 1].do_ops.push_back(do_op); @@ -190,10 +191,11 @@ void UndoRedo::add_undo_method(const Callable &p_callable) { } undo_op.type = Operation::TYPE_METHOD; undo_op.force_keep_in_merge_ends = force_keep_in_merge_ends; - undo_op.name = p_callable.get_method(); - if (undo_op.name == StringName()) { - // There's no `get_method()` for custom callables, so use `operator String()` instead. + // There's no `get_method()` for custom callables, so use `operator String()` instead. + if (p_callable.is_custom()) { undo_op.name = static_cast<String>(p_callable); + } else { + undo_op.name = p_callable.get_method(); } actions.write[current_action + 1].undo_ops.push_back(undo_op); diff --git a/doc/classes/AnimationMixer.xml b/doc/classes/AnimationMixer.xml index a77e9e28c6..c635eba2ab 100644 --- a/doc/classes/AnimationMixer.xml +++ b/doc/classes/AnimationMixer.xml @@ -273,7 +273,7 @@ The number of possible simultaneous sounds for each of the assigned AudioStreamPlayers. For example, if this value is [code]32[/code] and the animation has two audio tracks, the two [AudioStreamPlayer]s assigned can play simultaneously up to [code]32[/code] voices each. </member> - <member name="callback_mode_discrete" type="int" setter="set_callback_mode_discrete" getter="get_callback_mode_discrete" enum="AnimationMixer.AnimationCallbackModeDiscrete" default="0"> + <member name="callback_mode_discrete" type="int" setter="set_callback_mode_discrete" getter="get_callback_mode_discrete" enum="AnimationMixer.AnimationCallbackModeDiscrete" default="1"> Ordinarily, tracks can be set to [constant Animation.UPDATE_DISCRETE] to update infrequently, usually when using nearest interpolation. However, when blending with [constant Animation.UPDATE_CONTINUOUS] several results are considered. The [member callback_mode_discrete] specify it explicitly. See also [enum AnimationCallbackModeDiscrete]. To make the blended results look good, it is recommended to set this to [constant ANIMATION_CALLBACK_MODE_DISCRETE_FORCE_CONTINUOUS] to update every frame during blending. Other values exist for compatibility and they are fine if there is no blending, but not so, may produce artifacts. @@ -361,14 +361,14 @@ Make method calls immediately when reached in the animation. </constant> <constant name="ANIMATION_CALLBACK_MODE_DISCRETE_DOMINANT" value="0" enum="AnimationCallbackModeDiscrete"> - An [constant Animation.UPDATE_DISCRETE] track value takes precedence when blending [constant Animation.UPDATE_CONTINUOUS] or [constant Animation.UPDATE_CAPTURE] track values and [constant Animation.UPDATE_DISCRETE] track values. This is the default behavior for [AnimationPlayer]. - [b]Note:[/b] If a value track has non-numeric type key values, it is internally converted to use [constant ANIMATION_CALLBACK_MODE_DISCRETE_DOMINANT] with [constant Animation.UPDATE_DISCRETE]. + An [constant Animation.UPDATE_DISCRETE] track value takes precedence when blending [constant Animation.UPDATE_CONTINUOUS] or [constant Animation.UPDATE_CAPTURE] track values and [constant Animation.UPDATE_DISCRETE] track values. </constant> <constant name="ANIMATION_CALLBACK_MODE_DISCRETE_RECESSIVE" value="1" enum="AnimationCallbackModeDiscrete"> - An [constant Animation.UPDATE_CONTINUOUS] or [constant Animation.UPDATE_CAPTURE] track value takes precedence when blending the [constant Animation.UPDATE_CONTINUOUS] or [constant Animation.UPDATE_CAPTURE] track values and the [constant Animation.UPDATE_DISCRETE] track values. + An [constant Animation.UPDATE_CONTINUOUS] or [constant Animation.UPDATE_CAPTURE] track value takes precedence when blending the [constant Animation.UPDATE_CONTINUOUS] or [constant Animation.UPDATE_CAPTURE] track values and the [constant Animation.UPDATE_DISCRETE] track values. This is the default behavior for [AnimationPlayer]. </constant> <constant name="ANIMATION_CALLBACK_MODE_DISCRETE_FORCE_CONTINUOUS" value="2" enum="AnimationCallbackModeDiscrete"> Always treat the [constant Animation.UPDATE_DISCRETE] track value as [constant Animation.UPDATE_CONTINUOUS] with [constant Animation.INTERPOLATION_NEAREST]. This is the default behavior for [AnimationTree]. + If a value track has non-numeric type key values, it is internally converted to use [constant ANIMATION_CALLBACK_MODE_DISCRETE_RECESSIVE] with [constant Animation.UPDATE_DISCRETE]. </constant> </constants> </class> diff --git a/doc/classes/ConfirmationDialog.xml b/doc/classes/ConfirmationDialog.xml index 8fa5dac7bf..5cdff076bd 100644 --- a/doc/classes/ConfirmationDialog.xml +++ b/doc/classes/ConfirmationDialog.xml @@ -8,10 +8,10 @@ To get cancel action, you can use: [codeblocks] [gdscript] - get_cancel_button().pressed.connect(self.canceled) + get_cancel_button().pressed.connect(_on_canceled) [/gdscript] [csharp] - GetCancelButton().Pressed += Canceled; + GetCancelButton().Pressed += OnCanceled; [/csharp] [/codeblocks] </description> diff --git a/doc/classes/DisplayServer.xml b/doc/classes/DisplayServer.xml index 688e1b70ca..ff1c390b3c 100644 --- a/doc/classes/DisplayServer.xml +++ b/doc/classes/DisplayServer.xml @@ -63,6 +63,7 @@ <param index="2" name="callback" type="Callable" /> <description> Creates a new application status indicator with the specified icon, tooltip, and activation callback. + [param callback] should take two arguments: the pressed mouse button (one of the [enum MouseButton] constants) and the click position in screen coordinates (a [Vector2i]). </description> </method> <method name="cursor_get_shape" qualifiers="const"> @@ -875,7 +876,7 @@ <param index="1" name="open_callback" type="Callable" /> <param index="2" name="close_callback" type="Callable" /> <description> - Registers callables to emit when the menu is respectively about to show or closed. + Registers callables to emit when the menu is respectively about to show or closed. Callback methods should have zero arguments. </description> </method> <method name="has_feature" qualifiers="const"> @@ -930,6 +931,12 @@ Returns [code]true[/code] if touch events are available (Android or iOS), the capability is detected on the Web platform or if [member ProjectSettings.input_devices/pointing/emulate_touch_from_mouse] is [code]true[/code]. </description> </method> + <method name="is_window_transparency_available" qualifiers="const"> + <return type="bool" /> + <description> + Returns [code]true[/code] if the window background can be made transparent. This method returns [code]false[/code] if [member ProjectSettings.display/window/per_pixel_transparency/allowed] is set to [code]false[/code], or if transparency is not supported by the renderer or OS compositor. + </description> + </method> <method name="keyboard_get_current_layout" qualifiers="const"> <return type="int" /> <description> @@ -1181,7 +1188,7 @@ <param index="0" name="id" type="int" /> <param index="1" name="callback" type="Callable" /> <description> - Sets the application status indicator activation callback. + Sets the application status indicator activation callback. [param callback] should take two arguments: [int] mouse button index (one of [enum MouseButton] values) and [Vector2i] click position in screen coordinates. [b]Note:[/b] This method is implemented on macOS and Windows. </description> </method> @@ -1562,7 +1569,7 @@ <param index="0" name="callback" type="Callable" /> <param index="1" name="window_id" type="int" default="0" /> <description> - Sets the [param callback] that should be called when files are dropped from the operating system's file manager to the window specified by [param window_id]. + Sets the [param callback] that should be called when files are dropped from the operating system's file manager to the window specified by [param window_id]. [param callback] should take one [PackedStringArray] argument, which is the list of dropped files. [b]Warning:[/b] Advanced users only! Adding such a callback to a [Window] node will override its default implementation, which can introduce bugs. [b]Note:[/b] This method is implemented on Windows, macOS, Linux (X11/Wayland), and Web. </description> @@ -2045,7 +2052,7 @@ </constant> <constant name="WINDOW_FLAG_TRANSPARENT" value="3" enum="WindowFlags"> The window background can be transparent. - [b]Note:[/b] This flag has no effect if [member ProjectSettings.display/window/per_pixel_transparency/allowed] is set to [code]false[/code]. + [b]Note:[/b] This flag has no effect if [method is_window_transparency_available] returns [code]false[/code]. [b]Note:[/b] Transparency support is implemented on Linux (X11/Wayland), macOS, and Windows, but availability might vary depending on GPU driver, display manager, and compositor capabilities. </constant> <constant name="WINDOW_FLAG_NO_FOCUS" value="4" enum="WindowFlags"> diff --git a/doc/classes/EditorExportPlugin.xml b/doc/classes/EditorExportPlugin.xml index 436f471e5d..3e2b3ea111 100644 --- a/doc/classes/EditorExportPlugin.xml +++ b/doc/classes/EditorExportPlugin.xml @@ -313,7 +313,7 @@ <method name="skip"> <return type="void" /> <description> - To be called inside [method _export_file]. Skips the current file, so it's not included in the export. + To be called inside [method _export_file], [method _customize_resource], or [method _customize_scene]. Skips the current file, so it's not included in the export. </description> </method> </methods> diff --git a/doc/classes/EditorInspectorPlugin.xml b/doc/classes/EditorInspectorPlugin.xml index efa881591c..4f6ef76c4c 100644 --- a/doc/classes/EditorInspectorPlugin.xml +++ b/doc/classes/EditorInspectorPlugin.xml @@ -78,8 +78,11 @@ <param index="0" name="property" type="String" /> <param index="1" name="editor" type="Control" /> <param index="2" name="add_to_end" type="bool" default="false" /> + <param index="3" name="label" type="String" default="""" /> <description> Adds a property editor for an individual property. The [param editor] control must extend [EditorProperty]. + There can be multiple property editors for a property. If [param add_to_end] is [code]true[/code], this newly added editor will be displayed after all the other editors of the property whose [param add_to_end] is [code]false[/code]. For example, the editor uses this parameter to add an "Edit Region" button for [member Sprite2D.region_rect] below the regular [Rect2] editor. + [param label] can be used to choose a custom label for the property editor in the inspector. If left empty, the label is computed from the name of the property instead. </description> </method> <method name="add_property_editor_for_multiple_properties"> diff --git a/doc/classes/EditorSettings.xml b/doc/classes/EditorSettings.xml index 3e3d2205f2..e0a14e990b 100644 --- a/doc/classes/EditorSettings.xml +++ b/doc/classes/EditorSettings.xml @@ -925,14 +925,14 @@ <member name="run/auto_save/save_before_running" type="bool" setter="" getter=""> If [code]true[/code], saves all scenes and scripts automatically before running the project. Setting this to [code]false[/code] prevents the editor from saving if there are no changes which can speed up the project startup slightly, but it makes it possible to run a project that has unsaved changes. (Unsaved changes will not be visible in the running project.) </member> - <member name="run/output/always_clear_output_on_play" type="bool" setter="" getter=""> - If [code]true[/code], the editor will clear the Output panel when running the project. + <member name="run/bottom_panel/action_on_play" type="int" setter="" getter=""> + The action to execute on the bottom panel when running the project. </member> - <member name="run/output/always_close_output_on_stop" type="bool" setter="" getter=""> - If [code]true[/code], the editor will collapse the Output panel when stopping the project. + <member name="run/bottom_panel/action_on_stop" type="int" setter="" getter=""> + The action to execute on the bottom panel when stopping the project. </member> - <member name="run/output/always_open_output_on_play" type="bool" setter="" getter=""> - If [code]true[/code], the editor will expand the Output panel when running the project. + <member name="run/output/always_clear_output_on_play" type="bool" setter="" getter=""> + If [code]true[/code], the editor will clear the Output panel when running the project. </member> <member name="run/output/font_size" type="int" setter="" getter=""> The size of the font in the [b]Output[/b] panel at the bottom of the editor. This setting does not impact the font size of the script editor (see [member interface/editor/code_font_size]). @@ -1028,6 +1028,9 @@ <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> + <member name="text_editor/behavior/files/trim_final_newlines_on_save" type="bool" setter="" getter=""> + If [code]true[/code], trims all empty newlines after the final newline when saving a script. Final newlines refer to the empty newlines found at the end of files. Since these serve no practical purpose, they can and should be removed to make version control diffs less noisy. + </member> <member name="text_editor/behavior/files/trim_trailing_whitespace_on_save" type="bool" setter="" getter=""> If [code]true[/code], trims trailing whitespace when saving a script. Trailing whitespace refers to tab and space characters placed at the end of lines. Since these serve no practical purpose, they can and should be removed to make version control diffs less noisy. </member> diff --git a/doc/classes/Joint2D.xml b/doc/classes/Joint2D.xml index af0a54815f..0099c76d08 100644 --- a/doc/classes/Joint2D.xml +++ b/doc/classes/Joint2D.xml @@ -4,7 +4,7 @@ Abstract base class for all 2D physics joints. </brief_description> <description> - Abstract base class for all joints in 2D physics. 2D joints bind together two physics bodies and apply a constraint. + Abstract base class for all joints in 2D physics. 2D joints bind together two physics bodies ([member node_a] and [member node_b]) and apply a constraint. </description> <tutorials> </tutorials> @@ -12,7 +12,7 @@ <method name="get_rid" qualifiers="const"> <return type="RID" /> <description> - Returns the joint's [RID]. + Returns the joint's internal [RID] from the [PhysicsServer2D]. </description> </method> </methods> @@ -22,13 +22,13 @@ When set to [code]0[/code], the default value from [member ProjectSettings.physics/2d/solver/default_constraint_bias] is used. </member> <member name="disable_collision" type="bool" setter="set_exclude_nodes_from_collision" getter="get_exclude_nodes_from_collision" default="true"> - If [code]true[/code], [member node_a] and [member node_b] can not collide. + If [code]true[/code], the two bodies bound together do not collide with each other. </member> <member name="node_a" type="NodePath" setter="set_node_a" getter="get_node_a" default="NodePath("")"> - The first body attached to the joint. Must derive from [PhysicsBody2D]. + Path to the first body (A) attached to the joint. The node must inherit [PhysicsBody2D]. </member> <member name="node_b" type="NodePath" setter="set_node_b" getter="get_node_b" default="NodePath("")"> - The second body attached to the joint. Must derive from [PhysicsBody2D]. + Path to the second body (B) attached to the joint. The node must inherit [PhysicsBody2D]. </member> </members> </class> diff --git a/doc/classes/Joint3D.xml b/doc/classes/Joint3D.xml index ea0dda881a..950129806a 100644 --- a/doc/classes/Joint3D.xml +++ b/doc/classes/Joint3D.xml @@ -4,7 +4,7 @@ Abstract base class for all 3D physics joints. </brief_description> <description> - Abstract base class for all joints in 3D physics. 3D joints bind together two physics bodies and apply a constraint. + Abstract base class for all joints in 3D physics. 3D joints bind together two physics bodies ([member node_a] and [member node_b]) and apply a constraint. If only one body is defined, it is attached to a fixed [StaticBody3D] without collision shapes. </description> <tutorials> <link title="3D Truck Town Demo">https://godotengine.org/asset-library/asset/2752</link> @@ -13,19 +13,21 @@ <method name="get_rid" qualifiers="const"> <return type="RID" /> <description> - Returns the joint's [RID]. + Returns the joint's internal [RID] from the [PhysicsServer3D]. </description> </method> </methods> <members> <member name="exclude_nodes_from_collision" type="bool" setter="set_exclude_nodes_from_collision" getter="get_exclude_nodes_from_collision" default="true"> - If [code]true[/code], the two bodies of the nodes are not able to collide with each other. + If [code]true[/code], the two bodies bound together do not collide with each other. </member> <member name="node_a" type="NodePath" setter="set_node_a" getter="get_node_a" default="NodePath("")"> - The node attached to the first side (A) of the joint. + Path to the first node (A) attached to the joint. The node must inherit [PhysicsBody3D]. + If left empty and [member node_b] is set, the body is attached to a fixed [StaticBody3D] without collision shapes. </member> <member name="node_b" type="NodePath" setter="set_node_b" getter="get_node_b" default="NodePath("")"> - The node attached to the second side (B) of the joint. + Path to the second node (B) attached to the joint. The node must inherit [PhysicsBody3D]. + If left empty and [member node_a] is set, the body is attached to a fixed [StaticBody3D] without collision shapes. </member> <member name="solver_priority" type="int" setter="set_solver_priority" getter="get_solver_priority" default="1"> The priority used to define which solver is executed first for multiple joints. The lower the value, the higher the priority. diff --git a/doc/classes/Light3D.xml b/doc/classes/Light3D.xml index c1fc49cf9f..bda5fb69de 100644 --- a/doc/classes/Light3D.xml +++ b/doc/classes/Light3D.xml @@ -199,7 +199,7 @@ </constant> <constant name="BAKE_DISABLED" value="0" enum="BakeMode"> Light is ignored when baking. This is the fastest mode, but the light will be taken into account when baking global illumination. This mode should generally be used for dynamic lights that change quickly, as the effect of global illumination is less noticeable on those lights. - [b]Note:[/b] Hiding a light does [i]not[/i] affect baking [LightmapGI]. Hiding a light will still affect baking [VoxelGI] and SDFGI (see [member Environment.sdfgi_enabled). + [b]Note:[/b] Hiding a light does [i]not[/i] affect baking [LightmapGI]. Hiding a light will still affect baking [VoxelGI] and SDFGI (see [member Environment.sdfgi_enabled]). </constant> <constant name="BAKE_STATIC" value="1" enum="BakeMode"> Light is taken into account in static baking ([VoxelGI], [LightmapGI], SDFGI ([member Environment.sdfgi_enabled])). The light can be moved around or modified, but its global illumination will not update in real-time. This is suitable for subtle changes (such as flickering torches), but generally not large changes such as toggling a light on and off. diff --git a/doc/classes/Mesh.xml b/doc/classes/Mesh.xml index 966e870940..6b5a50d97b 100644 --- a/doc/classes/Mesh.xml +++ b/doc/classes/Mesh.xml @@ -4,7 +4,7 @@ A [Resource] that contains vertex array-based geometry. </brief_description> <description> - Mesh is a type of [Resource] that contains vertex array-based geometry, divided in [i]surfaces[/i]. Each surface contains a completely separate array and a material used to draw it. Design wise, a mesh with multiple surfaces is preferred to a single surface, because objects created in 3D editing software commonly contain multiple materials. + Mesh is a type of [Resource] that contains vertex array-based geometry, divided in [i]surfaces[/i]. Each surface contains a completely separate array and a material used to draw it. Design wise, a mesh with multiple surfaces is preferred to a single surface, because objects created in 3D editing software commonly contain multiple materials. The maximum number of surfaces per mesh is [constant RenderingServer.MAX_MESH_SURFACES]. </description> <tutorials> <link title="3D Material Testers Demo">https://godotengine.org/asset-library/asset/2742</link> diff --git a/doc/classes/MeshLibrary.xml b/doc/classes/MeshLibrary.xml index c5e8d8cbe7..f65e29af8e 100644 --- a/doc/classes/MeshLibrary.xml +++ b/doc/classes/MeshLibrary.xml @@ -29,7 +29,7 @@ <return type="int" /> <param index="0" name="name" type="String" /> <description> - Returns the first item with the given name. + Returns the first item with the given name, or [code]-1[/code] if no item is found. </description> </method> <method name="get_item_list" qualifiers="const"> diff --git a/doc/classes/NavigationAgent2D.xml b/doc/classes/NavigationAgent2D.xml index 6f0561e66e..94c372106b 100644 --- a/doc/classes/NavigationAgent2D.xml +++ b/doc/classes/NavigationAgent2D.xml @@ -261,7 +261,7 @@ <signal name="velocity_computed"> <param index="0" name="safe_velocity" type="Vector2" /> <description> - Notifies when the collision avoidance velocity is calculated. Emitted when [member velocity] is set. Only emitted when [member avoidance_enabled] is true. + Notifies when the collision avoidance velocity is calculated. Emitted every update as long as [member avoidance_enabled] is [code]true[/code] and the agent has a navigation map. </description> </signal> <signal name="waypoint_reached"> diff --git a/doc/classes/NavigationAgent3D.xml b/doc/classes/NavigationAgent3D.xml index 64ee35a84b..5f9f4991d1 100644 --- a/doc/classes/NavigationAgent3D.xml +++ b/doc/classes/NavigationAgent3D.xml @@ -271,7 +271,7 @@ <signal name="velocity_computed"> <param index="0" name="safe_velocity" type="Vector3" /> <description> - Notifies when the collision avoidance velocity is calculated. Emitted when [member velocity] is set. Only emitted when [member avoidance_enabled] is true. + Notifies when the collision avoidance velocity is calculated. Emitted every update as long as [member avoidance_enabled] is [code]true[/code] and the agent has a navigation map. </description> </signal> <signal name="waypoint_reached"> diff --git a/doc/classes/PhysicsMaterial.xml b/doc/classes/PhysicsMaterial.xml index 1601a1040e..03cbfb4ce7 100644 --- a/doc/classes/PhysicsMaterial.xml +++ b/doc/classes/PhysicsMaterial.xml @@ -14,7 +14,7 @@ </member> <member name="bounce" type="float" setter="set_bounce" getter="get_bounce" default="0.0"> The body's bounciness. Values range from [code]0[/code] (no bounce) to [code]1[/code] (full bounciness). - [b]Note:[/b] Even with [member bounce] set to [code]1.0[/code], some energy will be lost over time due to linear and angular damping. To have a [PhysicsBody3D] that preserves all its energy over time, set [member bounce] to [code]1.0[/code], the body's linear damp mode to [b]Replace[/b] (if applicable), its linear damp to [code]0.0[/code], its angular damp mode to [b]Replace[/b] (if applicable), and its angular damp to [code]0.0[/code]. + [b]Note:[/b] Even with [member bounce] set to [code]1.0[/code], some energy will be lost over time due to linear and angular damping. To have a physics body that preserves all its energy over time, set [member bounce] to [code]1.0[/code], the body's linear damp mode to [b]Replace[/b] (if applicable), its linear damp to [code]0.0[/code], its angular damp mode to [b]Replace[/b] (if applicable), and its angular damp to [code]0.0[/code]. </member> <member name="friction" type="float" setter="set_friction" getter="get_friction" default="1.0"> The body's friction. Values range from [code]0[/code] (frictionless) to [code]1[/code] (maximum friction). diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index 5ac4c96d93..32e71ce030 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -902,8 +902,9 @@ <member name="display/window/vsync/vsync_mode" type="int" setter="" getter="" default="1"> Sets the V-Sync mode for the main game window. The editor's own V-Sync mode can be set using [member EditorSettings.interface/editor/vsync_mode]. See [enum DisplayServer.VSyncMode] for possible values and how they affect the behavior of your application. - 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. + Depending on the platform and rendering method, the engine will fall back to [b]Enabled[/b] if the desired mode is not supported. + V-Sync can be disabled on the command line using the [code]--disable-vsync[/code] [url=$DOCS_URL/tutorials/editor/command_line_tutorial.html]command line argument[/url]. + [b]Note:[/b] The [b]Adaptive[/b] and [b]Mailbox[/b] V-Sync modes are only supported in the Forward+ and Mobile rendering methods, not Compatibility. [b]Note:[/b] This property is only read when the project starts. To change the V-Sync mode at runtime, call [method DisplayServer.window_set_vsync_mode] instead. </member> <member name="dotnet/project/assembly_name" type="String" setter="" getter="" default=""""> diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml index 3ddc0d8f7b..3c9f0fc7af 100644 --- a/doc/classes/RenderingServer.xml +++ b/doc/classes/RenderingServer.xml @@ -4239,6 +4239,9 @@ <constant name="MAX_2D_DIRECTIONAL_LIGHTS" value="8"> The maximum number of directional lights that can be rendered at a given time in 2D. </constant> + <constant name="MAX_MESH_SURFACES" value="256"> + The maximum number of surfaces a mesh can have. + </constant> <constant name="TEXTURE_LAYERED_2D_ARRAY" value="0" enum="TextureLayeredType"> Array of 2-dimensional textures (see [Texture2DArray]). </constant> diff --git a/doc/classes/RichTextLabel.xml b/doc/classes/RichTextLabel.xml index f4e3c1209f..24d2d26beb 100644 --- a/doc/classes/RichTextLabel.xml +++ b/doc/classes/RichTextLabel.xml @@ -335,7 +335,7 @@ <method name="push_cell"> <return type="void" /> <description> - Adds a [code skip-lint][cell][/code] tag to the tag stack. Must be inside a [code skip-lint][table][/code] tag. See [method push_table] for details. + Adds a [code skip-lint][cell][/code] tag to the tag stack. Must be inside a [code skip-lint][table][/code] tag. See [method push_table] for details. Use [method set_table_column_expand] to set column expansion ratio, [method set_cell_border_color] to set cell border, [method set_cell_row_background_color] to set cell background, [method set_cell_size_override] to override cell size, and [method set_cell_padding] to set padding. </description> </method> <method name="push_color"> @@ -492,7 +492,7 @@ <param index="1" name="inline_align" type="int" enum="InlineAlignment" default="0" /> <param index="2" name="align_to_row" type="int" default="-1" /> <description> - Adds a [code skip-lint][table=columns,inline_align][/code] tag to the tag stack. + Adds a [code skip-lint][table=columns,inline_align][/code] tag to the tag stack. Use [method set_table_column_expand] to set column expansion ratio. Use [method push_cell] to add cells. </description> </method> <method name="push_underline"> diff --git a/doc/classes/XRServer.xml b/doc/classes/XRServer.xml index 4179ba821c..49b13986ca 100644 --- a/doc/classes/XRServer.xml +++ b/doc/classes/XRServer.xml @@ -135,6 +135,11 @@ Emitted when an interface is removed. </description> </signal> + <signal name="reference_frame_changed"> + <description> + Emitted when the reference frame transform changes. + </description> + </signal> <signal name="tracker_added"> <param index="0" name="tracker_name" type="StringName" /> <param index="1" name="type" type="int" /> diff --git a/doc/tools/doc_status.py b/doc/tools/doc_status.py index a23c11b585..57c03bfdee 100755 --- a/doc/tools/doc_status.py +++ b/doc/tools/doc_status.py @@ -1,11 +1,11 @@ #!/usr/bin/env python3 import fnmatch -import os -import sys -import re import math +import os import platform +import re +import sys import xml.etree.ElementTree as ET from typing import Dict, List, Set @@ -286,7 +286,7 @@ class ClassStatus: status.progresses[tag.tag].increment(is_deprecated or is_experimental or has_descr) elif tag.tag in ["constants", "members", "theme_items"]: for sub_tag in list(tag): - if not sub_tag.text is None: + if sub_tag.text is not None: is_deprecated = "deprecated" in sub_tag.attrib is_experimental = "experimental" in sub_tag.attrib has_descr = len(sub_tag.text.strip()) > 0 diff --git a/doc/tools/make_rst.py b/doc/tools/make_rst.py index 43e1afe720..761a7f8f4a 100755 --- a/doc/tools/make_rst.py +++ b/doc/tools/make_rst.py @@ -4,17 +4,16 @@ import argparse import os -import platform import re import sys import xml.etree.ElementTree as ET from collections import OrderedDict -from typing import List, Dict, TextIO, Tuple, Optional, Any, Union +from typing import Any, Dict, List, Optional, TextIO, Tuple, Union # Import hardcoded version information from version.py root_directory = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../") sys.path.append(root_directory) # Include the root directory -import version +import version # noqa: E402 # $DOCS_URL/path/to/page.html(#fragment-tag) GODOT_DOCS_PATTERN = re.compile(r"^\$DOCS_URL/(.*)\.html(#.*)?$") @@ -706,7 +705,7 @@ def main() -> None: # <https://github.com/python/cpython/issues/73245> if should_color and sys.stdout.isatty() and sys.platform == "win32": try: - from ctypes import windll, byref, WinError # type: ignore + from ctypes import WinError, byref, windll # type: ignore from ctypes.wintypes import DWORD # type: ignore stdout_handle = windll.kernel32.GetStdHandle(DWORD(-11)) @@ -1111,11 +1110,13 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir: # Create signal signature and anchor point. - f.write(f".. _class_{class_name}_signal_{signal.name}:\n\n") + signal_anchor = f"class_{class_name}_signal_{signal.name}" + f.write(f".. _{signal_anchor}:\n\n") + self_link = f":ref:`🔗<{signal_anchor}>`" f.write(".. rst-class:: classref-signal\n\n") _, signature = make_method_signature(class_def, signal, "", state) - f.write(f"{signature}\n\n") + f.write(f"{signature} {self_link}\n\n") # Add signal description, or a call to action if it's missing. @@ -1148,13 +1149,15 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir: # Create enumeration signature and anchor point. - f.write(f".. _enum_{class_name}_{e.name}:\n\n") + enum_anchor = f"enum_{class_name}_{e.name}" + f.write(f".. _{enum_anchor}:\n\n") + self_link = f":ref:`🔗<{enum_anchor}>`" f.write(".. rst-class:: classref-enumeration\n\n") if e.is_bitfield: - f.write(f"flags **{e.name}**:\n\n") + f.write(f"flags **{e.name}**: {self_link}\n\n") else: - f.write(f"enum **{e.name}**:\n\n") + f.write(f"enum **{e.name}**: {self_link}\n\n") for value in e.values.values(): # Also create signature and anchor point for each enum constant. @@ -1192,10 +1195,12 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir: for constant in class_def.constants.values(): # Create constant signature and anchor point. - f.write(f".. _class_{class_name}_constant_{constant.name}:\n\n") + constant_anchor = f"class_{class_name}_constant_{constant.name}" + f.write(f".. _{constant_anchor}:\n\n") + self_link = f":ref:`🔗<{constant_anchor}>`" f.write(".. rst-class:: classref-constant\n\n") - f.write(f"**{constant.name}** = ``{constant.value}``\n\n") + f.write(f"**{constant.name}** = ``{constant.value}`` {self_link}\n\n") # Add constant description. @@ -1228,13 +1233,16 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir: # Create annotation signature and anchor point. + self_link = "" if i == 0: - f.write(f".. _class_{class_name}_annotation_{m.name}:\n\n") + annotation_anchor = f"class_{class_name}_annotation_{m.name}" + f.write(f".. _{annotation_anchor}:\n\n") + self_link = f" :ref:`🔗<{annotation_anchor}>`" f.write(".. rst-class:: classref-annotation\n\n") _, signature = make_method_signature(class_def, m, "", state) - f.write(f"{signature}\n\n") + f.write(f"{signature}{self_link}\n\n") # Add annotation description, or a call to action if it's missing. @@ -1268,13 +1276,17 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir: # Create property signature and anchor point. - f.write(f".. _class_{class_name}_property_{property_def.name}:\n\n") + property_anchor = f"class_{class_name}_property_{property_def.name}" + f.write(f".. _{property_anchor}:\n\n") + self_link = f":ref:`🔗<{property_anchor}>`" f.write(".. rst-class:: classref-property\n\n") property_default = "" if property_def.default_value is not None: property_default = f" = {property_def.default_value}" - f.write(f"{property_def.type_name.to_rst(state)} **{property_def.name}**{property_default}\n\n") + f.write( + f"{property_def.type_name.to_rst(state)} **{property_def.name}**{property_default} {self_link}\n\n" + ) # Create property setter and getter records. @@ -1328,13 +1340,16 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir: # Create constructor signature and anchor point. + self_link = "" if i == 0: - f.write(f".. _class_{class_name}_constructor_{m.name}:\n\n") + constructor_anchor = f"class_{class_name}_constructor_{m.name}" + f.write(f".. _{constructor_anchor}:\n\n") + self_link = f" :ref:`🔗<{constructor_anchor}>`" f.write(".. rst-class:: classref-constructor\n\n") ret_type, signature = make_method_signature(class_def, m, "", state) - f.write(f"{ret_type} {signature}\n\n") + f.write(f"{ret_type} {signature}{self_link}\n\n") # Add constructor description, or a call to action if it's missing. @@ -1367,17 +1382,21 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir: # Create method signature and anchor point. + self_link = "" + if i == 0: method_qualifier = "" if m.name.startswith("_"): method_qualifier = "private_" - - f.write(f".. _class_{class_name}_{method_qualifier}method_{m.name}:\n\n") + method_anchor = f"class_{class_name}_{method_qualifier}method_{m.name}" + f.write(f".. _{method_anchor}:\n\n") + self_link = f" :ref:`🔗<{method_anchor}>`" f.write(".. rst-class:: classref-method\n\n") ret_type, signature = make_method_signature(class_def, m, "", state) - f.write(f"{ret_type} {signature}\n\n") + + f.write(f"{ret_type} {signature}{self_link}\n\n") # Add method description, or a call to action if it's missing. @@ -1410,16 +1429,16 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir: # Create operator signature and anchor point. - operator_anchor = f".. _class_{class_name}_operator_{sanitize_operator_name(m.name, state)}" + operator_anchor = f"class_{class_name}_operator_{sanitize_operator_name(m.name, state)}" for parameter in m.parameters: operator_anchor += f"_{parameter.type_name.type_name}" - operator_anchor += f":\n\n" - f.write(operator_anchor) + f.write(f".. _{operator_anchor}:\n\n") + self_link = f":ref:`🔗<{operator_anchor}>`" f.write(".. rst-class:: classref-operator\n\n") ret_type, signature = make_method_signature(class_def, m, "", state) - f.write(f"{ret_type} {signature}\n\n") + f.write(f"{ret_type} {signature} {self_link}\n\n") # Add operator description, or a call to action if it's missing. @@ -1452,13 +1471,17 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir: # Create theme property signature and anchor point. - f.write(f".. _class_{class_name}_theme_{theme_item_def.data_name}_{theme_item_def.name}:\n\n") + theme_item_anchor = f"class_{class_name}_theme_{theme_item_def.data_name}_{theme_item_def.name}" + f.write(f".. _{theme_item_anchor}:\n\n") + self_link = f":ref:`🔗<{theme_item_anchor}>`" f.write(".. rst-class:: classref-themeproperty\n\n") theme_item_default = "" if theme_item_def.default_value is not None: theme_item_default = f" = {theme_item_def.default_value}" - f.write(f"{theme_item_def.type_name.to_rst(state)} **{theme_item_def.name}**{theme_item_default}\n\n") + f.write( + f"{theme_item_def.type_name.to_rst(state)} **{theme_item_def.name}**{theme_item_default} {self_link}\n\n" + ) # Add theme property description, or a call to action if it's missing. @@ -1553,7 +1576,7 @@ def make_method_signature( out += f":ref:`{op_name}<class_{class_def.name}_{ref_type}_{sanitize_operator_name(definition.name, state)}" for parameter in definition.parameters: out += f"_{parameter.type_name.type_name}" - out += f">`" + out += ">`" elif ref_type == "method": ref_type_qualifier = "" if definition.name.startswith("_"): diff --git a/drivers/d3d12/rendering_device_driver_d3d12.cpp b/drivers/d3d12/rendering_device_driver_d3d12.cpp index 9407826ebf..b042d41524 100644 --- a/drivers/d3d12/rendering_device_driver_d3d12.cpp +++ b/drivers/d3d12/rendering_device_driver_d3d12.cpp @@ -2199,9 +2199,21 @@ Error RenderingDeviceDriverD3D12::swap_chain_resize(CommandQueueID p_cmd_queue, swap_chain_desc.SampleDesc.Count = 1; swap_chain_desc.Flags = creation_flags; swap_chain_desc.Scaling = DXGI_SCALING_NONE; + if (OS::get_singleton()->is_layered_allowed()) { + swap_chain_desc.AlphaMode = DXGI_ALPHA_MODE_PREMULTIPLIED; + has_comp_alpha[(uint64_t)p_cmd_queue.id] = true; + } else { + swap_chain_desc.AlphaMode = DXGI_ALPHA_MODE_IGNORE; + has_comp_alpha[(uint64_t)p_cmd_queue.id] = false; + } ComPtr<IDXGISwapChain1> swap_chain_1; res = context_driver->dxgi_factory_get()->CreateSwapChainForHwnd(command_queue->d3d_queue.Get(), surface->hwnd, &swap_chain_desc, nullptr, nullptr, swap_chain_1.GetAddressOf()); + if (!SUCCEEDED(res) && swap_chain_desc.AlphaMode != DXGI_ALPHA_MODE_IGNORE) { + swap_chain_desc.AlphaMode = DXGI_ALPHA_MODE_IGNORE; + has_comp_alpha[(uint64_t)p_cmd_queue.id] = false; + res = context_driver->dxgi_factory_get()->CreateSwapChainForHwnd(command_queue->d3d_queue.Get(), surface->hwnd, &swap_chain_desc, nullptr, nullptr, swap_chain_1.GetAddressOf()); + } ERR_FAIL_COND_V(!SUCCEEDED(res), ERR_CANT_CREATE); swap_chain_1.As(&swap_chain->d3d_swap_chain); @@ -5980,6 +5992,13 @@ const RDD::Capabilities &RenderingDeviceDriverD3D12::get_capabilities() const { return device_capabilities; } +bool RenderingDeviceDriverD3D12::is_composite_alpha_supported(CommandQueueID p_queue) const { + if (has_comp_alpha.has((uint64_t)p_queue.id)) { + return has_comp_alpha[(uint64_t)p_queue.id]; + } + return false; +} + /******************/ RenderingDeviceDriverD3D12::RenderingDeviceDriverD3D12(RenderingContextDriverD3D12 *p_context_driver) { diff --git a/drivers/d3d12/rendering_device_driver_d3d12.h b/drivers/d3d12/rendering_device_driver_d3d12.h index 8e1223bdaa..3a9677485e 100644 --- a/drivers/d3d12/rendering_device_driver_d3d12.h +++ b/drivers/d3d12/rendering_device_driver_d3d12.h @@ -973,6 +973,7 @@ private: uint32_t frames_drawn = 0; uint32_t segment_serial = 0; bool segment_begun = false; + HashMap<uint64_t, bool> has_comp_alpha; public: virtual void begin_segment(uint32_t p_frame_index, uint32_t p_frames_drawn) override final; @@ -994,6 +995,8 @@ public: virtual String get_pipeline_cache_uuid() const override final; virtual const Capabilities &get_capabilities() const override final; + virtual bool is_composite_alpha_supported(CommandQueueID p_queue) const override final; + static bool is_in_developer_mode(); private: diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp index 03f947cd05..6cbdf5e935 100644 --- a/drivers/gles3/rasterizer_scene_gles3.cpp +++ b/drivers/gles3/rasterizer_scene_gles3.cpp @@ -1359,23 +1359,26 @@ void RasterizerSceneGLES3::_fill_render_list(RenderListType p_render_list, const // LOD if (p_render_data->screen_mesh_lod_threshold > 0.0 && mesh_storage->mesh_surface_has_lod(surf->surface)) { - // Get the LOD support points on the mesh AABB. - Vector3 lod_support_min = inst->transformed_aabb.get_support(p_render_data->cam_transform.basis.get_column(Vector3::AXIS_Z)); - Vector3 lod_support_max = inst->transformed_aabb.get_support(-p_render_data->cam_transform.basis.get_column(Vector3::AXIS_Z)); - - // Get the distances to those points on the AABB from the camera origin. - float distance_min = (float)p_render_data->cam_transform.origin.distance_to(lod_support_min); - float distance_max = (float)p_render_data->cam_transform.origin.distance_to(lod_support_max); - float distance = 0.0; - if (distance_min * distance_max < 0.0) { - //crossing plane - distance = 0.0; - } else if (distance_min >= 0.0) { - distance = distance_min; - } else if (distance_max <= 0.0) { - distance = -distance_max; + // Check if camera is NOT inside the mesh AABB. + if (!inst->transformed_aabb.has_point(p_render_data->main_cam_transform.origin)) { + // Get the LOD support points on the mesh AABB. + Vector3 lod_support_min = inst->transformed_aabb.get_support(p_render_data->main_cam_transform.basis.get_column(Vector3::AXIS_Z)); + Vector3 lod_support_max = inst->transformed_aabb.get_support(-p_render_data->main_cam_transform.basis.get_column(Vector3::AXIS_Z)); + + // Get the distances to those points on the AABB from the camera origin. + float distance_min = (float)p_render_data->main_cam_transform.origin.distance_to(lod_support_min); + float distance_max = (float)p_render_data->main_cam_transform.origin.distance_to(lod_support_max); + + if (distance_min * distance_max < 0.0) { + //crossing plane + distance = 0.0; + } else if (distance_min >= 0.0) { + distance = distance_min; + } else if (distance_max <= 0.0) { + distance = -distance_max; + } } if (p_render_data->cam_orthogonal) { @@ -1985,7 +1988,6 @@ void RasterizerSceneGLES3::_render_shadows(const RenderDataGLES3 *p_render_data, LocalVector<int> shadows; LocalVector<int> directional_shadows; - Plane camera_plane(-p_render_data->cam_transform.basis.get_column(Vector3::AXIS_Z), p_render_data->cam_transform.origin); float lod_distance_multiplier = p_render_data->cam_projection.get_lod_multiplier(); // Put lights into buckets for omni (cube shadows), directional, and spot. @@ -2014,20 +2016,20 @@ void RasterizerSceneGLES3::_render_shadows(const RenderDataGLES3 *p_render_data, // Render cubemap shadows. for (const int &index : cube_shadows) { - _render_shadow_pass(p_render_data->render_shadows[index].light, p_render_data->shadow_atlas, p_render_data->render_shadows[index].pass, p_render_data->render_shadows[index].instances, camera_plane, lod_distance_multiplier, p_render_data->screen_mesh_lod_threshold, p_render_data->render_info, p_viewport_size, p_render_data->cam_transform); + _render_shadow_pass(p_render_data->render_shadows[index].light, p_render_data->shadow_atlas, p_render_data->render_shadows[index].pass, p_render_data->render_shadows[index].instances, lod_distance_multiplier, p_render_data->screen_mesh_lod_threshold, p_render_data->render_info, p_viewport_size, p_render_data->cam_transform); } // Render directional shadows. for (uint32_t i = 0; i < directional_shadows.size(); i++) { - _render_shadow_pass(p_render_data->render_shadows[directional_shadows[i]].light, p_render_data->shadow_atlas, p_render_data->render_shadows[directional_shadows[i]].pass, p_render_data->render_shadows[directional_shadows[i]].instances, camera_plane, lod_distance_multiplier, p_render_data->screen_mesh_lod_threshold, p_render_data->render_info, p_viewport_size, p_render_data->cam_transform); + _render_shadow_pass(p_render_data->render_shadows[directional_shadows[i]].light, p_render_data->shadow_atlas, p_render_data->render_shadows[directional_shadows[i]].pass, p_render_data->render_shadows[directional_shadows[i]].instances, lod_distance_multiplier, p_render_data->screen_mesh_lod_threshold, p_render_data->render_info, p_viewport_size, p_render_data->cam_transform); } // Render positional shadows (Spotlight and Omnilight with dual-paraboloid). for (uint32_t i = 0; i < shadows.size(); i++) { - _render_shadow_pass(p_render_data->render_shadows[shadows[i]].light, p_render_data->shadow_atlas, p_render_data->render_shadows[shadows[i]].pass, p_render_data->render_shadows[shadows[i]].instances, camera_plane, lod_distance_multiplier, p_render_data->screen_mesh_lod_threshold, p_render_data->render_info, p_viewport_size, p_render_data->cam_transform); + _render_shadow_pass(p_render_data->render_shadows[shadows[i]].light, p_render_data->shadow_atlas, p_render_data->render_shadows[shadows[i]].pass, p_render_data->render_shadows[shadows[i]].instances, lod_distance_multiplier, p_render_data->screen_mesh_lod_threshold, p_render_data->render_info, p_viewport_size, p_render_data->cam_transform); } } } -void RasterizerSceneGLES3::_render_shadow_pass(RID p_light, RID p_shadow_atlas, int p_pass, const PagedArray<RenderGeometryInstance *> &p_instances, const Plane &p_camera_plane, float p_lod_distance_multiplier, float p_screen_mesh_lod_threshold, RenderingMethod::RenderInfo *p_render_info, const Size2i &p_viewport_size, const Transform3D &p_main_cam_transform) { +void RasterizerSceneGLES3::_render_shadow_pass(RID p_light, RID p_shadow_atlas, int p_pass, const PagedArray<RenderGeometryInstance *> &p_instances, float p_lod_distance_multiplier, float p_screen_mesh_lod_threshold, RenderingMethod::RenderInfo *p_render_info, const Size2i &p_viewport_size, const Transform3D &p_main_cam_transform) { GLES3::LightStorage *light_storage = GLES3::LightStorage::get_singleton(); ERR_FAIL_COND(!light_storage->owns_light_instance(p_light)); diff --git a/drivers/gles3/rasterizer_scene_gles3.h b/drivers/gles3/rasterizer_scene_gles3.h index c656ee3cc7..b6c7a0c5a5 100644 --- a/drivers/gles3/rasterizer_scene_gles3.h +++ b/drivers/gles3/rasterizer_scene_gles3.h @@ -70,21 +70,28 @@ enum SceneUniformLocation { SCENE_GLOBALS_UNIFORM_LOCATION, SCENE_DATA_UNIFORM_LOCATION, SCENE_MATERIAL_UNIFORM_LOCATION, - SCENE_EMPTY, // Unused, put here to avoid conflicts with SKY_DIRECTIONAL_LIGHT_UNIFORM_LOCATION. + SCENE_EMPTY1, // Unused, put here to avoid conflicts with SKY_DIRECTIONAL_LIGHT_UNIFORM_LOCATION. SCENE_OMNILIGHT_UNIFORM_LOCATION, SCENE_SPOTLIGHT_UNIFORM_LOCATION, SCENE_DIRECTIONAL_LIGHT_UNIFORM_LOCATION, SCENE_MULTIVIEW_UNIFORM_LOCATION, SCENE_POSITIONAL_SHADOW_UNIFORM_LOCATION, SCENE_DIRECTIONAL_SHADOW_UNIFORM_LOCATION, + SCENE_EMPTY2, // Unused, put here to avoid conflicts with SKY_MULTIVIEW_UNIFORM_LOCATION. }; enum SkyUniformLocation { SKY_TONEMAP_UNIFORM_LOCATION, SKY_GLOBALS_UNIFORM_LOCATION, - SKY_EMPTY, // Unused, put here to avoid conflicts with SCENE_DATA_UNIFORM_LOCATION. + SKY_EMPTY1, // Unused, put here to avoid conflicts with SCENE_DATA_UNIFORM_LOCATION. SKY_MATERIAL_UNIFORM_LOCATION, SKY_DIRECTIONAL_LIGHT_UNIFORM_LOCATION, + SKY_EMPTY2, // Unused, put here to avoid conflicts with SCENE_OMNILIGHT_UNIFORM_LOCATION. + SKY_EMPTY3, // Unused, put here to avoid conflicts with SCENE_SPOTLIGHT_UNIFORM_LOCATION. + SKY_EMPTY4, // Unused, put here to avoid conflicts with SCENE_DIRECTIONAL_LIGHT_UNIFORM_LOCATION. + SKY_EMPTY5, // Unused, put here to avoid conflicts with SCENE_MULTIVIEW_UNIFORM_LOCATION. + SKY_EMPTY6, // Unused, put here to avoid conflicts with SCENE_POSITIONAL_SHADOW_UNIFORM_LOCATION. + SKY_EMPTY7, // Unused, put here to avoid conflicts with SCENE_DIRECTIONAL_SHADOW_UNIFORM_LOCATION. SKY_MULTIVIEW_UNIFORM_LOCATION, }; @@ -633,7 +640,7 @@ private: void _setup_environment(const RenderDataGLES3 *p_render_data, bool p_no_fog, const Size2i &p_screen_size, bool p_flip_y, const Color &p_default_bg_color, bool p_pancake_shadows, float p_shadow_bias = 0.0); void _fill_render_list(RenderListType p_render_list, const RenderDataGLES3 *p_render_data, PassMode p_pass_mode, bool p_append = false); void _render_shadows(const RenderDataGLES3 *p_render_data, const Size2i &p_viewport_size = Size2i(1, 1)); - void _render_shadow_pass(RID p_light, RID p_shadow_atlas, int p_pass, const PagedArray<RenderGeometryInstance *> &p_instances, const Plane &p_camera_plane = Plane(), float p_lod_distance_multiplier = 0, float p_screen_mesh_lod_threshold = 0.0, RenderingMethod::RenderInfo *p_render_info = nullptr, const Size2i &p_viewport_size = Size2i(1, 1), const Transform3D &p_main_cam_transform = Transform3D()); + void _render_shadow_pass(RID p_light, RID p_shadow_atlas, int p_pass, const PagedArray<RenderGeometryInstance *> &p_instances, float p_lod_distance_multiplier = 0, float p_screen_mesh_lod_threshold = 0.0, RenderingMethod::RenderInfo *p_render_info = nullptr, const Size2i &p_viewport_size = Size2i(1, 1), const Transform3D &p_main_cam_transform = Transform3D()); void _render_post_processing(const RenderDataGLES3 *p_render_data); template <PassMode p_pass_mode> diff --git a/drivers/gles3/shaders/sky.glsl b/drivers/gles3/shaders/sky.glsl index 26549901a6..9de65ba960 100644 --- a/drivers/gles3/shaders/sky.glsl +++ b/drivers/gles3/shaders/sky.glsl @@ -116,7 +116,7 @@ uniform float z_far; uniform uint directional_light_count; #ifdef USE_MULTIVIEW -layout(std140) uniform MultiviewData { // ubo:5 +layout(std140) uniform MultiviewData { // ubo:11 highp mat4 projection_matrix_view[MAX_VIEWS]; highp mat4 inv_projection_matrix_view[MAX_VIEWS]; highp vec4 eye_offset[MAX_VIEWS]; diff --git a/drivers/vulkan/rendering_device_driver_vulkan.cpp b/drivers/vulkan/rendering_device_driver_vulkan.cpp index 896fc6ff91..b03a8418ed 100644 --- a/drivers/vulkan/rendering_device_driver_vulkan.cpp +++ b/drivers/vulkan/rendering_device_driver_vulkan.cpp @@ -2646,6 +2646,7 @@ Error RenderingDeviceDriverVulkan::swap_chain_resize(CommandQueueID p_cmd_queue, break; } } + has_comp_alpha[(uint64_t)p_cmd_queue.id] = (composite_alpha != VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR); } VkSwapchainCreateInfoKHR swap_create_info = {}; @@ -4945,6 +4946,13 @@ const RDD::Capabilities &RenderingDeviceDriverVulkan::get_capabilities() const { return device_capabilities; } +bool RenderingDeviceDriverVulkan::is_composite_alpha_supported(CommandQueueID p_queue) const { + if (has_comp_alpha.has((uint64_t)p_queue.id)) { + return has_comp_alpha[(uint64_t)p_queue.id]; + } + return false; +} + /******************/ RenderingDeviceDriverVulkan::RenderingDeviceDriverVulkan(RenderingContextDriverVulkan *p_context_driver) { diff --git a/drivers/vulkan/rendering_device_driver_vulkan.h b/drivers/vulkan/rendering_device_driver_vulkan.h index e70019962a..b9e7563069 100644 --- a/drivers/vulkan/rendering_device_driver_vulkan.h +++ b/drivers/vulkan/rendering_device_driver_vulkan.h @@ -494,6 +494,7 @@ private: static int caching_instance_count; PipelineCache pipelines_cache; String pipeline_cache_id; + HashMap<uint64_t, bool> has_comp_alpha; public: virtual void pipeline_free(PipelineID p_pipeline) override final; @@ -627,6 +628,8 @@ public: virtual String get_pipeline_cache_uuid() const override final; virtual const Capabilities &get_capabilities() const override final; + virtual bool is_composite_alpha_supported(CommandQueueID p_queue) const override final; + private: /*********************/ /**** BOOKKEEPING ****/ diff --git a/editor/SCsub b/editor/SCsub index e613a71238..029048969a 100644 --- a/editor/SCsub +++ b/editor/SCsub @@ -4,11 +4,12 @@ Import("env") env.editor_sources = [] -import os import glob +import os + import editor_builders -import methods +import methods if env.editor_build: # Generate doc data paths diff --git a/editor/action_map_editor.cpp b/editor/action_map_editor.cpp index 3023c5907a..f70730d540 100644 --- a/editor/action_map_editor.cpp +++ b/editor/action_map_editor.cpp @@ -428,6 +428,7 @@ void ActionMapEditor::update_action_list(const Vector<ActionInfo> &p_action_info // Update Tree... TreeItem *action_item = action_tree->create_item(root); + ERR_FAIL_NULL(action_item); action_item->set_meta("__action", action_info.action); action_item->set_meta("__name", action_info.name); @@ -604,7 +605,7 @@ ActionMapEditor::ActionMapEditor() { action_tree->set_column_custom_minimum_width(1, 80 * EDSCALE); action_tree->set_column_expand(2, false); action_tree->set_column_custom_minimum_width(2, 50 * EDSCALE); - action_tree->connect("item_edited", callable_mp(this, &ActionMapEditor::_action_edited)); + action_tree->connect("item_edited", callable_mp(this, &ActionMapEditor::_action_edited), CONNECT_DEFERRED); action_tree->connect("item_activated", callable_mp(this, &ActionMapEditor::_tree_item_activated)); action_tree->connect("button_clicked", callable_mp(this, &ActionMapEditor::_tree_button_pressed)); main_vbox->add_child(action_tree); diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp index ebb63dd57c..045774080c 100644 --- a/editor/animation_track_editor.cpp +++ b/editor/animation_track_editor.cpp @@ -7312,17 +7312,19 @@ AnimationTrackEditor::AnimationTrackEditor() { bottom_hb->add_child(zoom); timeline->set_zoom(zoom); + ED_SHORTCUT("animation_editor/auto_fit", TTR("Fit to panel"), KeyModifierMask::ALT | Key::F); + auto_fit = memnew(Button); auto_fit->set_flat(true); auto_fit->connect(SceneStringName(pressed), callable_mp(this, &AnimationTrackEditor::_auto_fit)); - auto_fit->set_shortcut(ED_SHORTCUT("animation_editor/auto_fit", TTR("Fit to panel"), KeyModifierMask::ALT | Key::F)); + auto_fit->set_shortcut(ED_GET_SHORTCUT("animation_editor/auto_fit")); bottom_hb->add_child(auto_fit); auto_fit_bezier = memnew(Button); auto_fit_bezier->set_flat(true); auto_fit_bezier->set_visible(false); auto_fit_bezier->connect(SceneStringName(pressed), callable_mp(this, &AnimationTrackEditor::_auto_fit_bezier)); - auto_fit_bezier->set_shortcut(ED_SHORTCUT("animation_editor/auto_fit", TTR("Fit to panel"), KeyModifierMask::ALT | Key::F)); + auto_fit_bezier->set_shortcut(ED_GET_SHORTCUT("animation_editor/auto_fit")); bottom_hb->add_child(auto_fit_bezier); edit = memnew(MenuButton); diff --git a/editor/code_editor.cpp b/editor/code_editor.cpp index ee0108df8e..253157a561 100644 --- a/editor/code_editor.cpp +++ b/editor/code_editor.cpp @@ -1124,6 +1124,31 @@ void CodeTextEditor::trim_trailing_whitespace() { } } +void CodeTextEditor::trim_final_newlines() { + int final_line = text_editor->get_line_count() - 1; + int check_line = final_line; + + String line = text_editor->get_line(check_line); + + while (line.is_empty() && check_line > -1) { + --check_line; + + line = text_editor->get_line(check_line); + } + + ++check_line; + + if (check_line < final_line) { + text_editor->begin_complex_operation(); + + text_editor->remove_text(check_line, 0, final_line, 0); + + text_editor->merge_overlapping_carets(); + text_editor->end_complex_operation(); + text_editor->queue_redraw(); + } +} + void CodeTextEditor::insert_final_newline() { int final_line = text_editor->get_line_count() - 1; String line = text_editor->get_line(final_line); diff --git a/editor/code_editor.h b/editor/code_editor.h index 75a2a68d58..af33a3fac8 100644 --- a/editor/code_editor.h +++ b/editor/code_editor.h @@ -224,6 +224,7 @@ protected: public: void trim_trailing_whitespace(); + void trim_final_newlines(); void insert_final_newline(); enum CaseStyle { diff --git a/editor/debugger/editor_debugger_node.cpp b/editor/debugger/editor_debugger_node.cpp index 1d3c7aec3f..2a98f50a3a 100644 --- a/editor/debugger/editor_debugger_node.cpp +++ b/editor/debugger/editor_debugger_node.cpp @@ -268,11 +268,7 @@ Error EditorDebuggerNode::start(const String &p_uri) { } stop(true); current_uri = p_uri; - if (EDITOR_GET("run/output/always_open_output_on_play")) { - EditorNode::get_bottom_panel()->make_item_visible(EditorNode::get_log()); - } else { - EditorNode::get_bottom_panel()->make_item_visible(this); - } + server = Ref<EditorDebuggerServer>(EditorDebuggerServer::create(p_uri.substr(0, p_uri.find("://") + 3))); const Error err = server->start(p_uri); if (err != OK) { @@ -314,12 +310,18 @@ void EditorDebuggerNode::stop(bool p_force) { void EditorDebuggerNode::_notification(int p_what) { switch (p_what) { case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: { - if (tabs->get_tab_count() > 1 && EditorThemeManager::is_generated_theme_outdated()) { + if (!EditorThemeManager::is_generated_theme_outdated()) { + return; + } + + if (tabs->get_tab_count() > 1) { add_theme_constant_override("margin_left", -EditorNode::get_singleton()->get_editor_theme()->get_stylebox(SNAME("BottomPanelDebuggerOverride"), EditorStringName(EditorStyles))->get_margin(SIDE_LEFT)); add_theme_constant_override("margin_right", -EditorNode::get_singleton()->get_editor_theme()->get_stylebox(SNAME("BottomPanelDebuggerOverride"), EditorStringName(EditorStyles))->get_margin(SIDE_RIGHT)); tabs->add_theme_style_override("panel", EditorNode::get_singleton()->get_editor_theme()->get_stylebox(SNAME("DebuggerPanel"), EditorStringName(EditorStyles))); } + + remote_scene_tree->update_icon_max_width(); } break; case NOTIFICATION_READY: { diff --git a/editor/debugger/editor_debugger_tree.cpp b/editor/debugger/editor_debugger_tree.cpp index 63053d2574..12b590da3c 100644 --- a/editor/debugger/editor_debugger_tree.cpp +++ b/editor/debugger/editor_debugger_tree.cpp @@ -31,6 +31,7 @@ #include "editor_debugger_tree.h" #include "editor/editor_node.h" +#include "editor/editor_string_names.h" #include "editor/gui/editor_file_dialog.h" #include "editor/scene_tree_dock.h" #include "scene/debugger/scene_debugger.h" @@ -62,6 +63,10 @@ void EditorDebuggerTree::_notification(int p_what) { connect("item_collapsed", callable_mp(this, &EditorDebuggerTree::_scene_tree_folded)); connect("item_mouse_selected", callable_mp(this, &EditorDebuggerTree::_scene_tree_rmb_selected)); } break; + + case NOTIFICATION_ENTER_TREE: { + update_icon_max_width(); + } break; } } @@ -293,6 +298,10 @@ Variant EditorDebuggerTree::get_drag_data(const Point2 &p_point) { return vformat("\"%s\"", path); } +void EditorDebuggerTree::update_icon_max_width() { + add_theme_constant_override("icon_max_width", get_theme_constant("class_icon_size", EditorStringName(Editor))); +} + String EditorDebuggerTree::get_selected_path() { if (!get_selected()) { return ""; diff --git a/editor/debugger/editor_debugger_tree.h b/editor/debugger/editor_debugger_tree.h index 895f33f1a2..dbffb0f219 100644 --- a/editor/debugger/editor_debugger_tree.h +++ b/editor/debugger/editor_debugger_tree.h @@ -72,6 +72,7 @@ public: virtual Variant get_drag_data(const Point2 &p_point) override; + void update_icon_max_width(); String get_selected_path(); ObjectID get_selected_object(); int get_current_debugger(); // Would love to have one tree for every debugger. diff --git a/editor/debugger/editor_profiler.cpp b/editor/debugger/editor_profiler.cpp index 69cf13ea0b..0e2a7ee599 100644 --- a/editor/debugger/editor_profiler.cpp +++ b/editor/debugger/editor_profiler.cpp @@ -34,6 +34,7 @@ #include "editor/editor_settings.h" #include "editor/editor_string_names.h" #include "editor/themes/editor_scale.h" +#include "editor/themes/editor_theme_manager.h" #include "scene/resources/image_texture.h" void EditorProfiler::_make_metric_ptrs(Metric &m) { @@ -423,6 +424,15 @@ void EditorProfiler::_notification(int p_what) { case NOTIFICATION_TRANSLATION_CHANGED: { activate->set_icon(get_editor_theme_icon(SNAME("Play"))); clear_button->set_icon(get_editor_theme_icon(SNAME("Clear"))); + + theme_cache.seek_line_color = get_theme_color(SNAME("font_color"), EditorStringName(Editor)); + theme_cache.seek_line_color.a = 0.8; + theme_cache.seek_line_hover_color = theme_cache.seek_line_color; + theme_cache.seek_line_hover_color.a = 0.4; + + if (total_metrics > 0) { + _update_plot(); + } } break; } } @@ -434,11 +444,11 @@ void EditorProfiler::_graph_tex_draw() { if (seeking) { int frame = cursor_metric_edit->get_value() - _get_frame_metric(0).frame_number; int cur_x = (2 * frame + 1) * graph->get_size().x / (2 * frame_metrics.size()) + 1; - graph->draw_line(Vector2(cur_x, 0), Vector2(cur_x, graph->get_size().y), Color(1, 1, 1, 0.8)); + graph->draw_line(Vector2(cur_x, 0), Vector2(cur_x, graph->get_size().y), theme_cache.seek_line_color); } if (hover_metric > -1 && hover_metric < total_metrics) { int cur_x = (2 * hover_metric + 1) * graph->get_size().x / (2 * frame_metrics.size()) + 1; - graph->draw_line(Vector2(cur_x, 0), Vector2(cur_x, graph->get_size().y), Color(1, 1, 1, 0.4)); + graph->draw_line(Vector2(cur_x, 0), Vector2(cur_x, graph->get_size().y), theme_cache.seek_line_hover_color); } } diff --git a/editor/debugger/editor_profiler.h b/editor/debugger/editor_profiler.h index 620d21fe98..64253070b1 100644 --- a/editor/debugger/editor_profiler.h +++ b/editor/debugger/editor_profiler.h @@ -94,6 +94,11 @@ public: }; private: + struct ThemeCache { + Color seek_line_color; + Color seek_line_hover_color; + } theme_cache; + Button *activate = nullptr; Button *clear_button = nullptr; TextureRect *graph = nullptr; diff --git a/editor/debugger/script_editor_debugger.cpp b/editor/debugger/script_editor_debugger.cpp index 156e740509..37bb048b19 100644 --- a/editor/debugger/script_editor_debugger.cpp +++ b/editor/debugger/script_editor_debugger.cpp @@ -1009,7 +1009,6 @@ void ScriptEditorDebugger::start(Ref<RemoteDebuggerPeer> p_peer) { set_process(true); camera_override = CameraOverride::OVERRIDE_NONE; - tabs->set_current_tab(0); _set_reason_text(TTR("Debug session started."), MESSAGE_SUCCESS); _update_buttons_state(); emit_signal(SNAME("started")); diff --git a/editor/editor_builders.py b/editor/editor_builders.py index cfe6e56b49..625d570666 100644 --- a/editor/editor_builders.py +++ b/editor/editor_builders.py @@ -7,6 +7,7 @@ import subprocess import tempfile import uuid import zlib + from methods import print_warning diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp index 9884241708..fa5cdd185f 100644 --- a/editor/editor_help.cpp +++ b/editor/editor_help.cpp @@ -37,7 +37,7 @@ #include "core/object/script_language.h" #include "core/os/keyboard.h" #include "core/string/string_builder.h" -#include "core/version.h" +#include "core/version_generated.gen.h" #include "editor/doc_data_compressed.gen.h" #include "editor/editor_node.h" #include "editor/editor_paths.h" @@ -2340,6 +2340,9 @@ void EditorHelp::_help_callback(const String &p_topic) { if (class_desc->is_ready()) { // call_deferred() is not enough. + if (class_desc->is_connected(SceneStringName(draw), callable_mp(class_desc, &RichTextLabel::scroll_to_paragraph))) { + class_desc->disconnect(SceneStringName(draw), callable_mp(class_desc, &RichTextLabel::scroll_to_paragraph)); + } class_desc->connect(SceneStringName(draw), callable_mp(class_desc, &RichTextLabel::scroll_to_paragraph).bind(line), CONNECT_ONE_SHOT | CONNECT_DEFERRED); } else { scroll_to = line; @@ -2890,7 +2893,7 @@ void EditorHelp::_load_doc_thread(void *p_udata) { callable_mp_static(&EditorHelp::_gen_extensions_docs).call_deferred(); } else { // We have to go back to the main thread to start from scratch, bypassing any possibly existing cache. - callable_mp_static(&EditorHelp::generate_doc).bind(false).call_deferred(); + callable_mp_static(&EditorHelp::generate_doc).call_deferred(false); } OS::get_singleton()->benchmark_end_measure("EditorHelp", vformat("Generate Documentation (Run %d)", doc_generation_count)); @@ -3380,6 +3383,7 @@ EditorHelpBit::HelpData EditorHelpBit::_get_theme_item_help_data(const StringNam if (theme_item.name == p_theme_item_name) { result = current; found = true; + if (!is_native) { break; } diff --git a/editor/editor_inspector.compat.inc b/editor/editor_inspector.compat.inc new file mode 100644 index 0000000000..53c410ba26 --- /dev/null +++ b/editor/editor_inspector.compat.inc @@ -0,0 +1,41 @@ +/**************************************************************************/ +/* editor_inspector.compat.inc */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef DISABLE_DEPRECATED + +void EditorInspectorPlugin::_add_property_editor_bind_compat_92322(const String &p_for_property, Control *p_prop, bool p_add_to_end) { + add_property_editor(p_for_property, p_prop, p_add_to_end, ""); +} + +void EditorInspectorPlugin::_bind_compatibility_methods() { + ClassDB::bind_compatibility_method(D_METHOD("add_property_editor", "property", "editor", "add_to_end"), &EditorInspectorPlugin::_add_property_editor_bind_compat_92322, DEFVAL(false)); +} + +#endif // DISABLE_DEPRECATED diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index 7c5e3877f2..fafefa7771 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -29,6 +29,7 @@ /**************************************************************************/ #include "editor_inspector.h" +#include "editor_inspector.compat.inc" #include "core/os/keyboard.h" #include "editor/doc_tools.h" @@ -1128,11 +1129,12 @@ void EditorInspectorPlugin::add_custom_control(Control *control) { added_editors.push_back(ae); } -void EditorInspectorPlugin::add_property_editor(const String &p_for_property, Control *p_prop, bool p_add_to_end) { +void EditorInspectorPlugin::add_property_editor(const String &p_for_property, Control *p_prop, bool p_add_to_end, const String &p_label) { AddedEditor ae; ae.properties.push_back(p_for_property); ae.property_editor = p_prop; ae.add_to_end = p_add_to_end; + ae.label = p_label; added_editors.push_back(ae); } @@ -1174,7 +1176,7 @@ void EditorInspectorPlugin::parse_end(Object *p_object) { void EditorInspectorPlugin::_bind_methods() { ClassDB::bind_method(D_METHOD("add_custom_control", "control"), &EditorInspectorPlugin::add_custom_control); - ClassDB::bind_method(D_METHOD("add_property_editor", "property", "editor", "add_to_end"), &EditorInspectorPlugin::add_property_editor, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("add_property_editor", "property", "editor", "add_to_end", "label"), &EditorInspectorPlugin::add_property_editor, DEFVAL(false), DEFVAL(String())); ClassDB::bind_method(D_METHOD("add_property_editor_for_multiple_properties", "label", "properties", "editor"), &EditorInspectorPlugin::add_property_editor_for_multiple_properties); GDVIRTUAL_BIND(_can_handle, "object") @@ -2867,15 +2869,6 @@ void EditorInspector::update_tree() { // Otherwise the category was probably added via `@export_category` or `_get_property_list()`. const bool is_custom_category = p.hint_string.is_empty(); - if ((is_custom_category && !show_custom_categories) || (!is_custom_category && !show_standard_categories)) { - continue; - } - - // Hide the "MultiNodeEdit" category for MultiNodeEdit. - if (Object::cast_to<MultiNodeEdit>(object) && p.name == "MultiNodeEdit") { - continue; - } - // Iterate over remaining properties. If no properties in category, skip the category. List<PropertyInfo>::Element *N = E_property->next(); bool valid = true; @@ -2896,22 +2889,20 @@ void EditorInspector::update_tree() { continue; // Empty, ignore it. } - // Create an EditorInspectorCategory and add it to the inspector. - EditorInspectorCategory *category = memnew(EditorInspectorCategory); - main_vbox->add_child(category); - category_vbox = nullptr; //reset + String category_label; + String category_tooltip; + Ref<Texture> category_icon; // Do not add an icon, do not change the current class (`doc_name`) for custom categories. if (is_custom_category) { - category->label = p.name; - category->set_tooltip_text(p.name); + category_label = p.name; + category_tooltip = p.name; } else { - String type = p.name; - String label = p.name; doc_name = p.name; + category_label = p.name; // Use category's owner script to update some of its information. - if (!EditorNode::get_editor_data().is_type_recognized(type) && ResourceLoader::exists(p.hint_string)) { + if (!EditorNode::get_editor_data().is_type_recognized(p.name) && ResourceLoader::exists(p.hint_string)) { Ref<Script> scr = ResourceLoader::load(p.hint_string, "Script"); if (scr.is_valid()) { StringName script_name = EditorNode::get_editor_data().script_class_get_name(scr->get_path()); @@ -2924,32 +2915,50 @@ void EditorInspector::update_tree() { doc_name = docs[docs.size() - 1].name; } if (script_name != StringName()) { - label = script_name; + category_label = script_name; } // Find the icon corresponding to the script. if (script_name != StringName()) { - category->icon = EditorNode::get_singleton()->get_class_icon(script_name); + category_icon = EditorNode::get_singleton()->get_class_icon(script_name); } else { - category->icon = EditorNode::get_singleton()->get_object_icon(scr.ptr(), "Object"); + category_icon = EditorNode::get_singleton()->get_object_icon(scr.ptr(), "Object"); } } } - if (category->icon.is_null() && !type.is_empty()) { - category->icon = EditorNode::get_singleton()->get_class_icon(type); + if (category_icon.is_null() && !p.name.is_empty()) { + category_icon = EditorNode::get_singleton()->get_class_icon(p.name); } - // Set the category label. - category->label = label; - category->doc_class_name = doc_name; - if (use_doc_hints) { // `|` separators used in `EditorHelpBit`. - category->set_tooltip_text("class|" + doc_name + "|"); + category_tooltip = "class|" + doc_name + "|"; } } + if ((is_custom_category && !show_custom_categories) || (!is_custom_category && !show_standard_categories)) { + continue; + } + + // Hide the "MultiNodeEdit" category for MultiNodeEdit. + if (Object::cast_to<MultiNodeEdit>(object) && p.name == "MultiNodeEdit") { + continue; + } + + // Create an EditorInspectorCategory and add it to the inspector. + EditorInspectorCategory *category = memnew(EditorInspectorCategory); + main_vbox->add_child(category); + category_vbox = nullptr; // Reset. + + // Set the category info. + category->label = category_label; + category->set_tooltip_text(category_tooltip); + category->icon = category_icon; + if (!is_custom_category) { + category->doc_class_name = doc_name; + } + // Add editors at the start of a category. for (Ref<EditorInspectorPlugin> &ped : valid_plugins) { ped->parse_category(object, p.name); @@ -3782,7 +3791,6 @@ void EditorInspector::_edit_set(const String &p_name, const Variant &p_value, bo } emit_signal(_prop_edited, p_name); - } else if (Object::cast_to<MultiNodeEdit>(object)) { Object::cast_to<MultiNodeEdit>(object)->set_property_field(p_name, p_value, p_changed_field); _edit_request_change(object, p_name); @@ -3959,7 +3967,7 @@ void EditorInspector::_property_checked(const String &p_path, bool p_checked) { //property checked if (autoclear) { if (!p_checked) { - object->set(p_path, Variant()); + _edit_set(p_path, Variant(), false, ""); } else { Variant to_create; List<PropertyInfo> pinfo; @@ -3971,7 +3979,7 @@ void EditorInspector::_property_checked(const String &p_path, bool p_checked) { break; } } - object->set(p_path, to_create); + _edit_set(p_path, to_create, false, ""); } if (editor_property_map.has(p_path)) { @@ -3982,7 +3990,6 @@ void EditorInspector::_property_checked(const String &p_path, bool p_checked) { E->update_cache(); } } - } else { emit_signal(SNAME("property_toggled"), p_path, p_checked); } diff --git a/editor/editor_inspector.h b/editor/editor_inspector.h index 3cbee5c502..a0ced55bd8 100644 --- a/editor/editor_inspector.h +++ b/editor/editor_inspector.h @@ -252,9 +252,13 @@ protected: GDVIRTUAL7R(bool, _parse_property, Object *, Variant::Type, String, PropertyHint, String, BitField<PropertyUsageFlags>, bool) GDVIRTUAL1(_parse_end, Object *) +#ifndef DISABLE_DEPRECATED + void _add_property_editor_bind_compat_92322(const String &p_for_property, Control *p_prop, bool p_add_to_end); + static void _bind_compatibility_methods(); +#endif // DISABLE_DEPRECATED public: void add_custom_control(Control *control); - void add_property_editor(const String &p_for_property, Control *p_prop, bool p_add_to_end = false); + void add_property_editor(const String &p_for_property, Control *p_prop, bool p_add_to_end = false, const String &p_label = String()); void add_property_editor_for_multiple_properties(const String &p_label, const Vector<String> &p_properties, Control *p_prop); virtual bool can_handle(Object *p_object); @@ -344,7 +348,7 @@ class EditorInspectorArray : public EditorInspectorSection { MODE_NONE, MODE_USE_COUNT_PROPERTY, MODE_USE_MOVE_ARRAY_ELEMENT_FUNCTION, - } mode; + } mode = MODE_NONE; StringName count_property; StringName array_element_prefix; String swap_method; diff --git a/editor/editor_log.cpp b/editor/editor_log.cpp index 6615133dea..166d09af30 100644 --- a/editor/editor_log.cpp +++ b/editor/editor_log.cpp @@ -59,7 +59,7 @@ void EditorLog::_error_handler(void *p_self, const char *p_func, const char *p_f MessageType message_type = p_type == ERR_HANDLER_WARNING ? MSG_TYPE_WARNING : MSG_TYPE_ERROR; if (self->current != Thread::get_caller_id()) { - callable_mp(self, &EditorLog::add_message).bind(err_str, message_type).call_deferred(); + callable_mp(self, &EditorLog::add_message).call_deferred(err_str, message_type); } else { self->add_message(err_str, message_type); } @@ -273,6 +273,10 @@ void EditorLog::_undo_redo_cbk(void *p_self, const String &p_name) { } void EditorLog::_rebuild_log() { + if (messages.is_empty()) { + return; + } + log->clear(); int line_count = 0; diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 68fcc613ee..0df4df36bc 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -671,7 +671,7 @@ void EditorNode::_notification(int p_what) { callable_mp(this, &EditorNode::_begin_first_scan).call_deferred(); - DisplayServer::get_singleton()->set_system_theme_change_callback(callable_mp(this, &EditorNode::_update_theme)); + DisplayServer::get_singleton()->set_system_theme_change_callback(callable_mp(this, &EditorNode::_update_theme).bind(false)); /* DO NOT LOAD SCENES HERE, WAIT FOR FILE SCANNING AND REIMPORT TO COMPLETE */ } break; @@ -4107,7 +4107,7 @@ Error EditorNode::load_scene(const String &p_scene, bool p_ignore_broken_deps, b return OK; } -HashMap<StringName, Variant> EditorNode::get_modified_properties_for_node(Node *p_node) { +HashMap<StringName, Variant> EditorNode::get_modified_properties_for_node(Node *p_node, bool p_node_references_only) { HashMap<StringName, Variant> modified_property_map; List<PropertyInfo> pinfo; @@ -4119,7 +4119,17 @@ HashMap<StringName, Variant> EditorNode::get_modified_properties_for_node(Node * Variant current_value = p_node->get(E.name); if (is_valid_revert) { if (PropertyUtils::is_property_value_different(current_value, revert_value)) { - modified_property_map[E.name] = current_value; + // If this property is a direct node reference, save a NodePath instead to prevent corrupted references. + if (E.type == Variant::OBJECT && E.hint == PROPERTY_HINT_NODE_TYPE) { + Node *target_node = Object::cast_to<Node>(current_value); + if (target_node) { + modified_property_map[E.name] = p_node->get_path_to(target_node); + } + } else { + if (!p_node_references_only) { + modified_property_map[E.name] = current_value; + } + } } } } @@ -4137,10 +4147,118 @@ void EditorNode::update_ownership_table_for_addition_node_ancestors(Node *p_curr } } -void EditorNode::update_diff_data_for_node( - Node *p_edited_scene, +void EditorNode::update_node_from_node_modification_entry(Node *p_node, ModificationNodeEntry &p_node_modification) { + if (p_node) { + // First, attempt to restore the script property since it may affect the get_property_list method. + Variant *script_property_table_entry = p_node_modification.property_table.getptr(CoreStringName(script)); + if (script_property_table_entry) { + p_node->set_script(*script_property_table_entry); + } + + // Get properties for this node. + List<PropertyInfo> pinfo; + p_node->get_property_list(&pinfo); + + // Get names of all valid property names. + HashMap<StringName, bool> property_node_reference_table; + for (const PropertyInfo &E : pinfo) { + if (E.usage & PROPERTY_USAGE_STORAGE) { + if (E.type == Variant::OBJECT && E.hint == PROPERTY_HINT_NODE_TYPE) { + property_node_reference_table[E.name] = true; + } else { + property_node_reference_table[E.name] = false; + } + } + } + + // Restore the modified properties for this node. + for (const KeyValue<StringName, Variant> &E : p_node_modification.property_table) { + bool *property_node_reference_table_entry = property_node_reference_table.getptr(E.key); + if (property_node_reference_table_entry) { + // If the property is a node reference, attempt to restore from the node path instead. + bool is_node_reference = *property_node_reference_table_entry; + if (is_node_reference) { + if (E.value.get_type() == Variant::NODE_PATH) { + p_node->set(E.key, p_node->get_node_or_null(E.value)); + } + } else { + p_node->set(E.key, E.value); + } + } + } + + // Restore the connections to other nodes. + for (const ConnectionWithNodePath &E : p_node_modification.connections_to) { + Connection conn = E.connection; + + // Get the node the callable is targeting. + Node *target_node = Object::cast_to<Node>(conn.callable.get_object()); + + // If the callable object no longer exists or is marked for deletion, + // attempt to reaccquire the closest match by using the node path + // we saved earlier. + if (!target_node || !target_node->is_queued_for_deletion()) { + target_node = p_node->get_node_or_null(E.node_path); + } + + if (target_node) { + // Reconstruct the callable. + Callable new_callable = Callable(target_node, conn.callable.get_method()); + + if (!p_node->is_connected(conn.signal.get_name(), new_callable)) { + ERR_FAIL_COND(p_node->connect(conn.signal.get_name(), new_callable, conn.flags) != OK); + } + } + } + + // Restore the connections from other nodes. + for (const Connection &E : p_node_modification.connections_from) { + Connection conn = E; + + bool valid = p_node->has_method(conn.callable.get_method()) || Ref<Script>(p_node->get_script()).is_null() || Ref<Script>(p_node->get_script())->has_method(conn.callable.get_method()); + ERR_CONTINUE_MSG(!valid, vformat("Attempt to connect signal '%s.%s' to nonexistent method '%s.%s'.", conn.signal.get_object()->get_class(), conn.signal.get_name(), conn.callable.get_object()->get_class(), conn.callable.get_method())); + + // Get the object which the signal is connected from. + Object *source_object = conn.signal.get_object(); + + if (source_object) { + ERR_FAIL_COND(source_object->connect(conn.signal.get_name(), Callable(p_node, conn.callable.get_method()), conn.flags) != OK); + } + } + + // Re-add the groups. + for (const Node::GroupInfo &E : p_node_modification.groups) { + p_node->add_to_group(E.name, E.persistent); + } + } +} + +void EditorNode::update_node_reference_modification_table_for_node( Node *p_root, Node *p_node, + List<Node *> p_excluded_nodes, + HashMap<NodePath, ModificationNodeEntry> &p_modification_table) { + if (!p_excluded_nodes.find(p_node)) { + HashMap<StringName, Variant> modified_properties = get_modified_properties_for_node(p_node, false); + + if (!modified_properties.is_empty()) { + ModificationNodeEntry modification_node_entry; + modification_node_entry.property_table = modified_properties; + + p_modification_table[p_root->get_path_to(p_node)] = modification_node_entry; + } + + for (int i = 0; i < p_node->get_child_count(); i++) { + Node *child = p_node->get_child(i); + update_node_reference_modification_table_for_node(p_root, child, p_excluded_nodes, p_modification_table); + } + } +} + +void EditorNode::update_reimported_diff_data_for_node( + Node *p_edited_scene, + Node *p_reimported_root, + Node *p_node, HashMap<NodePath, ModificationNodeEntry> &p_modification_table, List<AdditiveNodeEntry> &p_addition_list) { bool node_part_of_subscene = p_node != p_edited_scene && @@ -4150,14 +4268,14 @@ void EditorNode::update_diff_data_for_node( // Loop through the owners until either we reach the root node or nullptr Node *valid_node_owner = p_node->get_owner(); while (valid_node_owner) { - if (valid_node_owner == p_root) { + if (valid_node_owner == p_reimported_root) { break; } valid_node_owner = valid_node_owner->get_owner(); } - if ((valid_node_owner == p_root && (p_root != p_edited_scene || !p_edited_scene->get_scene_file_path().is_empty())) || node_part_of_subscene || p_node == p_root) { - HashMap<StringName, Variant> modified_properties = get_modified_properties_for_node(p_node); + if ((valid_node_owner == p_reimported_root && (p_reimported_root != p_edited_scene || !p_edited_scene->get_scene_file_path().is_empty())) || node_part_of_subscene || p_node == p_reimported_root) { + HashMap<StringName, Variant> modified_properties = get_modified_properties_for_node(p_node, false); // Find all valid connections to other nodes. List<Connection> connections_to; @@ -4189,7 +4307,7 @@ void EditorNode::update_diff_data_for_node( if (source_node) { valid_source_owner = source_node->get_owner(); while (valid_source_owner) { - if (valid_source_owner == p_root) { + if (valid_source_owner == p_reimported_root) { break; } valid_source_owner = valid_source_owner->get_owner(); @@ -4215,41 +4333,55 @@ void EditorNode::update_diff_data_for_node( modification_node_entry.connections_from = valid_connections_from; modification_node_entry.groups = groups; - p_modification_table[p_root->get_path_to(p_node)] = modification_node_entry; + p_modification_table[p_reimported_root->get_path_to(p_node)] = modification_node_entry; } } else { - AdditiveNodeEntry new_additive_node_entry; - new_additive_node_entry.node = p_node; - new_additive_node_entry.parent = p_root->get_path_to(p_node->get_parent()); - new_additive_node_entry.owner = p_node->get_owner(); - new_additive_node_entry.index = p_node->get_index(); + // Only save additional nodes which have an owner since this was causing issues transient ownerless nodes + // which get recreated upon scene tree entry. + // For now instead, assume all ownerless nodes are transient and will have to be recreated. + if (p_node->get_owner()) { + HashMap<StringName, Variant> modified_properties = get_modified_properties_for_node(p_node, true); + + if (p_node->get_parent()->get_owner() != nullptr && p_node->get_parent()->get_owner() != p_edited_scene) { + AdditiveNodeEntry new_additive_node_entry; + new_additive_node_entry.node = p_node; + new_additive_node_entry.parent = p_reimported_root->get_path_to(p_node->get_parent()); + new_additive_node_entry.owner = p_node->get_owner(); + new_additive_node_entry.index = p_node->get_index(); + + Node2D *node_2d = Object::cast_to<Node2D>(p_node); + if (node_2d) { + new_additive_node_entry.transform_2d = node_2d->get_relative_transform_to_parent(node_2d->get_parent()); + } + Node3D *node_3d = Object::cast_to<Node3D>(p_node); + if (node_3d) { + new_additive_node_entry.transform_3d = node_3d->get_relative_transform(node_3d->get_parent()); + } - Node2D *node_2d = Object::cast_to<Node2D>(p_node); - if (node_2d) { - new_additive_node_entry.transform_2d = node_2d->get_relative_transform_to_parent(node_2d->get_parent()); - } - Node3D *node_3d = Object::cast_to<Node3D>(p_node); - if (node_3d) { - new_additive_node_entry.transform_3d = node_3d->get_relative_transform(node_3d->get_parent()); - } + // Gathers the ownership of all ancestor nodes for later use. + HashMap<Node *, Node *> ownership_table; + for (int i = 0; i < p_node->get_child_count(); i++) { + Node *child = p_node->get_child(i); + update_ownership_table_for_addition_node_ancestors(child, ownership_table); + } - // Gathers the ownership of all ancestor nodes for later use. - HashMap<Node *, Node *> ownership_table; - for (int i = 0; i < p_node->get_child_count(); i++) { - Node *child = p_node->get_child(i); - update_ownership_table_for_addition_node_ancestors(child, ownership_table); - } + new_additive_node_entry.ownership_table = ownership_table; - new_additive_node_entry.ownership_table = ownership_table; + p_addition_list.push_back(new_additive_node_entry); + } - p_addition_list.push_back(new_additive_node_entry); + if (!modified_properties.is_empty()) { + ModificationNodeEntry modification_node_entry; + modification_node_entry.property_table = modified_properties; - return; + p_modification_table[p_reimported_root->get_path_to(p_node)] = modification_node_entry; + } + } } for (int i = 0; i < p_node->get_child_count(); i++) { Node *child = p_node->get_child(i); - update_diff_data_for_node(p_edited_scene, p_root, child, p_modification_table, p_addition_list); + update_reimported_diff_data_for_node(p_edited_scene, p_reimported_root, child, p_modification_table, p_addition_list); } } // @@ -4411,17 +4543,19 @@ void EditorNode::_project_run_started() { log->clear(); } - if (bool(EDITOR_GET("run/output/always_open_output_on_play"))) { + int action_on_play = EDITOR_GET("run/bottom_panel/action_on_play"); + if (action_on_play == ACTION_ON_PLAY_OPEN_OUTPUT) { bottom_panel->make_item_visible(log); + } else if (action_on_play == ACTION_ON_PLAY_OPEN_DEBUGGER) { + bottom_panel->make_item_visible(EditorDebuggerNode::get_singleton()); } } void EditorNode::_project_run_stopped() { - if (!bool(EDITOR_GET("run/output/always_close_output_on_stop"))) { - return; + int action_on_stop = EDITOR_GET("run/bottom_panel/action_on_stop"); + if (action_on_stop == ACTION_ON_STOP_CLOSE_BUTTOM_PANEL) { + bottom_panel->hide_bottom_panel(); } - - bottom_panel->make_item_visible(log, false); } void EditorNode::notify_all_debug_sessions_exited() { @@ -5541,19 +5675,18 @@ void EditorNode::_add_dropped_files_recursive(const Vector<String> &p_files, Str } void EditorNode::_file_access_close_error_notify(const String &p_str) { - callable_mp_static(&EditorNode::_file_access_close_error_notify_impl).bind(p_str).call_deferred(); + callable_mp_static(&EditorNode::_file_access_close_error_notify_impl).call_deferred(p_str); } void EditorNode::_file_access_close_error_notify_impl(const String &p_str) { add_io_error(vformat(TTR("Unable to write to file '%s', file in use, locked or lacking permissions."), p_str)); } -// Since we felt that a bespoke NOTIFICATION might not be desirable, this function -// provides the hardcoded callbacks to address known bugs which occur on certain -// nodes during reimport. -// Ideally, we should probably agree on a standardized method name which could be -// called from here instead. -void EditorNode::_notify_scene_updated(Node *p_node) { +// Recursive function to inform nodes that an array of nodes have had their scene reimported. +// It will attempt to call a method named '_nodes_scene_reimported' on every node in the +// tree so that editor scripts which create transient nodes will have the opportunity +// to recreate them. +void EditorNode::_notify_nodes_scene_reimported(Node *p_node, Array p_reimported_nodes) { Skeleton3D *skel_3d = Object::cast_to<Skeleton3D>(p_node); if (skel_3d) { skel_3d->reset_bone_poses(); @@ -5564,8 +5697,12 @@ void EditorNode::_notify_scene_updated(Node *p_node) { } } + if (p_node->has_method("_nodes_scene_reimported")) { + p_node->call("_nodes_scene_reimported", p_reimported_nodes); + } + for (int i = 0; i < p_node->get_child_count(); i++) { - _notify_scene_updated(p_node->get_child(i)); + _notify_nodes_scene_reimported(p_node->get_child(i), p_reimported_nodes); } } @@ -5635,6 +5772,7 @@ void EditorNode::find_all_instances_inheriting_path_in_node(Node *p_root, Node * void EditorNode::reload_instances_with_path_in_edited_scenes(const String &p_instance_path) { int original_edited_scene_idx = editor_data.get_edited_scene(); HashMap<int, List<Node *>> edited_scene_map; + Array replaced_nodes; // Walk through each opened scene to get a global list of all instances which match // the current reimported scenes. @@ -5675,12 +5813,16 @@ void EditorNode::reload_instances_with_path_in_edited_scenes(const String &p_ins // Update the version editor_data.is_scene_changed(current_scene_idx); + // Contains modifications in the edited scene which reference nodes inside of any nodes we will be reimporting. + HashMap<NodePath, ModificationNodeEntry> edited_scene_global_modification_table; + update_node_reference_modification_table_for_node(current_edited_scene, current_edited_scene, edited_scene_map_elem.value, edited_scene_global_modification_table); + for (Node *original_node : edited_scene_map_elem.value) { // Walk the tree for the current node and extract relevant diff data, storing it in the modification table. // For additional nodes which are part of the current scene, they get added to the addition table. HashMap<NodePath, ModificationNodeEntry> modification_table; List<AdditiveNodeEntry> addition_list; - update_diff_data_for_node(current_edited_scene, original_node, original_node, modification_table, addition_list); + update_reimported_diff_data_for_node(current_edited_scene, original_node, original_node, modification_table, addition_list); // Disconnect all relevant connections, all connections from and persistent connections to. for (const KeyValue<NodePath, ModificationNodeEntry> &modification_table_entry : modification_table) { @@ -5762,7 +5904,7 @@ void EditorNode::reload_instances_with_path_in_edited_scenes(const String &p_ins // be properly updated. for (String path : required_load_paths) { if (!local_scene_cache.find(path)) { - current_packed_scene = ResourceLoader::load(path, "", ResourceFormatLoader::CACHE_MODE_REPLACE, &err); + current_packed_scene = ResourceLoader::load(path, "", ResourceFormatLoader::CACHE_MODE_REPLACE_DEEP, &err); local_scene_cache[path] = current_packed_scene; } else { current_packed_scene = local_scene_cache[path]; @@ -5780,6 +5922,9 @@ void EditorNode::reload_instances_with_path_in_edited_scenes(const String &p_ins ERR_FAIL_NULL(instantiated_node); + // For clear instance state for path recaching. + instantiated_node->set_scene_instance_state(Ref<SceneState>()); + bool original_node_is_displayed_folded = original_node->is_displayed_folded(); bool original_node_scene_instance_load_placeholder = original_node->get_scene_instance_load_placeholder(); @@ -5885,77 +6030,30 @@ void EditorNode::reload_instances_with_path_in_edited_scenes(const String &p_ins NodePath new_current_path = E.key; Node *modifiable_node = instantiated_node->get_node_or_null(new_current_path); - if (modifiable_node) { - // Get properties for this node. - List<PropertyInfo> pinfo; - modifiable_node->get_property_list(&pinfo); - - // Get names of all valid property names (TODO: make this more efficient). - List<String> property_names; - for (const PropertyInfo &E2 : pinfo) { - if (E2.usage & PROPERTY_USAGE_STORAGE) { - property_names.push_back(E2.name); - } - } - - // Restore the modified properties for this node. - for (const KeyValue<StringName, Variant> &E2 : E.value.property_table) { - if (property_names.find(E2.key)) { - modifiable_node->set(E2.key, E2.value); - } - } - // Restore the connections to other nodes. - for (const ConnectionWithNodePath &E2 : E.value.connections_to) { - Connection conn = E2.connection; - - // Get the node the callable is targeting. - Node *target_node = cast_to<Node>(conn.callable.get_object()); - - // If the callable object no longer exists or is marked for deletion, - // attempt to reaccquire the closest match by using the node path - // we saved earlier. - if (!target_node || !target_node->is_queued_for_deletion()) { - target_node = modifiable_node->get_node_or_null(E2.node_path); - } - - if (target_node) { - // Reconstruct the callable. - Callable new_callable = Callable(target_node, conn.callable.get_method()); - - if (!modifiable_node->is_connected(conn.signal.get_name(), new_callable)) { - ERR_FAIL_COND(modifiable_node->connect(conn.signal.get_name(), new_callable, conn.flags) != OK); - } - } - } - - // Restore the connections from other nodes. - for (const Connection &E2 : E.value.connections_from) { - Connection conn = E2; - - bool valid = modifiable_node->has_method(conn.callable.get_method()) || Ref<Script>(modifiable_node->get_script()).is_null() || Ref<Script>(modifiable_node->get_script())->has_method(conn.callable.get_method()); - ERR_CONTINUE_MSG(!valid, vformat("Attempt to connect signal '%s.%s' to nonexistent method '%s.%s'.", conn.signal.get_object()->get_class(), conn.signal.get_name(), conn.callable.get_object()->get_class(), conn.callable.get_method())); - - // Get the object which the signal is connected from. - Object *source_object = conn.signal.get_object(); + update_node_from_node_modification_entry(modifiable_node, E.value); + } + // Add the newly instantiated node to the edited scene's replaced node list. + replaced_nodes.push_back(instantiated_node); + } - if (source_object) { - ERR_FAIL_COND(source_object->connect(conn.signal.get_name(), Callable(modifiable_node, conn.callable.get_method()), conn.flags) != OK); - } - } + // Attempt to restore the modified properties and signals for the instantitated node and all its owned children. + for (KeyValue<NodePath, ModificationNodeEntry> &E : edited_scene_global_modification_table) { + NodePath new_current_path = E.key; + Node *modifiable_node = current_edited_scene->get_node_or_null(new_current_path); - // Re-add the groups. - for (const Node::GroupInfo &E2 : E.value.groups) { - modifiable_node->add_to_group(E2.name, E2.persistent); - } - } + if (modifiable_node) { + update_node_from_node_modification_entry(modifiable_node, E.value); } } // Cleanup the history of the changes. editor_history.cleanup_history(); - - _notify_scene_updated(current_edited_scene); } + + // For the whole editor, call the _notify_nodes_scene_reimported with a list of replaced nodes. + // To inform anything that depends on them that they should update as appropriate. + _notify_nodes_scene_reimported(this, replaced_nodes); + edited_scene_map.clear(); } editor_data.set_edited_scene(original_edited_scene_idx); @@ -6163,7 +6261,7 @@ static Node *_resource_get_edited_scene() { } void EditorNode::_print_handler(void *p_this, const String &p_string, bool p_error, bool p_rich) { - callable_mp_static(&EditorNode::_print_handler_impl).bind(p_string, p_error, p_rich).call_deferred(); + callable_mp_static(&EditorNode::_print_handler_impl).call_deferred(p_string, p_error, p_rich); } void EditorNode::_print_handler_impl(const String &p_string, bool p_error, bool p_rich) { @@ -6297,6 +6395,14 @@ EditorNode::EditorNode() { EditorSettings::create(); } + ED_SHORTCUT("editor/lock_selected_nodes", TTR("Lock Selected Node(s)"), KeyModifierMask::CMD_OR_CTRL | Key::L); + ED_SHORTCUT("editor/unlock_selected_nodes", TTR("Unlock Selected Node(s)"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::L); + ED_SHORTCUT("editor/group_selected_nodes", TTR("Group Selected Node(s)"), KeyModifierMask::CMD_OR_CTRL | Key::G); + ED_SHORTCUT("editor/ungroup_selected_nodes", TTR("Ungroup Selected Node(s)"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::G); + + // Used in the GPUParticles/CPUParticles 2D/3D editor plugins. + ED_SHORTCUT("particles/restart_emission", TTR("Restart Emission"), KeyModifierMask::CTRL | Key::R); + FileAccess::set_backup_save(EDITOR_GET("filesystem/on_save/safe_save_on_backup_then_rename")); _update_vsync_mode(); @@ -7220,6 +7326,8 @@ EditorNode::EditorNode() { disk_changed = memnew(ConfirmationDialog); { + disk_changed->set_title(TTR("Files have been modified on disk")); + VBoxContainer *vbc = memnew(VBoxContainer); disk_changed->add_child(vbc); @@ -7233,9 +7341,9 @@ EditorNode::EditorNode() { disk_changed->connect("confirmed", callable_mp(this, &EditorNode::_reload_modified_scenes)); disk_changed->connect("confirmed", callable_mp(this, &EditorNode::_reload_project_settings)); - disk_changed->set_ok_button_text(TTR("Reload")); + disk_changed->set_ok_button_text(TTR("Discard local changes and reload")); - disk_changed->add_button(TTR("Resave"), !DisplayServer::get_singleton()->get_swap_cancel_ok(), "resave"); + disk_changed->add_button(TTR("Keep local changes and overwrite"), !DisplayServer::get_singleton()->get_swap_cancel_ok(), "resave"); disk_changed->connect("custom_action", callable_mp(this, &EditorNode::_resave_scenes)); } diff --git a/editor/editor_node.h b/editor/editor_node.h index 6b3359eaee..5d7bd5b4f8 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -140,6 +140,17 @@ public: SCENE_NAME_CASING_KEBAB_CASE, }; + enum ActionOnPlay { + ACTION_ON_PLAY_DO_NOTHING, + ACTION_ON_PLAY_OPEN_OUTPUT, + ACTION_ON_PLAY_OPEN_DEBUGGER, + }; + + enum ActionOnStop { + ACTION_ON_STOP_DO_NOTHING, + ACTION_ON_STOP_CLOSE_BUTTOM_PANEL, + }; + struct ExecuteThreadArgs { String path; List<String> args; @@ -662,7 +673,7 @@ private: void _begin_first_scan(); - void _notify_scene_updated(Node *p_node); + void _notify_nodes_scene_reimported(Node *p_node, Array p_reimported_nodes); protected: friend class FileSystemDock; @@ -779,7 +790,7 @@ public: Error load_scene(const String &p_scene, bool p_ignore_broken_deps = false, bool p_set_inherited = false, bool p_clear_errors = true, bool p_force_open_imported = false, bool p_silent_change_tab = false); Error load_resource(const String &p_resource, bool p_ignore_broken_deps = false); - HashMap<StringName, Variant> get_modified_properties_for_node(Node *p_node); + HashMap<StringName, Variant> get_modified_properties_for_node(Node *p_node, bool p_node_references_only); struct AdditiveNodeEntry { Node *node = nullptr; @@ -806,11 +817,18 @@ public: }; void update_ownership_table_for_addition_node_ancestors(Node *p_current_node, HashMap<Node *, Node *> &p_ownership_table); + void update_node_from_node_modification_entry(Node *p_node, ModificationNodeEntry &p_node_modification); - void update_diff_data_for_node( - Node *p_edited_scene, + void update_node_reference_modification_table_for_node( Node *p_root, Node *p_node, + List<Node *> p_excluded_nodes, + HashMap<NodePath, ModificationNodeEntry> &p_modification_table); + + void update_reimported_diff_data_for_node( + Node *p_edited_scene, + Node *p_reimported_root, + Node *p_node, HashMap<NodePath, ModificationNodeEntry> &p_modification_table, List<AdditiveNodeEntry> &p_addition_list); diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp index 49b30bd06e..a455258a6d 100644 --- a/editor/editor_properties.cpp +++ b/editor/editor_properties.cpp @@ -2645,7 +2645,7 @@ void EditorPropertyColor::_color_changed(const Color &p_color) { } void EditorPropertyColor::_popup_closed() { - get_edited_object()->set(get_edited_property(), last_color); + get_edited_object()->set(get_edited_property(), was_checked ? Variant(last_color) : Variant()); if (!picker->get_pick_color().is_equal_approx(last_color)) { emit_changed(get_edited_property(), picker->get_pick_color(), "", false); } @@ -2653,6 +2653,7 @@ void EditorPropertyColor::_popup_closed() { void EditorPropertyColor::_picker_opening() { last_color = picker->get_pick_color(); + was_checked = !is_checkable() || is_checked(); } void EditorPropertyColor::_notification(int p_what) { @@ -2921,8 +2922,7 @@ void EditorPropertyNodePath::update_property() { assign->set_icon(EditorNode::get_singleton()->get_object_icon(target_node, "Node")); } -void EditorPropertyNodePath::setup(const NodePath &p_base_hint, const Vector<StringName> &p_valid_types, bool p_use_path_from_scene_root, bool p_editing_node) { - base_hint = p_base_hint; +void EditorPropertyNodePath::setup(const Vector<StringName> &p_valid_types, bool p_use_path_from_scene_root, bool p_editing_node) { valid_types = p_valid_types; editing_node = p_editing_node; use_path_from_scene_root = p_use_path_from_scene_root; @@ -2942,10 +2942,6 @@ void EditorPropertyNodePath::_notification(int p_what) { } Node *EditorPropertyNodePath::get_base_node() { - if (!base_hint.is_empty() && get_tree()->get_root()->has_node(base_hint)) { - return get_tree()->get_root()->get_node(base_hint); - } - Node *base_node = Object::cast_to<Node>(get_edited_object()); if (!base_node) { @@ -3217,7 +3213,7 @@ void EditorPropertyResource::_open_editor_pressed() { Ref<Resource> res = get_edited_property_value(); if (res.is_valid()) { // May clear the editor so do it deferred. - callable_mp(EditorNode::get_singleton(), &EditorNode::edit_item).bind(res.ptr(), this).call_deferred(); + callable_mp(EditorNode::get_singleton(), &EditorNode::edit_item).call_deferred(res.ptr(), this); } } @@ -3799,7 +3795,7 @@ EditorProperty *EditorInspectorDefaultPlugin::get_editor_for_property(Object *p_ if (p_hint == PROPERTY_HINT_NODE_PATH_VALID_TYPES && !p_hint_text.is_empty()) { Vector<String> types = p_hint_text.split(",", false); Vector<StringName> sn = Variant(types); //convert via variant - editor->setup(NodePath(), sn, (p_usage & PROPERTY_USAGE_NODE_PATH_FROM_SCENE_ROOT)); + editor->setup(sn, (p_usage & PROPERTY_USAGE_NODE_PATH_FROM_SCENE_ROOT)); } return editor; @@ -3813,7 +3809,7 @@ EditorProperty *EditorInspectorDefaultPlugin::get_editor_for_property(Object *p_ EditorPropertyNodePath *editor = memnew(EditorPropertyNodePath); Vector<String> types = p_hint_text.split(",", false); Vector<StringName> sn = Variant(types); //convert via variant - editor->setup(NodePath(), sn, false, true); + editor->setup(sn, false, true); return editor; } else { EditorPropertyResource *editor = memnew(EditorPropertyResource); diff --git a/editor/editor_properties.h b/editor/editor_properties.h index d16c80bfc4..e9e788ab7b 100644 --- a/editor/editor_properties.h +++ b/editor/editor_properties.h @@ -625,6 +625,7 @@ class EditorPropertyColor : public EditorProperty { Color last_color; bool live_changes_enabled = true; + bool was_checked = false; protected: virtual void _set_read_only(bool p_read_only) override; @@ -652,7 +653,6 @@ class EditorPropertyNodePath : public EditorProperty { LineEdit *edit = nullptr; SceneTreeDialog *scene_tree = nullptr; - NodePath base_hint; bool use_path_from_scene_root = false; bool editing_node = false; @@ -678,7 +678,7 @@ protected: public: virtual void update_property() override; - void setup(const NodePath &p_base_hint, const Vector<StringName> &p_valid_types, bool p_use_path_from_scene_root = true, bool p_editing_node = false); + void setup(const Vector<StringName> &p_valid_types, bool p_use_path_from_scene_root = true, bool p_editing_node = false); EditorPropertyNodePath(); }; diff --git a/editor/editor_properties_array_dict.cpp b/editor/editor_properties_array_dict.cpp index ca1070d7f3..b5f34ecb3d 100644 --- a/editor/editor_properties_array_dict.cpp +++ b/editor/editor_properties_array_dict.cpp @@ -235,6 +235,9 @@ void EditorPropertyArray::_property_changed(const String &p_property, Variant p_ Variant array = object->get_array().duplicate(); array.set(index, p_value); emit_changed(get_edited_property(), array, p_name, p_changing); + if (p_changing) { + object->set_array(array); + } } void EditorPropertyArray::_change_type(Object *p_button, int p_slot_index) { @@ -960,6 +963,7 @@ void EditorPropertyDictionary::update_property() { memdelete(container); button_add_item = nullptr; container = nullptr; + add_panel = nullptr; slots.clear(); } return; diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index d7bc3502ce..1e292a3a7b 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -651,6 +651,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { // Behavior: Files _initial_set("text_editor/behavior/files/trim_trailing_whitespace_on_save", false); + _initial_set("text_editor/behavior/files/trim_final_newlines_on_save", true); _initial_set("text_editor/behavior/files/autosave_interval_secs", 0); _initial_set("text_editor/behavior/files/restore_scripts_on_load", true); _initial_set("text_editor/behavior/files/convert_indent_on_save", true); @@ -818,11 +819,13 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { // Auto save _initial_set("run/auto_save/save_before_running", true); + // Bottom panel + EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "run/bottom_panel/action_on_play", EditorNode::ACTION_ON_PLAY_OPEN_OUTPUT, "Do Nothing,Open Output,Open Debugger") + EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "run/bottom_panel/action_on_stop", EditorNode::ACTION_ON_STOP_DO_NOTHING, "Do Nothing,Close Bottom Panel") + // Output EDITOR_SETTING(Variant::INT, PROPERTY_HINT_RANGE, "run/output/font_size", 13, "8,48,1") _initial_set("run/output/always_clear_output_on_play", true); - _initial_set("run/output/always_open_output_on_play", true); - _initial_set("run/output/always_close_output_on_stop", false); EDITOR_SETTING(Variant::INT, PROPERTY_HINT_RANGE, "run/output/max_lines", 10000, "100,100000,1") diff --git a/editor/export/editor_export_platform.cpp b/editor/export/editor_export_platform.cpp index aa44189782..5a95b553e9 100644 --- a/editor/export/editor_export_platform.cpp +++ b/editor/export/editor_export_platform.cpp @@ -797,6 +797,10 @@ String EditorExportPlatform::_export_customize(const String &p_path, LocalVector if (!customize_scenes_plugins.is_empty()) { for (Ref<EditorExportPlugin> &plugin : customize_scenes_plugins) { Node *customized = plugin->_customize_scene(node, p_path); + if (plugin->skipped) { + plugin->_clear(); + return String(); + } if (customized != nullptr) { node = customized; modified = true; @@ -830,6 +834,10 @@ String EditorExportPlatform::_export_customize(const String &p_path, LocalVector if (!customize_resources_plugins.is_empty()) { for (Ref<EditorExportPlugin> &plugin : customize_resources_plugins) { Ref<Resource> new_res = plugin->_customize_resource(res, p_path); + if (plugin->skipped) { + plugin->_clear(); + return String(); + } if (new_res.is_valid()) { modified = true; if (new_res != res) { @@ -1135,6 +1143,10 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> & // Before doing this, try to see if it can be customized. String export_path = _export_customize(path, customize_resources_plugins, customize_scenes_plugins, export_cache, export_base_path, false); + if (export_path.is_empty()) { + // Skipped from plugin. + continue; + } if (export_path != path) { // It was actually customized. diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp index 5f311ae445..c07667ac12 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -3279,7 +3279,7 @@ void FileSystemDock::_file_and_folders_fill_popup(PopupMenu *p_popup, const Vect if (p_paths.size() == 1) { const String &fpath = p_paths[0]; - bool added_separator = false; + [[maybe_unused]] bool added_separator = false; if (favorites_list.has(fpath)) { TreeItem *favorites_item = tree->get_root()->get_first_child(); @@ -3310,15 +3310,15 @@ void FileSystemDock::_file_and_folders_fill_popup(PopupMenu *p_popup, const Vect // Opening the system file manager is not supported on the Android and web editors. const bool is_directory = fpath.ends_with("/"); - p_popup->add_icon_shortcut(get_editor_theme_icon(SNAME("Filesystem")), ED_GET_SHORTCUT("filesystem_dock/show_in_explorer"), FILE_SHOW_IN_EXPLORER); - p_popup->set_item_text(p_popup->get_item_index(FILE_SHOW_IN_EXPLORER), is_directory ? TTR("Open in File Manager") : TTR("Show in File Manager")); + p_popup->add_icon_shortcut(get_editor_theme_icon(SNAME("Terminal")), ED_GET_SHORTCUT("filesystem_dock/open_in_terminal"), FILE_OPEN_IN_TERMINAL); + p_popup->set_item_text(p_popup->get_item_index(FILE_OPEN_IN_TERMINAL), is_directory ? TTR("Open in Terminal") : TTR("Open Containing Folder in Terminal")); if (!is_directory) { p_popup->add_icon_shortcut(get_editor_theme_icon(SNAME("ExternalLink")), ED_GET_SHORTCUT("filesystem_dock/open_in_external_program"), FILE_OPEN_EXTERNAL); } - p_popup->add_icon_shortcut(get_editor_theme_icon(SNAME("Terminal")), ED_GET_SHORTCUT("filesystem_dock/open_in_terminal"), FILE_OPEN_IN_TERMINAL); - p_popup->set_item_text(p_popup->get_item_index(FILE_OPEN_IN_TERMINAL), is_directory ? TTR("Open in Terminal") : TTR("Open Containing Folder in Terminal")); + p_popup->add_icon_shortcut(get_editor_theme_icon(SNAME("Filesystem")), ED_GET_SHORTCUT("filesystem_dock/show_in_explorer"), FILE_SHOW_IN_EXPLORER); + p_popup->set_item_text(p_popup->get_item_index(FILE_SHOW_IN_EXPLORER), is_directory ? TTR("Open in File Manager") : TTR("Show in File Manager")); #endif current_path = fpath; @@ -3362,8 +3362,8 @@ void FileSystemDock::_tree_empty_click(const Vector2 &p_pos, MouseButton p_butto #if !defined(ANDROID_ENABLED) && !defined(WEB_ENABLED) // Opening the system file manager is not supported on the Android and web editors. tree_popup->add_separator(); - tree_popup->add_icon_shortcut(get_editor_theme_icon(SNAME("Filesystem")), ED_GET_SHORTCUT("filesystem_dock/show_in_explorer"), FILE_SHOW_IN_EXPLORER); tree_popup->add_icon_shortcut(get_editor_theme_icon(SNAME("Terminal")), ED_GET_SHORTCUT("filesystem_dock/open_in_terminal"), FILE_OPEN_IN_TERMINAL); + tree_popup->add_icon_shortcut(get_editor_theme_icon(SNAME("Filesystem")), ED_GET_SHORTCUT("filesystem_dock/show_in_explorer"), FILE_SHOW_IN_EXPLORER); #endif tree_popup->set_position(tree->get_screen_position() + p_pos); @@ -3425,8 +3425,8 @@ void FileSystemDock::_file_list_empty_clicked(const Vector2 &p_pos, MouseButton file_list_popup->add_icon_item(get_editor_theme_icon(SNAME("Object")), TTR("New Resource..."), FILE_NEW_RESOURCE); file_list_popup->add_icon_item(get_editor_theme_icon(SNAME("TextFile")), TTR("New TextFile..."), FILE_NEW_TEXTFILE); file_list_popup->add_separator(); - file_list_popup->add_icon_shortcut(get_editor_theme_icon(SNAME("Filesystem")), ED_GET_SHORTCUT("filesystem_dock/show_in_explorer"), FILE_SHOW_IN_EXPLORER); file_list_popup->add_icon_shortcut(get_editor_theme_icon(SNAME("Terminal")), ED_GET_SHORTCUT("filesystem_dock/open_in_terminal"), FILE_OPEN_IN_TERMINAL); + file_list_popup->add_icon_shortcut(get_editor_theme_icon(SNAME("Filesystem")), ED_GET_SHORTCUT("filesystem_dock/show_in_explorer"), FILE_SHOW_IN_EXPLORER); file_list_popup->set_position(files->get_screen_position() + p_pos); file_list_popup->reset_size(); diff --git a/editor/gui/editor_toaster.cpp b/editor/gui/editor_toaster.cpp index 7aa5335b77..df6c494392 100644 --- a/editor/gui/editor_toaster.cpp +++ b/editor/gui/editor_toaster.cpp @@ -149,7 +149,7 @@ void EditorToaster::_notification(int p_what) { void EditorToaster::_error_handler(void *p_self, const char *p_func, const char *p_file, int p_line, const char *p_error, const char *p_errorexp, bool p_editor_notify, ErrorHandlerType p_type) { // This may be called from a thread. Since we will deal with non-thread-safe elements, // we have to put it in the queue for safety. - callable_mp_static(&EditorToaster::_error_handler_impl).bind(String::utf8(p_file), p_line, String::utf8(p_error), String::utf8(p_errorexp), p_editor_notify, p_type).call_deferred(); + callable_mp_static(&EditorToaster::_error_handler_impl).call_deferred(String::utf8(p_file), p_line, String::utf8(p_error), String::utf8(p_errorexp), p_editor_notify, p_type); } void EditorToaster::_error_handler_impl(const String &p_file, int p_line, const String &p_error, const String &p_errorexp, bool p_editor_notify, int p_type) { diff --git a/editor/gui/scene_tree_editor.cpp b/editor/gui/scene_tree_editor.cpp index fa0dad41dc..ddf22c46e6 100644 --- a/editor/gui/scene_tree_editor.cpp +++ b/editor/gui/scene_tree_editor.cpp @@ -614,9 +614,9 @@ void SceneTreeEditor::_update_tree(bool p_scroll_to_selected) { updating_tree = true; tree->clear(); + last_hash = hash_djb2_one_64(0); if (get_scene_node()) { _add_nodes(get_scene_node(), nullptr); - last_hash = hash_djb2_one_64(0); _compute_hash(get_scene_node(), last_hash); } updating_tree = false; diff --git a/editor/icons/SCsub b/editor/icons/SCsub index d2d752cff4..0d9ac43c46 100644 --- a/editor/icons/SCsub +++ b/editor/icons/SCsub @@ -3,8 +3,8 @@ Import("env") import os -import editor_icons_builders +import editor_icons_builders env["BUILDERS"]["MakeEditorIconsBuilder"] = Builder( action=env.Run(editor_icons_builders.make_editor_icons_action), diff --git a/editor/import/3d/resource_importer_scene.cpp b/editor/import/3d/resource_importer_scene.cpp index 070c44419a..c0d38af26a 100644 --- a/editor/import/3d/resource_importer_scene.cpp +++ b/editor/import/3d/resource_importer_scene.cpp @@ -279,7 +279,7 @@ bool ResourceImporterScene::get_option_visibility(const String &p_path, const St } } - if (animation_importer && (p_option.begins_with("nodes/") || p_option.begins_with("meshes/") || p_option.begins_with("skins/"))) { + if (animation_importer && (p_option == "nodes/root_type" || p_option == "nodes/root_name" || p_option.begins_with("meshes/") || p_option.begins_with("skins/"))) { return false; // Nothing to do here for animations. } @@ -2329,6 +2329,7 @@ void ResourceImporterScene::get_import_options(const String &p_path, List<Import } script_ext_hint += "*." + E; } + bool trimming_defaults_on = p_path.get_extension().to_lower() == "fbx"; r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "nodes/apply_root_scale"), true)); r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "nodes/root_scale", PROPERTY_HINT_RANGE, "0.001,1000,0.001"), 1.0)); @@ -2342,7 +2343,7 @@ void ResourceImporterScene::get_import_options(const String &p_path, List<Import r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "skins/use_named_skins"), true)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/import"), true)); r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "animation/fps", PROPERTY_HINT_RANGE, "1,120,1"), 30)); - r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/trimming"), false)); + r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/trimming"), trimming_defaults_on)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/remove_immutable_tracks"), true)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/import_rest_as_RESET"), false)); r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "import_script/path", PROPERTY_HINT_FILE, script_ext_hint), "")); diff --git a/editor/import/resource_importer_shader_file.cpp b/editor/import/resource_importer_shader_file.cpp index bde2e3d0bf..6b20a8c9d5 100644 --- a/editor/import/resource_importer_shader_file.cpp +++ b/editor/import/resource_importer_shader_file.cpp @@ -106,7 +106,7 @@ Error ResourceImporterShaderFile::import(const String &p_source_file, const Stri if (err != OK) { if (!ShaderFileEditor::singleton->is_visible_in_tree()) { - callable_mp_static(&EditorNode::add_io_error).bind(vformat(TTR("Error importing GLSL shader file: '%s'. Open the file in the filesystem dock in order to see the reason."), p_source_file)).call_deferred(); + callable_mp_static(&EditorNode::add_io_error).call_deferred(vformat(TTR("Error importing GLSL shader file: '%s'. Open the file in the filesystem dock in order to see the reason."), p_source_file)); } } diff --git a/editor/import/resource_importer_texture.cpp b/editor/import/resource_importer_texture.cpp index 8cf104725a..487b8fc175 100644 --- a/editor/import/resource_importer_texture.cpp +++ b/editor/import/resource_importer_texture.cpp @@ -269,9 +269,9 @@ void ResourceImporterTexture::save_to_ctex_format(Ref<FileAccess> f, const Ref<I for (int i = 0; i < p_image->get_mipmap_count() + 1; i++) { Vector<uint8_t> data; if (use_webp) { - data = Image::webp_lossless_packer(p_image->get_image_from_mipmap(i)); + data = Image::webp_lossless_packer(i ? p_image->get_image_from_mipmap(i) : p_image); } else { - data = Image::png_packer(p_image->get_image_from_mipmap(i)); + data = Image::png_packer(i ? p_image->get_image_from_mipmap(i) : p_image); } int data_len = data.size(); f->store_32(data_len); @@ -289,7 +289,7 @@ void ResourceImporterTexture::save_to_ctex_format(Ref<FileAccess> f, const Ref<I f->store_32(p_image->get_format()); for (int i = 0; i < p_image->get_mipmap_count() + 1; i++) { - Vector<uint8_t> data = Image::webp_lossy_packer(p_image->get_image_from_mipmap(i), p_lossy_quality); + Vector<uint8_t> data = Image::webp_lossy_packer(i ? p_image->get_image_from_mipmap(i) : p_image, p_lossy_quality); int data_len = data.size(); f->store_32(data_len); diff --git a/editor/import_dock.cpp b/editor/import_dock.cpp index fbe76e1d5c..2d87e6592f 100644 --- a/editor/import_dock.cpp +++ b/editor/import_dock.cpp @@ -188,10 +188,20 @@ void ImportDock::_update_options(const String &p_path, const Ref<ConfigFile> &p_ params->checked.clear(); params->base_options_path = p_path; + HashMap<StringName, Variant> import_options; + List<String> section_keys; + p_config->get_section_keys("params", §ion_keys); + for (const String §ion_key : section_keys) { + import_options[section_key] = p_config->get_value("params", section_key); + } + if (params->importer.is_valid()) { + params->importer->handle_compatibility_options(import_options); + } + for (const ResourceImporter::ImportOption &E : options) { params->properties.push_back(E.option); - if (p_config.is_valid() && p_config->has_section_key("params", E.option.name)) { - params->values[E.option.name] = p_config->get_value("params", E.option.name); + if (p_config.is_valid() && import_options.has(E.option.name)) { + params->values[E.option.name] = import_options[E.option.name]; } else { params->values[E.option.name] = E.default_value; } diff --git a/editor/input_event_configuration_dialog.cpp b/editor/input_event_configuration_dialog.cpp index 865729c7c3..2ecce2f739 100644 --- a/editor/input_event_configuration_dialog.cpp +++ b/editor/input_event_configuration_dialog.cpp @@ -446,6 +446,11 @@ void InputEventConfigurationDialog::_key_location_selected(int p_location) { _set_event(k, original_event); } +void InputEventConfigurationDialog::_input_list_item_activated() { + TreeItem *selected = input_list_tree->get_selected(); + selected->set_collapsed(!selected->is_collapsed()); +} + void InputEventConfigurationDialog::_input_list_item_selected() { TreeItem *selected = input_list_tree->get_selected(); @@ -670,6 +675,7 @@ InputEventConfigurationDialog::InputEventConfigurationDialog() { input_list_tree = memnew(Tree); input_list_tree->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED); input_list_tree->set_custom_minimum_size(Size2(0, 100 * EDSCALE)); // Min height for tree + input_list_tree->connect("item_activated", callable_mp(this, &InputEventConfigurationDialog::_input_list_item_activated)); input_list_tree->connect("item_selected", callable_mp(this, &InputEventConfigurationDialog::_input_list_item_selected)); input_list_tree->set_v_size_flags(Control::SIZE_EXPAND_FILL); manual_vbox->add_child(input_list_tree); diff --git a/editor/input_event_configuration_dialog.h b/editor/input_event_configuration_dialog.h index 1d2cc8ba36..b27f25a5b7 100644 --- a/editor/input_event_configuration_dialog.h +++ b/editor/input_event_configuration_dialog.h @@ -107,6 +107,7 @@ private: void _search_term_updated(const String &p_term); void _update_input_list(); + void _input_list_item_activated(); void _input_list_item_selected(); void _mod_toggled(bool p_checked, int p_index); diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp index 1366a38bec..1cf11f2a43 100644 --- a/editor/plugins/animation_player_editor_plugin.cpp +++ b/editor/plugins/animation_player_editor_plugin.cpp @@ -547,13 +547,18 @@ void AnimationPlayerEditor::_animation_name_edited() { } break; case TOOL_NEW_ANIM: { - String current = animation->get_item_text(animation->get_selected()); - Ref<Animation> current_anim = player->get_animation(current); Ref<Animation> new_anim = Ref<Animation>(memnew(Animation)); new_anim->set_name(new_name); - if (current_anim.is_valid()) { - new_anim->set_step(current_anim->get_step()); + + if (animation->get_item_count() > 0) { + String current = animation->get_item_text(animation->get_selected()); + Ref<Animation> current_anim = player->get_animation(current); + + if (current_anim.is_valid()) { + new_anim->set_step(current_anim->get_step()); + } } + String library_name; Ref<AnimationLibrary> al; library_name = library->get_item_metadata(library->get_selected()); @@ -881,6 +886,7 @@ void AnimationPlayerEditor::_update_player() { tool_anim->set_disabled(player == nullptr); pin->set_disabled(player == nullptr); + _set_controls_disabled(player == nullptr); if (!player) { AnimationPlayerEditor::get_singleton()->get_track_editor()->update_keying(); @@ -931,17 +937,6 @@ void AnimationPlayerEditor::_update_player() { ITEM_CHECK_DISABLED(TOOL_NEW_ANIM); #undef ITEM_CHECK_DISABLED - stop->set_disabled(no_anims_found); - play->set_disabled(no_anims_found); - play_bw->set_disabled(no_anims_found); - play_bw_from->set_disabled(no_anims_found); - play_from->set_disabled(no_anims_found); - frame->set_editable(!no_anims_found); - animation->set_disabled(no_anims_found); - autoplay->set_disabled(no_anims_found); - onion_toggle->set_disabled(no_anims_found); - onion_skinning->set_disabled(no_anims_found); - _update_animation_list_icons(); updating = false; @@ -958,7 +953,9 @@ void AnimationPlayerEditor::_update_player() { _animation_selected(0); } - if (!no_anims_found) { + if (no_anims_found) { + _set_controls_disabled(true); + } else { String current = animation->get_item_text(animation->get_selected()); Ref<Animation> anim = player->get_animation(current); @@ -974,6 +971,20 @@ void AnimationPlayerEditor::_update_player() { _update_animation(); } +void AnimationPlayerEditor::_set_controls_disabled(bool p_disabled) { + frame->set_editable(!p_disabled); + + stop->set_disabled(p_disabled); + play->set_disabled(p_disabled); + play_bw->set_disabled(p_disabled); + play_bw_from->set_disabled(p_disabled); + play_from->set_disabled(p_disabled); + animation->set_disabled(p_disabled); + autoplay->set_disabled(p_disabled); + onion_toggle->set_disabled(p_disabled); + onion_skinning->set_disabled(p_disabled); +} + void AnimationPlayerEditor::_update_animation_list_icons() { for (int i = 0; i < animation->get_item_count(); i++) { String anim_name = animation->get_item_text(i); @@ -1076,9 +1087,6 @@ void AnimationPlayerEditor::_ensure_dummy_player() { } } - // Make some options disabled. - onion_toggle->set_disabled(dummy_exists); - onion_skinning->set_disabled(dummy_exists); int selected = animation->get_selected(); autoplay->set_disabled(selected != -1 ? (animation->get_item_text(selected).is_empty() ? true : dummy_exists) : true); @@ -1711,7 +1719,7 @@ void AnimationPlayerEditor::_prepare_onion_layers_2_step_prepare(int p_step_offs OS::get_singleton()->get_main_loop()->process(0); // This is the key: process the frame and let all callbacks/updates/notifications happen // so everything (transforms, skeletons, etc.) is up-to-date visually. - callable_mp(this, &AnimationPlayerEditor::_prepare_onion_layers_2_step_capture).bind(p_step_offset, p_capture_idx).call_deferred(); + callable_mp(this, &AnimationPlayerEditor::_prepare_onion_layers_2_step_capture).call_deferred(p_step_offset, p_capture_idx); return; } else { next_capture_idx++; diff --git a/editor/plugins/animation_player_editor_plugin.h b/editor/plugins/animation_player_editor_plugin.h index 70b31759fc..e624522566 100644 --- a/editor/plugins/animation_player_editor_plugin.h +++ b/editor/plugins/animation_player_editor_plugin.h @@ -206,6 +206,7 @@ class AnimationPlayerEditor : public VBoxContainer { void _current_animation_changed(const String &p_name); void _update_animation(); void _update_player(); + void _set_controls_disabled(bool p_disabled); void _update_animation_list_icons(); void _update_name_dialog_library_dropdown(); void _blend_edited(); diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index 2d47887027..8b44d6b486 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -5419,7 +5419,7 @@ CanvasItemEditor::CanvasItemEditor() { lock_button->connect(SceneStringName(pressed), callable_mp(this, &CanvasItemEditor::_popup_callback).bind(LOCK_SELECTED)); lock_button->set_tooltip_text(TTR("Lock selected node, preventing selection and movement.")); // Define the shortcut globally (without a context) so that it works if the Scene tree dock is currently focused. - lock_button->set_shortcut(ED_SHORTCUT("editor/lock_selected_nodes", TTR("Lock Selected Node(s)"), KeyModifierMask::CMD_OR_CTRL | Key::L)); + lock_button->set_shortcut(ED_GET_SHORTCUT("editor/lock_selected_nodes")); unlock_button = memnew(Button); unlock_button->set_theme_type_variation("FlatButton"); @@ -5427,7 +5427,7 @@ CanvasItemEditor::CanvasItemEditor() { unlock_button->connect(SceneStringName(pressed), callable_mp(this, &CanvasItemEditor::_popup_callback).bind(UNLOCK_SELECTED)); unlock_button->set_tooltip_text(TTR("Unlock selected node, allowing selection and movement.")); // Define the shortcut globally (without a context) so that it works if the Scene tree dock is currently focused. - unlock_button->set_shortcut(ED_SHORTCUT("editor/unlock_selected_nodes", TTR("Unlock Selected Node(s)"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::L)); + unlock_button->set_shortcut(ED_GET_SHORTCUT("editor/unlock_selected_nodes")); group_button = memnew(Button); group_button->set_theme_type_variation("FlatButton"); @@ -5435,7 +5435,7 @@ CanvasItemEditor::CanvasItemEditor() { group_button->connect(SceneStringName(pressed), callable_mp(this, &CanvasItemEditor::_popup_callback).bind(GROUP_SELECTED)); group_button->set_tooltip_text(TTR("Groups the selected node with its children. This causes the parent to be selected when any child node is clicked in 2D and 3D view.")); // Define the shortcut globally (without a context) so that it works if the Scene tree dock is currently focused. - group_button->set_shortcut(ED_SHORTCUT("editor/group_selected_nodes", TTR("Group Selected Node(s)"), KeyModifierMask::CMD_OR_CTRL | Key::G)); + group_button->set_shortcut(ED_GET_SHORTCUT("editor/group_selected_nodes")); ungroup_button = memnew(Button); ungroup_button->set_theme_type_variation("FlatButton"); @@ -5443,7 +5443,7 @@ CanvasItemEditor::CanvasItemEditor() { ungroup_button->connect(SceneStringName(pressed), callable_mp(this, &CanvasItemEditor::_popup_callback).bind(UNGROUP_SELECTED)); ungroup_button->set_tooltip_text(TTR("Ungroups the selected node from its children. Child nodes will be individual items in 2D and 3D view.")); // Define the shortcut globally (without a context) so that it works if the Scene tree dock is currently focused. - ungroup_button->set_shortcut(ED_SHORTCUT("editor/ungroup_selected_nodes", TTR("Ungroup Selected Node(s)"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::G)); + ungroup_button->set_shortcut(ED_GET_SHORTCUT("editor/ungroup_selected_nodes")); main_menu_hbox->add_child(memnew(VSeparator)); @@ -5638,7 +5638,7 @@ CanvasItemEditor::CanvasItemEditor() { clear(); // Make sure values are initialized. // Update the menus' checkboxes. - callable_mp(this, &CanvasItemEditor::set_state).bind(get_state()).call_deferred(); + callable_mp(this, &CanvasItemEditor::set_state).call_deferred(get_state()); } CanvasItemEditor *CanvasItemEditor::singleton = nullptr; diff --git a/editor/plugins/cpu_particles_2d_editor_plugin.cpp b/editor/plugins/cpu_particles_2d_editor_plugin.cpp index dfc8323fc0..1d53a1b4d4 100644 --- a/editor/plugins/cpu_particles_2d_editor_plugin.cpp +++ b/editor/plugins/cpu_particles_2d_editor_plugin.cpp @@ -33,6 +33,7 @@ #include "canvas_item_editor_plugin.h" #include "core/io/image_loader.h" #include "editor/editor_node.h" +#include "editor/editor_settings.h" #include "editor/editor_undo_redo_manager.h" #include "editor/gui/editor_file_dialog.h" #include "editor/scene_tree_dock.h" @@ -268,7 +269,7 @@ CPUParticles2DEditorPlugin::CPUParticles2DEditorPlugin() { toolbar->hide(); menu = memnew(MenuButton); - menu->get_popup()->add_item(TTR("Restart"), MENU_RESTART); + menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("particles/restart_emission"), MENU_RESTART); menu->get_popup()->add_item(TTR("Load Emission Mask"), MENU_LOAD_EMISSION_MASK); menu->get_popup()->add_item(TTR("Convert to GPUParticles2D"), MENU_CONVERT_TO_GPU_PARTICLES); menu->set_text(TTR("CPUParticles2D")); diff --git a/editor/plugins/cpu_particles_3d_editor_plugin.cpp b/editor/plugins/cpu_particles_3d_editor_plugin.cpp index b5e3f102cf..baf70e45f0 100644 --- a/editor/plugins/cpu_particles_3d_editor_plugin.cpp +++ b/editor/plugins/cpu_particles_3d_editor_plugin.cpp @@ -31,6 +31,7 @@ #include "cpu_particles_3d_editor_plugin.h" #include "editor/editor_node.h" +#include "editor/editor_settings.h" #include "editor/editor_undo_redo_manager.h" #include "editor/gui/scene_tree_editor.h" #include "editor/plugins/node_3d_editor_plugin.h" @@ -168,7 +169,7 @@ CPUParticles3DEditor::CPUParticles3DEditor() { particles_editor_hb->hide(); options->set_text(TTR("CPUParticles3D")); - options->get_popup()->add_item(TTR("Restart"), MENU_OPTION_RESTART); + options->get_popup()->add_shortcut(ED_GET_SHORTCUT("particles/restart_emission"), MENU_OPTION_RESTART); options->get_popup()->add_item(TTR("Generate AABB"), MENU_OPTION_GENERATE_AABB); options->get_popup()->add_item(TTR("Create Emission Points From Node"), MENU_OPTION_CREATE_EMISSION_VOLUME_FROM_NODE); options->get_popup()->add_item(TTR("Convert to GPUParticles3D"), MENU_OPTION_CONVERT_TO_GPU_PARTICLES); diff --git a/editor/plugins/gpu_particles_2d_editor_plugin.cpp b/editor/plugins/gpu_particles_2d_editor_plugin.cpp index e9f1b07c34..328b272562 100644 --- a/editor/plugins/gpu_particles_2d_editor_plugin.cpp +++ b/editor/plugins/gpu_particles_2d_editor_plugin.cpp @@ -33,6 +33,7 @@ #include "canvas_item_editor_plugin.h" #include "core/io/image_loader.h" #include "editor/editor_node.h" +#include "editor/editor_settings.h" #include "editor/editor_undo_redo_manager.h" #include "editor/gui/editor_file_dialog.h" #include "editor/scene_tree_dock.h" @@ -370,7 +371,7 @@ GPUParticles2DEditorPlugin::GPUParticles2DEditorPlugin() { toolbar->hide(); menu = memnew(MenuButton); - menu->get_popup()->add_item(TTR("Restart"), MENU_RESTART); + menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("particles/restart_emission"), MENU_RESTART); menu->get_popup()->add_item(TTR("Generate Visibility Rect"), MENU_GENERATE_VISIBILITY_RECT); menu->get_popup()->add_item(TTR("Load Emission Mask"), MENU_LOAD_EMISSION_MASK); // menu->get_popup()->add_item(TTR("Clear Emission Mask"), MENU_CLEAR_EMISSION_MASK); diff --git a/editor/plugins/gpu_particles_3d_editor_plugin.cpp b/editor/plugins/gpu_particles_3d_editor_plugin.cpp index 04b0a8aa26..9063109ece 100644 --- a/editor/plugins/gpu_particles_3d_editor_plugin.cpp +++ b/editor/plugins/gpu_particles_3d_editor_plugin.cpp @@ -32,6 +32,7 @@ #include "core/io/resource_loader.h" #include "editor/editor_node.h" +#include "editor/editor_settings.h" #include "editor/editor_undo_redo_manager.h" #include "editor/plugins/node_3d_editor_plugin.h" #include "editor/scene_tree_dock.h" @@ -414,7 +415,7 @@ GPUParticles3DEditor::GPUParticles3DEditor() { particles_editor_hb->hide(); options->set_text(TTR("GPUParticles3D")); - options->get_popup()->add_item(TTR("Restart"), MENU_OPTION_RESTART); + options->get_popup()->add_shortcut(ED_GET_SHORTCUT("particles/restart_emission"), MENU_OPTION_RESTART); options->get_popup()->add_item(TTR("Generate AABB"), MENU_OPTION_GENERATE_AABB); options->get_popup()->add_item(TTR("Create Emission Points From Node"), MENU_OPTION_CREATE_EMISSION_VOLUME_FROM_NODE); options->get_popup()->add_item(TTR("Convert to CPUParticles3D"), MENU_OPTION_CONVERT_TO_CPU_PARTICLES); diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp index c8b229c62a..69b66cd7b2 100644 --- a/editor/plugins/node_3d_editor_plugin.cpp +++ b/editor/plugins/node_3d_editor_plugin.cpp @@ -739,9 +739,21 @@ void Node3DEditorViewport::_select_clicked(bool p_allow_locked) { return; } + Node *edited_scene = EditorNode::get_singleton()->get_edited_scene(); + + // Prevent selection of nodes not owned by the edited scene. + while (node && node != edited_scene->get_parent()) { + Node *node_owner = node->get_owner(); + if (node_owner == edited_scene || node == edited_scene || (node_owner != nullptr && edited_scene->is_editable_instance(node_owner))) { + break; + } + node = node->get_parent(); + selected = Object::cast_to<Node3D>(node); + } + if (!p_allow_locked) { // Replace the node by the group if grouped - while (node && node != EditorNode::get_singleton()->get_edited_scene()->get_parent()) { + while (node && node != edited_scene->get_parent()) { Node3D *selected_tmp = Object::cast_to<Node3D>(node); if (selected_tmp && node->has_meta("_edit_group_")) { selected = selected_tmp; @@ -1044,25 +1056,34 @@ void Node3DEditorViewport::_select_region() { found_nodes.insert(sp); - Node *item = Object::cast_to<Node>(sp); - if (item != edited_scene) { - item = edited_scene->get_deepest_editable_node(item); + Node *node = Object::cast_to<Node>(sp); + if (node != edited_scene) { + node = edited_scene->get_deepest_editable_node(node); + } + + // Prevent selection of nodes not owned by the edited scene. + while (node && node != edited_scene->get_parent()) { + Node *node_owner = node->get_owner(); + if (node_owner == edited_scene || node == edited_scene || (node_owner != nullptr && edited_scene->is_editable_instance(node_owner))) { + break; + } + node = node->get_parent(); } // Replace the node by the group if grouped - if (item->is_class("Node3D")) { - Node3D *sel = Object::cast_to<Node3D>(item); - while (item && item != EditorNode::get_singleton()->get_edited_scene()->get_parent()) { - Node3D *selected_tmp = Object::cast_to<Node3D>(item); - if (selected_tmp && item->has_meta("_edit_group_")) { + if (node->is_class("Node3D")) { + Node3D *sel = Object::cast_to<Node3D>(node); + while (node && node != EditorNode::get_singleton()->get_edited_scene()->get_parent()) { + Node3D *selected_tmp = Object::cast_to<Node3D>(node); + if (selected_tmp && node->has_meta("_edit_group_")) { sel = selected_tmp; } - item = item->get_parent(); + node = node->get_parent(); } - item = sel; + node = sel; } - if (_is_node_locked(item)) { + if (_is_node_locked(node)) { continue; } @@ -1074,7 +1095,7 @@ void Node3DEditorViewport::_select_region() { } if (seg->intersect_frustum(camera, frustum)) { - selected.push_back(item); + selected.push_back(node); } } } @@ -1531,23 +1552,35 @@ bool Node3DEditorViewport ::_is_node_locked(const Node *p_node) { } void Node3DEditorViewport::_list_select(Ref<InputEventMouseButton> b) { - _find_items_at_pos(b->get_position(), selection_results, spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SELECT); + Vector<_RayResult> potential_selection_results; + _find_items_at_pos(b->get_position(), potential_selection_results, spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SELECT); - Node *scene = EditorNode::get_singleton()->get_edited_scene(); + Node *edited_scene = EditorNode::get_singleton()->get_edited_scene(); - for (int i = 0; i < selection_results.size(); i++) { - Node3D *item = selection_results[i].item; - if (item != scene && item->get_owner() != scene && item != scene->get_deepest_editable_node(item)) { - //invalid result - selection_results.remove_at(i); - i--; + // Filter to a list of nodes which include either the edited scene or nodes directly owned by the edited scene. + // If a node has an invalid owner, recursively check their parents until a valid node is found. + for (int i = 0; i < potential_selection_results.size(); i++) { + Node3D *node = potential_selection_results[i].item; + while (true) { + if (node == nullptr || node == edited_scene->get_parent()) { + break; + } else { + Node *node_owner = node->get_owner(); + if (node == edited_scene || node_owner == edited_scene || (node_owner != nullptr && edited_scene->is_editable_instance(node_owner))) { + if (selection_results.has(node)) { + selection_results.append(node); + } + break; + } + } + node = Object::cast_to<Node3D>(node->get_parent()); } } clicked_wants_append = b->is_shift_pressed(); if (selection_results.size() == 1) { - clicked = selection_results[0].item->get_instance_id(); + clicked = selection_results[0]->get_instance_id(); selection_results.clear(); if (clicked.is_valid()) { @@ -1558,7 +1591,7 @@ void Node3DEditorViewport::_list_select(Ref<InputEventMouseButton> b) { StringName root_name = root_path.get_name(root_path.get_name_count() - 1); for (int i = 0; i < selection_results.size(); i++) { - Node3D *spat = selection_results[i].item; + Node3D *spat = selection_results[i]; Ref<Texture2D> icon = EditorNode::get_singleton()->get_object_icon(spat, "Node"); @@ -2901,10 +2934,8 @@ void Node3DEditorViewport::_notification(int p_what) { // FPS Counter. bool show_fps = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_FRAME_TIME)); - if (show_fps != fps_label->is_visible()) { - cpu_time_label->set_visible(show_fps); - gpu_time_label->set_visible(show_fps); - fps_label->set_visible(show_fps); + if (show_fps != frame_time_panel->is_visible()) { + frame_time_panel->set_visible(show_fps); RS::get_singleton()->viewport_set_measure_render_time(viewport->get_viewport_rid(), show_fps); for (int i = 0; i < FRAME_TIME_HISTORY; i++) { // Initialize to 120 FPS, so that the initial estimation until we get enough data is always reasonable. @@ -3033,9 +3064,15 @@ void Node3DEditorViewport::_notification(int p_what) { frame_time_gradient->set_color(2, get_theme_color(SNAME("error_color"), EditorStringName(Editor))); info_label->add_theme_style_override("normal", gui_base->get_theme_stylebox(SNAME("Information3dViewport"), EditorStringName(EditorStyles))); - cpu_time_label->add_theme_style_override("normal", gui_base->get_theme_stylebox(SNAME("Information3dViewport"), EditorStringName(EditorStyles))); - gpu_time_label->add_theme_style_override("normal", gui_base->get_theme_stylebox(SNAME("Information3dViewport"), EditorStringName(EditorStyles))); - fps_label->add_theme_style_override("normal", gui_base->get_theme_stylebox(SNAME("Information3dViewport"), EditorStringName(EditorStyles))); + + frame_time_panel->add_theme_style_override("panel", gui_base->get_theme_stylebox(SNAME("Information3dViewport"), EditorStringName(EditorStyles))); + // Set a minimum width to prevent the width from changing all the time + // when numbers vary rapidly. This minimum width is set based on a + // GPU time of 999.99 ms in the current editor language. + const float min_width = get_theme_font(SNAME("main"), EditorStringName(EditorFonts))->get_string_size(vformat(TTR("GPU Time: %s ms"), 999.99)).x; + frame_time_panel->set_custom_minimum_size(Size2(min_width, 0) * EDSCALE); + frame_time_vbox->add_theme_constant_override("separation", Math::round(-1 * EDSCALE)); + cinema_label->add_theme_style_override("normal", gui_base->get_theme_stylebox(SNAME("Information3dViewport"), EditorStringName(EditorStyles))); locked_label->add_theme_style_override("normal", gui_base->get_theme_stylebox(SNAME("Information3dViewport"), EditorStringName(EditorStyles))); } break; @@ -3750,7 +3787,7 @@ void Node3DEditorViewport::_selection_result_pressed(int p_result) { return; } - clicked = selection_results_menu[p_result].item->get_instance_id(); + clicked = selection_results_menu[p_result]->get_instance_id(); if (clicked.is_valid()) { _select_clicked(spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SELECT); @@ -5379,10 +5416,6 @@ Node3DEditorViewport::Node3DEditorViewport(Node3DEditor *p_spatial_editor, int p top_right_vbox = memnew(VBoxContainer); top_right_vbox->set_anchors_and_offsets_preset(PRESET_TOP_RIGHT, PRESET_MODE_MINSIZE, 10.0 * EDSCALE); top_right_vbox->set_h_grow_direction(GROW_DIRECTION_BEGIN); - // Make sure frame time labels don't touch the viewport's edge. - top_right_vbox->set_custom_minimum_size(Size2(100, 0) * EDSCALE); - // Prevent visible spacing between frame time labels. - top_right_vbox->add_theme_constant_override("separation", 0); const int navigation_control_size = 150; @@ -5414,18 +5447,22 @@ Node3DEditorViewport::Node3DEditorViewport(Node3DEditor *p_spatial_editor, int p rotation_control->set_viewport(this); top_right_vbox->add_child(rotation_control); + frame_time_panel = memnew(PanelContainer); + top_right_vbox->add_child(frame_time_panel); + frame_time_panel->hide(); + + frame_time_vbox = memnew(VBoxContainer); + frame_time_panel->add_child(frame_time_vbox); + // Individual Labels are used to allow coloring each label with its own color. cpu_time_label = memnew(Label); - top_right_vbox->add_child(cpu_time_label); - cpu_time_label->hide(); + frame_time_vbox->add_child(cpu_time_label); gpu_time_label = memnew(Label); - top_right_vbox->add_child(gpu_time_label); - gpu_time_label->hide(); + frame_time_vbox->add_child(gpu_time_label); fps_label = memnew(Label); - top_right_vbox->add_child(fps_label); - fps_label->hide(); + frame_time_vbox->add_child(fps_label); surface->add_child(top_right_vbox); @@ -8476,7 +8513,7 @@ Node3DEditor::Node3DEditor() { tool_button[TOOL_LOCK_SELECTED]->connect(SceneStringName(pressed), callable_mp(this, &Node3DEditor::_menu_item_pressed).bind(MENU_LOCK_SELECTED)); tool_button[TOOL_LOCK_SELECTED]->set_tooltip_text(TTR("Lock selected node, preventing selection and movement.")); // Define the shortcut globally (without a context) so that it works if the Scene tree dock is currently focused. - tool_button[TOOL_LOCK_SELECTED]->set_shortcut(ED_SHORTCUT("editor/lock_selected_nodes", TTR("Lock Selected Node(s)"), KeyModifierMask::CMD_OR_CTRL | Key::L)); + tool_button[TOOL_LOCK_SELECTED]->set_shortcut(ED_GET_SHORTCUT("editor/lock_selected_nodes")); tool_button[TOOL_UNLOCK_SELECTED] = memnew(Button); main_menu_hbox->add_child(tool_button[TOOL_UNLOCK_SELECTED]); @@ -8484,7 +8521,7 @@ Node3DEditor::Node3DEditor() { tool_button[TOOL_UNLOCK_SELECTED]->connect(SceneStringName(pressed), callable_mp(this, &Node3DEditor::_menu_item_pressed).bind(MENU_UNLOCK_SELECTED)); tool_button[TOOL_UNLOCK_SELECTED]->set_tooltip_text(TTR("Unlock selected node, allowing selection and movement.")); // Define the shortcut globally (without a context) so that it works if the Scene tree dock is currently focused. - tool_button[TOOL_UNLOCK_SELECTED]->set_shortcut(ED_SHORTCUT("editor/unlock_selected_nodes", TTR("Unlock Selected Node(s)"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::L)); + tool_button[TOOL_UNLOCK_SELECTED]->set_shortcut(ED_GET_SHORTCUT("editor/unlock_selected_nodes")); tool_button[TOOL_GROUP_SELECTED] = memnew(Button); main_menu_hbox->add_child(tool_button[TOOL_GROUP_SELECTED]); @@ -8492,7 +8529,7 @@ Node3DEditor::Node3DEditor() { tool_button[TOOL_GROUP_SELECTED]->connect(SceneStringName(pressed), callable_mp(this, &Node3DEditor::_menu_item_pressed).bind(MENU_GROUP_SELECTED)); tool_button[TOOL_GROUP_SELECTED]->set_tooltip_text(TTR("Groups the selected node with its children. This selects the parent when any child node is clicked in 2D and 3D view.")); // Define the shortcut globally (without a context) so that it works if the Scene tree dock is currently focused. - tool_button[TOOL_GROUP_SELECTED]->set_shortcut(ED_SHORTCUT("editor/group_selected_nodes", TTR("Group Selected Node(s)"), KeyModifierMask::CMD_OR_CTRL | Key::G)); + tool_button[TOOL_GROUP_SELECTED]->set_shortcut(ED_GET_SHORTCUT("editor/group_selected_nodes")); tool_button[TOOL_UNGROUP_SELECTED] = memnew(Button); main_menu_hbox->add_child(tool_button[TOOL_UNGROUP_SELECTED]); @@ -8500,7 +8537,7 @@ Node3DEditor::Node3DEditor() { tool_button[TOOL_UNGROUP_SELECTED]->connect(SceneStringName(pressed), callable_mp(this, &Node3DEditor::_menu_item_pressed).bind(MENU_UNGROUP_SELECTED)); tool_button[TOOL_UNGROUP_SELECTED]->set_tooltip_text(TTR("Ungroups the selected node from its children. Child nodes will be individual items in 2D and 3D view.")); // Define the shortcut globally (without a context) so that it works if the Scene tree dock is currently focused. - tool_button[TOOL_UNGROUP_SELECTED]->set_shortcut(ED_SHORTCUT("editor/ungroup_selected_nodes", TTR("Ungroup Selected Node(s)"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::G)); + tool_button[TOOL_UNGROUP_SELECTED]->set_shortcut(ED_GET_SHORTCUT("editor/ungroup_selected_nodes")); main_menu_hbox->add_child(memnew(VSeparator)); diff --git a/editor/plugins/node_3d_editor_plugin.h b/editor/plugins/node_3d_editor_plugin.h index 4990b11a47..859d075732 100644 --- a/editor/plugins/node_3d_editor_plugin.h +++ b/editor/plugins/node_3d_editor_plugin.h @@ -255,6 +255,8 @@ private: ViewportNavigationControl *look_control = nullptr; ViewportRotationControl *rotation_control = nullptr; Gradient *frame_time_gradient = nullptr; + PanelContainer *frame_time_panel = nullptr; + VBoxContainer *frame_time_vbox = nullptr; Label *cpu_time_label = nullptr; Label *gpu_time_label = nullptr; Label *fps_label = nullptr; @@ -296,8 +298,8 @@ private: ObjectID clicked; ObjectID material_target; - Vector<_RayResult> selection_results; - Vector<_RayResult> selection_results_menu; + Vector<Node3D *> selection_results; + Vector<Node3D *> selection_results_menu; bool clicked_wants_append = false; bool selection_in_progress = false; diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index 63eecd357d..4812c623c9 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -765,7 +765,7 @@ void ScriptEditor::_update_recent_scripts() { } recent_scripts->add_separator(); - recent_scripts->add_shortcut(ED_SHORTCUT("script_editor/clear_recent", TTR("Clear Recent Files"))); + recent_scripts->add_shortcut(ED_GET_SHORTCUT("script_editor/clear_recent")); recent_scripts->set_item_disabled(recent_scripts->get_item_id(recent_scripts->get_item_count() - 1), rc.is_empty()); recent_scripts->reset_size(); @@ -1008,6 +1008,10 @@ void ScriptEditor::_resave_scripts(const String &p_str) { se->trim_trailing_whitespace(); } + if (trim_final_newlines_on_save) { + se->trim_final_newlines(); + } + se->insert_final_newline(); if (convert_indent_on_save) { @@ -1402,6 +1406,10 @@ void ScriptEditor::_menu_option(int p_option) { current->trim_trailing_whitespace(); } + if (trim_final_newlines_on_save) { + current->trim_final_newlines(); + } + current->insert_final_newline(); if (convert_indent_on_save) { @@ -2602,6 +2610,10 @@ void ScriptEditor::save_current_script() { current->trim_trailing_whitespace(); } + if (trim_final_newlines_on_save) { + current->trim_final_newlines(); + } + current->insert_final_newline(); if (convert_indent_on_save) { @@ -2646,6 +2658,10 @@ void ScriptEditor::save_all_scripts() { se->trim_trailing_whitespace(); } + if (trim_final_newlines_on_save) { + se->trim_final_newlines(); + } + se->insert_final_newline(); if (!se->is_unsaved()) { @@ -2883,6 +2899,7 @@ void ScriptEditor::_apply_editor_settings() { } trim_trailing_whitespace_on_save = EDITOR_GET("text_editor/behavior/files/trim_trailing_whitespace_on_save"); + trim_final_newlines_on_save = EDITOR_GET("text_editor/behavior/files/trim_final_newlines_on_save"); convert_indent_on_save = EDITOR_GET("text_editor/behavior/files/convert_indent_on_save"); members_overview_enabled = EDITOR_GET("text_editor/script_list/show_members_overview"); @@ -3591,13 +3608,13 @@ void ScriptEditor::_update_selected_editor_menu() { script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find_next", TTR("Find Next"), Key::F3), HELP_SEARCH_FIND_NEXT); script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find_previous", TTR("Find Previous"), KeyModifierMask::SHIFT | Key::F3), HELP_SEARCH_FIND_PREVIOUS); script_search_menu->get_popup()->add_separator(); - script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find_in_files", TTR("Find in Files"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::F), SEARCH_IN_FILES); - script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/replace_in_files", TTR("Replace in Files"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::R), REPLACE_IN_FILES); + script_search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_editor/find_in_files"), SEARCH_IN_FILES); + script_search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_editor/replace_in_files"), REPLACE_IN_FILES); script_search_menu->show(); } else { if (tab_container->get_tab_count() == 0) { - script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find_in_files", TTR("Find in Files"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::F), SEARCH_IN_FILES); - script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/replace_in_files", TTR("Replace in Files"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::R), REPLACE_IN_FILES); + script_search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_editor/find_in_files"), SEARCH_IN_FILES); + script_search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_editor/replace_in_files"), REPLACE_IN_FILES); script_search_menu->show(); } else { script_search_menu->hide(); @@ -4101,7 +4118,7 @@ ScriptEditor::ScriptEditor(WindowWrapper *p_wrapper) { file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/new", TTR("New Script..."), KeyModifierMask::CMD_OR_CTRL | Key::N), FILE_NEW); file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/new_textfile", TTR("New Text File..."), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::N), FILE_NEW_TEXTFILE); file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/open", TTR("Open...")), FILE_OPEN); - file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/reopen_closed_script", TTR("Reopen Closed Script"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::T), FILE_REOPEN_CLOSED); + file_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_editor/reopen_closed_script"), FILE_REOPEN_CLOSED); recent_scripts = memnew(PopupMenu); file_menu->get_popup()->add_submenu_node_item(TTR("Open Recent"), recent_scripts, FILE_OPEN_RECENT); @@ -4249,12 +4266,18 @@ ScriptEditor::ScriptEditor(WindowWrapper *p_wrapper) { disk_changed = memnew(ConfirmationDialog); { + disk_changed->set_title(TTR("Files have been modified on disk")); + VBoxContainer *vbc = memnew(VBoxContainer); disk_changed->add_child(vbc); - Label *dl = memnew(Label); - dl->set_text(TTR("The following files are newer on disk.\nWhat action should be taken?:")); - vbc->add_child(dl); + Label *files_are_newer_label = memnew(Label); + files_are_newer_label->set_text(TTR("The following files are newer on disk.")); + vbc->add_child(files_are_newer_label); + + Label *what_action_label = memnew(Label); + what_action_label->set_text(TTR("What action should be taken?:")); + vbc->add_child(what_action_label); disk_changed_list = memnew(Tree); vbc->add_child(disk_changed_list); @@ -4262,9 +4285,9 @@ ScriptEditor::ScriptEditor(WindowWrapper *p_wrapper) { disk_changed_list->set_v_size_flags(SIZE_EXPAND_FILL); disk_changed->connect("confirmed", callable_mp(this, &ScriptEditor::reload_scripts).bind(false)); - disk_changed->set_ok_button_text(TTR("Reload")); + disk_changed->set_ok_button_text(TTR("Discard local changes and reload")); - disk_changed->add_button(TTR("Resave"), !DisplayServer::get_singleton()->get_swap_cancel_ok(), "resave"); + disk_changed->add_button(TTR("Keep local changes and overwrite"), !DisplayServer::get_singleton()->get_swap_cancel_ok(), "resave"); disk_changed->connect("custom_action", callable_mp(this, &ScriptEditor::_resave_scripts)); } @@ -4301,6 +4324,7 @@ ScriptEditor::ScriptEditor(WindowWrapper *p_wrapper) { edit_pass = 0; trim_trailing_whitespace_on_save = EDITOR_GET("text_editor/behavior/files/trim_trailing_whitespace_on_save"); + trim_final_newlines_on_save = EDITOR_GET("text_editor/behavior/files/trim_final_newlines_on_save"); convert_indent_on_save = EDITOR_GET("text_editor/behavior/files/convert_indent_on_save"); ScriptServer::edit_request_func = _open_script_request; @@ -4502,6 +4526,15 @@ void ScriptEditorPlugin::edited_scene_changed() { } ScriptEditorPlugin::ScriptEditorPlugin() { + ED_SHORTCUT("script_editor/reopen_closed_script", TTR("Reopen Closed Script"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::T); + ED_SHORTCUT("script_editor/clear_recent", TTR("Clear Recent Scripts")); + ED_SHORTCUT("script_editor/find_in_files", TTR("Find in Files"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::F); + ED_SHORTCUT("script_editor/replace_in_files", TTR("Replace in Files"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::R); + + ED_SHORTCUT("script_text_editor/convert_to_uppercase", TTR("Uppercase"), KeyModifierMask::SHIFT | Key::F4); + ED_SHORTCUT("script_text_editor/convert_to_lowercase", TTR("Lowercase"), KeyModifierMask::SHIFT | Key::F5); + ED_SHORTCUT("script_text_editor/capitalize", TTR("Capitalize"), KeyModifierMask::SHIFT | Key::F6); + window_wrapper = memnew(WindowWrapper); window_wrapper->set_window_title(vformat(TTR("%s - Godot Engine"), TTR("Script Editor"))); window_wrapper->set_margins_enabled(true); @@ -4530,9 +4563,6 @@ ScriptEditorPlugin::ScriptEditorPlugin() { 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}.")); - - ED_SHORTCUT("script_editor/reopen_closed_script", TTR("Reopen Closed Script"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::T); - ED_SHORTCUT("script_editor/clear_recent", TTR("Clear Recent Scripts")); } ScriptEditorPlugin::~ScriptEditorPlugin() { diff --git a/editor/plugins/script_editor_plugin.h b/editor/plugins/script_editor_plugin.h index 6f8e71ce75..9db1aff76a 100644 --- a/editor/plugins/script_editor_plugin.h +++ b/editor/plugins/script_editor_plugin.h @@ -174,6 +174,7 @@ public: virtual void set_executing_line(int p_line) = 0; virtual void clear_executing_line() = 0; virtual void trim_trailing_whitespace() = 0; + virtual void trim_final_newlines() = 0; virtual void insert_final_newline() = 0; virtual void convert_indent() = 0; virtual void ensure_focus() = 0; @@ -408,6 +409,7 @@ class ScriptEditor : public PanelContainer { bool open_textfile_after_create = true; bool trim_trailing_whitespace_on_save; + bool trim_final_newlines_on_save; bool convert_indent_on_save; bool external_editor_active; diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp index 191d58228b..b11ad8b7d5 100644 --- a/editor/plugins/script_text_editor.cpp +++ b/editor/plugins/script_text_editor.cpp @@ -431,6 +431,10 @@ void ScriptTextEditor::trim_trailing_whitespace() { code_editor->trim_trailing_whitespace(); } +void ScriptTextEditor::trim_final_newlines() { + code_editor->trim_final_newlines(); +} + void ScriptTextEditor::insert_final_newline() { code_editor->insert_final_newline(); } @@ -1427,6 +1431,9 @@ void ScriptTextEditor::_edit_option(int p_op) { case EDIT_TRIM_TRAILING_WHITESAPCE: { trim_trailing_whitespace(); } break; + case EDIT_TRIM_FINAL_NEWLINES: { + trim_final_newlines(); + } break; case EDIT_CONVERT_INDENT_TO_SPACES: { code_editor->set_indent_using_spaces(true); convert_indent(); @@ -1862,6 +1869,11 @@ void ScriptTextEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data if (drop_modifier_pressed && ResourceLoader::exists(path)) { Ref<Resource> resource = ResourceLoader::load(path); + if (resource.is_null()) { + // Resource exists, but failed to load. We need only path and name, so we can use a dummy Resource instead. + resource.instantiate(); + resource->set_path_cache(path); + } text_to_drop += _get_dropped_resource_line(resource, is_empty_line); } else { text_to_drop += _quote_drop_data(path); @@ -2133,10 +2145,11 @@ void ScriptTextEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) { void ScriptTextEditor::_color_changed(const Color &p_color) { String new_args; + const int decimals = 3; if (p_color.a == 1.0f) { - new_args = String("(" + rtos(p_color.r) + ", " + rtos(p_color.g) + ", " + rtos(p_color.b) + ")"); + new_args = String("(" + String::num(p_color.r, decimals) + ", " + String::num(p_color.g, decimals) + ", " + String::num(p_color.b, decimals) + ")"); } else { - new_args = String("(" + rtos(p_color.r) + ", " + rtos(p_color.g) + ", " + rtos(p_color.b) + ", " + rtos(p_color.a) + ")"); + new_args = String("(" + String::num(p_color.r, decimals) + ", " + String::num(p_color.g, decimals) + ", " + String::num(p_color.b, decimals) + ", " + String::num(p_color.a, decimals) + ")"); } String line = code_editor->get_text_editor()->get_line(color_position.x); @@ -2299,6 +2312,7 @@ void ScriptTextEditor::_enable_code_editor() { edit_menu->get_popup()->add_separator(); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_text_completion_query"), EDIT_COMPLETE); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/trim_trailing_whitespace"), EDIT_TRIM_TRAILING_WHITESAPCE); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/trim_final_newlines"), EDIT_TRIM_FINAL_NEWLINES); { PopupMenu *sub_menu = memnew(PopupMenu); sub_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/convert_indent_to_spaces"), EDIT_CONVERT_INDENT_TO_SPACES); @@ -2311,9 +2325,9 @@ void ScriptTextEditor::_enable_code_editor() { edit_menu->get_popup()->add_separator(); { PopupMenu *sub_menu = memnew(PopupMenu); - sub_menu->add_shortcut(ED_SHORTCUT("script_text_editor/convert_to_uppercase", TTR("Uppercase"), KeyModifierMask::SHIFT | Key::F4), EDIT_TO_UPPERCASE); - sub_menu->add_shortcut(ED_SHORTCUT("script_text_editor/convert_to_lowercase", TTR("Lowercase"), KeyModifierMask::SHIFT | Key::F5), EDIT_TO_LOWERCASE); - sub_menu->add_shortcut(ED_SHORTCUT("script_text_editor/capitalize", TTR("Capitalize"), KeyModifierMask::SHIFT | Key::F6), EDIT_CAPITALIZE); + sub_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/convert_to_uppercase"), EDIT_TO_UPPERCASE); + sub_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/convert_to_lowercase"), EDIT_TO_LOWERCASE); + sub_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/capitalize"), EDIT_CAPITALIZE); sub_menu->connect("id_pressed", callable_mp(this, &ScriptTextEditor::_edit_option)); edit_menu->get_popup()->add_submenu_node_item(TTR("Convert Case"), sub_menu); } @@ -2484,6 +2498,7 @@ void ScriptTextEditor::register_editor() { ED_SHORTCUT("script_text_editor/evaluate_selection", TTR("Evaluate Selection"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::E); ED_SHORTCUT("script_text_editor/toggle_word_wrap", TTR("Toggle Word Wrap"), KeyModifierMask::ALT | Key::Z); ED_SHORTCUT("script_text_editor/trim_trailing_whitespace", TTR("Trim Trailing Whitespace"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::ALT | Key::T); + ED_SHORTCUT("script_text_editor/trim_final_newlines", TTR("Trim Final Newlines"), Key::NONE); ED_SHORTCUT("script_text_editor/convert_indent_to_spaces", TTR("Convert Indent to Spaces"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::Y); ED_SHORTCUT("script_text_editor/convert_indent_to_tabs", TTR("Convert Indent to Tabs"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::I); ED_SHORTCUT("script_text_editor/auto_indent", TTR("Auto Indent"), KeyModifierMask::CMD_OR_CTRL | Key::I); diff --git a/editor/plugins/script_text_editor.h b/editor/plugins/script_text_editor.h index de89fe458c..8c2ec1561b 100644 --- a/editor/plugins/script_text_editor.h +++ b/editor/plugins/script_text_editor.h @@ -118,6 +118,7 @@ class ScriptTextEditor : public ScriptEditorBase { EDIT_COMPLETE, EDIT_AUTO_INDENT, EDIT_TRIM_TRAILING_WHITESAPCE, + EDIT_TRIM_FINAL_NEWLINES, EDIT_CONVERT_INDENT_TO_SPACES, EDIT_CONVERT_INDENT_TO_TABS, EDIT_TOGGLE_COMMENT, @@ -228,6 +229,7 @@ public: virtual Variant get_navigation_state() override; virtual void ensure_focus() override; virtual void trim_trailing_whitespace() override; + virtual void trim_final_newlines() override; virtual void insert_final_newline() override; virtual void convert_indent() override; virtual void tag_saved_version() override; diff --git a/editor/plugins/shader_editor_plugin.cpp b/editor/plugins/shader_editor_plugin.cpp index 222d010a7a..a83f95f680 100644 --- a/editor/plugins/shader_editor_plugin.cpp +++ b/editor/plugins/shader_editor_plugin.cpp @@ -436,8 +436,14 @@ void ShaderEditorPlugin::_menu_item_pressed(int p_index) { int index = shader_tabs->get_current_tab(); ERR_FAIL_INDEX(index, shader_tabs->get_tab_count()); TextShaderEditor *editor = edited_shaders[index].shader_editor; - if (editor && editor->get_trim_trailing_whitespace_on_save()) { - editor->trim_trailing_whitespace(); + if (editor) { + if (editor->get_trim_trailing_whitespace_on_save()) { + editor->trim_trailing_whitespace(); + } + + if (editor->get_trim_final_newlines_on_save()) { + editor->trim_final_newlines(); + } } if (edited_shaders[index].shader.is_valid()) { EditorNode::get_singleton()->save_resource(edited_shaders[index].shader); @@ -452,8 +458,14 @@ void ShaderEditorPlugin::_menu_item_pressed(int p_index) { int index = shader_tabs->get_current_tab(); ERR_FAIL_INDEX(index, shader_tabs->get_tab_count()); TextShaderEditor *editor = edited_shaders[index].shader_editor; - if (editor && editor->get_trim_trailing_whitespace_on_save()) { - editor->trim_trailing_whitespace(); + if (editor) { + if (editor->get_trim_trailing_whitespace_on_save()) { + editor->trim_trailing_whitespace(); + } + + if (editor->get_trim_final_newlines_on_save()) { + editor->trim_final_newlines(); + } } String path; if (edited_shaders[index].shader.is_valid()) { diff --git a/editor/plugins/sprite_frames_editor_plugin.cpp b/editor/plugins/sprite_frames_editor_plugin.cpp index 56f8e1173f..c14336418c 100644 --- a/editor/plugins/sprite_frames_editor_plugin.cpp +++ b/editor/plugins/sprite_frames_editor_plugin.cpp @@ -2181,6 +2181,7 @@ SpriteFramesEditor::SpriteFramesEditor() { split_sheet_h->set_min(1); split_sheet_h->set_max(128); split_sheet_h->set_step(1); + split_sheet_h->set_select_all_on_focus(true); split_sheet_h_hb->add_child(split_sheet_h); split_sheet_h->connect("value_changed", callable_mp(this, &SpriteFramesEditor::_sheet_spin_changed).bind(PARAM_FRAME_COUNT)); split_sheet_settings_vb->add_child(split_sheet_h_hb); @@ -2197,6 +2198,7 @@ SpriteFramesEditor::SpriteFramesEditor() { split_sheet_v->set_min(1); split_sheet_v->set_max(128); split_sheet_v->set_step(1); + split_sheet_v->set_select_all_on_focus(true); split_sheet_v_hb->add_child(split_sheet_v); split_sheet_v->connect("value_changed", callable_mp(this, &SpriteFramesEditor::_sheet_spin_changed).bind(PARAM_FRAME_COUNT)); split_sheet_settings_vb->add_child(split_sheet_v_hb); @@ -2216,6 +2218,7 @@ SpriteFramesEditor::SpriteFramesEditor() { split_sheet_size_x->set_min(1); split_sheet_size_x->set_step(1); split_sheet_size_x->set_suffix("px"); + split_sheet_size_x->set_select_all_on_focus(true); split_sheet_size_x->connect("value_changed", callable_mp(this, &SpriteFramesEditor::_sheet_spin_changed).bind(PARAM_SIZE)); split_sheet_size_vb->add_child(split_sheet_size_x); split_sheet_size_y = memnew(SpinBox); @@ -2223,6 +2226,7 @@ SpriteFramesEditor::SpriteFramesEditor() { split_sheet_size_y->set_min(1); split_sheet_size_y->set_step(1); split_sheet_size_y->set_suffix("px"); + split_sheet_size_y->set_select_all_on_focus(true); split_sheet_size_y->connect("value_changed", callable_mp(this, &SpriteFramesEditor::_sheet_spin_changed).bind(PARAM_SIZE)); split_sheet_size_vb->add_child(split_sheet_size_y); split_sheet_size_hb->add_child(split_sheet_size_vb); @@ -2242,12 +2246,14 @@ SpriteFramesEditor::SpriteFramesEditor() { split_sheet_sep_x->set_min(0); split_sheet_sep_x->set_step(1); split_sheet_sep_x->set_suffix("px"); + split_sheet_sep_x->set_select_all_on_focus(true); split_sheet_sep_x->connect("value_changed", callable_mp(this, &SpriteFramesEditor::_sheet_spin_changed).bind(PARAM_USE_CURRENT)); split_sheet_sep_vb->add_child(split_sheet_sep_x); split_sheet_sep_y = memnew(SpinBox); split_sheet_sep_y->set_min(0); split_sheet_sep_y->set_step(1); split_sheet_sep_y->set_suffix("px"); + split_sheet_sep_y->set_select_all_on_focus(true); split_sheet_sep_y->connect("value_changed", callable_mp(this, &SpriteFramesEditor::_sheet_spin_changed).bind(PARAM_USE_CURRENT)); split_sheet_sep_vb->add_child(split_sheet_sep_y); split_sheet_sep_hb->add_child(split_sheet_sep_vb); @@ -2267,12 +2273,14 @@ SpriteFramesEditor::SpriteFramesEditor() { split_sheet_offset_x->set_min(0); split_sheet_offset_x->set_step(1); split_sheet_offset_x->set_suffix("px"); + split_sheet_offset_x->set_select_all_on_focus(true); split_sheet_offset_x->connect("value_changed", callable_mp(this, &SpriteFramesEditor::_sheet_spin_changed).bind(PARAM_USE_CURRENT)); split_sheet_offset_vb->add_child(split_sheet_offset_x); split_sheet_offset_y = memnew(SpinBox); split_sheet_offset_y->set_min(0); split_sheet_offset_y->set_step(1); split_sheet_offset_y->set_suffix("px"); + split_sheet_offset_y->set_select_all_on_focus(true); split_sheet_offset_y->connect("value_changed", callable_mp(this, &SpriteFramesEditor::_sheet_spin_changed).bind(PARAM_USE_CURRENT)); split_sheet_offset_vb->add_child(split_sheet_offset_y); split_sheet_offset_hb->add_child(split_sheet_offset_vb); diff --git a/editor/plugins/text_editor.cpp b/editor/plugins/text_editor.cpp index 2aa04d7874..c4c3f3b4e3 100644 --- a/editor/plugins/text_editor.cpp +++ b/editor/plugins/text_editor.cpp @@ -288,6 +288,10 @@ void TextEditor::trim_trailing_whitespace() { code_editor->trim_trailing_whitespace(); } +void TextEditor::trim_final_newlines() { + code_editor->trim_final_newlines(); +} + void TextEditor::insert_final_newline() { code_editor->insert_final_newline(); } @@ -414,6 +418,9 @@ void TextEditor::_edit_option(int p_op) { case EDIT_TRIM_TRAILING_WHITESAPCE: { trim_trailing_whitespace(); } break; + case EDIT_TRIM_FINAL_NEWLINES: { + trim_final_newlines(); + } break; case EDIT_CONVERT_INDENT_TO_SPACES: { code_editor->set_indent_using_spaces(true); convert_indent(); @@ -641,15 +648,16 @@ TextEditor::TextEditor() { edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/duplicate_lines"), EDIT_DUPLICATE_LINES); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_word_wrap"), EDIT_TOGGLE_WORD_WRAP); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/trim_trailing_whitespace"), EDIT_TRIM_TRAILING_WHITESAPCE); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/trim_final_newlines"), EDIT_TRIM_FINAL_NEWLINES); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/convert_indent_to_spaces"), EDIT_CONVERT_INDENT_TO_SPACES); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/convert_indent_to_tabs"), EDIT_CONVERT_INDENT_TO_TABS); edit_menu->get_popup()->add_separator(); PopupMenu *convert_case = memnew(PopupMenu); edit_menu->get_popup()->add_submenu_node_item(TTR("Convert Case"), convert_case); - convert_case->add_shortcut(ED_SHORTCUT("script_text_editor/convert_to_uppercase", TTR("Uppercase")), EDIT_TO_UPPERCASE); - convert_case->add_shortcut(ED_SHORTCUT("script_text_editor/convert_to_lowercase", TTR("Lowercase")), EDIT_TO_LOWERCASE); - convert_case->add_shortcut(ED_SHORTCUT("script_text_editor/capitalize", TTR("Capitalize")), EDIT_CAPITALIZE); + convert_case->add_shortcut(ED_GET_SHORTCUT("script_text_editor/convert_to_uppercase"), EDIT_TO_UPPERCASE); + convert_case->add_shortcut(ED_GET_SHORTCUT("script_text_editor/convert_to_lowercase"), EDIT_TO_LOWERCASE); + convert_case->add_shortcut(ED_GET_SHORTCUT("script_text_editor/capitalize"), EDIT_CAPITALIZE); convert_case->connect("id_pressed", callable_mp(this, &TextEditor::_edit_option)); highlighter_menu = memnew(PopupMenu); diff --git a/editor/plugins/text_editor.h b/editor/plugins/text_editor.h index 38fddc45df..268e5c32b4 100644 --- a/editor/plugins/text_editor.h +++ b/editor/plugins/text_editor.h @@ -63,6 +63,7 @@ private: EDIT_PASTE, EDIT_SELECT_ALL, EDIT_TRIM_TRAILING_WHITESAPCE, + EDIT_TRIM_FINAL_NEWLINES, EDIT_CONVERT_INDENT_TO_SPACES, EDIT_CONVERT_INDENT_TO_TABS, EDIT_MOVE_LINE_UP, @@ -133,6 +134,7 @@ public: virtual void set_executing_line(int p_line) override; virtual void clear_executing_line() override; virtual void trim_trailing_whitespace() override; + virtual void trim_final_newlines() override; virtual void insert_final_newline() override; virtual void convert_indent() override; virtual void ensure_focus() override; diff --git a/editor/plugins/text_shader_editor.cpp b/editor/plugins/text_shader_editor.cpp index bb74bf8d1f..fb8bed381e 100644 --- a/editor/plugins/text_shader_editor.cpp +++ b/editor/plugins/text_shader_editor.cpp @@ -30,12 +30,12 @@ #include "text_shader_editor.h" +#include "core/config/project_settings.h" #include "core/version_generated.gen.h" +#include "editor/editor_file_system.h" #include "editor/editor_node.h" #include "editor/editor_settings.h" #include "editor/editor_string_names.h" -#include "editor/filesystem_dock.h" -#include "editor/project_settings_editor.h" #include "editor/themes/editor_scale.h" #include "editor/themes/editor_theme_manager.h" #include "scene/gui/split_container.h" @@ -735,6 +735,13 @@ void TextShaderEditor::_menu_option(int p_option) { } } +void TextShaderEditor::_prepare_edit_menu() { + const CodeEdit *tx = code_editor->get_text_editor(); + PopupMenu *popup = edit_menu->get_popup(); + popup->set_item_disabled(popup->get_item_index(EDIT_UNDO), !tx->has_undo()); + popup->set_item_disabled(popup->get_item_index(EDIT_REDO), !tx->has_redo()); +} + void TextShaderEditor::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: @@ -763,6 +770,7 @@ void TextShaderEditor::_apply_editor_settings() { code_editor->update_editor_settings(); trim_trailing_whitespace_on_save = EDITOR_GET("text_editor/behavior/files/trim_trailing_whitespace_on_save"); + trim_final_newlines_on_save = EDITOR_GET("text_editor/behavior/files/trim_final_newlines_on_save"); } void TextShaderEditor::_show_warnings_panel(bool p_show) { @@ -927,6 +935,10 @@ void TextShaderEditor::save_external_data(const String &p_str) { trim_trailing_whitespace(); } + if (trim_final_newlines_on_save) { + trim_final_newlines(); + } + apply_shaders(); Ref<Shader> edited_shader = code_editor->get_edited_shader(); @@ -953,6 +965,10 @@ void TextShaderEditor::trim_trailing_whitespace() { code_editor->trim_trailing_whitespace(); } +void TextShaderEditor::trim_final_newlines() { + code_editor->trim_final_newlines(); +} + void TextShaderEditor::validate_script() { code_editor->_validate_script(); } @@ -1088,6 +1104,9 @@ void TextShaderEditor::_make_context_menu(bool p_selection, Vector2 p_position) context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_comment"), EDIT_TOGGLE_COMMENT); context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_bookmark"), BOOKMARK_TOGGLE); + context_menu->set_item_disabled(context_menu->get_item_index(EDIT_UNDO), !code_editor->get_text_editor()->has_undo()); + context_menu->set_item_disabled(context_menu->get_item_index(EDIT_REDO), !code_editor->get_text_editor()->has_redo()); + context_menu->set_position(get_screen_position() + p_position); context_menu->reset_size(); context_menu->popup(); @@ -1128,6 +1147,7 @@ TextShaderEditor::TextShaderEditor() { edit_menu->set_shortcut_context(this); edit_menu->set_text(TTR("Edit")); edit_menu->set_switch_on_hover(true); + edit_menu->connect("about_to_popup", callable_mp(this, &TextShaderEditor::_prepare_edit_menu)); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_undo"), EDIT_UNDO); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_redo"), EDIT_REDO); diff --git a/editor/plugins/text_shader_editor.h b/editor/plugins/text_shader_editor.h index be16148744..61066ed7c6 100644 --- a/editor/plugins/text_shader_editor.h +++ b/editor/plugins/text_shader_editor.h @@ -34,7 +34,6 @@ #include "editor/code_editor.h" #include "scene/gui/margin_container.h" #include "scene/gui/menu_button.h" -#include "scene/gui/panel_container.h" #include "scene/gui/rich_text_label.h" #include "servers/rendering/shader_warnings.h" @@ -153,6 +152,7 @@ class TextShaderEditor : public MarginContainer { bool compilation_success = true; void _menu_option(int p_option); + void _prepare_edit_menu(); mutable Ref<Shader> shader; mutable Ref<ShaderInclude> shader_inc; @@ -176,6 +176,7 @@ class TextShaderEditor : public MarginContainer { uint32_t dependencies_version = 0xFFFFFFFF; bool trim_trailing_whitespace_on_save; + bool trim_final_newlines_on_save; protected: void _notification(int p_what); @@ -189,6 +190,7 @@ protected: public: bool was_compilation_successful() const { return compilation_success; } bool get_trim_trailing_whitespace_on_save() const { return trim_trailing_whitespace_on_save; } + bool get_trim_final_newlines_on_save() const { return trim_final_newlines_on_save; } void apply_shaders(); void ensure_select_current(); void edit(const Ref<Shader> &p_shader); @@ -196,6 +198,7 @@ public: void goto_line_selection(int p_line, int p_begin, int p_end); void save_external_data(const String &p_str = ""); void trim_trailing_whitespace(); + void trim_final_newlines(); void validate_script(); bool is_unsaved() const; void tag_saved_version(); diff --git a/editor/plugins/tiles/tile_atlas_view.cpp b/editor/plugins/tiles/tile_atlas_view.cpp index 2ee0dc0316..52b58b74a3 100644 --- a/editor/plugins/tiles/tile_atlas_view.cpp +++ b/editor/plugins/tiles/tile_atlas_view.cpp @@ -59,6 +59,9 @@ void TileAtlasView::_zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<In } Size2i TileAtlasView::_compute_base_tiles_control_size() { + if (tile_set_atlas_source.is_null()) { + return Size2i(); + } // Update the texture. Vector2i size; Ref<Texture2D> texture = tile_set_atlas_source->get_texture(); @@ -69,6 +72,9 @@ Size2i TileAtlasView::_compute_base_tiles_control_size() { } Size2i TileAtlasView::_compute_alternative_tiles_control_size() { + if (tile_set_atlas_source.is_null()) { + return Size2i(); + } Vector2i size; for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) { Vector2i tile_id = tile_set_atlas_source->get_tile_id(i); @@ -89,6 +95,9 @@ Size2i TileAtlasView::_compute_alternative_tiles_control_size() { } void TileAtlasView::_update_zoom_and_panning(bool p_zoom_on_mouse_pos) { + if (tile_set_atlas_source.is_null()) { + return; + } float zoom = zoom_widget->get_zoom(); // Compute the minimum sizes. @@ -153,6 +162,9 @@ void TileAtlasView::_center_view() { } void TileAtlasView::_base_tiles_root_control_gui_input(const Ref<InputEvent> &p_event) { + if (tile_set_atlas_source.is_null()) { + return; + } base_tiles_root_control->set_tooltip_text(""); Ref<InputEventMouseMotion> mm = p_event; @@ -169,6 +181,9 @@ void TileAtlasView::_base_tiles_root_control_gui_input(const Ref<InputEvent> &p_ } void TileAtlasView::_draw_base_tiles() { + if (tile_set.is_null() || tile_set_atlas_source.is_null()) { + return; + } Ref<Texture2D> texture = tile_set_atlas_source->get_texture(); if (texture.is_valid()) { Vector2i margins = tile_set_atlas_source->get_margins(); @@ -250,7 +265,7 @@ void TileAtlasView::_draw_base_tiles() { Vector2 offset_pos = Rect2(base_frame_rect).get_center() + Vector2(tile_set_atlas_source->get_tile_data(atlas_coords, 0)->get_texture_origin()); // Draw the tile. - TileMap::draw_tile(ci_rid, offset_pos, tile_set, source_id, atlas_coords, 0, frame); + TileMapLayer::draw_tile(ci_rid, offset_pos, tile_set, source_id, atlas_coords, 0, frame); } } @@ -314,6 +329,9 @@ void TileAtlasView::_clear_material_canvas_items() { } void TileAtlasView::_draw_base_tiles_texture_grid() { + if (tile_set_atlas_source.is_null()) { + return; + } Ref<Texture2D> texture = tile_set_atlas_source->get_texture(); if (texture.is_valid()) { Vector2i margins = tile_set_atlas_source->get_margins(); @@ -344,6 +362,9 @@ void TileAtlasView::_draw_base_tiles_texture_grid() { } void TileAtlasView::_draw_base_tiles_shape_grid() { + if (tile_set.is_null() || tile_set_atlas_source.is_null()) { + return; + } // Draw the shapes. Color grid_color = EDITOR_GET("editors/tiles_editor/grid_color"); Vector2i tile_shape_size = tile_set->get_tile_size(); @@ -382,6 +403,9 @@ void TileAtlasView::_alternative_tiles_root_control_gui_input(const Ref<InputEve } void TileAtlasView::_draw_alternatives() { + if (tile_set.is_null() || tile_set_atlas_source.is_null()) { + return; + } // Draw the alternative tiles. Ref<Texture2D> texture = tile_set_atlas_source->get_texture(); if (texture.is_valid()) { @@ -411,7 +435,7 @@ void TileAtlasView::_draw_alternatives() { } // Draw the tile. - TileMap::draw_tile(ci_rid, offset_pos, tile_set, source_id, atlas_coords, alternative_id); + TileMapLayer::draw_tile(ci_rid, offset_pos, tile_set, source_id, atlas_coords, alternative_id); // Increment the x position. current_pos.x += transposed ? texture_region_size.y : texture_region_size.x; @@ -432,12 +456,12 @@ void TileAtlasView::_draw_background_right() { } void TileAtlasView::set_atlas_source(TileSet *p_tile_set, TileSetAtlasSource *p_tile_set_atlas_source, int p_source_id) { - tile_set = p_tile_set; - tile_set_atlas_source = p_tile_set_atlas_source; + tile_set = Ref<TileSet>(p_tile_set); + tile_set_atlas_source = Ref<TileSetAtlasSource>(p_tile_set_atlas_source); _clear_material_canvas_items(); - if (!tile_set) { + if (tile_set.is_null()) { return; } @@ -485,6 +509,10 @@ void TileAtlasView::set_padding(Side p_side, int p_padding) { } Vector2i TileAtlasView::get_atlas_tile_coords_at_pos(const Vector2 p_pos, bool p_clamp) const { + if (tile_set_atlas_source.is_null()) { + return Vector2i(); + } + Ref<Texture2D> texture = tile_set_atlas_source->get_texture(); if (!texture.is_valid()) { return TileSetSource::INVALID_ATLAS_COORDS; @@ -508,6 +536,10 @@ Vector2i TileAtlasView::get_atlas_tile_coords_at_pos(const Vector2 p_pos, bool p } void TileAtlasView::_update_alternative_tiles_rect_cache() { + if (tile_set_atlas_source.is_null()) { + return; + } + alternative_tiles_rect_cache.clear(); Rect2i current; diff --git a/editor/plugins/tiles/tile_atlas_view.h b/editor/plugins/tiles/tile_atlas_view.h index e5b4863b05..8fcf942056 100644 --- a/editor/plugins/tiles/tile_atlas_view.h +++ b/editor/plugins/tiles/tile_atlas_view.h @@ -45,8 +45,8 @@ class TileAtlasView : public Control { GDCLASS(TileAtlasView, Control); private: - TileSet *tile_set = nullptr; - TileSetAtlasSource *tile_set_atlas_source = nullptr; + Ref<TileSet> tile_set; + Ref<TileSetAtlasSource> tile_set_atlas_source; int source_id = TileSet::INVALID_SOURCE; enum DragType { diff --git a/editor/plugins/tiles/tile_data_editors.cpp b/editor/plugins/tiles/tile_data_editors.cpp index 94cbb371af..a94080e253 100644 --- a/editor/plugins/tiles/tile_data_editors.cpp +++ b/editor/plugins/tiles/tile_data_editors.cpp @@ -1325,7 +1325,7 @@ TileDataDefaultEditor::TileDataDefaultEditor() { picker_button = memnew(Button); picker_button->set_theme_type_variation("FlatButton"); picker_button->set_toggle_mode(true); - picker_button->set_shortcut(ED_SHORTCUT("tiles_editor/picker", TTR("Picker"), Key::P)); + picker_button->set_shortcut(ED_GET_SHORTCUT("tiles_editor/picker")); toolbar->add_child(picker_button); } @@ -2812,7 +2812,7 @@ TileDataTerrainsEditor::TileDataTerrainsEditor() { picker_button = memnew(Button); picker_button->set_theme_type_variation("FlatButton"); picker_button->set_toggle_mode(true); - picker_button->set_shortcut(ED_SHORTCUT("tiles_editor/picker", TTR("Picker"), Key::P)); + picker_button->set_shortcut(ED_GET_SHORTCUT("tiles_editor/picker")); toolbar->add_child(picker_button); // Setup diff --git a/editor/plugins/tiles/tile_map_layer_editor.cpp b/editor/plugins/tiles/tile_map_layer_editor.cpp index 98c05e9b15..5fd4fb3602 100644 --- a/editor/plugins/tiles/tile_map_layer_editor.cpp +++ b/editor/plugins/tiles/tile_map_layer_editor.cpp @@ -2183,13 +2183,6 @@ TileMapLayerEditorTilesPlugin::TileMapLayerEditorTilesPlugin() { ->get_viewport_control() ->connect(SceneStringName(mouse_exited), callable_mp(this, &TileMapLayerEditorTilesPlugin::_mouse_exited_viewport)); - // --- Shortcuts --- - ED_SHORTCUT("tiles_editor/cut", TTR("Cut"), KeyModifierMask::CMD_OR_CTRL | Key::X); - ED_SHORTCUT("tiles_editor/copy", TTR("Copy"), KeyModifierMask::CMD_OR_CTRL | Key::C); - ED_SHORTCUT("tiles_editor/paste", TTR("Paste"), KeyModifierMask::CMD_OR_CTRL | Key::V); - ED_SHORTCUT("tiles_editor/cancel", TTR("Cancel"), Key::ESCAPE); - ED_SHORTCUT("tiles_editor/delete", TTR("Delete"), Key::KEY_DELETE); - // --- Initialize references --- tile_map_clipboard.instantiate(); selection_pattern.instantiate(); @@ -2217,7 +2210,7 @@ TileMapLayerEditorTilesPlugin::TileMapLayerEditorTilesPlugin() { paint_tool_button->set_theme_type_variation("FlatButton"); paint_tool_button->set_toggle_mode(true); paint_tool_button->set_button_group(tool_buttons_group); - paint_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/paint_tool", TTR("Paint"), Key::D)); + paint_tool_button->set_shortcut(ED_GET_SHORTCUT("tiles_editor/paint_tool")); paint_tool_button->set_tooltip_text(TTR("Shift: Draw line.") + "\n" + keycode_get_string((Key)KeyModifierMask::CMD_OR_CTRL) + TTR("Shift: Draw rectangle.")); paint_tool_button->connect(SceneStringName(pressed), callable_mp(this, &TileMapLayerEditorTilesPlugin::_update_toolbar)); tilemap_tiles_tools_buttons->add_child(paint_tool_button); @@ -2228,7 +2221,7 @@ TileMapLayerEditorTilesPlugin::TileMapLayerEditorTilesPlugin() { line_tool_button->set_toggle_mode(true); line_tool_button->set_button_group(tool_buttons_group); // TRANSLATORS: This refers to the line tool in the tilemap editor. - line_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/line_tool", TTR("Line", "Tool"), Key::L)); + line_tool_button->set_shortcut(ED_GET_SHORTCUT("tiles_editor/line_tool")); line_tool_button->connect(SceneStringName(pressed), callable_mp(this, &TileMapLayerEditorTilesPlugin::_update_toolbar)); tilemap_tiles_tools_buttons->add_child(line_tool_button); viewport_shortcut_buttons.push_back(line_tool_button); @@ -2237,7 +2230,7 @@ TileMapLayerEditorTilesPlugin::TileMapLayerEditorTilesPlugin() { rect_tool_button->set_theme_type_variation("FlatButton"); rect_tool_button->set_toggle_mode(true); rect_tool_button->set_button_group(tool_buttons_group); - rect_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/rect_tool", TTR("Rect"), Key::R)); + rect_tool_button->set_shortcut(ED_GET_SHORTCUT("tiles_editor/rect_tool")); rect_tool_button->connect(SceneStringName(pressed), callable_mp(this, &TileMapLayerEditorTilesPlugin::_update_toolbar)); tilemap_tiles_tools_buttons->add_child(rect_tool_button); viewport_shortcut_buttons.push_back(rect_tool_button); @@ -2246,7 +2239,7 @@ TileMapLayerEditorTilesPlugin::TileMapLayerEditorTilesPlugin() { bucket_tool_button->set_theme_type_variation("FlatButton"); bucket_tool_button->set_toggle_mode(true); bucket_tool_button->set_button_group(tool_buttons_group); - bucket_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/bucket_tool", TTR("Bucket"), Key::B)); + bucket_tool_button->set_shortcut(ED_GET_SHORTCUT("tiles_editor/bucket_tool")); bucket_tool_button->connect(SceneStringName(pressed), callable_mp(this, &TileMapLayerEditorTilesPlugin::_update_toolbar)); tilemap_tiles_tools_buttons->add_child(bucket_tool_button); toolbar->add_child(tilemap_tiles_tools_buttons); @@ -2263,7 +2256,7 @@ TileMapLayerEditorTilesPlugin::TileMapLayerEditorTilesPlugin() { picker_button = memnew(Button); picker_button->set_theme_type_variation("FlatButton"); picker_button->set_toggle_mode(true); - picker_button->set_shortcut(ED_SHORTCUT("tiles_editor/picker", TTR("Picker"), Key::P)); + picker_button->set_shortcut(ED_GET_SHORTCUT("tiles_editor/picker")); Key key = (OS::get_singleton()->has_feature("macos") || OS::get_singleton()->has_feature("web_macos") || OS::get_singleton()->has_feature("web_ios")) ? Key::META : Key::CTRL; picker_button->set_tooltip_text(vformat(TTR("Alternatively hold %s with other tools to pick tile."), find_keycode_name(key))); picker_button->connect(SceneStringName(pressed), callable_mp(CanvasItemEditor::get_singleton(), &CanvasItemEditor::update_viewport)); @@ -2274,7 +2267,7 @@ TileMapLayerEditorTilesPlugin::TileMapLayerEditorTilesPlugin() { erase_button = memnew(Button); erase_button->set_theme_type_variation("FlatButton"); erase_button->set_toggle_mode(true); - erase_button->set_shortcut(ED_SHORTCUT("tiles_editor/eraser", TTR("Eraser"), Key::E)); + erase_button->set_shortcut(ED_GET_SHORTCUT("tiles_editor/eraser")); erase_button->set_tooltip_text(TTR("Alternatively use RMB to erase tiles.")); erase_button->connect(SceneStringName(pressed), callable_mp(CanvasItemEditor::get_singleton(), &CanvasItemEditor::update_viewport)); tools_settings->add_child(erase_button); @@ -3552,7 +3545,7 @@ TileMapLayerEditorTerrainsPlugin::TileMapLayerEditorTerrainsPlugin() { paint_tool_button->set_toggle_mode(true); paint_tool_button->set_button_group(tool_buttons_group); paint_tool_button->set_pressed(true); - paint_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/paint_tool", TTR("Paint"), Key::D)); + paint_tool_button->set_shortcut(ED_GET_SHORTCUT("tiles_editor/paint_tool")); paint_tool_button->connect(SceneStringName(pressed), callable_mp(this, &TileMapLayerEditorTerrainsPlugin::_update_toolbar)); tilemap_tiles_tools_buttons->add_child(paint_tool_button); viewport_shortcut_buttons.push_back(paint_tool_button); @@ -3561,7 +3554,7 @@ TileMapLayerEditorTerrainsPlugin::TileMapLayerEditorTerrainsPlugin() { line_tool_button->set_theme_type_variation("FlatButton"); line_tool_button->set_toggle_mode(true); line_tool_button->set_button_group(tool_buttons_group); - line_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/line_tool", TTR("Line"), Key::L)); + line_tool_button->set_shortcut(ED_GET_SHORTCUT("tiles_editor/line_tool")); line_tool_button->connect(SceneStringName(pressed), callable_mp(this, &TileMapLayerEditorTerrainsPlugin::_update_toolbar)); tilemap_tiles_tools_buttons->add_child(line_tool_button); viewport_shortcut_buttons.push_back(line_tool_button); @@ -3570,7 +3563,7 @@ TileMapLayerEditorTerrainsPlugin::TileMapLayerEditorTerrainsPlugin() { rect_tool_button->set_theme_type_variation("FlatButton"); rect_tool_button->set_toggle_mode(true); rect_tool_button->set_button_group(tool_buttons_group); - rect_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/rect_tool", TTR("Rect"), Key::R)); + rect_tool_button->set_shortcut(ED_GET_SHORTCUT("tiles_editor/rect_tool")); rect_tool_button->connect(SceneStringName(pressed), callable_mp(this, &TileMapLayerEditorTerrainsPlugin::_update_toolbar)); tilemap_tiles_tools_buttons->add_child(rect_tool_button); viewport_shortcut_buttons.push_back(rect_tool_button); @@ -3579,7 +3572,7 @@ TileMapLayerEditorTerrainsPlugin::TileMapLayerEditorTerrainsPlugin() { bucket_tool_button->set_theme_type_variation("FlatButton"); bucket_tool_button->set_toggle_mode(true); bucket_tool_button->set_button_group(tool_buttons_group); - bucket_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/bucket_tool", TTR("Bucket"), Key::B)); + bucket_tool_button->set_shortcut(ED_GET_SHORTCUT("tiles_editor/bucket_tool")); bucket_tool_button->connect(SceneStringName(pressed), callable_mp(this, &TileMapLayerEditorTerrainsPlugin::_update_toolbar)); tilemap_tiles_tools_buttons->add_child(bucket_tool_button); viewport_shortcut_buttons.push_back(bucket_tool_button); @@ -3597,7 +3590,7 @@ TileMapLayerEditorTerrainsPlugin::TileMapLayerEditorTerrainsPlugin() { picker_button = memnew(Button); picker_button->set_theme_type_variation("FlatButton"); picker_button->set_toggle_mode(true); - picker_button->set_shortcut(ED_SHORTCUT("tiles_editor/picker", TTR("Picker"), Key::P)); + picker_button->set_shortcut(ED_GET_SHORTCUT("tiles_editor/picker")); picker_button->connect(SceneStringName(pressed), callable_mp(CanvasItemEditor::get_singleton(), &CanvasItemEditor::update_viewport)); tools_settings->add_child(picker_button); viewport_shortcut_buttons.push_back(picker_button); @@ -3606,7 +3599,7 @@ TileMapLayerEditorTerrainsPlugin::TileMapLayerEditorTerrainsPlugin() { erase_button = memnew(Button); erase_button->set_theme_type_variation("FlatButton"); erase_button->set_toggle_mode(true); - erase_button->set_shortcut(ED_SHORTCUT("tiles_editor/eraser", TTR("Eraser"), Key::E)); + erase_button->set_shortcut(ED_GET_SHORTCUT("tiles_editor/eraser")); erase_button->connect(SceneStringName(pressed), callable_mp(CanvasItemEditor::get_singleton(), &CanvasItemEditor::update_viewport)); tools_settings->add_child(erase_button); viewport_shortcut_buttons.push_back(erase_button); diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp index e4fd32a637..03070bc6b5 100644 --- a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp +++ b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp @@ -2627,7 +2627,7 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() { tools_settings_erase_button = memnew(Button); tools_settings_erase_button->set_theme_type_variation("FlatButton"); tools_settings_erase_button->set_toggle_mode(true); - tools_settings_erase_button->set_shortcut(ED_SHORTCUT("tiles_editor/eraser", TTR("Eraser"), Key::E)); + tools_settings_erase_button->set_shortcut(ED_GET_SHORTCUT("tiles_editor/eraser")); tools_settings_erase_button->set_shortcut_context(this); tool_settings->add_child(tools_settings_erase_button); @@ -2687,7 +2687,7 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() { tile_create_help->set_grow_direction_preset(Control::PRESET_BOTTOM_LEFT); base_tile_popup_menu = memnew(PopupMenu); - base_tile_popup_menu->add_shortcut(ED_SHORTCUT("tiles_editor/delete", TTR("Delete"), Key::KEY_DELETE), TILE_DELETE); + base_tile_popup_menu->add_shortcut(ED_GET_SHORTCUT("tiles_editor/delete"), TILE_DELETE); base_tile_popup_menu->add_item(TTR("Create an Alternative Tile"), TILE_CREATE_ALTERNATIVE); base_tile_popup_menu->connect("id_pressed", callable_mp(this, &TileSetAtlasSourceEditor::_menu_option)); tile_atlas_view->add_child(base_tile_popup_menu); @@ -2938,6 +2938,18 @@ bool EditorInspectorPluginTileData::parse_property(Object *p_object, const Varia add_property_editor(p_path, ep); return true; } + } else if (p_path.begins_with("custom_data_") && p_path.trim_prefix("custom_data_").is_valid_int()) { + // Custom data layers. + int layer_index = components[0].trim_prefix("custom_data_").to_int(); + ERR_FAIL_COND_V(layer_index < 0, false); + EditorProperty *ep = EditorInspectorDefaultPlugin::get_editor_for_property(p_object, p_type, p_path, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT); + const TileSetAtlasSourceEditor::AtlasTileProxyObject *proxy_obj = Object::cast_to<TileSetAtlasSourceEditor::AtlasTileProxyObject>(p_object); + const TileSetAtlasSource *atlas_source = *proxy_obj->get_edited_tile_set_atlas_source(); + ERR_FAIL_NULL_V(atlas_source, false); + const TileSet *tile_set = atlas_source->get_tile_set(); + ERR_FAIL_NULL_V(tile_set, false); + add_property_editor(p_path, ep, false, tile_set->get_custom_data_layer_name(layer_index)); + return true; } return false; } diff --git a/editor/plugins/tiles/tiles_editor_plugin.cpp b/editor/plugins/tiles/tiles_editor_plugin.cpp index 6833a36138..f1cc69ce18 100644 --- a/editor/plugins/tiles/tiles_editor_plugin.cpp +++ b/editor/plugins/tiles/tiles_editor_plugin.cpp @@ -303,6 +303,19 @@ TilesEditorUtils::TilesEditorUtils() { singleton = this; // Pattern preview generation thread. pattern_preview_thread.start(_thread_func, this); + + ED_SHORTCUT("tiles_editor/cut", TTR("Cut"), KeyModifierMask::CMD_OR_CTRL | Key::X); + ED_SHORTCUT("tiles_editor/copy", TTR("Copy"), KeyModifierMask::CMD_OR_CTRL | Key::C); + ED_SHORTCUT("tiles_editor/paste", TTR("Paste"), KeyModifierMask::CMD_OR_CTRL | Key::V); + ED_SHORTCUT("tiles_editor/cancel", TTR("Cancel"), Key::ESCAPE); + ED_SHORTCUT("tiles_editor/delete", TTR("Delete"), Key::KEY_DELETE); + + ED_SHORTCUT("tiles_editor/paint_tool", TTR("Paint"), Key::D); + ED_SHORTCUT("tiles_editor/line_tool", TTR("Line", "Tool"), Key::L); + ED_SHORTCUT("tiles_editor/rect_tool", TTR("Rect"), Key::R); + ED_SHORTCUT("tiles_editor/bucket_tool", TTR("Bucket"), Key::B); + ED_SHORTCUT("tiles_editor/eraser", TTR("Eraser"), Key::E); + ED_SHORTCUT("tiles_editor/picker", TTR("Picker"), Key::P); } TilesEditorUtils::~TilesEditorUtils() { diff --git a/editor/project_manager/project_list.cpp b/editor/project_manager/project_list.cpp index 32f088ec60..f474464f6c 100644 --- a/editor/project_manager/project_list.cpp +++ b/editor/project_manager/project_list.cpp @@ -32,6 +32,8 @@ #include "core/config/project_settings.h" #include "core/io/dir_access.h" +#include "core/os/time.h" +#include "core/version.h" #include "editor/editor_paths.h" #include "editor/editor_settings.h" #include "editor/editor_string_names.h" @@ -130,12 +132,29 @@ void ProjectListItemControl::set_project_icon(const Ref<Texture2D> &p_icon) { project_icon->set_texture(p_icon); } +void ProjectListItemControl::set_last_edited_info(const String &p_info) { + last_edited_info->set_text(p_info); +} + +void ProjectListItemControl::set_project_version(const String &p_info) { + project_version->set_text(p_info); +} + void ProjectListItemControl::set_unsupported_features(PackedStringArray p_features) { if (p_features.size() > 0) { String tooltip_text = ""; for (int i = 0; i < p_features.size(); i++) { if (ProjectList::project_feature_looks_like_version(p_features[i])) { - tooltip_text += TTR("This project was last edited in a different Godot version: ") + p_features[i] + "\n"; + PackedStringArray project_version_split = p_features[i].split("."); + int project_version_major = 0, project_version_minor = 0; + if (project_version_split.size() >= 2) { + project_version_major = project_version_split[0].to_int(); + project_version_minor = project_version_split[1].to_int(); + } + if (VERSION_MAJOR != project_version_major || VERSION_MINOR <= project_version_minor) { + // Don't show a warning if the project was last edited in a previous minor version. + tooltip_text += TTR("This project was last edited in a different Godot version: ") + p_features[i] + "\n"; + } p_features.remove_at(i); i--; } @@ -144,6 +163,10 @@ void ProjectListItemControl::set_unsupported_features(PackedStringArray p_featur String unsupported_features_str = String(", ").join(p_features); tooltip_text += TTR("This project uses features unsupported by the current build:") + "\n" + unsupported_features_str; } + if (tooltip_text.is_empty()) { + return; + } + project_version->set_tooltip_text(tooltip_text); project_unsupported_features->set_tooltip_text(tooltip_text); project_unsupported_features->show(); } else { @@ -272,12 +295,24 @@ ProjectListItemControl::ProjectListItemControl() { project_path->set_modulate(Color(1, 1, 1, 0.5)); path_hb->add_child(project_path); + last_edited_info = memnew(Label); + last_edited_info->set_name("LastEditedInfo"); + last_edited_info->set_mouse_filter(Control::MOUSE_FILTER_PASS); + last_edited_info->set_tooltip_text("Last edited timestamp"); + last_edited_info->set_modulate(Color(1, 1, 1, 0.5)); + path_hb->add_child(last_edited_info); + project_unsupported_features = memnew(TextureRect); project_unsupported_features->set_name("ProjectUnsupportedFeatures"); project_unsupported_features->set_stretch_mode(TextureRect::STRETCH_KEEP_CENTERED); path_hb->add_child(project_unsupported_features); project_unsupported_features->hide(); + project_version = memnew(Label); + project_version->set_name("ProjectVersion"); + project_version->set_mouse_filter(Control::MOUSE_FILTER_PASS); + path_hb->add_child(project_version); + Control *spacer = memnew(Control); spacer->set_custom_minimum_size(Size2(10, 10)); path_hb->add_child(spacer); @@ -409,6 +444,24 @@ ProjectList::Item ProjectList::load_project_data(const String &p_path, bool p_fa PackedStringArray project_features = cf->get_value("application", "config/features", PackedStringArray()); PackedStringArray unsupported_features = ProjectSettings::get_unsupported_features(project_features); + String project_version = "?"; + for (int i = 0; i < project_features.size(); i++) { + if (ProjectList::project_feature_looks_like_version(project_features[i])) { + project_version = project_features[i]; + break; + } + } + + if (config_version < ProjectSettings::CONFIG_VERSION) { + // Previous versions may not have unsupported features. + if (config_version == 4) { + unsupported_features.push_back("3.x"); + project_version = "3.x"; + } else { + unsupported_features.push_back("Unknown version"); + } + } + uint64_t last_edited = 0; if (cf_err == OK) { // The modification date marks the date the project was last edited. @@ -433,7 +486,7 @@ ProjectList::Item ProjectList::load_project_data(const String &p_path, bool p_fa ProjectManager::get_singleton()->add_new_tag(tag); } - return Item(project_name, description, tags, p_path, icon, main_scene, unsupported_features, last_edited, p_favorite, grayed, missing, config_version); + return Item(project_name, description, project_version, tags, p_path, icon, main_scene, unsupported_features, last_edited, p_favorite, grayed, missing, config_version); } void ProjectList::_update_icons_async() { @@ -706,6 +759,8 @@ void ProjectList::_create_project_item_control(int p_index) { hb->set_tooltip_text(item.description); hb->set_tags(item.tags, this); hb->set_unsupported_features(item.unsupported_features.duplicate()); + hb->set_project_version(item.project_version); + hb->set_last_edited_info(Time::get_singleton()->get_datetime_string_from_unix_time(item.last_edited, true)); hb->set_is_favorite(item.favorite); hb->set_is_missing(item.missing); diff --git a/editor/project_manager/project_list.h b/editor/project_manager/project_list.h index 981df0f3a0..6e0f5830ac 100644 --- a/editor/project_manager/project_list.h +++ b/editor/project_manager/project_list.h @@ -51,6 +51,8 @@ class ProjectListItemControl : public HBoxContainer { TextureRect *project_icon = nullptr; Label *project_title = nullptr; Label *project_path = nullptr; + Label *last_edited_info = nullptr; + Label *project_version = nullptr; TextureRect *project_unsupported_features = nullptr; HBoxContainer *tag_container = nullptr; @@ -71,6 +73,8 @@ public: void set_project_path(const String &p_path); void set_tags(const PackedStringArray &p_tags, ProjectList *p_parent_list); void set_project_icon(const Ref<Texture2D> &p_icon); + void set_last_edited_info(const String &p_info); + void set_project_version(const String &p_version); void set_unsupported_features(PackedStringArray p_features); bool should_load_project_icon() const; @@ -100,6 +104,7 @@ public: struct Item { String project_name; String description; + String project_version; PackedStringArray tags; String tag_sort_string; String path; @@ -118,6 +123,7 @@ public: Item(const String &p_name, const String &p_description, + const String &p_project_version, const PackedStringArray &p_tags, const String &p_path, const String &p_icon, @@ -130,6 +136,7 @@ public: int p_version) { project_name = p_name; description = p_description; + project_version = p_project_version; tags = p_tags; path = p_path; icon = p_icon; diff --git a/editor/project_settings_editor.cpp b/editor/project_settings_editor.cpp index cff95cadc8..dc9d1df4e8 100644 --- a/editor/project_settings_editor.cpp +++ b/editor/project_settings_editor.cpp @@ -54,7 +54,7 @@ void ProjectSettingsEditor::popup_project_settings(bool p_clear_filter) { if (saved_size != Rect2()) { popup(saved_size); } else { - popup_centered_clamped(Size2(900, 700) * EDSCALE, 0.8); + popup_centered_clamped(Size2(1200, 700) * EDSCALE, 0.8); } _add_feature_overrides(); diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index 66d26c0a6c..9209c26876 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -3104,7 +3104,23 @@ void SceneTreeDock::set_edited_scene(Node *p_scene) { edited_scene = p_scene; } +static bool _is_same_selection(const Vector<Node *> &p_first, const List<Node *> &p_second) { + if (p_first.size() != p_second.size()) { + return false; + } + for (Node *node : p_second) { + if (!p_first.has(node)) { + return false; + } + } + return true; +} + void SceneTreeDock::set_selection(const Vector<Node *> &p_nodes) { + // If the nodes selected are the same independently of order then return early. + if (_is_same_selection(p_nodes, editor_selection->get_full_selected_node_list())) { + return; + } editor_selection->clear(); for (Node *node : p_nodes) { editor_selection->add_node(node); @@ -3317,9 +3333,9 @@ void SceneTreeDock::_files_dropped(const Vector<String> &p_files, NodePath p_to, // Either instantiate scenes or create AudioStreamPlayers. int to_pos = -1; _normalize_drop(node, to_pos, p_type); - if (res_type == "PackedScene") { + if (ClassDB::is_parent_class(res_type, "PackedScene")) { _perform_instantiate_scenes(p_files, node, to_pos); - } else { + } else if (ClassDB::is_parent_class(res_type, "AudioStream")) { _perform_create_audio_stream_players(p_files, node, to_pos); } } @@ -3651,7 +3667,7 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) { if (profile_allow_editing) { menu->add_separator(); - menu->add_icon_shortcut(get_editor_theme_icon(SNAME("Remove")), ED_SHORTCUT("scene_tree/delete", TTR("Delete Node(s)"), Key::KEY_DELETE), TOOL_ERASE); + menu->add_icon_shortcut(get_editor_theme_icon(SNAME("Remove")), ED_GET_SHORTCUT("scene_tree/delete"), TOOL_ERASE); } menu->reset_size(); menu->set_position(p_menu_pos); diff --git a/editor/themes/SCsub b/editor/themes/SCsub index 65cfb6a8be..e8f96e4299 100644 --- a/editor/themes/SCsub +++ b/editor/themes/SCsub @@ -3,8 +3,8 @@ Import("env") import glob -import editor_theme_builders +import editor_theme_builders # Fonts flist = glob.glob(env.Dir("#thirdparty").abspath + "/fonts/*.ttf") diff --git a/gles3_builders.py b/gles3_builders.py index 1491927feb..a4928c81c5 100644 --- a/gles3_builders.py +++ b/gles3_builders.py @@ -1,9 +1,10 @@ """Functions used to generate source files during build time""" import os.path -from methods import print_error from typing import Optional +from methods import print_error + class GLES3HeaderStruct: def __init__(self): @@ -91,11 +92,11 @@ def include_file_in_gles3_header(filename: str, header_data: GLES3HeaderStruct, includeline = line.replace("#include ", "").strip()[1:-1] included_file = os.path.relpath(os.path.dirname(filename) + "/" + includeline) - if not included_file in header_data.vertex_included_files and header_data.reading == "vertex": + if included_file not in header_data.vertex_included_files and header_data.reading == "vertex": header_data.vertex_included_files += [included_file] if include_file_in_gles3_header(included_file, header_data, depth + 1) is None: print_error(f'In file "{filename}": #include "{includeline}" could not be found!"') - elif not included_file in header_data.fragment_included_files and header_data.reading == "fragment": + elif included_file not in header_data.fragment_included_files and header_data.reading == "fragment": header_data.fragment_included_files += [included_file] if include_file_in_gles3_header(included_file, header_data, depth + 1) is None: print_error(f'In file "{filename}": #include "{includeline}" could not be found!"') @@ -121,7 +122,7 @@ def include_file_in_gles3_header(filename: str, header_data: GLES3HeaderStruct, # unfiorm array x = x[: x.find("[")] - if not x in header_data.texunit_names: + if x not in header_data.texunit_names: header_data.texunits += [(x, texunit)] header_data.texunit_names += [x] @@ -142,7 +143,7 @@ def include_file_in_gles3_header(filename: str, header_data: GLES3HeaderStruct, # unfiorm array x = x[: x.find("[")] - if not x in header_data.ubo_names: + if x not in header_data.ubo_names: header_data.ubos += [(x, ubo)] header_data.ubo_names += [x] @@ -157,7 +158,7 @@ def include_file_in_gles3_header(filename: str, header_data: GLES3HeaderStruct, # unfiorm array x = x[: x.find("[")] - if not x in header_data.uniforms: + if x not in header_data.uniforms: header_data.uniforms += [x] if (line.strip().find("out ") == 0 or line.strip().find("flat ") == 0) and line.find("tfb:") != -1: diff --git a/glsl_builders.py b/glsl_builders.py index 22f4de74b1..05aab3acbb 100644 --- a/glsl_builders.py +++ b/glsl_builders.py @@ -1,8 +1,9 @@ """Functions used to generate source files during build time""" import os.path +from typing import Iterable, Optional + from methods import print_error -from typing import Optional, Iterable def generate_inline_code(input_lines: Iterable[str], insert_newline: bool = True): @@ -77,15 +78,15 @@ def include_file_in_rd_header(filename: str, header_data: RDHeaderStruct, depth: else: included_file = os.path.relpath(os.path.dirname(filename) + "/" + includeline) - if not included_file in header_data.vertex_included_files and header_data.reading == "vertex": + if included_file not in header_data.vertex_included_files and header_data.reading == "vertex": header_data.vertex_included_files += [included_file] if include_file_in_rd_header(included_file, header_data, depth + 1) is None: print_error(f'In file "{filename}": #include "{includeline}" could not be found!"') - elif not included_file in header_data.fragment_included_files and header_data.reading == "fragment": + elif included_file not in header_data.fragment_included_files and header_data.reading == "fragment": header_data.fragment_included_files += [included_file] if include_file_in_rd_header(included_file, header_data, depth + 1) is None: print_error(f'In file "{filename}": #include "{includeline}" could not be found!"') - elif not included_file in header_data.compute_included_files and header_data.reading == "compute": + elif included_file not in header_data.compute_included_files and header_data.reading == "compute": header_data.compute_included_files += [included_file] if include_file_in_rd_header(included_file, header_data, depth + 1) is None: print_error(f'In file "{filename}": #include "{includeline}" could not be found!"') diff --git a/main/main.cpp b/main/main.cpp index 65f637d778..2bd421e5af 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -2686,9 +2686,18 @@ Error Main::setup2() { Color boot_bg_color = GLOBAL_DEF_BASIC("application/boot_splash/bg_color", boot_splash_bg_color); DisplayServer::set_early_window_clear_color_override(true, boot_bg_color); + DisplayServer::Context context; + if (editor) { + context = DisplayServer::CONTEXT_EDITOR; + } else if (project_manager) { + context = DisplayServer::CONTEXT_PROJECTMAN; + } else { + context = DisplayServer::CONTEXT_ENGINE; + } + // rendering_driver now held in static global String in main and initialized in setup() Error err; - display_server = DisplayServer::create(display_driver_idx, rendering_driver, window_mode, window_vsync_mode, window_flags, window_position, window_size, init_screen, err); + display_server = DisplayServer::create(display_driver_idx, rendering_driver, window_mode, window_vsync_mode, window_flags, window_position, window_size, init_screen, context, err); if (err != OK || display_server == nullptr) { // We can't use this display server, try other ones as fallback. // Skip headless (always last registered) because that's not what users @@ -2697,7 +2706,7 @@ Error Main::setup2() { if (i == display_driver_idx) { continue; // Don't try the same twice. } - display_server = DisplayServer::create(i, rendering_driver, window_mode, window_vsync_mode, window_flags, window_position, window_size, init_screen, err); + display_server = DisplayServer::create(i, rendering_driver, window_mode, window_vsync_mode, window_flags, window_position, window_size, init_screen, context, err); if (err == OK && display_server != nullptr) { break; } @@ -2832,8 +2841,10 @@ Error Main::setup2() { OS::get_singleton()->benchmark_end_measure("Startup", "Servers"); +#ifndef WEB_ENABLED // Add a blank line for readability. Engine::get_singleton()->print_header(""); +#endif // WEB_ENABLED register_core_singletons(); @@ -3807,16 +3818,12 @@ int Main::start() { ERR_PRINT("Failed to load scene"); } } - DisplayServer::get_singleton()->set_context(DisplayServer::CONTEXT_EDITOR); if (!debug_server_uri.is_empty()) { EditorDebuggerNode::get_singleton()->start(debug_server_uri); EditorDebuggerNode::get_singleton()->set_keep_open(true); } } #endif - if (!editor) { - DisplayServer::get_singleton()->set_context(DisplayServer::CONTEXT_ENGINE); - } } if (!project_manager && !editor) { // game @@ -3874,7 +3881,6 @@ int Main::start() { ProgressDialog *progress_dialog = memnew(ProgressDialog); pmanager->add_child(progress_dialog); sml->get_root()->add_child(pmanager); - DisplayServer::get_singleton()->set_context(DisplayServer::CONTEXT_PROJECTMAN); OS::get_singleton()->benchmark_end_measure("Startup", "Project Manager"); } diff --git a/methods.py b/methods.py index da221cc0ea..f3798d121a 100644 --- a/methods.py +++ b/methods.py @@ -1,17 +1,14 @@ +import contextlib +import glob import os -import sys import re -import glob import subprocess -import contextlib +import sys from collections import OrderedDict -from collections.abc import Mapping from enum import Enum -from typing import Generator, Optional -from io import TextIOWrapper, StringIO +from io import StringIO, TextIOWrapper from pathlib import Path -from os.path import normpath, basename - +from typing import Generator, Optional # Get the "Godot" folder name ahead of time base_folder_path = str(os.path.abspath(Path(__file__).parent)) + "/" @@ -199,7 +196,7 @@ def add_module_version_string(self, s): def get_version_info(module_version_string="", silent=False): build_name = "custom_build" - if os.getenv("BUILD_NAME") != None: + if os.getenv("BUILD_NAME") is not None: build_name = str(os.getenv("BUILD_NAME")) if not silent: print(f"Using custom build name: '{build_name}'.") @@ -221,7 +218,7 @@ def get_version_info(module_version_string="", silent=False): # For dev snapshots (alpha, beta, RC, etc.) we do not commit status change to Git, # so this define provides a way to override it without having to modify the source. - if os.getenv("GODOT_VERSION_STATUS") != None: + if os.getenv("GODOT_VERSION_STATUS") is not None: version_info["status"] = str(os.getenv("GODOT_VERSION_STATUS")) if not silent: print(f"Using version status '{version_info['status']}', overriding the original '{version.status}'.") @@ -435,7 +432,7 @@ def module_check_dependencies(self, module): required_deps = self.module_dependencies[module][0] if module in self.module_dependencies else [] for dep in required_deps: opt = "module_{}_enabled".format(dep) - if not opt in self or not self[opt]: + if opt not in self or not self[opt]: missing_deps.append(dep) if missing_deps != []: @@ -450,7 +447,6 @@ def module_check_dependencies(self, module): def sort_module_list(env): - out = OrderedDict() deps = {k: v[0] + list(filter(lambda x: x in env.module_list, v[1])) for k, v in env.module_dependencies.items()} frontier = list(env.module_list.keys()) @@ -535,6 +531,7 @@ def no_verbose(env): link_shared_library_message = "{}Linking Shared Library {}$TARGET{} ...{}".format(*colors) java_library_message = "{}Creating Java Archive {}$TARGET{} ...{}".format(*colors) compiled_resource_message = "{}Creating Compiled Resource {}$TARGET{} ...{}".format(*colors) + zip_archive_message = "{}Archiving {}$TARGET{} ...{}".format(*colors) generated_file_message = "{}Generating {}$TARGET{} ...{}".format(*colors) env["CXXCOMSTR"] = compile_source_message @@ -548,6 +545,7 @@ def no_verbose(env): env["JARCOMSTR"] = java_library_message env["JAVACCOMSTR"] = java_compile_source_message env["RCCOMSTR"] = compiled_resource_message + env["ZIPCOMSTR"] = zip_archive_message env["GENCOMSTR"] = generated_file_message @@ -650,7 +648,7 @@ def detect_visual_c_compiler_version(tools_env): def find_visual_c_batch_file(env): - from SCons.Tool.MSCommon.vc import get_default_version, get_host_target, find_batch_file, find_vc_pdir + from SCons.Tool.MSCommon.vc import find_batch_file, find_vc_pdir, get_default_version, get_host_target msvc_version = get_default_version(env) @@ -696,10 +694,7 @@ def glob_recursive(pattern, node="."): def add_to_vs_project(env, sources): for x in sources: - if type(x) == type(""): - fname = env.File(x).path - else: - fname = env.File(x)[0].path + fname = env.File(x).path if isinstance(x, str) else env.File(x)[0].path pieces = fname.split(".") if len(pieces) > 0: basename = pieces[0] @@ -884,7 +879,8 @@ def show_progress(env): return import sys - from SCons.Script import Progress, Command, AlwaysBuild + + from SCons.Script import AlwaysBuild, Command, Progress screen = sys.stdout # Progress reporting is not available in non-TTY environments since it @@ -895,7 +891,8 @@ def show_progress(env): node_count_interval = 1 node_count_fname = str(env.Dir("#")) + "/.scons_node_count" - import time, math + import math + import time class cache_progress: # The default is 1 GB cache and 12 hours half life @@ -903,7 +900,7 @@ def show_progress(env): self.path = path self.limit = limit self.exponent_scale = math.log(2) / half_life - if env["verbose"] and path != None: + if env["verbose"] and path is not None: screen.write( "Current cache limit is {} (used: {})\n".format( self.convert_size(limit), self.convert_size(self.get_size(path)) @@ -1049,11 +1046,11 @@ def generate_vs_project(env, original_args, project_name="godot"): if type(f) is Node.FS.Dir: results += glob_recursive_2(pattern, dirs, f) r = Glob(str(node) + "/" + pattern, source=True) - if len(r) > 0 and not str(node) in dirs: + if len(r) > 0 and str(node) not in dirs: d = "" for part in str(node).split("\\"): d += part - if not d in dirs: + if d not in dirs: dirs.append(d) d += "\\" results += r @@ -1066,7 +1063,7 @@ def generate_vs_project(env, original_args, project_name="godot"): if val is not None: try: return _text2bool(val) - except: + except (ValueError, AttributeError): return default else: return default @@ -1239,13 +1236,13 @@ def generate_vs_project(env, original_args, project_name="godot"): others_active = [] for x in envsources: fname = "" - if type(x) == type(""): + if isinstance(x, str): fname = env.File(x).path else: # Some object files might get added directly as a File object and not a list. try: fname = env.File(x)[0].path - except: + except Exception: fname = x.path pass @@ -1324,7 +1321,7 @@ def generate_vs_project(env, original_args, project_name="godot"): itemlist = {} for item in activeItems: key = os.path.dirname(item).replace("\\", "_") - if not key in itemlist: + if key not in itemlist: itemlist[key] = [item] else: itemlist[key] += [item] @@ -1465,14 +1462,14 @@ def generate_vs_project(env, original_args, project_name="godot"): if godot_platform != "windows": configurations += [ f'<ProjectConfiguration Include="editor|{proj_plat}">', - f" <Configuration>editor</Configuration>", + " <Configuration>editor</Configuration>", f" <Platform>{proj_plat}</Platform>", "</ProjectConfiguration>", ] properties += [ f"<PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='editor|{proj_plat}'\">", - f" <GodotConfiguration>editor</GodotConfiguration>", + " <GodotConfiguration>editor</GodotConfiguration>", f" <GodotPlatform>{proj_plat}</GodotPlatform>", "</PropertyGroup>", ] diff --git a/misc/dist/html/service-worker.js b/misc/dist/html/service-worker.js index a5da7482f4..5211873e0b 100644 --- a/misc/dist/html/service-worker.js +++ b/misc/dist/html/service-worker.js @@ -160,8 +160,6 @@ self.addEventListener('message', (event) => { caches.delete(CACHE_NAME); } else if (msg === 'update') { self.skipWaiting().then(() => self.clients.claim()).then(() => self.clients.matchAll()).then((all) => all.forEach((c) => c.navigate(c.url))); - } else { - onClientMessage(event); } }); }); diff --git a/misc/dist/ios_xcode/godot_ios/godot_ios-Info.plist b/misc/dist/ios_xcode/godot_ios/godot_ios-Info.plist index ee5f1d35ae..3d2ae6b52b 100644 --- a/misc/dist/ios_xcode/godot_ios/godot_ios-Info.plist +++ b/misc/dist/ios_xcode/godot_ios/godot_ios-Info.plist @@ -54,7 +54,7 @@ </array> <key>UISupportedInterfaceOrientations~ipad</key> <array> - $interface_orientations + $ipad_interface_orientations </array> $additional_plist_content $plist_launch_screen_name diff --git a/misc/extension_api_validation/4.2-stable.expected b/misc/extension_api_validation/4.2-stable.expected index 6761512c15..b27f80ee29 100644 --- a/misc/extension_api_validation/4.2-stable.expected +++ b/misc/extension_api_validation/4.2-stable.expected @@ -357,3 +357,10 @@ Validate extension JSON: Error: Field 'classes/RenderingServer/methods/canvas_it Validate extension JSON: Error: Field 'classes/RenderingServer/methods/canvas_item_add_rect/arguments': size changed value in new API, from 3 to 4. Optional arguments added. Compatibility methods registered. + + +GH-92322 +-------- +Validate extension JSON: Error: Field 'classes/EditorInspectorPlugin/methods/add_property_editor/arguments': size changed value in new API, from 3 to 4. + +Optional arguments added. Compatibility methods registered. diff --git a/misc/scripts/install_d3d12_sdk_windows.py b/misc/scripts/install_d3d12_sdk_windows.py index aed80d81a4..d7574e6222 100755 --- a/misc/scripts/install_d3d12_sdk_windows.py +++ b/misc/scripts/install_d3d12_sdk_windows.py @@ -1,15 +1,15 @@ #!/usr/bin/env python import os -import urllib.request import shutil import subprocess import sys +import urllib.request # Enable ANSI escape code support on Windows 10 and later (for colored console output). # <https://github.com/python/cpython/issues/73245> if sys.platform == "win32": - from ctypes import windll, c_int, byref + from ctypes import byref, c_int, windll stdout_handle = windll.kernel32.GetStdHandle(c_int(-11)) mode = c_int(0) diff --git a/modules/SCsub b/modules/SCsub index 739c5de0b5..db0e563dc4 100644 --- a/modules/SCsub +++ b/modules/SCsub @@ -1,8 +1,9 @@ #!/usr/bin/env python -import methods import os +import methods + Import("env") env_modules = env.Clone() diff --git a/modules/fbx/editor/editor_scene_importer_ufbx.cpp b/modules/fbx/editor/editor_scene_importer_ufbx.cpp index 3cc919fae2..fb5e324390 100644 --- a/modules/fbx/editor/editor_scene_importer_ufbx.cpp +++ b/modules/fbx/editor/editor_scene_importer_ufbx.cpp @@ -80,6 +80,7 @@ Node *EditorSceneFormatImporterUFBX::import_scene(const String &p_path, uint32_t state->set_import_as_skeleton_bones(true); } p_flags |= EditorSceneFormatImporter::IMPORT_USE_NAMED_SKIN_BINDS; + state->set_bake_fps(p_options["animation/fps"]); Error err = fbx->append_from_file(path, state, p_flags, p_path.get_base_dir()); if (err != OK) { if (r_err) { @@ -87,7 +88,7 @@ Node *EditorSceneFormatImporterUFBX::import_scene(const String &p_path, uint32_t } return nullptr; } - return fbx->generate_scene(state, (float)p_options["animation/fps"], (bool)p_options["animation/trimming"], false); + return fbx->generate_scene(state, state->get_bake_fps(), (bool)p_options["animation/trimming"], false); } Variant EditorSceneFormatImporterUFBX::get_option_visibility(const String &p_path, bool p_for_animation, @@ -111,7 +112,7 @@ void EditorSceneFormatImporterUFBX::get_import_options(const String &p_path, void EditorSceneFormatImporterUFBX::handle_compatibility_options(HashMap<StringName, Variant> &p_import_params) const { if (!p_import_params.has("fbx/importer")) { - p_import_params["fbx/importer"] = EditorSceneFormatImporterUFBX::FBX_IMPORTER_UFBX; + p_import_params["fbx/importer"] = EditorSceneFormatImporterUFBX::FBX_IMPORTER_FBX2GLTF; } } diff --git a/modules/fbx/fbx_document.cpp b/modules/fbx/fbx_document.cpp index 1361e871de..b9d9ec7b6c 100644 --- a/modules/fbx/fbx_document.cpp +++ b/modules/fbx/fbx_document.cpp @@ -1381,6 +1381,10 @@ Error FBXDocument::_parse_animations(Ref<FBXState> p_state) { additional_data["time_end"] = fbx_anim_stack->time_end; animation->set_additional_data("GODOT_animation_time_begin_time_end", additional_data); ufbx_bake_opts opts = {}; + opts.resample_rate = p_state->get_bake_fps(); + opts.minimum_sample_rate = p_state->get_bake_fps(); + opts.max_keyframe_segments = 1024; + ufbx_error error; ufbx_unique_ptr<ufbx_baked_anim> fbx_baked_anim{ ufbx_bake_anim(fbx_scene, fbx_anim_stack->anim, &opts, &error) }; if (!fbx_baked_anim) { @@ -1759,7 +1763,7 @@ void FBXDocument::_generate_skeleton_bone_node(Ref<FBXState> p_state, const GLTF } } -void FBXDocument::_import_animation(Ref<FBXState> p_state, AnimationPlayer *p_animation_player, const GLTFAnimationIndex p_index, const float p_bake_fps, const bool p_trimming, const bool p_remove_immutable_tracks) { +void FBXDocument::_import_animation(Ref<FBXState> p_state, AnimationPlayer *p_animation_player, const GLTFAnimationIndex p_index, const bool p_trimming, const bool p_remove_immutable_tracks) { Ref<GLTFAnimation> anim = p_state->animations[p_index]; String anim_name = anim->get_name(); @@ -1771,7 +1775,7 @@ void FBXDocument::_import_animation(Ref<FBXState> p_state, AnimationPlayer *p_an Ref<Animation> animation; animation.instantiate(); animation->set_name(anim_name); - animation->set_step(1.0 / p_bake_fps); + animation->set_step(1.0 / p_state->get_bake_fps()); if (anim->get_loop()) { animation->set_loop_mode(Animation::LOOP_LINEAR); @@ -2118,6 +2122,7 @@ Node *FBXDocument::generate_scene(Ref<GLTFState> p_state, float p_bake_fps, bool ERR_FAIL_COND_V(state.is_null(), nullptr); ERR_FAIL_NULL_V(state, nullptr); ERR_FAIL_INDEX_V(0, state->root_nodes.size(), nullptr); + p_state->set_bake_fps(p_bake_fps); GLTFNodeIndex fbx_root = state->root_nodes.write[0]; Node *fbx_root_node = state->get_scene_node(fbx_root); Node *root = fbx_root_node; @@ -2131,7 +2136,7 @@ Node *FBXDocument::generate_scene(Ref<GLTFState> p_state, float p_bake_fps, bool root->add_child(ap, true); ap->set_owner(root); for (int i = 0; i < state->animations.size(); i++) { - _import_animation(state, ap, i, p_bake_fps, p_trimming, p_remove_immutable_tracks); + _import_animation(state, ap, i, p_trimming, p_remove_immutable_tracks); } } ERR_FAIL_NULL_V(root, nullptr); diff --git a/modules/fbx/fbx_document.h b/modules/fbx/fbx_document.h index c9256df444..4a3bb176c2 100644 --- a/modules/fbx/fbx_document.h +++ b/modules/fbx/fbx_document.h @@ -99,7 +99,7 @@ public: void _generate_scene_node(Ref<FBXState> p_state, const GLTFNodeIndex p_node_index, Node *p_scene_parent, Node *p_scene_root); void _generate_skeleton_bone_node(Ref<FBXState> p_state, const GLTFNodeIndex p_node_index, Node *p_scene_parent, Node *p_scene_root); void _import_animation(Ref<FBXState> p_state, AnimationPlayer *p_animation_player, - const GLTFAnimationIndex p_index, const float p_bake_fps, const bool p_trimming, const bool p_remove_immutable_tracks); + const GLTFAnimationIndex p_index, const bool p_trimming, const bool p_remove_immutable_tracks); Error _parse(Ref<FBXState> p_state, String p_path, Ref<FileAccess> p_file); }; diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index a2680c932f..7fe96146da 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -4061,10 +4061,23 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident mark_lambda_use_self(); return; // No need to capture. } - // If the identifier is local, check if it's any kind of capture by comparing their source function. - // Only capture locals and enum values. Constants are still accessible from the lambda using the script reference. If not, this method is done. - if (p_identifier->source == GDScriptParser::IdentifierNode::UNDEFINED_SOURCE || p_identifier->source == GDScriptParser::IdentifierNode::MEMBER_CONSTANT) { - return; + + switch (p_identifier->source) { + case GDScriptParser::IdentifierNode::FUNCTION_PARAMETER: + case GDScriptParser::IdentifierNode::LOCAL_VARIABLE: + case GDScriptParser::IdentifierNode::LOCAL_ITERATOR: + case GDScriptParser::IdentifierNode::LOCAL_BIND: + break; // Need to capture. + case GDScriptParser::IdentifierNode::UNDEFINED_SOURCE: // A global. + case GDScriptParser::IdentifierNode::LOCAL_CONSTANT: + case GDScriptParser::IdentifierNode::MEMBER_VARIABLE: + case GDScriptParser::IdentifierNode::MEMBER_CONSTANT: + case GDScriptParser::IdentifierNode::MEMBER_FUNCTION: + case GDScriptParser::IdentifierNode::MEMBER_SIGNAL: + case GDScriptParser::IdentifierNode::MEMBER_CLASS: + case GDScriptParser::IdentifierNode::INHERITED_VARIABLE: + case GDScriptParser::IdentifierNode::STATIC_VARIABLE: + return; // No need to capture. } GDScriptParser::FunctionNode *function_test = current_lambda->function; diff --git a/modules/gdscript/tests/scripts/runtime/features/lambda_captures.gd b/modules/gdscript/tests/scripts/runtime/features/lambda_captures.gd new file mode 100644 index 0000000000..bbdf745540 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/lambda_captures.gd @@ -0,0 +1,26 @@ +# GH-92217 +# TODO: Add more tests. + +static var static_var: int: + set(value): + prints("set static_var", value) + get: + print("get static_var") + return 0 + +var member_var: int: + set(value): + prints("set member_var", value) + get: + print("get member_var") + return 0 + +func test(): + var lambda := func (): + var _tmp := static_var + _tmp = member_var + + static_var = 1 + member_var = 1 + + lambda.call() diff --git a/modules/gdscript/tests/scripts/runtime/features/lambda_captures.out b/modules/gdscript/tests/scripts/runtime/features/lambda_captures.out new file mode 100644 index 0000000000..0bdf74a43f --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/lambda_captures.out @@ -0,0 +1,5 @@ +GDTEST_OK +get static_var +get member_var +set static_var 1 +set member_var 1 diff --git a/modules/gltf/doc_classes/GLTFAccessor.xml b/modules/gltf/doc_classes/GLTFAccessor.xml index ba7323b7cd..54762faed7 100644 --- a/modules/gltf/doc_classes/GLTFAccessor.xml +++ b/modules/gltf/doc_classes/GLTFAccessor.xml @@ -12,34 +12,50 @@ <link title="Runtime file loading and saving">$DOCS_URL/tutorials/io/runtime_file_loading_and_saving.html</link> </tutorials> <members> + <member name="accessor_type" type="int" setter="set_accessor_type" getter="get_accessor_type" default="0"> + The GLTF accessor type as an enum. Possible values are 0 for "SCALAR", 1 for "VEC2", 2 for "VEC3", 3 for "VEC4", 4 for "MAT2", 5 for "MAT3", and 6 for "MAT4". + </member> <member name="buffer_view" type="int" setter="set_buffer_view" getter="get_buffer_view" default="-1"> The index of the buffer view this accessor is referencing. If [code]-1[/code], this accessor is not referencing any buffer view. </member> <member name="byte_offset" type="int" setter="set_byte_offset" getter="get_byte_offset" default="0"> + The offset relative to the start of the buffer view in bytes. </member> <member name="component_type" type="int" setter="set_component_type" getter="get_component_type" default="0"> + The GLTF component type as an enum. Possible values are 5120 for "BYTE", 5121 for "UNSIGNED_BYTE", 5122 for "SHORT", 5123 for "UNSIGNED_SHORT", 5125 for "UNSIGNED_INT", and 5126 for "FLOAT". A value of 5125 or "UNSIGNED_INT" must not be used for any accessor that is not referenced by mesh.primitive.indices. </member> <member name="count" type="int" setter="set_count" getter="get_count" default="0"> + The number of elements referenced by this accessor. </member> <member name="max" type="PackedFloat64Array" setter="set_max" getter="get_max" default="PackedFloat64Array()"> + Maximum value of each component in this accessor. </member> <member name="min" type="PackedFloat64Array" setter="set_min" getter="get_min" default="PackedFloat64Array()"> + Minimum value of each component in this accessor. </member> <member name="normalized" type="bool" setter="set_normalized" getter="get_normalized" default="false"> + Specifies whether integer data values are normalized before usage. </member> <member name="sparse_count" type="int" setter="set_sparse_count" getter="get_sparse_count" default="0"> + Number of deviating accessor values stored in the sparse array. </member> <member name="sparse_indices_buffer_view" type="int" setter="set_sparse_indices_buffer_view" getter="get_sparse_indices_buffer_view" default="0"> + The index of the buffer view with sparse indices. The referenced buffer view MUST NOT have its target or byteStride properties defined. The buffer view and the optional byteOffset MUST be aligned to the componentType byte length. </member> <member name="sparse_indices_byte_offset" type="int" setter="set_sparse_indices_byte_offset" getter="get_sparse_indices_byte_offset" default="0"> + The offset relative to the start of the buffer view in bytes. </member> <member name="sparse_indices_component_type" type="int" setter="set_sparse_indices_component_type" getter="get_sparse_indices_component_type" default="0"> + The indices component data type as an enum. Possible values are 5121 for "UNSIGNED_BYTE", 5123 for "UNSIGNED_SHORT", and 5125 for "UNSIGNED_INT". </member> <member name="sparse_values_buffer_view" type="int" setter="set_sparse_values_buffer_view" getter="get_sparse_values_buffer_view" default="0"> + The index of the bufferView with sparse values. The referenced buffer view MUST NOT have its target or byteStride properties defined. </member> <member name="sparse_values_byte_offset" type="int" setter="set_sparse_values_byte_offset" getter="get_sparse_values_byte_offset" default="0"> + The offset relative to the start of the bufferView in bytes. </member> - <member name="type" type="int" setter="set_type" getter="get_type" default="0"> + <member name="type" type="int" setter="set_type" getter="get_type" default="0" deprecated="Use [member accessor_type] instead."> + The GLTF accessor type as an enum. Use [member accessor_type] instead. </member> </members> </class> diff --git a/modules/gltf/doc_classes/GLTFDocument.xml b/modules/gltf/doc_classes/GLTFDocument.xml index 1f172633da..1b51def28e 100644 --- a/modules/gltf/doc_classes/GLTFDocument.xml +++ b/modules/gltf/doc_classes/GLTFDocument.xml @@ -60,6 +60,7 @@ <param index="3" name="remove_immutable_tracks" type="bool" default="true" /> <description> Takes a [GLTFState] object through the [param state] parameter and returns a Godot Engine scene node. + The [param bake_fps] parameter overrides the bake_fps in [param state]. </description> </method> <method name="register_gltf_document_extension" qualifiers="static"> diff --git a/modules/gltf/doc_classes/GLTFState.xml b/modules/gltf/doc_classes/GLTFState.xml index 6c7c5ee0e6..21a0527813 100644 --- a/modules/gltf/doc_classes/GLTFState.xml +++ b/modules/gltf/doc_classes/GLTFState.xml @@ -275,6 +275,9 @@ </method> </methods> <members> + <member name="bake_fps" type="float" setter="set_bake_fps" getter="get_bake_fps" default="30.0"> + The baking fps of the animation for either import or export. + </member> <member name="base_path" type="String" setter="set_base_path" getter="get_base_path" default=""""> The folder path associated with this GLTF data. This is used to find other files the GLTF file references, like images or binary buffers. This will be set during import when appending from a file, and will be set during export when writing to a file. </member> diff --git a/modules/gltf/editor/editor_scene_exporter_gltf_plugin.cpp b/modules/gltf/editor/editor_scene_exporter_gltf_plugin.cpp index fee8156375..022d2e4477 100644 --- a/modules/gltf/editor/editor_scene_exporter_gltf_plugin.cpp +++ b/modules/gltf/editor/editor_scene_exporter_gltf_plugin.cpp @@ -107,6 +107,7 @@ void SceneExporterGLTFPlugin::_export_scene_as_gltf(const String &p_file_path) { state->set_copyright(_export_settings->get_copyright()); int32_t flags = 0; flags |= EditorSceneFormatImporter::IMPORT_USE_NAMED_SKIN_BINDS; + state->set_bake_fps(_export_settings->get_bake_fps()); Error err = _gltf_document->append_from_scene(root, state, flags); if (err != OK) { ERR_PRINT(vformat("glTF2 save scene error %s.", itos(err))); diff --git a/modules/gltf/editor/editor_scene_exporter_gltf_settings.cpp b/modules/gltf/editor/editor_scene_exporter_gltf_settings.cpp index 16f32af903..511da078d8 100644 --- a/modules/gltf/editor/editor_scene_exporter_gltf_settings.cpp +++ b/modules/gltf/editor/editor_scene_exporter_gltf_settings.cpp @@ -182,4 +182,16 @@ void EditorSceneExporterGLTFSettings::_bind_methods() { ClassDB::bind_method(D_METHOD("get_copyright"), &EditorSceneExporterGLTFSettings::get_copyright); ClassDB::bind_method(D_METHOD("set_copyright", "copyright"), &EditorSceneExporterGLTFSettings::set_copyright); ADD_PROPERTY(PropertyInfo(Variant::STRING, "copyright", PROPERTY_HINT_PLACEHOLDER_TEXT, "Example: 2014 Godette"), "set_copyright", "get_copyright"); + + ClassDB::bind_method(D_METHOD("get_bake_fps"), &EditorSceneExporterGLTFSettings::get_bake_fps); + ClassDB::bind_method(D_METHOD("set_bake_fps", "bake_fps"), &EditorSceneExporterGLTFSettings::set_bake_fps); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bake_fps"), "set_bake_fps", "get_bake_fps"); +} + +double EditorSceneExporterGLTFSettings::get_bake_fps() const { + return _bake_fps; +} + +void EditorSceneExporterGLTFSettings::set_bake_fps(const double p_bake_fps) { + _bake_fps = p_bake_fps; } diff --git a/modules/gltf/editor/editor_scene_exporter_gltf_settings.h b/modules/gltf/editor/editor_scene_exporter_gltf_settings.h index e1ce674274..898cddfd68 100644 --- a/modules/gltf/editor/editor_scene_exporter_gltf_settings.h +++ b/modules/gltf/editor/editor_scene_exporter_gltf_settings.h @@ -42,6 +42,7 @@ class EditorSceneExporterGLTFSettings : public RefCounted { HashMap<String, Ref<GLTFDocumentExtension>> _config_name_to_extension_map; String _copyright; + double _bake_fps = 30.0; protected: static void _bind_methods(); @@ -58,6 +59,9 @@ public: String get_copyright() const; void set_copyright(const String &p_copyright); + + double get_bake_fps() const; + void set_bake_fps(const double p_bake_fps); }; #endif // TOOLS_ENABLED diff --git a/modules/gltf/editor/editor_scene_importer_gltf.cpp b/modules/gltf/editor/editor_scene_importer_gltf.cpp index e4ed79d15e..b38c64de01 100644 --- a/modules/gltf/editor/editor_scene_importer_gltf.cpp +++ b/modules/gltf/editor/editor_scene_importer_gltf.cpp @@ -59,6 +59,10 @@ Node *EditorSceneFormatImporterGLTF::import_scene(const String &p_path, uint32_t int32_t enum_option = p_options["gltf/embedded_image_handling"]; state->set_handle_binary_image(enum_option); } + if (p_options.has(SNAME("nodes/import_as_skeleton_bones")) ? (bool)p_options[SNAME("nodes/import_as_skeleton_bones")] : false) { + state->set_import_as_skeleton_bones(true); + } + state->set_bake_fps(p_options["animation/fps"]); Error err = gltf->append_from_file(p_path, state, p_flags); if (err != OK) { if (r_err) { @@ -72,9 +76,9 @@ Node *EditorSceneFormatImporterGLTF::import_scene(const String &p_path, uint32_t #ifndef DISABLE_DEPRECATED bool trimming = p_options.has("animation/trimming") ? (bool)p_options["animation/trimming"] : false; - return gltf->generate_scene(state, (float)p_options["animation/fps"], trimming, false); + return gltf->generate_scene(state, state->get_bake_fps(), trimming, false); #else - return gltf->generate_scene(state, (float)p_options["animation/fps"], (bool)p_options["animation/trimming"], false); + return gltf->generate_scene(state, state->get_bake_fps(), (bool)p_options["animation/trimming"], false); #endif } diff --git a/modules/gltf/gltf_defines.h b/modules/gltf/gltf_defines.h index d044105b42..c1918e5908 100644 --- a/modules/gltf/gltf_defines.h +++ b/modules/gltf/gltf_defines.h @@ -66,14 +66,4 @@ using GLTFSkinIndex = int; using GLTFTextureIndex = int; using GLTFTextureSamplerIndex = int; -enum GLTFType { - TYPE_SCALAR, - TYPE_VEC2, - TYPE_VEC3, - TYPE_VEC4, - TYPE_MAT2, - TYPE_MAT3, - TYPE_MAT4, -}; - #endif // GLTF_DEFINES_H diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp index b92176a63a..42428231e6 100644 --- a/modules/gltf/gltf_document.cpp +++ b/modules/gltf/gltf_document.cpp @@ -886,7 +886,7 @@ Error GLTFDocument::_encode_accessors(Ref<GLTFState> p_state) { Ref<GLTFAccessor> accessor = p_state->accessors[i]; d["componentType"] = accessor->component_type; d["count"] = accessor->count; - d["type"] = _get_accessor_type_name(accessor->type); + d["type"] = _get_accessor_type_name(accessor->accessor_type); d["normalized"] = accessor->normalized; d["max"] = accessor->max; d["min"] = accessor->min; @@ -934,58 +934,58 @@ Error GLTFDocument::_encode_accessors(Ref<GLTFState> p_state) { return OK; } -String GLTFDocument::_get_accessor_type_name(const GLTFType p_type) { - if (p_type == GLTFType::TYPE_SCALAR) { +String GLTFDocument::_get_accessor_type_name(const GLTFAccessorType p_accessor_type) { + if (p_accessor_type == GLTFAccessorType::TYPE_SCALAR) { return "SCALAR"; } - if (p_type == GLTFType::TYPE_VEC2) { + if (p_accessor_type == GLTFAccessorType::TYPE_VEC2) { return "VEC2"; } - if (p_type == GLTFType::TYPE_VEC3) { + if (p_accessor_type == GLTFAccessorType::TYPE_VEC3) { return "VEC3"; } - if (p_type == GLTFType::TYPE_VEC4) { + if (p_accessor_type == GLTFAccessorType::TYPE_VEC4) { return "VEC4"; } - if (p_type == GLTFType::TYPE_MAT2) { + if (p_accessor_type == GLTFAccessorType::TYPE_MAT2) { return "MAT2"; } - if (p_type == GLTFType::TYPE_MAT3) { + if (p_accessor_type == GLTFAccessorType::TYPE_MAT3) { return "MAT3"; } - if (p_type == GLTFType::TYPE_MAT4) { + if (p_accessor_type == GLTFAccessorType::TYPE_MAT4) { return "MAT4"; } ERR_FAIL_V("SCALAR"); } -GLTFType GLTFDocument::_get_type_from_str(const String &p_string) { +GLTFAccessorType GLTFDocument::_get_accessor_type_from_str(const String &p_string) { if (p_string == "SCALAR") { - return GLTFType::TYPE_SCALAR; + return GLTFAccessorType::TYPE_SCALAR; } if (p_string == "VEC2") { - return GLTFType::TYPE_VEC2; + return GLTFAccessorType::TYPE_VEC2; } if (p_string == "VEC3") { - return GLTFType::TYPE_VEC3; + return GLTFAccessorType::TYPE_VEC3; } if (p_string == "VEC4") { - return GLTFType::TYPE_VEC4; + return GLTFAccessorType::TYPE_VEC4; } if (p_string == "MAT2") { - return GLTFType::TYPE_MAT2; + return GLTFAccessorType::TYPE_MAT2; } if (p_string == "MAT3") { - return GLTFType::TYPE_MAT3; + return GLTFAccessorType::TYPE_MAT3; } if (p_string == "MAT4") { - return GLTFType::TYPE_MAT4; + return GLTFAccessorType::TYPE_MAT4; } - ERR_FAIL_V(GLTFType::TYPE_SCALAR); + ERR_FAIL_V(GLTFAccessorType::TYPE_SCALAR); } Error GLTFDocument::_parse_accessors(Ref<GLTFState> p_state) { @@ -1004,7 +1004,7 @@ Error GLTFDocument::_parse_accessors(Ref<GLTFState> p_state) { ERR_FAIL_COND_V(!d.has("count"), ERR_PARSE_ERROR); accessor->count = d["count"]; ERR_FAIL_COND_V(!d.has("type"), ERR_PARSE_ERROR); - accessor->type = _get_type_from_str(d["type"]); + accessor->accessor_type = _get_accessor_type_from_str(d["type"]); if (d.has("bufferView")) { accessor->buffer_view = d["bufferView"]; //optional because it may be sparse... @@ -1088,26 +1088,12 @@ String GLTFDocument::_get_component_type_name(const uint32_t p_component) { return "<Error>"; } -String GLTFDocument::_get_type_name(const GLTFType p_component) { - static const char *names[] = { - "float", - "vec2", - "vec3", - "vec4", - "mat2", - "mat3", - "mat4" - }; - - return names[p_component]; -} - -Error GLTFDocument::_encode_buffer_view(Ref<GLTFState> p_state, const double *p_src, const int p_count, const GLTFType p_type, const int p_component_type, const bool p_normalized, const int p_byte_offset, const bool p_for_vertex, GLTFBufferViewIndex &r_accessor, const bool p_for_vertex_indices) { +Error GLTFDocument::_encode_buffer_view(Ref<GLTFState> p_state, const double *p_src, const int p_count, const GLTFAccessorType p_accessor_type, const int p_component_type, const bool p_normalized, const int p_byte_offset, const bool p_for_vertex, GLTFBufferViewIndex &r_accessor, const bool p_for_vertex_indices) { const int component_count_for_type[7] = { 1, 2, 3, 4, 4, 9, 16 }; - const int component_count = component_count_for_type[p_type]; + const int component_count = component_count_for_type[p_accessor_type]; const int component_size = _get_component_type_size(p_component_type); ERR_FAIL_COND_V(component_size == 0, FAILED); @@ -1117,18 +1103,18 @@ Error GLTFDocument::_encode_buffer_view(Ref<GLTFState> p_state, const double *p_ switch (p_component_type) { case COMPONENT_TYPE_BYTE: case COMPONENT_TYPE_UNSIGNED_BYTE: { - if (p_type == TYPE_MAT2) { + if (p_accessor_type == TYPE_MAT2) { skip_every = 2; skip_bytes = 2; } - if (p_type == TYPE_MAT3) { + if (p_accessor_type == TYPE_MAT3) { skip_every = 3; skip_bytes = 1; } } break; case COMPONENT_TYPE_SHORT: case COMPONENT_TYPE_UNSIGNED_SHORT: { - if (p_type == TYPE_MAT3) { + if (p_accessor_type == TYPE_MAT3) { skip_every = 6; skip_bytes = 4; } @@ -1147,7 +1133,7 @@ Error GLTFDocument::_encode_buffer_view(Ref<GLTFState> p_state, const double *p_ stride += 4 - (stride % 4); //according to spec must be multiple of 4 } //use to debug - print_verbose("glTF: encoding type " + _get_type_name(p_type) + " component type: " + _get_component_type_name(p_component_type) + " stride: " + itos(stride) + " amount " + itos(p_count)); + print_verbose("glTF: encoding accessor type " + _get_accessor_type_name(p_accessor_type) + " component type: " + _get_component_type_name(p_component_type) + " stride: " + itos(stride) + " amount " + itos(p_count)); print_verbose("glTF: encoding accessor offset " + itos(p_byte_offset) + " view offset: " + itos(bv->byte_offset) + " total buffer len: " + itos(gltf_buffer.size()) + " view len " + itos(bv->byte_length)); @@ -1310,7 +1296,7 @@ Error GLTFDocument::_encode_buffer_view(Ref<GLTFState> p_state, const double *p_ return OK; } -Error GLTFDocument::_decode_buffer_view(Ref<GLTFState> p_state, double *p_dst, const GLTFBufferViewIndex p_buffer_view, const int p_skip_every, const int p_skip_bytes, const int p_element_size, const int p_count, const GLTFType p_type, const int p_component_count, const int p_component_type, const int p_component_size, const bool p_normalized, const int p_byte_offset, const bool p_for_vertex) { +Error GLTFDocument::_decode_buffer_view(Ref<GLTFState> p_state, double *p_dst, const GLTFBufferViewIndex p_buffer_view, const int p_skip_every, const int p_skip_bytes, const int p_element_size, const int p_count, const GLTFAccessorType p_accessor_type, const int p_component_count, const int p_component_type, const int p_component_size, const bool p_normalized, const int p_byte_offset, const bool p_for_vertex) { const Ref<GLTFBufferView> bv = p_state->buffer_views[p_buffer_view]; int stride = p_element_size; @@ -1328,7 +1314,7 @@ Error GLTFDocument::_decode_buffer_view(Ref<GLTFState> p_state, double *p_dst, c const uint8_t *bufptr = buffer.ptr(); //use to debug - print_verbose("glTF: type " + _get_type_name(p_type) + " component type: " + _get_component_type_name(p_component_type) + " stride: " + itos(stride) + " amount " + itos(p_count)); + print_verbose("glTF: accessor type " + _get_accessor_type_name(p_accessor_type) + " component type: " + _get_component_type_name(p_component_type) + " stride: " + itos(stride) + " amount " + itos(p_count)); print_verbose("glTF: accessor offset " + itos(p_byte_offset) + " view offset: " + itos(bv->byte_offset) + " total buffer len: " + itos(buffer.size()) + " view len " + itos(bv->byte_length)); const int buffer_end = (stride * (p_count - 1)) + p_element_size; @@ -1430,7 +1416,7 @@ Vector<double> GLTFDocument::_decode_accessor(Ref<GLTFState> p_state, const GLTF 1, 2, 3, 4, 4, 9, 16 }; - const int component_count = component_count_for_type[a->type]; + const int component_count = component_count_for_type[a->accessor_type]; const int component_size = _get_component_type_size(a->component_type); ERR_FAIL_COND_V(component_size == 0, Vector<double>()); int element_size = component_count * component_size; @@ -1441,12 +1427,12 @@ Vector<double> GLTFDocument::_decode_accessor(Ref<GLTFState> p_state, const GLTF switch (a->component_type) { case COMPONENT_TYPE_BYTE: case COMPONENT_TYPE_UNSIGNED_BYTE: { - if (a->type == TYPE_MAT2) { + if (a->accessor_type == TYPE_MAT2) { skip_every = 2; skip_bytes = 2; element_size = 8; //override for this case } - if (a->type == TYPE_MAT3) { + if (a->accessor_type == TYPE_MAT3) { skip_every = 3; skip_bytes = 1; element_size = 12; //override for this case @@ -1454,7 +1440,7 @@ Vector<double> GLTFDocument::_decode_accessor(Ref<GLTFState> p_state, const GLTF } break; case COMPONENT_TYPE_SHORT: case COMPONENT_TYPE_UNSIGNED_SHORT: { - if (a->type == TYPE_MAT3) { + if (a->accessor_type == TYPE_MAT3) { skip_every = 6; skip_bytes = 4; element_size = 16; //override for this case @@ -1471,7 +1457,7 @@ Vector<double> GLTFDocument::_decode_accessor(Ref<GLTFState> p_state, const GLTF if (a->buffer_view >= 0) { ERR_FAIL_INDEX_V(a->buffer_view, p_state->buffer_views.size(), Vector<double>()); - const Error err = _decode_buffer_view(p_state, dst, a->buffer_view, skip_every, skip_bytes, element_size, a->count, a->type, component_count, a->component_type, component_size, a->normalized, a->byte_offset, p_for_vertex); + const Error err = _decode_buffer_view(p_state, dst, a->buffer_view, skip_every, skip_bytes, element_size, a->count, a->accessor_type, component_count, a->component_type, component_size, a->normalized, a->byte_offset, p_for_vertex); if (err != OK) { return Vector<double>(); } @@ -1495,7 +1481,7 @@ Vector<double> GLTFDocument::_decode_accessor(Ref<GLTFState> p_state, const GLTF Vector<double> data; data.resize(component_count * a->sparse_count); - err = _decode_buffer_view(p_state, data.ptrw(), a->sparse_values_buffer_view, skip_every, skip_bytes, element_size, a->sparse_count, a->type, component_count, a->component_type, component_size, a->normalized, a->sparse_values_byte_offset, p_for_vertex); + err = _decode_buffer_view(p_state, data.ptrw(), a->sparse_values_buffer_view, skip_every, skip_bytes, element_size, a->sparse_count, a->accessor_type, component_count, a->component_type, component_size, a->normalized, a->sparse_values_byte_offset, p_for_vertex); if (err != OK) { return Vector<double>(); } @@ -1550,7 +1536,7 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_ints(Ref<GLTFState> p_state, p_state->buffers.push_back(Vector<uint8_t>()); } int64_t size = p_state->buffers[0].size(); - const GLTFType type = GLTFType::TYPE_SCALAR; + const GLTFAccessorType accessor_type = GLTFAccessorType::TYPE_SCALAR; int component_type; if (max_index > 65535 || p_for_vertex) { component_type = GLTFDocument::COMPONENT_TYPE_INT; @@ -1562,10 +1548,10 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_ints(Ref<GLTFState> p_state, accessor->min = type_min; accessor->normalized = false; accessor->count = ret_size; - accessor->type = type; + accessor->accessor_type = accessor_type; accessor->component_type = component_type; accessor->byte_offset = 0; - Error err = _encode_buffer_view(p_state, attribs.ptr(), attribs.size(), type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i, p_for_vertex_indices); + Error err = _encode_buffer_view(p_state, attribs.ptr(), attribs.size(), accessor_type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i, p_for_vertex_indices); if (err != OK) { return -1; } @@ -1664,17 +1650,17 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_vec2(Ref<GLTFState> p_state, p_state->buffers.push_back(Vector<uint8_t>()); } int64_t size = p_state->buffers[0].size(); - const GLTFType type = GLTFType::TYPE_VEC2; + const GLTFAccessorType accessor_type = GLTFAccessorType::TYPE_VEC2; const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT; accessor->max = type_max; accessor->min = type_min; accessor->normalized = false; accessor->count = p_attribs.size(); - accessor->type = type; + accessor->accessor_type = accessor_type; accessor->component_type = component_type; accessor->byte_offset = 0; - Error err = _encode_buffer_view(p_state, attribs.ptr(), p_attribs.size(), type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i); + Error err = _encode_buffer_view(p_state, attribs.ptr(), p_attribs.size(), accessor_type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i); if (err != OK) { return -1; } @@ -1717,17 +1703,17 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_color(Ref<GLTFState> p_state p_state->buffers.push_back(Vector<uint8_t>()); } int64_t size = p_state->buffers[0].size(); - const GLTFType type = GLTFType::TYPE_VEC4; + const GLTFAccessorType accessor_type = GLTFAccessorType::TYPE_VEC4; const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT; accessor->max = type_max; accessor->min = type_min; accessor->normalized = false; accessor->count = p_attribs.size(); - accessor->type = type; + accessor->accessor_type = accessor_type; accessor->component_type = component_type; accessor->byte_offset = 0; - Error err = _encode_buffer_view(p_state, attribs.ptr(), p_attribs.size(), type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i); + Error err = _encode_buffer_view(p_state, attribs.ptr(), p_attribs.size(), accessor_type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i); if (err != OK) { return -1; } @@ -1784,17 +1770,17 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_weights(Ref<GLTFState> p_sta p_state->buffers.push_back(Vector<uint8_t>()); } int64_t size = p_state->buffers[0].size(); - const GLTFType type = GLTFType::TYPE_VEC4; + const GLTFAccessorType accessor_type = GLTFAccessorType::TYPE_VEC4; const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT; accessor->max = type_max; accessor->min = type_min; accessor->normalized = false; accessor->count = p_attribs.size(); - accessor->type = type; + accessor->accessor_type = accessor_type; accessor->component_type = component_type; accessor->byte_offset = 0; - Error err = _encode_buffer_view(p_state, attribs.ptr(), p_attribs.size(), type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i); + Error err = _encode_buffer_view(p_state, attribs.ptr(), p_attribs.size(), accessor_type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i); if (err != OK) { return -1; } @@ -1835,17 +1821,17 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_joints(Ref<GLTFState> p_stat p_state->buffers.push_back(Vector<uint8_t>()); } int64_t size = p_state->buffers[0].size(); - const GLTFType type = GLTFType::TYPE_VEC4; + const GLTFAccessorType accessor_type = GLTFAccessorType::TYPE_VEC4; const int component_type = GLTFDocument::COMPONENT_TYPE_UNSIGNED_SHORT; accessor->max = type_max; accessor->min = type_min; accessor->normalized = false; accessor->count = p_attribs.size(); - accessor->type = type; + accessor->accessor_type = accessor_type; accessor->component_type = component_type; accessor->byte_offset = 0; - Error err = _encode_buffer_view(p_state, attribs.ptr(), p_attribs.size(), type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i); + Error err = _encode_buffer_view(p_state, attribs.ptr(), p_attribs.size(), accessor_type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i); if (err != OK) { return -1; } @@ -1888,17 +1874,17 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_quaternions(Ref<GLTFState> p p_state->buffers.push_back(Vector<uint8_t>()); } int64_t size = p_state->buffers[0].size(); - const GLTFType type = GLTFType::TYPE_VEC4; + const GLTFAccessorType accessor_type = GLTFAccessorType::TYPE_VEC4; const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT; accessor->max = type_max; accessor->min = type_min; accessor->normalized = false; accessor->count = p_attribs.size(); - accessor->type = type; + accessor->accessor_type = accessor_type; accessor->component_type = component_type; accessor->byte_offset = 0; - Error err = _encode_buffer_view(p_state, attribs.ptr(), p_attribs.size(), type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i); + Error err = _encode_buffer_view(p_state, attribs.ptr(), p_attribs.size(), accessor_type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i); if (err != OK) { return -1; } @@ -1963,17 +1949,17 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_floats(Ref<GLTFState> p_stat p_state->buffers.push_back(Vector<uint8_t>()); } int64_t size = p_state->buffers[0].size(); - const GLTFType type = GLTFType::TYPE_SCALAR; + const GLTFAccessorType accessor_type = GLTFAccessorType::TYPE_SCALAR; const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT; accessor->max = type_max; accessor->min = type_min; accessor->normalized = false; accessor->count = ret_size; - accessor->type = type; + accessor->accessor_type = accessor_type; accessor->component_type = component_type; accessor->byte_offset = 0; - Error err = _encode_buffer_view(p_state, attribs.ptr(), attribs.size(), type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i); + Error err = _encode_buffer_view(p_state, attribs.ptr(), attribs.size(), accessor_type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i); if (err != OK) { return -1; } @@ -2013,17 +1999,17 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_vec3(Ref<GLTFState> p_state, p_state->buffers.push_back(Vector<uint8_t>()); } int64_t size = p_state->buffers[0].size(); - const GLTFType type = GLTFType::TYPE_VEC3; + const GLTFAccessorType accessor_type = GLTFAccessorType::TYPE_VEC3; const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT; accessor->max = type_max; accessor->min = type_min; accessor->normalized = false; accessor->count = p_attribs.size(); - accessor->type = type; + accessor->accessor_type = accessor_type; accessor->component_type = component_type; accessor->byte_offset = 0; - Error err = _encode_buffer_view(p_state, attribs.ptr(), p_attribs.size(), type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i); + Error err = _encode_buffer_view(p_state, attribs.ptr(), p_attribs.size(), accessor_type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i); if (err != OK) { return -1; } @@ -2089,12 +2075,12 @@ GLTFAccessorIndex GLTFDocument::_encode_sparse_accessor_as_vec3(Ref<GLTFState> p p_state->buffers.push_back(Vector<uint8_t>()); } int64_t size = p_state->buffers[0].size(); - const GLTFType type = GLTFType::TYPE_VEC3; + const GLTFAccessorType accessor_type = GLTFAccessorType::TYPE_VEC3; const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT; sparse_accessor->normalized = false; sparse_accessor->count = p_attribs.size(); - sparse_accessor->type = type; + sparse_accessor->accessor_type = accessor_type; sparse_accessor->component_type = component_type; if (p_reference_accessor < p_state->accessors.size() && p_reference_accessor >= 0 && p_state->accessors[p_reference_accessor].is_valid()) { sparse_accessor->byte_offset = p_state->accessors[p_reference_accessor]->byte_offset; @@ -2117,11 +2103,11 @@ GLTFAccessorIndex GLTFDocument::_encode_sparse_accessor_as_vec3(Ref<GLTFState> p } else { sparse_accessor->sparse_indices_component_type = GLTFDocument::COMPONENT_TYPE_UNSIGNED_SHORT; } - if (_encode_buffer_view(p_state, changed_indices.ptr(), changed_indices.size(), GLTFType::TYPE_SCALAR, sparse_accessor->sparse_indices_component_type, sparse_accessor->normalized, sparse_accessor->sparse_indices_byte_offset, false, buffer_view_i_indices) != OK) { + if (_encode_buffer_view(p_state, changed_indices.ptr(), changed_indices.size(), GLTFAccessorType::TYPE_SCALAR, sparse_accessor->sparse_indices_component_type, sparse_accessor->normalized, sparse_accessor->sparse_indices_byte_offset, false, buffer_view_i_indices) != OK) { return -1; } // We use changed_indices.size() here, because we must pass the number of vec3 values rather than the number of components. - if (_encode_buffer_view(p_state, changed_values.ptr(), changed_indices.size(), sparse_accessor->type, sparse_accessor->component_type, sparse_accessor->normalized, sparse_accessor->sparse_values_byte_offset, false, buffer_view_i_values) != OK) { + if (_encode_buffer_view(p_state, changed_values.ptr(), changed_indices.size(), sparse_accessor->accessor_type, sparse_accessor->component_type, sparse_accessor->normalized, sparse_accessor->sparse_values_byte_offset, false, buffer_view_i_values) != OK) { return -1; } sparse_accessor->sparse_indices_buffer_view = buffer_view_i_indices; @@ -2130,7 +2116,7 @@ GLTFAccessorIndex GLTFDocument::_encode_sparse_accessor_as_vec3(Ref<GLTFState> p } else if (changed_indices.size() > 0) { GLTFBufferIndex buffer_view_i; sparse_accessor->byte_offset = 0; - Error err = _encode_buffer_view(p_state, attribs.ptr(), p_attribs.size(), type, component_type, sparse_accessor->normalized, size, p_for_vertex, buffer_view_i); + Error err = _encode_buffer_view(p_state, attribs.ptr(), p_attribs.size(), accessor_type, component_type, sparse_accessor->normalized, size, p_for_vertex, buffer_view_i); if (err != OK) { return -1; } @@ -2194,17 +2180,17 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_xform(Ref<GLTFState> p_state p_state->buffers.push_back(Vector<uint8_t>()); } int64_t size = p_state->buffers[0].size(); - const GLTFType type = GLTFType::TYPE_MAT4; + const GLTFAccessorType accessor_type = GLTFAccessorType::TYPE_MAT4; const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT; accessor->max = type_max; accessor->min = type_min; accessor->normalized = false; accessor->count = p_attribs.size(); - accessor->type = type; + accessor->accessor_type = accessor_type; accessor->component_type = component_type; accessor->byte_offset = 0; - Error err = _encode_buffer_view(p_state, attribs.ptr(), p_attribs.size(), type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i); + Error err = _encode_buffer_view(p_state, attribs.ptr(), p_attribs.size(), accessor_type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i); if (err != OK) { return -1; } @@ -2247,10 +2233,10 @@ Vector<Color> GLTFDocument::_decode_accessor_as_color(Ref<GLTFState> p_state, co return ret; } - const int type = p_state->accessors[p_accessor]->type; - ERR_FAIL_COND_V(!(type == TYPE_VEC3 || type == TYPE_VEC4), ret); + const int accessor_type = p_state->accessors[p_accessor]->accessor_type; + ERR_FAIL_COND_V(!(accessor_type == TYPE_VEC3 || accessor_type == TYPE_VEC4), ret); int vec_len = 3; - if (type == TYPE_VEC4) { + if (accessor_type == TYPE_VEC4) { vec_len = 4; } @@ -4860,7 +4846,7 @@ Error GLTFDocument::_serialize_animations(Ref<GLTFState> p_state) { t["sampler"] = samplers.size(); Dictionary s; Vector<real_t> times; - const double increment = 1.0 / BAKE_FPS; + const double increment = 1.0 / p_state->get_bake_fps(); { double time = 0.0; bool last = false; @@ -5133,7 +5119,11 @@ GLTFMeshIndex GLTFDocument::_convert_mesh_to_gltf(Ref<GLTFState> p_state, MeshIn ERR_FAIL_COND_V_MSG(p_mesh_instance->get_mesh().is_null(), -1, "glTF: Tried to export a MeshInstance3D node named " + p_mesh_instance->get_name() + ", but it has no mesh. This node will be exported without a mesh."); Ref<Mesh> mesh_resource = p_mesh_instance->get_mesh(); ERR_FAIL_COND_V_MSG(mesh_resource->get_surface_count() == 0, -1, "glTF: Tried to export a MeshInstance3D node named " + p_mesh_instance->get_name() + ", but its mesh has no surfaces. This node will be exported without a mesh."); - + TypedArray<Material> instance_materials; + for (int32_t surface_i = 0; surface_i < mesh_resource->get_surface_count(); surface_i++) { + Ref<Material> mat = p_mesh_instance->get_active_material(surface_i); + instance_materials.append(mat); + } Ref<ImporterMesh> current_mesh = _mesh_to_importer_mesh(mesh_resource); Vector<float> blend_weights; int32_t blend_count = mesh_resource->get_blend_shape_count(); @@ -5144,17 +5134,6 @@ GLTFMeshIndex GLTFDocument::_convert_mesh_to_gltf(Ref<GLTFState> p_state, MeshIn Ref<GLTFMesh> gltf_mesh; gltf_mesh.instantiate(); - TypedArray<Material> instance_materials; - for (int32_t surface_i = 0; surface_i < current_mesh->get_surface_count(); surface_i++) { - Ref<Material> mat = current_mesh->get_surface_material(surface_i); - if (p_mesh_instance->get_surface_override_material(surface_i).is_valid()) { - mat = p_mesh_instance->get_surface_override_material(surface_i); - } - if (p_mesh_instance->get_material_override().is_valid()) { - mat = p_mesh_instance->get_material_override(); - } - instance_materials.append(mat); - } gltf_mesh->set_instance_materials(instance_materials); gltf_mesh->set_mesh(current_mesh); gltf_mesh->set_blend_weights(blend_weights); @@ -5323,11 +5302,22 @@ void GLTFDocument::_convert_csg_shape_to_gltf(CSGShape3D *p_current, GLTFNodeInd Ref<ImporterMesh> mesh; mesh.instantiate(); { - Ref<Mesh> csg_mesh = csg->get_meshes()[1]; - + Ref<ArrayMesh> csg_mesh = csg->get_meshes()[1]; for (int32_t surface_i = 0; surface_i < csg_mesh->get_surface_count(); surface_i++) { Array array = csg_mesh->surface_get_arrays(surface_i); - Ref<Material> mat = csg_mesh->surface_get_material(surface_i); + + Ref<Material> mat; + + Ref<Material> mat_override = csg->get_material_override(); + if (mat_override.is_valid()) { + mat = mat_override; + } + + Ref<Material> mat_surface_override = csg_mesh->surface_get_material(surface_i); + if (mat_surface_override.is_valid() && mat.is_null()) { + mat = mat_surface_override; + } + String mat_name; if (mat.is_valid()) { mat_name = mat->get_name(); @@ -5335,6 +5325,7 @@ void GLTFDocument::_convert_csg_shape_to_gltf(CSGShape3D *p_current, GLTFNodeInd // Assign default material when no material is assigned. mat = Ref<StandardMaterial3D>(memnew(StandardMaterial3D)); } + mesh->add_surface(csg_mesh->surface_get_primitive_type(surface_i), array, csg_mesh->surface_get_blend_shape_arrays(surface_i), csg_mesh->surface_get_lods(surface_i), mat, mat_name, csg_mesh->surface_get_format(surface_i)); @@ -5348,7 +5339,7 @@ void GLTFDocument::_convert_csg_shape_to_gltf(CSGShape3D *p_current, GLTFNodeInd GLTFMeshIndex mesh_i = p_state->meshes.size(); p_state->meshes.push_back(gltf_mesh); p_gltf_node->mesh = mesh_i; - p_gltf_node->transform = csg->get_meshes()[0]; + p_gltf_node->transform = csg->get_transform(); p_gltf_node->set_original_name(csg->get_name()); p_gltf_node->set_name(_gen_unique_name(p_state, csg->get_name())); } @@ -5902,7 +5893,8 @@ T GLTFDocument::_interpolate_track(const Vector<real_t> &p_times, const Vector<T ERR_FAIL_V(p_values[0]); } -void GLTFDocument::_import_animation(Ref<GLTFState> p_state, AnimationPlayer *p_animation_player, const GLTFAnimationIndex p_index, const float p_bake_fps, const bool p_trimming, const bool p_remove_immutable_tracks) { +void GLTFDocument::_import_animation(Ref<GLTFState> p_state, AnimationPlayer *p_animation_player, const GLTFAnimationIndex p_index, const bool p_trimming, const bool p_remove_immutable_tracks) { + ERR_FAIL_COND(p_state.is_null()); Ref<GLTFAnimation> anim = p_state->animations[p_index]; String anim_name = anim->get_name(); @@ -5914,7 +5906,7 @@ void GLTFDocument::_import_animation(Ref<GLTFState> p_state, AnimationPlayer *p_ Ref<Animation> animation; animation.instantiate(); animation->set_name(anim_name); - animation->set_step(1.0 / p_bake_fps); + animation->set_step(1.0 / p_state->get_bake_fps()); if (anim->get_loop()) { animation->set_loop_mode(Animation::LOOP_LINEAR); @@ -6081,7 +6073,7 @@ void GLTFDocument::_import_animation(Ref<GLTFState> p_state, AnimationPlayer *p_ } } - const double increment = 1.0 / p_bake_fps; + const double increment = 1.0 / p_state->get_bake_fps(); double time = anim_start; Vector3 base_pos; @@ -6158,7 +6150,7 @@ void GLTFDocument::_import_animation(Ref<GLTFState> p_state, AnimationPlayer *p_ } } else { // CATMULLROMSPLINE or CUBIC_SPLINE have to be baked, apologies. - const double increment = 1.0 / p_bake_fps; + const double increment = 1.0 / p_state->get_bake_fps(); double time = 0.0; bool last = false; while (true) { @@ -6372,7 +6364,7 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> p_sta p_track.scale_track.times.clear(); p_track.scale_track.values.clear(); // CATMULLROMSPLINE or CUBIC_SPLINE have to be baked, apologies. - const double increment = 1.0 / BAKE_FPS; + const double increment = 1.0 / p_state->get_bake_fps(); double time = 0.0; bool last = false; while (true) { @@ -6407,7 +6399,7 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> p_sta p_track.position_track.times.clear(); p_track.position_track.values.clear(); // CATMULLROMSPLINE or CUBIC_SPLINE have to be baked, apologies. - const double increment = 1.0 / BAKE_FPS; + const double increment = 1.0 / p_state->get_bake_fps(); double time = 0.0; bool last = false; while (true) { @@ -6442,7 +6434,7 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> p_sta p_track.rotation_track.times.clear(); p_track.rotation_track.values.clear(); // CATMULLROMSPLINE or CUBIC_SPLINE have to be baked, apologies. - const double increment = 1.0 / BAKE_FPS; + const double increment = 1.0 / p_state->get_bake_fps(); double time = 0.0; bool last = false; while (true) { @@ -6482,7 +6474,7 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> p_sta p_track.position_track.times.clear(); p_track.position_track.values.clear(); // CATMULLROMSPLINE or CUBIC_SPLINE have to be baked, apologies. - const double increment = 1.0 / BAKE_FPS; + const double increment = 1.0 / p_state->get_bake_fps(); double time = 0.0; bool last = false; while (true) { @@ -6515,7 +6507,7 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> p_sta p_track.rotation_track.times.clear(); p_track.rotation_track.values.clear(); // CATMULLROMSPLINE or CUBIC_SPLINE have to be baked, apologies. - const double increment = 1.0 / BAKE_FPS; + const double increment = 1.0 / p_state->get_bake_fps(); double time = 0.0; bool last = false; while (true) { @@ -6551,7 +6543,7 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> p_sta p_track.scale_track.times.clear(); p_track.scale_track.values.clear(); // CATMULLROMSPLINE or CUBIC_SPLINE have to be baked, apologies. - const double increment = 1.0 / BAKE_FPS; + const double increment = 1.0 / p_state->get_bake_fps(); double time = 0.0; bool last = false; while (true) { @@ -6577,14 +6569,14 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> p_sta } } } else if (track_type == Animation::TYPE_BEZIER) { - const int32_t keys = anim_end * BAKE_FPS; + const int32_t keys = anim_end * p_state->get_bake_fps(); if (path.contains(":scale")) { if (!p_track.scale_track.times.size()) { p_track.scale_track.interpolation = gltf_interpolation; Vector<real_t> new_times; new_times.resize(keys); for (int32_t key_i = 0; key_i < keys; key_i++) { - new_times.write[key_i] = key_i / BAKE_FPS; + new_times.write[key_i] = key_i / p_state->get_bake_fps(); } p_track.scale_track.times = new_times; @@ -6597,11 +6589,11 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> p_sta for (int32_t key_i = 0; key_i < keys; key_i++) { Vector3 bezier_track = p_track.scale_track.values[key_i]; if (path.contains(":scale:x")) { - bezier_track.x = p_animation->bezier_track_interpolate(p_track_i, key_i / BAKE_FPS); + bezier_track.x = p_animation->bezier_track_interpolate(p_track_i, key_i / p_state->get_bake_fps()); } else if (path.contains(":scale:y")) { - bezier_track.y = p_animation->bezier_track_interpolate(p_track_i, key_i / BAKE_FPS); + bezier_track.y = p_animation->bezier_track_interpolate(p_track_i, key_i / p_state->get_bake_fps()); } else if (path.contains(":scale:z")) { - bezier_track.z = p_animation->bezier_track_interpolate(p_track_i, key_i / BAKE_FPS); + bezier_track.z = p_animation->bezier_track_interpolate(p_track_i, key_i / p_state->get_bake_fps()); } p_track.scale_track.values.write[key_i] = bezier_track; } @@ -6612,7 +6604,7 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> p_sta Vector<real_t> new_times; new_times.resize(keys); for (int32_t key_i = 0; key_i < keys; key_i++) { - new_times.write[key_i] = key_i / BAKE_FPS; + new_times.write[key_i] = key_i / p_state->get_bake_fps(); } p_track.position_track.times = new_times; @@ -6622,11 +6614,11 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> p_sta for (int32_t key_i = 0; key_i < keys; key_i++) { Vector3 bezier_track = p_track.position_track.values[key_i]; if (path.contains(":position:x")) { - bezier_track.x = p_animation->bezier_track_interpolate(p_track_i, key_i / BAKE_FPS); + bezier_track.x = p_animation->bezier_track_interpolate(p_track_i, key_i / p_state->get_bake_fps()); } else if (path.contains(":position:y")) { - bezier_track.y = p_animation->bezier_track_interpolate(p_track_i, key_i / BAKE_FPS); + bezier_track.y = p_animation->bezier_track_interpolate(p_track_i, key_i / p_state->get_bake_fps()); } else if (path.contains(":position:z")) { - bezier_track.z = p_animation->bezier_track_interpolate(p_track_i, key_i / BAKE_FPS); + bezier_track.z = p_animation->bezier_track_interpolate(p_track_i, key_i / p_state->get_bake_fps()); } p_track.position_track.values.write[key_i] = bezier_track; } @@ -6636,7 +6628,7 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> p_sta Vector<real_t> new_times; new_times.resize(keys); for (int32_t key_i = 0; key_i < keys; key_i++) { - new_times.write[key_i] = key_i / BAKE_FPS; + new_times.write[key_i] = key_i / p_state->get_bake_fps(); } p_track.rotation_track.times = new_times; @@ -6645,13 +6637,13 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> p_sta for (int32_t key_i = 0; key_i < keys; key_i++) { Quaternion bezier_track = p_track.rotation_track.values[key_i]; if (path.contains(":rotation:x")) { - bezier_track.x = p_animation->bezier_track_interpolate(p_track_i, key_i / BAKE_FPS); + bezier_track.x = p_animation->bezier_track_interpolate(p_track_i, key_i / p_state->get_bake_fps()); } else if (path.contains(":rotation:y")) { - bezier_track.y = p_animation->bezier_track_interpolate(p_track_i, key_i / BAKE_FPS); + bezier_track.y = p_animation->bezier_track_interpolate(p_track_i, key_i / p_state->get_bake_fps()); } else if (path.contains(":rotation:z")) { - bezier_track.z = p_animation->bezier_track_interpolate(p_track_i, key_i / BAKE_FPS); + bezier_track.z = p_animation->bezier_track_interpolate(p_track_i, key_i / p_state->get_bake_fps()); } else if (path.contains(":rotation:w")) { - bezier_track.w = p_animation->bezier_track_interpolate(p_track_i, key_i / BAKE_FPS); + bezier_track.w = p_animation->bezier_track_interpolate(p_track_i, key_i / p_state->get_bake_fps()); } p_track.rotation_track.values.write[key_i] = bezier_track; } @@ -7305,6 +7297,7 @@ Node *GLTFDocument::generate_scene(Ref<GLTFState> p_state, float p_bake_fps, boo ERR_FAIL_NULL_V(state, nullptr); ERR_FAIL_INDEX_V(0, state->root_nodes.size(), nullptr); Error err = OK; + p_state->set_bake_fps(p_bake_fps); Node *root = _generate_scene_node_tree(state); ERR_FAIL_NULL_V(root, nullptr); _process_mesh_instances(state, root); @@ -7313,7 +7306,7 @@ Node *GLTFDocument::generate_scene(Ref<GLTFState> p_state, float p_bake_fps, boo root->add_child(ap, true); ap->set_owner(root); for (int i = 0; i < state->animations.size(); i++) { - _import_animation(state, ap, i, p_bake_fps, p_trimming, p_remove_immutable_tracks); + _import_animation(state, ap, i, p_trimming, p_remove_immutable_tracks); } } for (KeyValue<GLTFNodeIndex, Node *> E : state->scene_nodes) { diff --git a/modules/gltf/gltf_document.h b/modules/gltf/gltf_document.h index 1901e89d51..4f92ceccca 100644 --- a/modules/gltf/gltf_document.h +++ b/modules/gltf/gltf_document.h @@ -75,7 +75,6 @@ public: }; private: - const float BAKE_FPS = 30.0f; int _naming_version = 1; String _image_format = "PNG"; float _lossy_quality = 0.75f; @@ -112,8 +111,7 @@ private: int _get_component_type_size(const int p_component_type); Error _parse_scenes(Ref<GLTFState> p_state); Error _parse_nodes(Ref<GLTFState> p_state); - String _get_type_name(const GLTFType p_component); - String _get_accessor_type_name(const GLTFType p_type); + String _get_accessor_type_name(const GLTFAccessorType p_accessor_type); String _sanitize_animation_name(const String &p_name); String _gen_unique_animation_name(Ref<GLTFState> p_state, const String &p_name); String _sanitize_bone_name(const String &p_name); @@ -133,13 +131,13 @@ private: void _compute_node_heights(Ref<GLTFState> p_state); Error _parse_buffers(Ref<GLTFState> p_state, const String &p_base_path); Error _parse_buffer_views(Ref<GLTFState> p_state); - GLTFType _get_type_from_str(const String &p_string); + GLTFAccessorType _get_accessor_type_from_str(const String &p_string); Error _parse_accessors(Ref<GLTFState> p_state); Error _decode_buffer_view(Ref<GLTFState> p_state, double *p_dst, const GLTFBufferViewIndex p_buffer_view, const int p_skip_every, const int p_skip_bytes, const int p_element_size, const int p_count, - const GLTFType p_type, const int p_component_count, + const GLTFAccessorType p_accessor_type, const int p_component_count, const int p_component_type, const int p_component_size, const bool p_normalized, const int p_byte_offset, const bool p_for_vertex); @@ -268,7 +266,7 @@ private: const Vector<Transform3D> p_attribs, const bool p_for_vertex); Error _encode_buffer_view(Ref<GLTFState> p_state, const double *p_src, - const int p_count, const GLTFType p_type, + const int p_count, const GLTFAccessorType p_accessor_type, const int p_component_type, const bool p_normalized, const int p_byte_offset, const bool p_for_vertex, GLTFBufferViewIndex &r_accessor, const bool p_for_indices = false); @@ -328,7 +326,7 @@ public: void _generate_scene_node(Ref<GLTFState> p_state, const GLTFNodeIndex p_node_index, Node *p_scene_parent, Node *p_scene_root); void _generate_skeleton_bone_node(Ref<GLTFState> p_state, const GLTFNodeIndex p_node_index, Node *p_scene_parent, Node *p_scene_root); void _import_animation(Ref<GLTFState> p_state, AnimationPlayer *p_animation_player, - const GLTFAnimationIndex p_index, const float p_bake_fps, const bool p_trimming, const bool p_remove_immutable_tracks); + const GLTFAnimationIndex p_index, const bool p_trimming, const bool p_remove_immutable_tracks); void _convert_mesh_instances(Ref<GLTFState> p_state); GLTFCameraIndex _convert_camera(Ref<GLTFState> p_state, Camera3D *p_camera); void _convert_light_to_gltf(Light3D *p_light, Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node); diff --git a/modules/gltf/gltf_state.cpp b/modules/gltf/gltf_state.cpp index ed31aadc01..73a61ff77f 100644 --- a/modules/gltf/gltf_state.cpp +++ b/modules/gltf/gltf_state.cpp @@ -100,6 +100,8 @@ void GLTFState::_bind_methods() { ClassDB::bind_method(D_METHOD("set_additional_data", "extension_name", "additional_data"), &GLTFState::set_additional_data); ClassDB::bind_method(D_METHOD("get_handle_binary_image"), &GLTFState::get_handle_binary_image); ClassDB::bind_method(D_METHOD("set_handle_binary_image", "method"), &GLTFState::set_handle_binary_image); + ClassDB::bind_method(D_METHOD("set_bake_fps", "value"), &GLTFState::set_bake_fps); + ClassDB::bind_method(D_METHOD("get_bake_fps"), &GLTFState::get_bake_fps); ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "json"), "set_json", "get_json"); // Dictionary ADD_PROPERTY(PropertyInfo(Variant::INT, "major_version"), "set_major_version", "get_major_version"); // int @@ -130,6 +132,7 @@ void GLTFState::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "import_as_skeleton_bones"), "set_import_as_skeleton_bones", "get_import_as_skeleton_bones"); // bool ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "animations", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_animations", "get_animations"); // Vector<Ref<GLTFAnimation>> ADD_PROPERTY(PropertyInfo(Variant::INT, "handle_binary_image", PROPERTY_HINT_ENUM, "Discard All Textures,Extract Textures,Embed as Basis Universal,Embed as Uncompressed", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_handle_binary_image", "get_handle_binary_image"); // enum + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bake_fps"), "set_bake_fps", "get_bake_fps"); BIND_CONSTANT(HANDLE_BINARY_DISCARD_TEXTURES); BIND_CONSTANT(HANDLE_BINARY_EXTRACT_TEXTURES); diff --git a/modules/gltf/gltf_state.h b/modules/gltf/gltf_state.h index c9efffa3ae..07efafe13b 100644 --- a/modules/gltf/gltf_state.h +++ b/modules/gltf/gltf_state.h @@ -57,6 +57,7 @@ protected: int minor_version = 0; String copyright; Vector<uint8_t> glb_data; + double bake_fps = 30.0; bool use_named_skin_binds = false; bool use_khr_texture_transform = false; @@ -108,6 +109,14 @@ protected: static void _bind_methods(); public: + double get_bake_fps() const { + return bake_fps; + } + + void set_bake_fps(double value) { + bake_fps = value; + } + void add_used_extension(const String &p_extension, bool p_required = false); GLTFBufferViewIndex append_data_to_buffers(const Vector<uint8_t> &p_data, const bool p_deduplication); diff --git a/modules/gltf/structures/gltf_accessor.cpp b/modules/gltf/structures/gltf_accessor.cpp index 2119a0ee82..602f0d9dc4 100644 --- a/modules/gltf/structures/gltf_accessor.cpp +++ b/modules/gltf/structures/gltf_accessor.cpp @@ -41,8 +41,10 @@ void GLTFAccessor::_bind_methods() { ClassDB::bind_method(D_METHOD("set_normalized", "normalized"), &GLTFAccessor::set_normalized); ClassDB::bind_method(D_METHOD("get_count"), &GLTFAccessor::get_count); ClassDB::bind_method(D_METHOD("set_count", "count"), &GLTFAccessor::set_count); - ClassDB::bind_method(D_METHOD("get_type"), &GLTFAccessor::get_type); - ClassDB::bind_method(D_METHOD("set_type", "type"), &GLTFAccessor::set_type); + ClassDB::bind_method(D_METHOD("get_accessor_type"), &GLTFAccessor::get_accessor_type); + ClassDB::bind_method(D_METHOD("set_accessor_type", "accessor_type"), &GLTFAccessor::set_accessor_type); + ClassDB::bind_method(D_METHOD("get_type"), &GLTFAccessor::get_accessor_type); + ClassDB::bind_method(D_METHOD("set_type", "type"), &GLTFAccessor::set_accessor_type); ClassDB::bind_method(D_METHOD("get_min"), &GLTFAccessor::get_min); ClassDB::bind_method(D_METHOD("set_min", "min"), &GLTFAccessor::set_min); ClassDB::bind_method(D_METHOD("get_max"), &GLTFAccessor::get_max); @@ -65,7 +67,8 @@ void GLTFAccessor::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "component_type"), "set_component_type", "get_component_type"); // int ADD_PROPERTY(PropertyInfo(Variant::BOOL, "normalized"), "set_normalized", "get_normalized"); // bool ADD_PROPERTY(PropertyInfo(Variant::INT, "count"), "set_count", "get_count"); // int - ADD_PROPERTY(PropertyInfo(Variant::INT, "type"), "set_type", "get_type"); // GLTFType + ADD_PROPERTY(PropertyInfo(Variant::INT, "accessor_type"), "set_accessor_type", "get_accessor_type"); // GLTFAccessorType + ADD_PROPERTY(PropertyInfo(Variant::INT, "type"), "set_type", "get_type"); // Deprecated, GLTFAccessorType ADD_PROPERTY(PropertyInfo(Variant::PACKED_FLOAT64_ARRAY, "min"), "set_min", "get_min"); // Vector<real_t> ADD_PROPERTY(PropertyInfo(Variant::PACKED_FLOAT64_ARRAY, "max"), "set_max", "get_max"); // Vector<real_t> ADD_PROPERTY(PropertyInfo(Variant::INT, "sparse_count"), "set_sparse_count", "get_sparse_count"); // int @@ -116,12 +119,12 @@ void GLTFAccessor::set_count(int p_count) { count = p_count; } -int GLTFAccessor::get_type() { - return (int)type; +int GLTFAccessor::get_accessor_type() { + return (int)accessor_type; } -void GLTFAccessor::set_type(int p_type) { - type = (GLTFType)p_type; // TODO: Register enum +void GLTFAccessor::set_accessor_type(int p_accessor_type) { + accessor_type = (GLTFAccessorType)p_accessor_type; // TODO: Register enum } Vector<double> GLTFAccessor::get_min() { diff --git a/modules/gltf/structures/gltf_accessor.h b/modules/gltf/structures/gltf_accessor.h index 6b1734601a..51ca282630 100644 --- a/modules/gltf/structures/gltf_accessor.h +++ b/modules/gltf/structures/gltf_accessor.h @@ -35,6 +35,16 @@ #include "core/io/resource.h" +enum GLTFAccessorType { + TYPE_SCALAR, + TYPE_VEC2, + TYPE_VEC3, + TYPE_VEC4, + TYPE_MAT2, + TYPE_MAT3, + TYPE_MAT4, +}; + struct GLTFAccessor : public Resource { GDCLASS(GLTFAccessor, Resource); friend class GLTFDocument; @@ -45,7 +55,7 @@ private: int component_type = 0; bool normalized = false; int count = 0; - GLTFType type = GLTFType::TYPE_SCALAR; + GLTFAccessorType accessor_type = GLTFAccessorType::TYPE_SCALAR; Vector<double> min; Vector<double> max; int sparse_count = 0; @@ -74,8 +84,8 @@ public: int get_count(); void set_count(int p_count); - int get_type(); - void set_type(int p_type); + int get_accessor_type(); + void set_accessor_type(int p_accessor_type); Vector<double> get_min(); void set_min(Vector<double> p_min); diff --git a/modules/mono/build_scripts/build_assemblies.py b/modules/mono/build_scripts/build_assemblies.py index f9709362eb..efbf298f99 100755 --- a/modules/mono/build_scripts/build_assemblies.py +++ b/modules/mono/build_scripts/build_assemblies.py @@ -5,7 +5,7 @@ import os.path import shlex import subprocess from dataclasses import dataclass -from typing import Optional, List +from typing import List, Optional def find_dotnet_cli(): @@ -304,9 +304,7 @@ def generate_sdk_package_versions(): <GodotVersionConstants>{1}</GodotVersionConstants> </PropertyGroup> </Project> -""".format( - version_str, ";".join(version_defines) - ) +""".format(version_str, ";".join(version_defines)) # We write in ../SdkPackageVersions.props. with open(os.path.join(dirname(script_path), "SdkPackageVersions.props"), "w", encoding="utf-8", newline="\n") as f: @@ -323,9 +321,7 @@ def generate_sdk_package_versions(): public const string VersionDocsUrl = "https://docs.godotengine.org/en/{docs_branch}"; }} }} -""".format( - **version_info - ) +""".format(**version_info) generators_dir = os.path.join( dirname(script_path), diff --git a/modules/mono/build_scripts/mono_configure.py b/modules/mono/build_scripts/mono_configure.py index e5469c4980..98c50dcc22 100644 --- a/modules/mono/build_scripts/mono_configure.py +++ b/modules/mono/build_scripts/mono_configure.py @@ -1,7 +1,3 @@ -import os -import os.path - - def is_desktop(platform): return platform in ["windows", "macos", "linuxbsd"] diff --git a/modules/mono/config.py b/modules/mono/config.py index 3d087c9e27..5ebdb83b36 100644 --- a/modules/mono/config.py +++ b/modules/mono/config.py @@ -12,7 +12,7 @@ def configure(env): # Check if the platform has marked mono as supported. supported = env.get("supported", []) - if not "mono" in supported: + if "mono" not in supported: raise RuntimeError("This module does not currently support building for this platform") env.add_module_version_string("mono") diff --git a/modules/multiplayer/scene_cache_interface.cpp b/modules/multiplayer/scene_cache_interface.cpp index 2ea9ce8819..af43123b29 100644 --- a/modules/multiplayer/scene_cache_interface.cpp +++ b/modules/multiplayer/scene_cache_interface.cpp @@ -76,7 +76,7 @@ void SceneCacheInterface::on_peer_change(int p_id, bool p_connected) { for (KeyValue<int, ObjectID> E : pinfo->recv_nodes) { NodeCache *nc = nodes_cache.getptr(E.value); ERR_CONTINUE(!nc); - nc->recv_ids.erase(E.key); + nc->recv_ids.erase(p_id); } for (const ObjectID &oid : pinfo->sent_nodes) { NodeCache *nc = nodes_cache.getptr(oid); diff --git a/modules/navigation/2d/nav_mesh_generator_2d.cpp b/modules/navigation/2d/nav_mesh_generator_2d.cpp index 2198158f9c..ace361a08a 100644 --- a/modules/navigation/2d/nav_mesh_generator_2d.cpp +++ b/modules/navigation/2d/nav_mesh_generator_2d.cpp @@ -263,7 +263,7 @@ void NavMeshGenerator2D::generator_parse_geometry_node(Ref<NavigationPolygon> p_ // Special case for TileMap, so that internal layer get parsed even if p_recurse_children is false. for (int i = 0; i < p_node->get_child_count(); i++) { TileMapLayer *tile_map_layer = Object::cast_to<TileMapLayer>(p_node->get_child(i)); - if (tile_map_layer->get_index_in_tile_map() >= 0) { + if (tile_map_layer && tile_map_layer->get_index_in_tile_map() >= 0) { generator_parse_tile_map_layer_node(p_navigation_mesh, p_source_geometry_data, tile_map_layer); } } diff --git a/modules/navigation/3d/nav_mesh_generator_3d.cpp b/modules/navigation/3d/nav_mesh_generator_3d.cpp index cc3bbdbf01..df0bdc9537 100644 --- a/modules/navigation/3d/nav_mesh_generator_3d.cpp +++ b/modules/navigation/3d/nav_mesh_generator_3d.cpp @@ -895,9 +895,22 @@ void NavMeshGenerator3D::generator_bake_from_source_geometry_data(Ref<Navigation Vector<Vector3> nav_vertices; + HashMap<Vector3, int> recast_vertex_to_native_index; + LocalVector<int> recast_index_to_native_index; + recast_index_to_native_index.resize(detail_mesh->nverts); + for (int i = 0; i < detail_mesh->nverts; i++) { const float *v = &detail_mesh->verts[i * 3]; - nav_vertices.push_back(Vector3(v[0], v[1], v[2])); + const Vector3 vertex = Vector3(v[0], v[1], v[2]); + int *existing_index_ptr = recast_vertex_to_native_index.getptr(vertex); + if (!existing_index_ptr) { + int new_index = recast_vertex_to_native_index.size(); + recast_index_to_native_index[i] = new_index; + recast_vertex_to_native_index[vertex] = new_index; + nav_vertices.push_back(vertex); + } else { + recast_index_to_native_index[i] = *existing_index_ptr; + } } p_navigation_mesh->set_vertices(nav_vertices); p_navigation_mesh->clear_polygons(); @@ -912,9 +925,14 @@ void NavMeshGenerator3D::generator_bake_from_source_geometry_data(Ref<Navigation Vector<int> nav_indices; nav_indices.resize(3); // Polygon order in recast is opposite than godot's - nav_indices.write[0] = ((int)(detail_mesh_bverts + detail_mesh_tris[j * 4 + 0])); - nav_indices.write[1] = ((int)(detail_mesh_bverts + detail_mesh_tris[j * 4 + 2])); - nav_indices.write[2] = ((int)(detail_mesh_bverts + detail_mesh_tris[j * 4 + 1])); + int index1 = ((int)(detail_mesh_bverts + detail_mesh_tris[j * 4 + 0])); + int index2 = ((int)(detail_mesh_bverts + detail_mesh_tris[j * 4 + 2])); + int index3 = ((int)(detail_mesh_bverts + detail_mesh_tris[j * 4 + 1])); + + nav_indices.write[0] = recast_index_to_native_index[index1]; + nav_indices.write[1] = recast_index_to_native_index[index2]; + nav_indices.write[2] = recast_index_to_native_index[index3]; + p_navigation_mesh->add_polygon(nav_indices); } } diff --git a/modules/openxr/doc_classes/OpenXRExtensionWrapperExtension.xml b/modules/openxr/doc_classes/OpenXRExtensionWrapperExtension.xml index 79aa547c52..338d632524 100644 --- a/modules/openxr/doc_classes/OpenXRExtensionWrapperExtension.xml +++ b/modules/openxr/doc_classes/OpenXRExtensionWrapperExtension.xml @@ -84,6 +84,12 @@ Called right before the OpenXR instance is destroyed. </description> </method> + <method name="_on_main_swapchains_created" qualifiers="virtual"> + <return type="void" /> + <description> + Called right after the main swapchains are (re)created. + </description> + </method> <method name="_on_pre_render" qualifiers="virtual"> <return type="void" /> <description> diff --git a/modules/openxr/extensions/openxr_extension_wrapper.h b/modules/openxr/extensions/openxr_extension_wrapper.h index ce03df0b30..8d05657afc 100644 --- a/modules/openxr/extensions/openxr_extension_wrapper.h +++ b/modules/openxr/extensions/openxr_extension_wrapper.h @@ -84,6 +84,7 @@ public: // This is when controller data is queried and made available to game logic. virtual void on_process() {} virtual void on_pre_render() {} // `on_pre_render` is called right before we start rendering our XR viewports. + virtual void on_main_swapchains_created() {} // `on_main_swapchains_created` is called right after our main swapchains are (re)created. virtual void on_pre_draw_viewport(RID p_render_target) {} // `on_pre_draw_viewport` is called right before we start rendering this viewport virtual void on_post_draw_viewport(RID p_render_target) {} // `on_port_draw_viewport` is called right after we start rendering this viewport (note that on Vulkan draw commands may only be queued) diff --git a/modules/openxr/extensions/openxr_extension_wrapper_extension.cpp b/modules/openxr/extensions/openxr_extension_wrapper_extension.cpp index 3b31e1b1f6..e09ca484d5 100644 --- a/modules/openxr/extensions/openxr_extension_wrapper_extension.cpp +++ b/modules/openxr/extensions/openxr_extension_wrapper_extension.cpp @@ -50,6 +50,7 @@ void OpenXRExtensionWrapperExtension::_bind_methods() { GDVIRTUAL_BIND(_on_session_created, "session"); GDVIRTUAL_BIND(_on_process); GDVIRTUAL_BIND(_on_pre_render); + GDVIRTUAL_BIND(_on_main_swapchains_created); GDVIRTUAL_BIND(_on_session_destroyed); GDVIRTUAL_BIND(_on_state_idle); GDVIRTUAL_BIND(_on_state_ready); @@ -198,6 +199,10 @@ void OpenXRExtensionWrapperExtension::on_pre_render() { GDVIRTUAL_CALL(_on_pre_render); } +void OpenXRExtensionWrapperExtension::on_main_swapchains_created() { + GDVIRTUAL_CALL(_on_main_swapchains_created); +} + void OpenXRExtensionWrapperExtension::on_session_destroyed() { GDVIRTUAL_CALL(_on_session_destroyed); } diff --git a/modules/openxr/extensions/openxr_extension_wrapper_extension.h b/modules/openxr/extensions/openxr_extension_wrapper_extension.h index 71d2a57ff8..e37853903b 100644 --- a/modules/openxr/extensions/openxr_extension_wrapper_extension.h +++ b/modules/openxr/extensions/openxr_extension_wrapper_extension.h @@ -86,6 +86,7 @@ public: virtual void on_session_created(const XrSession p_session) override; virtual void on_process() override; virtual void on_pre_render() override; + virtual void on_main_swapchains_created() override; virtual void on_session_destroyed() override; GDVIRTUAL0(_on_register_metadata); @@ -95,6 +96,7 @@ public: GDVIRTUAL1(_on_session_created, uint64_t); GDVIRTUAL0(_on_process); GDVIRTUAL0(_on_pre_render); + GDVIRTUAL0(_on_main_swapchains_created); GDVIRTUAL0(_on_session_destroyed); virtual void on_state_idle() override; diff --git a/modules/openxr/extensions/openxr_fb_foveation_extension.cpp b/modules/openxr/extensions/openxr_fb_foveation_extension.cpp index bbdd2e3c8a..8ce808dd3c 100644 --- a/modules/openxr/extensions/openxr_fb_foveation_extension.cpp +++ b/modules/openxr/extensions/openxr_fb_foveation_extension.cpp @@ -101,7 +101,7 @@ void *OpenXRFBFoveationExtension::set_swapchain_create_info_and_get_next_pointer } } -void OpenXRFBFoveationExtension::on_state_ready() { +void OpenXRFBFoveationExtension::on_main_swapchains_created() { update_profile(); } @@ -127,26 +127,41 @@ void OpenXRFBFoveationExtension::set_foveation_dynamic(XrFoveationDynamicFB p_fo update_profile(); } -void OpenXRFBFoveationExtension::update_profile() { - if (!is_enabled()) { +void OpenXRFBFoveationExtension::_update_profile() { + // Must be called from rendering thread! + ERR_NOT_ON_RENDER_THREAD; + + OpenXRFBFoveationExtension *fov_ext = OpenXRFBFoveationExtension::get_singleton(); + ERR_FAIL_NULL(fov_ext); + + if (!fov_ext->is_enabled()) { + return; + } + + OpenXRAPI *openxr_api = OpenXRAPI::get_singleton(); + ERR_FAIL_NULL(openxr_api); + + XrSwapchain main_color_swapchain = openxr_api->get_color_swapchain(); + if (main_color_swapchain == XR_NULL_HANDLE) { + // Our swapchain hasn't been created yet, we'll call this again once it has. return; } XrFoveationLevelProfileCreateInfoFB level_profile_create_info; level_profile_create_info.type = XR_TYPE_FOVEATION_LEVEL_PROFILE_CREATE_INFO_FB; level_profile_create_info.next = nullptr; - level_profile_create_info.level = foveation_level; + level_profile_create_info.level = fov_ext->foveation_level; level_profile_create_info.verticalOffset = 0.0f; - level_profile_create_info.dynamic = foveation_dynamic; + level_profile_create_info.dynamic = fov_ext->foveation_dynamic; XrFoveationProfileCreateInfoFB profile_create_info; profile_create_info.type = XR_TYPE_FOVEATION_PROFILE_CREATE_INFO_FB; profile_create_info.next = &level_profile_create_info; XrFoveationProfileFB foveation_profile; - XrResult result = xrCreateFoveationProfileFB(OpenXRAPI::get_singleton()->get_session(), &profile_create_info, &foveation_profile); + XrResult result = fov_ext->xrCreateFoveationProfileFB(openxr_api->get_session(), &profile_create_info, &foveation_profile); if (XR_FAILED(result)) { - print_line("OpenXR: Unable to create the foveation profile [", OpenXRAPI::get_singleton()->get_error_string(result), "]"); + print_line("OpenXR: Unable to create the foveation profile [", openxr_api->get_error_string(result), "]"); return; } @@ -154,15 +169,15 @@ void OpenXRFBFoveationExtension::update_profile() { foveation_update_state.type = XR_TYPE_SWAPCHAIN_STATE_FOVEATION_FB; foveation_update_state.profile = foveation_profile; - result = swapchain_update_state_ext->xrUpdateSwapchainFB(OpenXRAPI::get_singleton()->get_color_swapchain(), (XrSwapchainStateBaseHeaderFB *)&foveation_update_state); + result = fov_ext->swapchain_update_state_ext->xrUpdateSwapchainFB(main_color_swapchain, (XrSwapchainStateBaseHeaderFB *)&foveation_update_state); if (XR_FAILED(result)) { - print_line("OpenXR: Unable to update the swapchain [", OpenXRAPI::get_singleton()->get_error_string(result), "]"); + print_line("OpenXR: Unable to update the swapchain [", openxr_api->get_error_string(result), "]"); // We still want to destroy our profile so keep going... } - result = xrDestroyFoveationProfileFB(foveation_profile); + result = fov_ext->xrDestroyFoveationProfileFB(foveation_profile); if (XR_FAILED(result)) { - print_line("OpenXR: Unable to destroy the foveation profile [", OpenXRAPI::get_singleton()->get_error_string(result), "]"); + print_line("OpenXR: Unable to destroy the foveation profile [", openxr_api->get_error_string(result), "]"); } } diff --git a/modules/openxr/extensions/openxr_fb_foveation_extension.h b/modules/openxr/extensions/openxr_fb_foveation_extension.h index 1c5e722731..84bd7011b5 100644 --- a/modules/openxr/extensions/openxr_fb_foveation_extension.h +++ b/modules/openxr/extensions/openxr_fb_foveation_extension.h @@ -60,7 +60,7 @@ public: virtual void *set_swapchain_create_info_and_get_next_pointer(void *p_next_pointer) override; - virtual void on_state_ready() override; + virtual void on_main_swapchains_created() override; bool is_enabled() const; @@ -82,7 +82,15 @@ private: XrFoveationLevelFB foveation_level = XR_FOVEATION_LEVEL_NONE_FB; XrFoveationDynamicFB foveation_dynamic = XR_FOVEATION_DYNAMIC_DISABLED_FB; - void update_profile(); + static void _update_profile(); + + void update_profile() { + // If we're rendering on a separate thread, we may still be processing the last frame, don't communicate this till we're ready... + RenderingServer *rendering_server = RenderingServer::get_singleton(); + ERR_FAIL_NULL(rendering_server); + + rendering_server->call_on_render_thread(callable_mp_static(&OpenXRFBFoveationExtension::_update_profile)); + } // Enable foveation on this swapchain XrSwapchainCreateInfoFoveationFB swapchain_create_info_foveation_fb; diff --git a/modules/openxr/openxr_api.cpp b/modules/openxr/openxr_api.cpp index 32512070d6..541e369925 100644 --- a/modules/openxr/openxr_api.cpp +++ b/modules/openxr/openxr_api.cpp @@ -1220,6 +1220,10 @@ bool OpenXRAPI::create_main_swapchains(Size2i p_size) { } }; + for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) { + wrapper->on_main_swapchains_created(); + } + return true; }; diff --git a/modules/openxr/scene/openxr_composition_layer.cpp b/modules/openxr/scene/openxr_composition_layer.cpp index 50cc7b9e7b..f69a907be9 100644 --- a/modules/openxr/scene/openxr_composition_layer.cpp +++ b/modules/openxr/scene/openxr_composition_layer.cpp @@ -151,6 +151,16 @@ void OpenXRCompositionLayer::update_fallback_mesh() { should_update_fallback_mesh = true; } +XrPosef OpenXRCompositionLayer::get_openxr_pose() { + Transform3D reference_frame = XRServer::get_singleton()->get_reference_frame(); + Transform3D transform = reference_frame.inverse() * get_transform(); + Quaternion quat(transform.basis.orthonormalized()); + return { + { (float)quat.x, (float)quat.y, (float)quat.z, (float)quat.w }, + { (float)transform.origin.x, (float)transform.origin.y, (float)transform.origin.z } + }; +} + bool OpenXRCompositionLayer::is_viewport_in_use(SubViewport *p_viewport) { 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) { @@ -165,7 +175,9 @@ void OpenXRCompositionLayer::set_layer_viewport(SubViewport *p_viewport) { return; } - 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 (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.")); + } layer_viewport = p_viewport; diff --git a/modules/openxr/scene/openxr_composition_layer.h b/modules/openxr/scene/openxr_composition_layer.h index 6792364295..55cae27d23 100644 --- a/modules/openxr/scene/openxr_composition_layer.h +++ b/modules/openxr/scene/openxr_composition_layer.h @@ -77,6 +77,8 @@ protected: void update_fallback_mesh(); + XrPosef get_openxr_pose(); + static Vector<OpenXRCompositionLayer *> composition_layer_nodes; bool is_viewport_in_use(SubViewport *p_viewport); diff --git a/modules/openxr/scene/openxr_composition_layer_cylinder.cpp b/modules/openxr/scene/openxr_composition_layer_cylinder.cpp index 728ba71006..6c8d2fc885 100644 --- a/modules/openxr/scene/openxr_composition_layer_cylinder.cpp +++ b/modules/openxr/scene/openxr_composition_layer_cylinder.cpp @@ -52,6 +52,7 @@ OpenXRCompositionLayerCylinder::OpenXRCompositionLayerCylinder() { aspect_ratio, // aspectRatio }; openxr_layer_provider = memnew(OpenXRViewportCompositionLayerProvider((XrCompositionLayerBaseHeader *)&composition_layer)); + XRServer::get_singleton()->connect("reference_frame_changed", callable_mp(this, &OpenXRCompositionLayerCylinder::update_transform)); } OpenXRCompositionLayerCylinder::~OpenXRCompositionLayerCylinder() { @@ -131,14 +132,15 @@ Ref<Mesh> OpenXRCompositionLayerCylinder::_create_fallback_mesh() { void OpenXRCompositionLayerCylinder::_notification(int p_what) { switch (p_what) { case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: { - Transform3D transform = get_transform(); - Quaternion quat(transform.basis.orthonormalized()); - composition_layer.pose.orientation = { (float)quat.x, (float)quat.y, (float)quat.z, (float)quat.w }; - composition_layer.pose.position = { (float)transform.origin.x, (float)transform.origin.y, (float)transform.origin.z }; + update_transform(); } break; } } +void OpenXRCompositionLayerCylinder::update_transform() { + composition_layer.pose = get_openxr_pose(); +} + void OpenXRCompositionLayerCylinder::set_radius(float p_radius) { ERR_FAIL_COND(p_radius <= 0); radius = p_radius; diff --git a/modules/openxr/scene/openxr_composition_layer_cylinder.h b/modules/openxr/scene/openxr_composition_layer_cylinder.h index bb1d242267..9bd5a42d36 100644 --- a/modules/openxr/scene/openxr_composition_layer_cylinder.h +++ b/modules/openxr/scene/openxr_composition_layer_cylinder.h @@ -50,6 +50,8 @@ protected: void _notification(int p_what); + void update_transform(); + virtual Ref<Mesh> _create_fallback_mesh() override; public: diff --git a/modules/openxr/scene/openxr_composition_layer_equirect.cpp b/modules/openxr/scene/openxr_composition_layer_equirect.cpp index 14cfea8da6..b6f5d27ffe 100644 --- a/modules/openxr/scene/openxr_composition_layer_equirect.cpp +++ b/modules/openxr/scene/openxr_composition_layer_equirect.cpp @@ -53,6 +53,7 @@ OpenXRCompositionLayerEquirect::OpenXRCompositionLayerEquirect() { -lower_vertical_angle, // lowerVerticalAngle }; openxr_layer_provider = memnew(OpenXRViewportCompositionLayerProvider((XrCompositionLayerBaseHeader *)&composition_layer)); + XRServer::get_singleton()->connect("reference_frame_changed", callable_mp(this, &OpenXRCompositionLayerEquirect::update_transform)); } OpenXRCompositionLayerEquirect::~OpenXRCompositionLayerEquirect() { @@ -139,14 +140,15 @@ Ref<Mesh> OpenXRCompositionLayerEquirect::_create_fallback_mesh() { void OpenXRCompositionLayerEquirect::_notification(int p_what) { switch (p_what) { case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: { - Transform3D transform = get_transform(); - Quaternion quat(transform.basis.orthonormalized()); - composition_layer.pose.orientation = { (float)quat.x, (float)quat.y, (float)quat.z, (float)quat.w }; - composition_layer.pose.position = { (float)transform.origin.x, (float)transform.origin.y, (float)transform.origin.z }; + update_transform(); } break; } } +void OpenXRCompositionLayerEquirect::update_transform() { + composition_layer.pose = get_openxr_pose(); +} + void OpenXRCompositionLayerEquirect::set_radius(float p_radius) { ERR_FAIL_COND(p_radius <= 0); radius = p_radius; diff --git a/modules/openxr/scene/openxr_composition_layer_equirect.h b/modules/openxr/scene/openxr_composition_layer_equirect.h index 66f8b0a91c..af6cd92cbe 100644 --- a/modules/openxr/scene/openxr_composition_layer_equirect.h +++ b/modules/openxr/scene/openxr_composition_layer_equirect.h @@ -51,6 +51,8 @@ protected: void _notification(int p_what); + void update_transform(); + virtual Ref<Mesh> _create_fallback_mesh() override; public: diff --git a/modules/openxr/scene/openxr_composition_layer_quad.cpp b/modules/openxr/scene/openxr_composition_layer_quad.cpp index 8c5b8ec26b..21919496d6 100644 --- a/modules/openxr/scene/openxr_composition_layer_quad.cpp +++ b/modules/openxr/scene/openxr_composition_layer_quad.cpp @@ -50,6 +50,7 @@ OpenXRCompositionLayerQuad::OpenXRCompositionLayerQuad() { { (float)quad_size.x, (float)quad_size.y }, // size }; openxr_layer_provider = memnew(OpenXRViewportCompositionLayerProvider((XrCompositionLayerBaseHeader *)&composition_layer)); + XRServer::get_singleton()->connect("reference_frame_changed", callable_mp(this, &OpenXRCompositionLayerQuad::update_transform)); } OpenXRCompositionLayerQuad::~OpenXRCompositionLayerQuad() { @@ -72,14 +73,15 @@ Ref<Mesh> OpenXRCompositionLayerQuad::_create_fallback_mesh() { void OpenXRCompositionLayerQuad::_notification(int p_what) { switch (p_what) { case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: { - Transform3D transform = get_transform(); - Quaternion quat(transform.basis.orthonormalized()); - composition_layer.pose.orientation = { (float)quat.x, (float)quat.y, (float)quat.z, (float)quat.w }; - composition_layer.pose.position = { (float)transform.origin.x, (float)transform.origin.y, (float)transform.origin.z }; + update_transform(); } break; } } +void OpenXRCompositionLayerQuad::update_transform() { + composition_layer.pose = get_openxr_pose(); +} + void OpenXRCompositionLayerQuad::set_quad_size(const Size2 &p_size) { quad_size = p_size; composition_layer.size = { (float)quad_size.x, (float)quad_size.y }; diff --git a/modules/openxr/scene/openxr_composition_layer_quad.h b/modules/openxr/scene/openxr_composition_layer_quad.h index 21bb9b2d85..0c3172dbb2 100644 --- a/modules/openxr/scene/openxr_composition_layer_quad.h +++ b/modules/openxr/scene/openxr_composition_layer_quad.h @@ -47,6 +47,8 @@ protected: void _notification(int p_what); + void update_transform(); + virtual Ref<Mesh> _create_fallback_mesh() override; public: diff --git a/modules/text_server_adv/gdextension_build/SConstruct b/modules/text_server_adv/gdextension_build/SConstruct index 58e1868e31..2f7a175d91 100644 --- a/modules/text_server_adv/gdextension_build/SConstruct +++ b/modules/text_server_adv/gdextension_build/SConstruct @@ -1,14 +1,15 @@ #!/usr/bin/env python import atexit import sys -import methods import time +import methods + # Enable ANSI escape code support on Windows 10 and later (for colored console output). # <https://github.com/python/cpython/issues/73245> if sys.stdout.isatty() and sys.platform == "win32": try: - from ctypes import windll, byref, WinError # type: ignore + from ctypes import WinError, byref, windll # type: ignore from ctypes.wintypes import DWORD # type: ignore stdout_handle = windll.kernel32.GetStdHandle(DWORD(-11)) @@ -720,7 +721,7 @@ if env["static_icu_data"]: env.Append(CXXFLAGS=["-DICU_STATIC_DATA"]) env.Append(CPPPATH=["../../../thirdparty/icu4c/"]) else: - thirdparty_sources += ["../icu_data/icudata_stub.cpp"] + thirdparty_icu_sources += ["../icu_data/icudata_stub.cpp"] env_icu.Append(CPPPATH=["../../../thirdparty/icu4c/common/", "../../../thirdparty/icu4c/i18n/"]) env_icu.Append( diff --git a/modules/text_server_adv/gdextension_build/methods.py b/modules/text_server_adv/gdextension_build/methods.py index 3453c3e8f0..43bf56abfe 100644 --- a/modules/text_server_adv/gdextension_build/methods.py +++ b/modules/text_server_adv/gdextension_build/methods.py @@ -81,9 +81,9 @@ def disable_warnings(self): self.Append(CCFLAGS=["/w"]) self.Append(CFLAGS=["/w"]) self.Append(CXXFLAGS=["/w"]) - self["CCFLAGS"] = [x for x in self["CCFLAGS"] if not x in warn_flags] - self["CFLAGS"] = [x for x in self["CFLAGS"] if not x in warn_flags] - self["CXXFLAGS"] = [x for x in self["CXXFLAGS"] if not x in warn_flags] + self["CCFLAGS"] = [x for x in self["CCFLAGS"] if x not in warn_flags] + self["CFLAGS"] = [x for x in self["CFLAGS"] if x not in warn_flags] + self["CXXFLAGS"] = [x for x in self["CXXFLAGS"] if x not in warn_flags] else: self.Append(CCFLAGS=["-w"]) self.Append(CFLAGS=["-w"]) @@ -117,31 +117,31 @@ def make_icu_data(target, source, env): def write_macos_plist(target, binary_name, identifier, name): os.makedirs(f"{target}/Resource/", exist_ok=True) with open(f"{target}/Resource/Info.plist", "w", encoding="utf-8", newline="\n") as f: - f.write(f'<?xml version="1.0" encoding="UTF-8"?>\n') - f.write( - f'<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n' - ) - f.write(f'<plist version="1.0">\n') - f.write(f"<dict>\n") - f.write(f"\t<key>CFBundleExecutable</key>\n") - f.write(f"\t<string>{binary_name}</string>\n") - f.write(f"\t<key>CFBundleIdentifier</key>\n") - f.write(f"\t<string>{identifier}</string>\n") - f.write(f"\t<key>CFBundleInfoDictionaryVersion</key>\n") - f.write(f"\t<string>6.0</string>\n") - f.write(f"\t<key>CFBundleName</key>\n") - f.write(f"\t<string>{name}</string>\n") - f.write(f"\t<key>CFBundlePackageType</key>\n") - f.write(f"\t<string>FMWK</string>\n") - f.write(f"\t<key>CFBundleShortVersionString</key>\n") - f.write(f"\t<string>1.0.0</string>\n") - f.write(f"\t<key>CFBundleSupportedPlatforms</key>\n") - f.write(f"\t<array>\n") - f.write(f"\t\t<string>MacOSX</string>\n") - f.write(f"\t</array>\n") - f.write(f"\t<key>CFBundleVersion</key>\n") - f.write(f"\t<string>1.0.0</string>\n") - f.write(f"\t<key>LSMinimumSystemVersion</key>\n") - f.write(f"\t<string>10.14</string>\n") - f.write(f"</dict>\n") - f.write(f"</plist>\n") + f.write(f"""\ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleExecutable</key> + <string>{binary_name}</string> + <key>CFBundleIdentifier</key> + <string>{identifier}</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundleName</key> + <string>{name}</string> + <key>CFBundlePackageType</key> + <string>FMWK</string> + <key>CFBundleShortVersionString</key> + <string>1.0.0</string> + <key>CFBundleSupportedPlatforms</key> + <array> + <string>MacOSX</string> + </array> + <key>CFBundleVersion</key> + <string>1.0.0</string> + <key>LSMinimumSystemVersion</key> + <string>10.14</string> +</dict> +</plist> +""") diff --git a/modules/text_server_fb/gdextension_build/SConstruct b/modules/text_server_fb/gdextension_build/SConstruct index 9326a53026..f6ae7149be 100644 --- a/modules/text_server_fb/gdextension_build/SConstruct +++ b/modules/text_server_fb/gdextension_build/SConstruct @@ -1,14 +1,15 @@ #!/usr/bin/env python import atexit import sys -import methods import time +import methods + # Enable ANSI escape code support on Windows 10 and later (for colored console output). # <https://github.com/python/cpython/issues/73245> if sys.stdout.isatty() and sys.platform == "win32": try: - from ctypes import windll, byref, WinError # type: ignore + from ctypes import WinError, byref, windll # type: ignore from ctypes.wintypes import DWORD # type: ignore stdout_handle = windll.kernel32.GetStdHandle(DWORD(-11)) diff --git a/modules/text_server_fb/gdextension_build/methods.py b/modules/text_server_fb/gdextension_build/methods.py index 3453c3e8f0..43bf56abfe 100644 --- a/modules/text_server_fb/gdextension_build/methods.py +++ b/modules/text_server_fb/gdextension_build/methods.py @@ -81,9 +81,9 @@ def disable_warnings(self): self.Append(CCFLAGS=["/w"]) self.Append(CFLAGS=["/w"]) self.Append(CXXFLAGS=["/w"]) - self["CCFLAGS"] = [x for x in self["CCFLAGS"] if not x in warn_flags] - self["CFLAGS"] = [x for x in self["CFLAGS"] if not x in warn_flags] - self["CXXFLAGS"] = [x for x in self["CXXFLAGS"] if not x in warn_flags] + self["CCFLAGS"] = [x for x in self["CCFLAGS"] if x not in warn_flags] + self["CFLAGS"] = [x for x in self["CFLAGS"] if x not in warn_flags] + self["CXXFLAGS"] = [x for x in self["CXXFLAGS"] if x not in warn_flags] else: self.Append(CCFLAGS=["-w"]) self.Append(CFLAGS=["-w"]) @@ -117,31 +117,31 @@ def make_icu_data(target, source, env): def write_macos_plist(target, binary_name, identifier, name): os.makedirs(f"{target}/Resource/", exist_ok=True) with open(f"{target}/Resource/Info.plist", "w", encoding="utf-8", newline="\n") as f: - f.write(f'<?xml version="1.0" encoding="UTF-8"?>\n') - f.write( - f'<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n' - ) - f.write(f'<plist version="1.0">\n') - f.write(f"<dict>\n") - f.write(f"\t<key>CFBundleExecutable</key>\n") - f.write(f"\t<string>{binary_name}</string>\n") - f.write(f"\t<key>CFBundleIdentifier</key>\n") - f.write(f"\t<string>{identifier}</string>\n") - f.write(f"\t<key>CFBundleInfoDictionaryVersion</key>\n") - f.write(f"\t<string>6.0</string>\n") - f.write(f"\t<key>CFBundleName</key>\n") - f.write(f"\t<string>{name}</string>\n") - f.write(f"\t<key>CFBundlePackageType</key>\n") - f.write(f"\t<string>FMWK</string>\n") - f.write(f"\t<key>CFBundleShortVersionString</key>\n") - f.write(f"\t<string>1.0.0</string>\n") - f.write(f"\t<key>CFBundleSupportedPlatforms</key>\n") - f.write(f"\t<array>\n") - f.write(f"\t\t<string>MacOSX</string>\n") - f.write(f"\t</array>\n") - f.write(f"\t<key>CFBundleVersion</key>\n") - f.write(f"\t<string>1.0.0</string>\n") - f.write(f"\t<key>LSMinimumSystemVersion</key>\n") - f.write(f"\t<string>10.14</string>\n") - f.write(f"</dict>\n") - f.write(f"</plist>\n") + f.write(f"""\ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleExecutable</key> + <string>{binary_name}</string> + <key>CFBundleIdentifier</key> + <string>{identifier}</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundleName</key> + <string>{name}</string> + <key>CFBundlePackageType</key> + <string>FMWK</string> + <key>CFBundleShortVersionString</key> + <string>1.0.0</string> + <key>CFBundleSupportedPlatforms</key> + <array> + <string>MacOSX</string> + </array> + <key>CFBundleVersion</key> + <string>1.0.0</string> + <key>LSMinimumSystemVersion</key> + <string>10.14</string> +</dict> +</plist> +""") diff --git a/modules/webxr/native/library_godot_webxr.js b/modules/webxr/native/library_godot_webxr.js index 722b448fd5..031530a047 100644 --- a/modules/webxr/native/library_godot_webxr.js +++ b/modules/webxr/native/library_godot_webxr.js @@ -67,7 +67,8 @@ const GodotWebXR = { GodotWebXR.orig_requestAnimationFrame = Browser.requestAnimationFrame; } Browser.requestAnimationFrame = enable - ? GodotWebXR.requestAnimationFrame : GodotWebXR.orig_requestAnimationFrame; + ? GodotWebXR.requestAnimationFrame + : GodotWebXR.orig_requestAnimationFrame; }, pauseResumeMainLoop: () => { // Once both GodotWebXR.session and GodotWebXR.space are set or @@ -76,9 +77,9 @@ const GodotWebXR = { // gets picked up automatically, however, in the Oculus Browser // on the Quest, we need to pause and resume the main loop. Browser.mainLoop.pause(); - runtimeKeepalivePush(); // eslint-disable-line no-undef + runtimeKeepalivePush(); window.setTimeout(function () { - runtimeKeepalivePop(); // eslint-disable-line no-undef + runtimeKeepalivePop(); Browser.mainLoop.resume(); }, 0); }, @@ -287,12 +288,12 @@ const GodotWebXR = { // Store onsimpleevent so we can use it later. GodotWebXR.onsimpleevent = onsimpleevent; - const gl_context_handle = _emscripten_webgl_get_current_context(); // eslint-disable-line no-undef + const gl_context_handle = _emscripten_webgl_get_current_context(); const gl = GL.getContext(gl_context_handle).GLctx; GodotWebXR.gl = gl; gl.makeXRCompatible().then(function () { - GodotWebXR.gl_binding = new XRWebGLBinding(session, gl); // eslint-disable-line no-undef + GodotWebXR.gl_binding = new XRWebGLBinding(session, gl); // This will trigger the layer to get created. GodotWebXR.getLayer(); diff --git a/platform/SCsub b/platform/SCsub index b24c189848..b07023efed 100644 --- a/platform/SCsub +++ b/platform/SCsub @@ -1,9 +1,10 @@ #!/usr/bin/env python -import methods from glob import glob from pathlib import Path +import methods + Import("env") env.platform_sources = [] diff --git a/platform/android/SCsub b/platform/android/SCsub index 4d76ffb180..bc1b5e9200 100644 --- a/platform/android/SCsub +++ b/platform/android/SCsub @@ -1,7 +1,8 @@ #!/usr/bin/env python -import sys import subprocess +import sys + from methods import print_warning Import("env") diff --git a/platform/android/android_input_handler.cpp b/platform/android/android_input_handler.cpp index 373dd399e4..41edc35276 100644 --- a/platform/android/android_input_handler.cpp +++ b/platform/android/android_input_handler.cpp @@ -176,6 +176,8 @@ void AndroidInputHandler::process_touch_event(int p_event, int p_pointer, const for (int i = 0; i < p_points.size(); i++) { touch.write[i].id = p_points[i].id; touch.write[i].pos = p_points[i].pos; + touch.write[i].pressure = p_points[i].pressure; + touch.write[i].tilt = p_points[i].tilt; } //send touch @@ -208,6 +210,8 @@ void AndroidInputHandler::process_touch_event(int p_event, int p_pointer, const ev->set_position(p_points[idx].pos); ev->set_relative(p_points[idx].pos - touch[i].pos); ev->set_relative_screen_position(ev->get_relative()); + ev->set_pressure(p_points[idx].pressure); + ev->set_tilt(p_points[idx].tilt); Input::get_singleton()->parse_input_event(ev); touch.write[i].pos = p_points[idx].pos; } diff --git a/platform/android/android_input_handler.h b/platform/android/android_input_handler.h index 78a484cf05..e3365d8cb8 100644 --- a/platform/android/android_input_handler.h +++ b/platform/android/android_input_handler.h @@ -42,6 +42,8 @@ public: struct TouchPos { int id = 0; Point2 pos; + float pressure = 0; + Vector2 tilt; }; struct MouseEventInfo { diff --git a/platform/android/detect.py b/platform/android/detect.py index 6a8c4ed86d..ed0ceb5862 100644 --- a/platform/android/detect.py +++ b/platform/android/detect.py @@ -1,10 +1,11 @@ import os -import sys import platform import subprocess -from methods import print_warning, print_error +import sys from typing import TYPE_CHECKING +from methods import print_error, print_warning + if TYPE_CHECKING: from SCons.Script.SConscript import SConsEnvironment @@ -66,11 +67,11 @@ def get_min_target_api(): def get_flags(): - return [ - ("arch", "arm64"), # Default for convenience. - ("target", "template_debug"), - ("supported", ["mono"]), - ] + return { + "arch": "arm64", # Default for convenience. + "target": "template_debug", + "supported": ["mono"], + } # Check if Android NDK version is installed diff --git a/platform/android/display_server_android.cpp b/platform/android/display_server_android.cpp index 9869756be1..288c37be29 100644 --- a/platform/android/display_server_android.cpp +++ b/platform/android/display_server_android.cpp @@ -511,8 +511,8 @@ Vector<String> DisplayServerAndroid::get_rendering_drivers_func() { return drivers; } -DisplayServer *DisplayServerAndroid::create_func(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Error &r_error) { - DisplayServer *ds = memnew(DisplayServerAndroid(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_position, p_resolution, p_screen, r_error)); +DisplayServer *DisplayServerAndroid::create_func(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error) { + DisplayServer *ds = memnew(DisplayServerAndroid(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_position, p_resolution, p_screen, p_context, r_error)); if (r_error != OK) { if (p_rendering_driver == "vulkan") { OS::get_singleton()->alert( @@ -579,7 +579,7 @@ void DisplayServerAndroid::notify_surface_changed(int p_width, int p_height) { } } -DisplayServerAndroid::DisplayServerAndroid(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Error &r_error) { +DisplayServerAndroid::DisplayServerAndroid(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error) { rendering_driver = p_rendering_driver; keep_screen_on = GLOBAL_GET("display/window/energy_saving/keep_screen_on"); diff --git a/platform/android/display_server_android.h b/platform/android/display_server_android.h index e1914f4d18..90bda18cfa 100644 --- a/platform/android/display_server_android.h +++ b/platform/android/display_server_android.h @@ -211,7 +211,7 @@ public: virtual void mouse_set_mode(MouseMode p_mode) override; virtual MouseMode mouse_get_mode() const override; - static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Error &r_error); + static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error); static Vector<String> get_rendering_drivers_func(); static void register_android_driver(); @@ -228,7 +228,7 @@ public: virtual void set_native_icon(const String &p_filename) override; virtual void set_icon(const Ref<Image> &p_icon) override; - DisplayServerAndroid(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Error &r_error); + DisplayServerAndroid(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error); ~DisplayServerAndroid(); }; diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotEditText.java b/platform/android/java/lib/src/org/godotengine/godot/input/GodotEditText.java index dc8a0e54bb..c085bb8886 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotEditText.java +++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotEditText.java @@ -266,8 +266,13 @@ public class GodotEditText extends EditText { boolean hasHardwareKeyboard() { Configuration config = getResources().getConfiguration(); - return config.keyboard != Configuration.KEYBOARD_NOKEYS && + boolean hasHardwareKeyboardConfig = config.keyboard != Configuration.KEYBOARD_NOKEYS && config.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO; + if (hasHardwareKeyboardConfig) { + return true; + } + + return mRenderView.getInputHandler().hasHardwareKeyboard(); } // =========================================================== diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.kt b/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.kt index cfbbcf7d0e..49b34a5229 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.kt +++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.kt @@ -65,7 +65,7 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi private var lastDragY: Float = 0.0f override fun onDown(event: MotionEvent): Boolean { - GodotInputHandler.handleMotionEvent(event.source, MotionEvent.ACTION_DOWN, event.buttonState, event.x, event.y, nextDownIsDoubleTap) + GodotInputHandler.handleMotionEvent(event, MotionEvent.ACTION_DOWN, nextDownIsDoubleTap) nextDownIsDoubleTap = false return true } @@ -85,20 +85,14 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi } // Cancel the previous down event - GodotInputHandler.handleMotionEvent( - event.source, - MotionEvent.ACTION_CANCEL, - event.buttonState, - event.x, - event.y - ) + GodotInputHandler.handleMotionEvent(event, MotionEvent.ACTION_CANCEL) // Turn a context click into a single tap right mouse button click. GodotInputHandler.handleMouseEvent( + event, MotionEvent.ACTION_DOWN, MotionEvent.BUTTON_SECONDARY, - event.x, - event.y + false ) contextClickInProgress = true } @@ -110,16 +104,7 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi if (!hasCapture) { // Dispatch a mouse relative ACTION_UP event to signal the end of the capture - GodotInputHandler.handleMouseEvent( - MotionEvent.ACTION_UP, - 0, - 0f, - 0f, - 0f, - 0f, - false, - true - ) + GodotInputHandler.handleMouseEvent(MotionEvent.ACTION_UP, true) } pointerCaptureInProgress = hasCapture } @@ -142,26 +127,11 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi return true } - val sourceMouseRelative = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - event.isFromSource(InputDevice.SOURCE_MOUSE_RELATIVE) - } else { - false - } - if (pointerCaptureInProgress || dragInProgress || contextClickInProgress) { if (contextClickInProgress || GodotInputHandler.isMouseEvent(event)) { // This may be an ACTION_BUTTON_RELEASE event which we don't handle, // so we convert it to an ACTION_UP event. - GodotInputHandler.handleMouseEvent( - MotionEvent.ACTION_UP, - event.buttonState, - event.x, - event.y, - 0f, - 0f, - false, - sourceMouseRelative - ) + GodotInputHandler.handleMouseEvent(event, MotionEvent.ACTION_UP) } else { GodotInputHandler.handleTouchEvent(event) } @@ -178,21 +148,7 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi private fun onActionMove(event: MotionEvent): Boolean { if (contextClickInProgress) { - val sourceMouseRelative = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - event.isFromSource(InputDevice.SOURCE_MOUSE_RELATIVE) - } else { - false - } - GodotInputHandler.handleMouseEvent( - event.actionMasked, - MotionEvent.BUTTON_SECONDARY, - event.x, - event.y, - 0f, - 0f, - false, - sourceMouseRelative - ) + GodotInputHandler.handleMouseEvent(event, event.actionMasked, MotionEvent.BUTTON_SECONDARY, false) return true } else if (!scaleInProgress) { // The 'onScroll' event is triggered with a long delay. @@ -213,7 +169,7 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi if (event.actionMasked == MotionEvent.ACTION_UP) { nextDownIsDoubleTap = false GodotInputHandler.handleMotionEvent(event) - } else if (event.actionMasked == MotionEvent.ACTION_MOVE && panningAndScalingEnabled == false) { + } else if (event.actionMasked == MotionEvent.ACTION_MOVE && !panningAndScalingEnabled) { GodotInputHandler.handleMotionEvent(event) } @@ -235,13 +191,7 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi if (dragInProgress || lastDragX != 0.0f || lastDragY != 0.0f) { if (originEvent != null) { // Cancel the drag - GodotInputHandler.handleMotionEvent( - originEvent.source, - MotionEvent.ACTION_CANCEL, - originEvent.buttonState, - originEvent.x, - originEvent.y - ) + GodotInputHandler.handleMotionEvent(originEvent, MotionEvent.ACTION_CANCEL) } dragInProgress = false lastDragX = 0.0f diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java b/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java index fe971cf442..43ae71f8e1 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java +++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java @@ -62,6 +62,7 @@ public class GodotInputHandler implements InputManager.InputDeviceListener { private final SparseIntArray mJoystickIds = new SparseIntArray(4); private final SparseArray<Joystick> mJoysticksDevices = new SparseArray<>(4); + private final HashSet<Integer> mHardwareKeyboardIds = new HashSet<>(); private final GodotRenderView mRenderView; private final InputManager mInputManager; @@ -114,6 +115,10 @@ public class GodotInputHandler implements InputManager.InputDeviceListener { rotaryInputAxis = axis; } + boolean hasHardwareKeyboard() { + return !mHardwareKeyboardIds.isEmpty(); + } + private boolean isKeyEventGameDevice(int source) { // Note that keyboards are often (SOURCE_KEYBOARD | SOURCE_DPAD) if (source == (InputDevice.SOURCE_KEYBOARD | InputDevice.SOURCE_DPAD)) @@ -195,7 +200,7 @@ public class GodotInputHandler implements InputManager.InputDeviceListener { } public boolean onTouchEvent(final MotionEvent event) { - lastSeenToolType = event.getToolType(0); + lastSeenToolType = getEventToolType(event); this.scaleGestureDetector.onTouchEvent(event); if (this.gestureDetector.onTouchEvent(event)) { @@ -221,7 +226,7 @@ public class GodotInputHandler implements InputManager.InputDeviceListener { } public boolean onGenericMotionEvent(MotionEvent event) { - lastSeenToolType = event.getToolType(0); + lastSeenToolType = getEventToolType(event); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && gestureDetector.onGenericMotionEvent(event)) { // The gesture detector has handled the event. @@ -310,11 +315,17 @@ public class GodotInputHandler implements InputManager.InputDeviceListener { return; } - int sources = device.getSources(); + // Device may be an external keyboard; store the device id + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && + device.supportsSource(InputDevice.SOURCE_KEYBOARD) && + device.isExternal() && + device.getKeyboardType() == InputDevice.KEYBOARD_TYPE_ALPHABETIC) { + mHardwareKeyboardIds.add(deviceId); + } // Device may not be a joystick or gamepad - if ((sources & InputDevice.SOURCE_GAMEPAD) != InputDevice.SOURCE_GAMEPAD && - (sources & InputDevice.SOURCE_JOYSTICK) != InputDevice.SOURCE_JOYSTICK) { + if (!device.supportsSource(InputDevice.SOURCE_GAMEPAD) && + !device.supportsSource(InputDevice.SOURCE_JOYSTICK)) { return; } @@ -359,6 +370,8 @@ public class GodotInputHandler implements InputManager.InputDeviceListener { @Override public void onInputDeviceRemoved(int deviceId) { + mHardwareKeyboardIds.remove(deviceId); + // Check if the device has not been already removed if (mJoystickIds.indexOfKey(deviceId) < 0) { return; @@ -440,50 +453,65 @@ public class GodotInputHandler implements InputManager.InputDeviceListener { return button; } - static boolean isMouseEvent(MotionEvent event) { - return isMouseEvent(event.getSource()); + private static int getEventToolType(MotionEvent event) { + return event.getPointerCount() > 0 ? event.getToolType(0) : MotionEvent.TOOL_TYPE_UNKNOWN; } - private static boolean isMouseEvent(int eventSource) { - boolean mouseSource = ((eventSource & InputDevice.SOURCE_MOUSE) == InputDevice.SOURCE_MOUSE) || ((eventSource & InputDevice.SOURCE_STYLUS) == InputDevice.SOURCE_STYLUS); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - mouseSource = mouseSource || ((eventSource & InputDevice.SOURCE_MOUSE_RELATIVE) == InputDevice.SOURCE_MOUSE_RELATIVE); + static boolean isMouseEvent(MotionEvent event) { + int toolType = getEventToolType(event); + int eventSource = event.getSource(); + + switch (toolType) { + case MotionEvent.TOOL_TYPE_FINGER: + return false; + + case MotionEvent.TOOL_TYPE_MOUSE: + case MotionEvent.TOOL_TYPE_STYLUS: + case MotionEvent.TOOL_TYPE_ERASER: + return true; + + case MotionEvent.TOOL_TYPE_UNKNOWN: + default: + boolean mouseSource = + ((eventSource & InputDevice.SOURCE_MOUSE) == InputDevice.SOURCE_MOUSE) || + ((eventSource & (InputDevice.SOURCE_TOUCHSCREEN | InputDevice.SOURCE_STYLUS)) == InputDevice.SOURCE_STYLUS); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + mouseSource = mouseSource || + ((eventSource & InputDevice.SOURCE_MOUSE_RELATIVE) == InputDevice.SOURCE_MOUSE_RELATIVE); + } + return mouseSource; } - return mouseSource; } static boolean handleMotionEvent(final MotionEvent event) { - if (isMouseEvent(event)) { - return handleMouseEvent(event); - } - - return handleTouchEvent(event); + return handleMotionEvent(event, event.getActionMasked()); } - static boolean handleMotionEvent(int eventSource, int eventAction, int buttonsMask, float x, float y) { - return handleMotionEvent(eventSource, eventAction, buttonsMask, x, y, false); + static boolean handleMotionEvent(final MotionEvent event, int eventActionOverride) { + return handleMotionEvent(event, eventActionOverride, false); } - static boolean handleMotionEvent(int eventSource, int eventAction, int buttonsMask, float x, float y, boolean doubleTap) { - return handleMotionEvent(eventSource, eventAction, buttonsMask, x, y, 0, 0, doubleTap); + static boolean handleMotionEvent(final MotionEvent event, int eventActionOverride, boolean doubleTap) { + if (isMouseEvent(event)) { + return handleMouseEvent(event, eventActionOverride, doubleTap); + } + return handleTouchEvent(event, eventActionOverride, doubleTap); } - static boolean handleMotionEvent(int eventSource, int eventAction, int buttonsMask, float x, float y, float deltaX, float deltaY, boolean doubleTap) { - if (isMouseEvent(eventSource)) { - return handleMouseEvent(eventAction, buttonsMask, x, y, deltaX, deltaY, doubleTap, false); - } + private static float getEventTiltX(MotionEvent event) { + // Orientation is returned as a radian value between 0 to pi clockwise or 0 to -pi counterclockwise. + final float orientation = event.getOrientation(); - return handleTouchEvent(eventAction, x, y, doubleTap); - } + // Tilt is zero is perpendicular to the screen and pi/2 is flat on the surface. + final float tilt = event.getAxisValue(MotionEvent.AXIS_TILT); - static boolean handleMouseEvent(final MotionEvent event) { - final int eventAction = event.getActionMasked(); - final float x = event.getX(); - final float y = event.getY(); - final int buttonsMask = event.getButtonState(); + float tiltMult = (float)Math.sin(tilt); - final float pressure = event.getPressure(); + // To be consistent with expected tilt. + return (float)-Math.sin(orientation) * tiltMult; + } + private static float getEventTiltY(MotionEvent event) { // Orientation is returned as a radian value between 0 to pi clockwise or 0 to -pi counterclockwise. final float orientation = event.getOrientation(); @@ -493,8 +521,26 @@ public class GodotInputHandler implements InputManager.InputDeviceListener { float tiltMult = (float)Math.sin(tilt); // To be consistent with expected tilt. - final float tiltX = (float)-Math.sin(orientation) * tiltMult; - final float tiltY = (float)Math.cos(orientation) * tiltMult; + return (float)Math.cos(orientation) * tiltMult; + } + + static boolean handleMouseEvent(final MotionEvent event) { + return handleMouseEvent(event, event.getActionMasked()); + } + + static boolean handleMouseEvent(final MotionEvent event, int eventActionOverride) { + return handleMouseEvent(event, eventActionOverride, false); + } + + static boolean handleMouseEvent(final MotionEvent event, int eventActionOverride, boolean doubleTap) { + return handleMouseEvent(event, eventActionOverride, event.getButtonState(), doubleTap); + } + + static boolean handleMouseEvent(final MotionEvent event, int eventActionOverride, int buttonMaskOverride, boolean doubleTap) { + final float x = event.getX(); + final float y = event.getY(); + + final float pressure = event.getPressure(); float verticalFactor = 0; float horizontalFactor = 0; @@ -516,15 +562,11 @@ public class GodotInputHandler implements InputManager.InputDeviceListener { if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { sourceMouseRelative = event.isFromSource(InputDevice.SOURCE_MOUSE_RELATIVE); } - return handleMouseEvent(eventAction, buttonsMask, x, y, horizontalFactor, verticalFactor, false, sourceMouseRelative, pressure, tiltX, tiltY); + return handleMouseEvent(eventActionOverride, buttonMaskOverride, x, y, horizontalFactor, verticalFactor, doubleTap, sourceMouseRelative, pressure, getEventTiltX(event), getEventTiltY(event)); } - static boolean handleMouseEvent(int eventAction, int buttonsMask, float x, float y) { - return handleMouseEvent(eventAction, buttonsMask, x, y, 0, 0, false, false); - } - - static boolean handleMouseEvent(int eventAction, int buttonsMask, float x, float y, float deltaX, float deltaY, boolean doubleClick, boolean sourceMouseRelative) { - return handleMouseEvent(eventAction, buttonsMask, x, y, deltaX, deltaY, doubleClick, sourceMouseRelative, 1, 0, 0); + static boolean handleMouseEvent(int eventAction, boolean sourceMouseRelative) { + return handleMouseEvent(eventAction, 0, 0f, 0f, 0f, 0f, false, sourceMouseRelative, 1f, 0f, 0f); } static boolean handleMouseEvent(int eventAction, int buttonsMask, float x, float y, float deltaX, float deltaY, boolean doubleClick, boolean sourceMouseRelative, float pressure, float tiltX, float tiltY) { @@ -563,37 +605,39 @@ public class GodotInputHandler implements InputManager.InputDeviceListener { } static boolean handleTouchEvent(final MotionEvent event) { + return handleTouchEvent(event, event.getActionMasked()); + } + + static boolean handleTouchEvent(final MotionEvent event, int eventActionOverride) { + return handleTouchEvent(event, eventActionOverride, false); + } + + static boolean handleTouchEvent(final MotionEvent event, int eventActionOverride, boolean doubleTap) { final int pointerCount = event.getPointerCount(); if (pointerCount == 0) { return true; } - final float[] positions = new float[pointerCount * 3]; // pointerId1, x1, y1, pointerId2, etc... + final float[] positions = new float[pointerCount * 6]; // pointerId1, x1, y1, pressure1, tiltX1, tiltY1, pointerId2, etc... for (int i = 0; i < pointerCount; i++) { - positions[i * 3 + 0] = event.getPointerId(i); - positions[i * 3 + 1] = event.getX(i); - positions[i * 3 + 2] = event.getY(i); + positions[i * 6 + 0] = event.getPointerId(i); + positions[i * 6 + 1] = event.getX(i); + positions[i * 6 + 2] = event.getY(i); + positions[i * 6 + 3] = event.getPressure(i); + positions[i * 6 + 4] = getEventTiltX(event); + positions[i * 6 + 5] = getEventTiltY(event); } - final int action = event.getActionMasked(); final int actionPointerId = event.getPointerId(event.getActionIndex()); - return handleTouchEvent(action, actionPointerId, pointerCount, positions, false); - } - - static boolean handleTouchEvent(int eventAction, float x, float y, boolean doubleTap) { - return handleTouchEvent(eventAction, 0, 1, new float[] { 0, x, y }, doubleTap); - } - - static boolean handleTouchEvent(int eventAction, int actionPointerId, int pointerCount, float[] positions, boolean doubleTap) { - switch (eventAction) { + switch (eventActionOverride) { case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: case MotionEvent.ACTION_MOVE: case MotionEvent.ACTION_POINTER_UP: case MotionEvent.ACTION_POINTER_DOWN: { - GodotLib.dispatchTouchEvent(eventAction, actionPointerId, pointerCount, positions, doubleTap); + GodotLib.dispatchTouchEvent(eventActionOverride, actionPointerId, pointerCount, positions, doubleTap); return true; } } diff --git a/platform/android/java_godot_lib_jni.cpp b/platform/android/java_godot_lib_jni.cpp index 93743c4e35..40068745d6 100644 --- a/platform/android/java_godot_lib_jni.cpp +++ b/platform/android/java_godot_lib_jni.cpp @@ -298,11 +298,13 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_dispatchTouchEvent(JN Vector<AndroidInputHandler::TouchPos> points; for (int i = 0; i < pointer_count; i++) { - jfloat p[3]; - env->GetFloatArrayRegion(position, i * 3, 3, p); + jfloat p[6]; + env->GetFloatArrayRegion(position, i * 6, 6, p); AndroidInputHandler::TouchPos tp; - tp.pos = Point2(p[1], p[2]); tp.id = (int)p[0]; + tp.pos = Point2(p[1], p[2]); + tp.pressure = p[3]; + tp.tilt = Vector2(p[4], p[5]); points.push_back(tp); } diff --git a/platform/ios/SCsub b/platform/ios/SCsub index f38914434b..cff7dcc1fd 100644 --- a/platform/ios/SCsub +++ b/platform/ios/SCsub @@ -2,11 +2,11 @@ Import("env") -import os, json -from platform_methods import architectures, lipo, get_build_version, detect_mvk -import subprocess +import os import shutil +from platform_methods import detect_mvk, lipo + def generate_bundle(target, source, env): bin_dir = Dir("#bin").abspath diff --git a/platform/ios/detect.py b/platform/ios/detect.py index e3bac4ec5c..35e1b9cd6d 100644 --- a/platform/ios/detect.py +++ b/platform/ios/detect.py @@ -1,9 +1,9 @@ import os import sys -from methods import print_error, detect_darwin_sdk_path - from typing import TYPE_CHECKING +from methods import detect_darwin_sdk_path, print_error + if TYPE_CHECKING: from SCons.Script.SConscript import SConsEnvironment @@ -47,13 +47,13 @@ def get_doc_path(): def get_flags(): - return [ - ("arch", "arm64"), # Default for convenience. - ("target", "template_debug"), - ("use_volk", False), - ("supported", ["mono"]), - ("builtin_pcre2_with_jit", False), - ] + return { + "arch": "arm64", # Default for convenience. + "target": "template_debug", + "use_volk": False, + "supported": ["mono"], + "builtin_pcre2_with_jit": False, + } def configure(env: "SConsEnvironment"): diff --git a/platform/ios/display_server_ios.h b/platform/ios/display_server_ios.h index c6015a058c..4dded5aa29 100644 --- a/platform/ios/display_server_ios.h +++ b/platform/ios/display_server_ios.h @@ -84,7 +84,7 @@ class DisplayServerIOS : public DisplayServer { void perform_event(const Ref<InputEvent> &p_event); - DisplayServerIOS(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Error &r_error); + DisplayServerIOS(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error); ~DisplayServerIOS(); public: @@ -93,7 +93,7 @@ public: static DisplayServerIOS *get_singleton(); static void register_ios_driver(); - static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Error &r_error); + static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error); static Vector<String> get_rendering_drivers_func(); // MARK: - Events diff --git a/platform/ios/display_server_ios.mm b/platform/ios/display_server_ios.mm index 62bc55dce8..a454dd5ba0 100644 --- a/platform/ios/display_server_ios.mm +++ b/platform/ios/display_server_ios.mm @@ -51,7 +51,7 @@ DisplayServerIOS *DisplayServerIOS::get_singleton() { return (DisplayServerIOS *)DisplayServer::get_singleton(); } -DisplayServerIOS::DisplayServerIOS(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Error &r_error) { +DisplayServerIOS::DisplayServerIOS(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error) { KeyMappingIOS::initialize(); rendering_driver = p_rendering_driver; @@ -155,8 +155,8 @@ DisplayServerIOS::~DisplayServerIOS() { #endif } -DisplayServer *DisplayServerIOS::create_func(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Error &r_error) { - return memnew(DisplayServerIOS(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_position, p_resolution, p_screen, r_error)); +DisplayServer *DisplayServerIOS::create_func(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error) { + return memnew(DisplayServerIOS(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_position, p_resolution, p_screen, p_context, r_error)); } Vector<String> DisplayServerIOS::get_rendering_drivers_func() { @@ -399,7 +399,12 @@ void DisplayServerIOS::set_system_theme_change_callback(const Callable &p_callab void DisplayServerIOS::emit_system_theme_changed() { if (system_theme_changed.is_valid()) { - system_theme_changed.call(); + Variant ret; + Callable::CallError ce; + system_theme_changed.callp(nullptr, 0, ret, ce); + if (ce.error != Callable::CallError::CALL_OK) { + ERR_PRINT(vformat("Failed to execute system theme changed callback: %s.", Variant::get_callable_error_text(system_theme_changed, nullptr, 0, ce))); + } } } diff --git a/platform/ios/export/export_plugin.cpp b/platform/ios/export/export_plugin.cpp index 6a452f08fa..769d97694a 100644 --- a/platform/ios/export/export_plugin.cpp +++ b/platform/ios/export/export_plugin.cpp @@ -541,6 +541,44 @@ void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_ } strnew += lines[i].replace("$interface_orientations", orientations); + } else if (lines[i].contains("$ipad_interface_orientations")) { + String orientations; + const DisplayServer::ScreenOrientation screen_orientation = + DisplayServer::ScreenOrientation(int(GLOBAL_GET("display/window/handheld/orientation"))); + + switch (screen_orientation) { + case DisplayServer::SCREEN_LANDSCAPE: + orientations += "<string>UIInterfaceOrientationLandscapeRight</string>\n"; + break; + case DisplayServer::SCREEN_PORTRAIT: + orientations += "<string>UIInterfaceOrientationPortrait</string>\n"; + break; + case DisplayServer::SCREEN_REVERSE_LANDSCAPE: + orientations += "<string>UIInterfaceOrientationLandscapeLeft</string>\n"; + break; + case DisplayServer::SCREEN_REVERSE_PORTRAIT: + orientations += "<string>UIInterfaceOrientationPortraitUpsideDown</string>\n"; + break; + case DisplayServer::SCREEN_SENSOR_LANDSCAPE: + // Allow both landscape orientations depending on sensor direction. + orientations += "<string>UIInterfaceOrientationLandscapeLeft</string>\n"; + orientations += "<string>UIInterfaceOrientationLandscapeRight</string>\n"; + break; + case DisplayServer::SCREEN_SENSOR_PORTRAIT: + // Allow both portrait orientations depending on sensor direction. + orientations += "<string>UIInterfaceOrientationPortrait</string>\n"; + orientations += "<string>UIInterfaceOrientationPortraitUpsideDown</string>\n"; + break; + case DisplayServer::SCREEN_SENSOR: + // Allow all screen orientations depending on sensor direction. + orientations += "<string>UIInterfaceOrientationLandscapeLeft</string>\n"; + orientations += "<string>UIInterfaceOrientationLandscapeRight</string>\n"; + orientations += "<string>UIInterfaceOrientationPortrait</string>\n"; + orientations += "<string>UIInterfaceOrientationPortraitUpsideDown</string>\n"; + break; + } + + strnew += lines[i].replace("$ipad_interface_orientations", orientations); } else if (lines[i].contains("$camera_usage_description")) { String description = p_preset->get("privacy/camera_usage_description"); strnew += lines[i].replace("$camera_usage_description", description) + "\n"; diff --git a/platform/ios/view_controller.mm b/platform/ios/view_controller.mm index 6f6c04c2c8..787e767109 100644 --- a/platform/ios/view_controller.mm +++ b/platform/ios/view_controller.mm @@ -258,7 +258,11 @@ case DisplayServer::SCREEN_PORTRAIT: return UIInterfaceOrientationMaskPortrait; case DisplayServer::SCREEN_REVERSE_LANDSCAPE: - return UIInterfaceOrientationMaskLandscapeRight; + if (UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPad) { + return UIInterfaceOrientationMaskLandscapeLeft; + } else { + return UIInterfaceOrientationMaskLandscapeRight; + } case DisplayServer::SCREEN_REVERSE_PORTRAIT: return UIInterfaceOrientationMaskPortraitUpsideDown; case DisplayServer::SCREEN_SENSOR_LANDSCAPE: @@ -268,7 +272,11 @@ case DisplayServer::SCREEN_SENSOR: return UIInterfaceOrientationMaskAll; case DisplayServer::SCREEN_LANDSCAPE: - return UIInterfaceOrientationMaskLandscapeLeft; + if (UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPad) { + return UIInterfaceOrientationMaskLandscapeRight; + } else { + return UIInterfaceOrientationMaskLandscapeLeft; + } } } diff --git a/platform/linuxbsd/detect.py b/platform/linuxbsd/detect.py index 47f3bcadc9..303a88ab26 100644 --- a/platform/linuxbsd/detect.py +++ b/platform/linuxbsd/detect.py @@ -1,11 +1,11 @@ import os import platform import sys -from methods import print_warning, print_error, get_compiler_version, using_gcc -from platform_methods import detect_arch - from typing import TYPE_CHECKING +from methods import get_compiler_version, print_error, print_warning, using_gcc +from platform_methods import detect_arch + if TYPE_CHECKING: from SCons.Script.SConscript import SConsEnvironment @@ -65,10 +65,10 @@ def get_doc_path(): def get_flags(): - return [ - ("arch", detect_arch()), - ("supported", ["mono"]), - ] + return { + "arch": detect_arch(), + "supported": ["mono"], + } def configure(env: "SConsEnvironment"): diff --git a/platform/linuxbsd/freedesktop_portal_desktop.cpp b/platform/linuxbsd/freedesktop_portal_desktop.cpp index b8b7d64ec6..671da7fc2a 100644 --- a/platform/linuxbsd/freedesktop_portal_desktop.cpp +++ b/platform/linuxbsd/freedesktop_portal_desktop.cpp @@ -597,7 +597,12 @@ void FreeDesktopPortalDesktop::_thread_monitor(void *p_ud) { void FreeDesktopPortalDesktop::_system_theme_changed_callback() { if (system_theme_changed.is_valid()) { - system_theme_changed.call(); + Variant ret; + Callable::CallError ce; + system_theme_changed.callp(nullptr, 0, ret, ce); + if (ce.error != Callable::CallError::CALL_OK) { + ERR_PRINT(vformat("Failed to execute system theme changed callback: %s.", Variant::get_callable_error_text(system_theme_changed, nullptr, 0, ce))); + } } } diff --git a/platform/linuxbsd/wayland/display_server_wayland.cpp b/platform/linuxbsd/wayland/display_server_wayland.cpp index f7995472d0..12d3a6fd2f 100644 --- a/platform/linuxbsd/wayland/display_server_wayland.cpp +++ b/platform/linuxbsd/wayland/display_server_wayland.cpp @@ -1137,7 +1137,14 @@ void DisplayServerWayland::process_events() { WindowData wd = main_window; if (wd.drop_files_callback.is_valid()) { - wd.drop_files_callback.call(dropfiles_msg->files); + Variant v_files = dropfiles_msg->files; + const Variant *v_args[1] = { &v_files }; + Variant ret; + Callable::CallError ce; + wd.drop_files_callback.callp((const Variant **)&v_args, 1, ret, ce); + if (ce.error != Callable::CallError::CALL_OK) { + ERR_PRINT(vformat("Failed to execute drop files callback: %s.", Variant::get_callable_error_text(wd.drop_files_callback, v_args, 1, ce))); + } } } } @@ -1209,6 +1216,15 @@ void DisplayServerWayland::set_context(Context p_context) { wayland_thread.window_set_app_id(MAIN_WINDOW_ID, app_id); } +bool DisplayServerWayland::is_window_transparency_available() const { +#if defined(RD_ENABLED) + if (rendering_device && !rendering_device->is_composite_alpha_supported()) { + return false; + } +#endif + return OS::get_singleton()->is_layered_allowed(); +} + Vector<String> DisplayServerWayland::get_rendering_drivers_func() { Vector<String> drivers; @@ -1224,8 +1240,8 @@ Vector<String> DisplayServerWayland::get_rendering_drivers_func() { return drivers; } -DisplayServer *DisplayServerWayland::create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Point2i *p_position, const Size2i &p_resolution, int p_screen, Error &r_error) { - DisplayServer *ds = memnew(DisplayServerWayland(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_resolution, r_error)); +DisplayServer *DisplayServerWayland::create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Point2i *p_position, const Size2i &p_resolution, int p_screen, Context p_context, Error &r_error) { + DisplayServer *ds = memnew(DisplayServerWayland(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_resolution, p_context, r_error)); if (r_error != OK) { ERR_PRINT("Can't create the Wayland display server."); memdelete(ds); @@ -1235,7 +1251,7 @@ DisplayServer *DisplayServerWayland::create_func(const String &p_rendering_drive return ds; } -DisplayServerWayland::DisplayServerWayland(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) { +DisplayServerWayland::DisplayServerWayland(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Context p_context, Error &r_error) { #ifdef GLES3_ENABLED #ifdef SOWRAP_ENABLED #ifdef DEBUG_ENABLED @@ -1247,6 +1263,7 @@ DisplayServerWayland::DisplayServerWayland(const String &p_rendering_driver, Win #endif // GLES3_ENABLED r_error = ERR_UNAVAILABLE; + context = p_context; Error thread_err = wayland_thread.init(); diff --git a/platform/linuxbsd/wayland/display_server_wayland.h b/platform/linuxbsd/wayland/display_server_wayland.h index 1bad358462..c24eb0ee62 100644 --- a/platform/linuxbsd/wayland/display_server_wayland.h +++ b/platform/linuxbsd/wayland/display_server_wayland.h @@ -280,12 +280,14 @@ public: virtual void set_context(Context p_context) override; - static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Point2i *p_position, const Size2i &p_resolution, int p_screen, Error &r_error); + virtual bool is_window_transparency_available() const override; + + static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Point2i *p_position, const Size2i &p_resolution, int p_screen, Context p_context, Error &r_error); static Vector<String> get_rendering_drivers_func(); static void register_wayland_driver(); - DisplayServerWayland(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error); + DisplayServerWayland(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Context p_context, Error &r_error); ~DisplayServerWayland(); }; diff --git a/platform/linuxbsd/wayland/wayland_thread.h b/platform/linuxbsd/wayland/wayland_thread.h index d35a5b7139..0756b6b0ea 100644 --- a/platform/linuxbsd/wayland/wayland_thread.h +++ b/platform/linuxbsd/wayland/wayland_thread.h @@ -43,6 +43,9 @@ #else #include <wayland-client-core.h> #include <wayland-cursor.h> +#ifdef GLES3_ENABLED +#include <wayland-egl.h> +#endif #include <xkbcommon/xkbcommon.h> #endif // SOWRAP_ENABLED diff --git a/platform/linuxbsd/x11/display_server_x11.cpp b/platform/linuxbsd/x11/display_server_x11.cpp index 663a2ec325..767ea927c1 100644 --- a/platform/linuxbsd/x11/display_server_x11.cpp +++ b/platform/linuxbsd/x11/display_server_x11.cpp @@ -4206,7 +4206,10 @@ void DisplayServerX11::popup_close(WindowID p_window) { WindowID win_id = E->get(); popup_list.erase(E); - _send_window_event(windows[win_id], DisplayServerX11::WINDOW_EVENT_CLOSE_REQUEST); + if (win_id != p_window) { + // Only request close on related windows, not this window. We are already processing it. + _send_window_event(windows[win_id], DisplayServerX11::WINDOW_EVENT_CLOSE_REQUEST); + } E = F; } } @@ -5008,7 +5011,14 @@ void DisplayServerX11::process_events() { } if (windows[window_id].drop_files_callback.is_valid()) { - windows[window_id].drop_files_callback.call(files); + Variant v_files = files; + const Variant *v_args[1] = { &v_files }; + Variant ret; + Callable::CallError ce; + windows[window_id].drop_files_callback.callp((const Variant **)&v_args, 1, ret, ce); + if (ce.error != Callable::CallError::CALL_OK) { + ERR_PRINT(vformat("Failed to execute drop files callback: %s.", Variant::get_callable_error_text(windows[window_id].drop_files_callback, v_args, 1, ce))); + } } //Reply that all is well. @@ -5192,6 +5202,23 @@ void DisplayServerX11::set_context(Context p_context) { } } +bool DisplayServerX11::is_window_transparency_available() const { + CharString net_wm_cm_name = vformat("_NET_WM_CM_S%d", XDefaultScreen(x11_display)).ascii(); + Atom net_wm_cm = XInternAtom(x11_display, net_wm_cm_name.get_data(), False); + if (net_wm_cm == None) { + return false; + } + if (XGetSelectionOwner(x11_display, net_wm_cm) == None) { + return false; + } +#if defined(RD_ENABLED) + if (rendering_device && !rendering_device->is_composite_alpha_supported()) { + return false; + } +#endif + return OS::get_singleton()->is_layered_allowed(); +} + void DisplayServerX11::set_native_icon(const String &p_filename) { WARN_PRINT("Native icon not supported by this display server."); } @@ -5333,8 +5360,8 @@ Vector<String> DisplayServerX11::get_rendering_drivers_func() { return drivers; } -DisplayServer *DisplayServerX11::create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Error &r_error) { - DisplayServer *ds = memnew(DisplayServerX11(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_position, p_resolution, p_screen, r_error)); +DisplayServer *DisplayServerX11::create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error) { + DisplayServer *ds = memnew(DisplayServerX11(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_position, p_resolution, p_screen, p_context, r_error)); if (r_error != OK) { if (p_rendering_driver == "vulkan") { String executable_name = OS::get_singleton()->get_executable_path().get_file(); @@ -5765,10 +5792,11 @@ static ::XIMStyle _get_best_xim_style(const ::XIMStyle &p_style_a, const ::XIMSt return p_style_a; } -DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Error &r_error) { +DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error) { KeyMappingX11::initialize(); native_menu = memnew(NativeMenu); + context = p_context; #ifdef SOWRAP_ENABLED #ifdef DEBUG_ENABLED diff --git a/platform/linuxbsd/x11/display_server_x11.h b/platform/linuxbsd/x11/display_server_x11.h index 8a7062857c..7c69df3df0 100644 --- a/platform/linuxbsd/x11/display_server_x11.h +++ b/platform/linuxbsd/x11/display_server_x11.h @@ -530,15 +530,17 @@ public: virtual void set_context(Context p_context) override; + virtual bool is_window_transparency_available() const override; + virtual void set_native_icon(const String &p_filename) override; virtual void set_icon(const Ref<Image> &p_icon) override; - static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Error &r_error); + static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error); static Vector<String> get_rendering_drivers_func(); static void register_x11_driver(); - DisplayServerX11(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Error &r_error); + DisplayServerX11(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error); ~DisplayServerX11(); }; diff --git a/platform/macos/SCsub b/platform/macos/SCsub index 59ef4ee85c..eb8524826f 100644 --- a/platform/macos/SCsub +++ b/platform/macos/SCsub @@ -2,11 +2,13 @@ Import("env") -import os, json -from platform_methods import architectures, lipo, get_build_version -import platform_macos_builders -import subprocess +import os import shutil +import subprocess + +import platform_macos_builders + +from platform_methods import get_build_version, lipo def generate_bundle(target, source, env): diff --git a/platform/macos/detect.py b/platform/macos/detect.py index a5ef29e34f..70cb00c6ff 100644 --- a/platform/macos/detect.py +++ b/platform/macos/detect.py @@ -1,10 +1,10 @@ import os import sys -from methods import print_error, detect_darwin_sdk_path, get_compiler_version, is_vanilla_clang -from platform_methods import detect_arch, detect_mvk - from typing import TYPE_CHECKING +from methods import detect_darwin_sdk_path, get_compiler_version, is_vanilla_clang, print_error +from platform_methods import detect_arch, detect_mvk + if TYPE_CHECKING: from SCons.Script.SConscript import SConsEnvironment @@ -53,11 +53,11 @@ def get_doc_path(): def get_flags(): - return [ - ("arch", detect_arch()), - ("use_volk", False), - ("supported", ["mono"]), - ] + return { + "arch": detect_arch(), + "use_volk": False, + "supported": ["mono"], + } def configure(env: "SConsEnvironment"): @@ -107,7 +107,7 @@ def configure(env: "SConsEnvironment"): env.Append(CCFLAGS=["-fobjc-arc"]) - if not "osxcross" in env: # regular native build + if "osxcross" not in env: # regular native build if env["macports_clang"] != "no": mpprefix = os.environ.get("MACPORTS_PREFIX", "/opt/local") mpclangver = env["macports_clang"] diff --git a/platform/macos/display_server_macos.h b/platform/macos/display_server_macos.h index db76b7d78a..608c34edc6 100644 --- a/platform/macos/display_server_macos.h +++ b/platform/macos/display_server_macos.h @@ -439,12 +439,14 @@ public: virtual Rect2 status_indicator_get_rect(IndicatorID p_id) const override; virtual void delete_status_indicator(IndicatorID p_id) override; - static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Error &r_error); + virtual bool is_window_transparency_available() const override; + + static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error); static Vector<String> get_rendering_drivers_func(); static void register_macos_driver(); - DisplayServerMacOS(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Error &r_error); + DisplayServerMacOS(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error); ~DisplayServerMacOS(); }; diff --git a/platform/macos/display_server_macos.mm b/platform/macos/display_server_macos.mm index e093f01a8a..0ccce1ad6a 100644 --- a/platform/macos/display_server_macos.mm +++ b/platform/macos/display_server_macos.mm @@ -907,7 +907,12 @@ void DisplayServerMacOS::set_system_theme_change_callback(const Callable &p_call void DisplayServerMacOS::emit_system_theme_changed() { if (system_theme_changed.is_valid()) { - system_theme_changed.call(); + Variant ret; + Callable::CallError ce; + system_theme_changed.callp(nullptr, 0, ret, ce); + if (ce.error != Callable::CallError::CALL_OK) { + ERR_PRINT(vformat("Failed to execute system theme changed callback: %s.", Variant::get_callable_error_text(system_theme_changed, nullptr, 0, ce))); + } } } @@ -3314,8 +3319,17 @@ void DisplayServerMacOS::delete_status_indicator(IndicatorID p_id) { indicators.erase(p_id); } -DisplayServer *DisplayServerMacOS::create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Error &r_error) { - DisplayServer *ds = memnew(DisplayServerMacOS(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_position, p_resolution, p_screen, r_error)); +bool DisplayServerMacOS::is_window_transparency_available() const { +#if defined(RD_ENABLED) + if (rendering_device && !rendering_device->is_composite_alpha_supported()) { + return false; + } +#endif + return OS::get_singleton()->is_layered_allowed(); +} + +DisplayServer *DisplayServerMacOS::create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error) { + DisplayServer *ds = memnew(DisplayServerMacOS(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_position, p_resolution, p_screen, p_context, r_error)); if (r_error != OK) { if (p_rendering_driver == "vulkan") { String executable_command; @@ -3439,7 +3453,10 @@ void DisplayServerMacOS::popup_close(WindowID p_window) { WindowID win_id = E->get(); popup_list.erase(E); - send_window_event(windows[win_id], DisplayServerMacOS::WINDOW_EVENT_CLOSE_REQUEST); + if (win_id != p_window) { + // Only request close on related windows, not this window. We are already processing it. + send_window_event(windows[win_id], DisplayServerMacOS::WINDOW_EVENT_CLOSE_REQUEST); + } E = F; } if (!was_empty && popup_list.is_empty()) { @@ -3500,7 +3517,7 @@ bool DisplayServerMacOS::mouse_process_popups(bool p_close) { return closed; } -DisplayServerMacOS::DisplayServerMacOS(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Error &r_error) { +DisplayServerMacOS::DisplayServerMacOS(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error) { KeyMappingMacOS::initialize(); Input::get_singleton()->set_event_dispatch_function(_dispatch_input_events); diff --git a/platform/macos/godot_content_view.mm b/platform/macos/godot_content_view.mm index 68a7288ad4..7dcb1ac9db 100644 --- a/platform/macos/godot_content_view.mm +++ b/platform/macos/godot_content_view.mm @@ -323,7 +323,14 @@ NSString *file = [NSURL URLWithString:url].path; files.push_back(String::utf8([file UTF8String])); } - wd.drop_files_callback.call(files); + Variant v_files = files; + const Variant *v_args[1] = { &v_files }; + Variant ret; + Callable::CallError ce; + wd.drop_files_callback.callp((const Variant **)&v_args, 1, ret, ce); + if (ce.error != Callable::CallError::CALL_OK) { + ERR_PRINT(vformat("Failed to execute drop files callback: %s.", Variant::get_callable_error_text(wd.drop_files_callback, v_args, 1, ce))); + } } return NO; diff --git a/platform/macos/godot_status_item.mm b/platform/macos/godot_status_item.mm index 0990a16b2b..1b16d496a2 100644 --- a/platform/macos/godot_status_item.mm +++ b/platform/macos/godot_status_item.mm @@ -66,10 +66,13 @@ if (cb.is_valid()) { Variant v_button = index; Variant v_pos = ds->mouse_get_position(); - Variant *v_args[2] = { &v_button, &v_pos }; + const Variant *v_args[2] = { &v_button, &v_pos }; Variant ret; Callable::CallError ce; cb.callp((const Variant **)&v_args, 2, ret, ce); + if (ce.error != Callable::CallError::CALL_OK) { + ERR_PRINT(vformat("Failed to execute status indicator callback: %s.", Variant::get_callable_error_text(cb, v_args, 2, ce))); + } } } diff --git a/platform/web/.eslintrc.engine.js b/platform/web/.eslintrc.engine.js deleted file mode 100644 index a76bd46b9e..0000000000 --- a/platform/web/.eslintrc.engine.js +++ /dev/null @@ -1,11 +0,0 @@ -module.exports = { - "extends": [ - "./.eslintrc.js", - ], - "globals": { - "InternalConfig": true, - "Godot": true, - "Features": true, - "Preloader": true, - }, -}; diff --git a/platform/web/.eslintrc.html.js b/platform/web/.eslintrc.html.js deleted file mode 100644 index 8c9a3d83da..0000000000 --- a/platform/web/.eslintrc.html.js +++ /dev/null @@ -1,21 +0,0 @@ -module.exports = { - "plugins": [ - "html", - "@html-eslint", - ], - "parser": "@html-eslint/parser", - "extends": ["plugin:@html-eslint/recommended", "./.eslintrc.js"], - "rules": { - "no-alert": "off", - "no-console": "off", - "@html-eslint/require-closing-tags": ["error", { "selfClosing": "never" }], - "@html-eslint/indent": ["error", "tab"], - }, - "globals": { - "Godot": true, - "Engine": true, - "$GODOT_CONFIG": true, - "$GODOT_THREADS_ENABLED": true, - "___GODOT_THREADS_ENABLED___": true, - }, -}; diff --git a/platform/web/.eslintrc.js b/platform/web/.eslintrc.js deleted file mode 100644 index 2c81f1f02d..0000000000 --- a/platform/web/.eslintrc.js +++ /dev/null @@ -1,51 +0,0 @@ -module.exports = { - "env": { - "browser": true, - "es2021": true, - }, - "extends": [ - "airbnb-base", - ], - "parserOptions": { - "ecmaVersion": 12, - }, - "ignorePatterns": "*.externs.js", - "rules": { - "func-names": "off", - // Use tabs for consistency with the C++ codebase. - "indent": ["error", "tab"], - "max-len": "off", - "no-else-return": ["error", {allowElseIf: true}], - "curly": ["error", "all"], - "brace-style": ["error", "1tbs", { "allowSingleLine": false }], - "no-bitwise": "off", - "no-continue": "off", - "no-self-assign": "off", - "no-tabs": "off", - "no-param-reassign": ["error", { "props": false }], - "no-plusplus": "off", - "no-unused-vars": ["error", { "args": "none" }], - "prefer-destructuring": "off", - "prefer-rest-params": "off", - "prefer-spread": "off", - "camelcase": "off", - "no-underscore-dangle": "off", - "max-classes-per-file": "off", - "prefer-arrow-callback": "off", - // Messes up with copyright headers in source files. - "spaced-comment": "off", - // Completely breaks emscripten libraries. - "object-shorthand": "off", - // Closure compiler (exported properties) - "quote-props": ["error", "consistent"], - "dot-notation": "off", - // No comma dangle for functions (it's madness, and ES2017) - "comma-dangle": ["error", { - "arrays": "always-multiline", - "objects": "always-multiline", - "imports": "always-multiline", - "exports": "always-multiline", - "functions": "never" - }], - } -}; diff --git a/platform/web/.eslintrc.libs.js b/platform/web/.eslintrc.libs.js deleted file mode 100644 index 8e579fd462..0000000000 --- a/platform/web/.eslintrc.libs.js +++ /dev/null @@ -1,26 +0,0 @@ -module.exports = { - "extends": [ - "./.eslintrc.js", - ], - "globals": { - "LibraryManager": true, - "mergeInto": true, - "autoAddDeps": true, - "HEAP8": true, - "HEAPU8": true, - "HEAP32": true, - "HEAPF32": true, - "ERRNO_CODES": true, - "FS": true, - "IDBFS": true, - "GodotOS": true, - "GodotConfig": true, - "GodotEventListeners": true, - "GodotRuntime": true, - "GodotFS": true, - "IDHandler": true, - "Browser": true, - "GL": true, - "XRWebGLLayer": true, - }, -}; diff --git a/platform/web/.eslintrc.sw.js b/platform/web/.eslintrc.sw.js deleted file mode 100644 index cba9ed8001..0000000000 --- a/platform/web/.eslintrc.sw.js +++ /dev/null @@ -1,14 +0,0 @@ -module.exports = { - "extends": [ - "./.eslintrc.js", - ], - "rules": { - "no-restricted-globals": 0, - }, - "globals": { - "onClientMessage": true, - "___GODOT_ENSURE_CROSSORIGIN_ISOLATION_HEADERS___": true, - "___GODOT_CACHE___": true, - "___GODOT_OPT_CACHE___": true, - }, -}; diff --git a/platform/web/SCsub b/platform/web/SCsub index bc5893ab3a..fea2fa7df9 100644 --- a/platform/web/SCsub +++ b/platform/web/SCsub @@ -6,9 +6,10 @@ Import("env") # The HTTP server "targets". Run with "scons p=web serve", or "scons p=web run" if "serve" in COMMAND_LINE_TARGETS or "run" in COMMAND_LINE_TARGETS: - from serve import serve import os + from serve import serve + port = os.environ.get("GODOT_WEB_TEST_PORT", 8060) try: port = int(port) diff --git a/platform/web/detect.py b/platform/web/detect.py index ccd884b225..c6568625c1 100644 --- a/platform/web/detect.py +++ b/platform/web/detect.py @@ -1,18 +1,19 @@ import os import sys +from typing import TYPE_CHECKING from emscripten_helpers import ( - run_closure_compiler, - create_engine_file, + add_js_externs, add_js_libraries, add_js_pre, - add_js_externs, + create_engine_file, create_template_zip, get_template_zip_path, + run_closure_compiler, ) -from methods import print_warning, print_error, get_compiler_version from SCons.Util import WhereIs -from typing import TYPE_CHECKING + +from methods import get_compiler_version, print_error, print_warning if TYPE_CHECKING: from SCons.Script.SConscript import SConsEnvironment @@ -64,21 +65,21 @@ def get_doc_path(): def get_flags(): - return [ - ("arch", "wasm32"), - ("target", "template_debug"), - ("builtin_pcre2_with_jit", False), - ("vulkan", False), + return { + "arch": "wasm32", + "target": "template_debug", + "builtin_pcre2_with_jit": False, + "vulkan": False, # Embree is heavy and requires too much memory (GH-70621). - ("module_raycast_enabled", False), + "module_raycast_enabled": False, # Use -Os to prioritize optimizing for reduced file size. This is # particularly valuable for the web platform because it directly # decreases download time. # -Os reduces file size by around 5 MiB over -O3. -Oz only saves about # 100 KiB over -Os, which does not justify the negative impact on # run-time performance. - ("optimize", "size"), - ] + "optimize": "size", + } def configure(env: "SConsEnvironment"): @@ -180,7 +181,7 @@ def configure(env: "SConsEnvironment"): # Use TempFileMunge since some AR invocations are too long for cmd.exe. # Use POSIX-style paths, required with TempFileMunge. env["ARCOM_POSIX"] = env["ARCOM"].replace("$TARGET", "$TARGET.posix").replace("$SOURCES", "$SOURCES.posix") - env["ARCOM"] = "${TEMPFILE(ARCOM_POSIX)}" + env["ARCOM"] = "${TEMPFILE('$ARCOM_POSIX','$ARCOMSTR')}" # All intermediate files are just object files. env["OBJPREFIX"] = "" diff --git a/platform/web/display_server_web.cpp b/platform/web/display_server_web.cpp index a51c161b9c..fab92b1894 100644 --- a/platform/web/display_server_web.cpp +++ b/platform/web/display_server_web.cpp @@ -70,7 +70,7 @@ bool DisplayServerWeb::check_size_force_redraw() { void DisplayServerWeb::fullscreen_change_callback(int p_fullscreen) { #ifdef PROXY_TO_PTHREAD_ENABLED if (!Thread::is_main_thread()) { - callable_mp_static(DisplayServerWeb::_fullscreen_change_callback).bind(p_fullscreen).call_deferred(); + callable_mp_static(DisplayServerWeb::_fullscreen_change_callback).call_deferred(p_fullscreen); return; } #endif @@ -96,7 +96,7 @@ void DisplayServerWeb::drop_files_js_callback(const char **p_filev, int p_filec) #ifdef PROXY_TO_PTHREAD_ENABLED if (!Thread::is_main_thread()) { - callable_mp_static(DisplayServerWeb::_drop_files_js_callback).bind(files).call_deferred(); + callable_mp_static(DisplayServerWeb::_drop_files_js_callback).call_deferred(files); return; } #endif @@ -112,7 +112,14 @@ void DisplayServerWeb::_drop_files_js_callback(const Vector<String> &p_files) { if (!ds->drop_files_callback.is_valid()) { return; } - ds->drop_files_callback.call(p_files); + Variant v_files = p_files; + const Variant *v_args[1] = { &v_files }; + Variant ret; + Callable::CallError ce; + ds->drop_files_callback.callp((const Variant **)&v_args, 1, ret, ce); + if (ce.error != Callable::CallError::CALL_OK) { + ERR_PRINT(vformat("Failed to execute drop files callback: %s.", Variant::get_callable_error_text(ds->drop_files_callback, v_args, 1, ce))); + } } // Web quit request callback. @@ -161,7 +168,7 @@ void DisplayServerWeb::key_callback(int p_pressed, int p_repeat, int p_modifiers #ifdef PROXY_TO_PTHREAD_ENABLED if (!Thread::is_main_thread()) { - callable_mp_static(DisplayServerWeb::_key_callback).bind(code, key, p_pressed, p_repeat, p_modifiers).call_deferred(); + callable_mp_static(DisplayServerWeb::_key_callback).call_deferred(code, key, p_pressed, p_repeat, p_modifiers); return; } #endif @@ -214,7 +221,7 @@ void DisplayServerWeb::_key_callback(const String &p_key_event_code, const Strin int DisplayServerWeb::mouse_button_callback(int p_pressed, int p_button, double p_x, double p_y, int p_modifiers) { #ifdef PROXY_TO_PTHREAD_ENABLED if (!Thread::is_main_thread()) { - callable_mp_static(DisplayServerWeb::_mouse_button_callback).bind(p_pressed, p_button, p_x, p_y, p_modifiers).call_deferred(); + callable_mp_static(DisplayServerWeb::_mouse_button_callback).call_deferred(p_pressed, p_button, p_x, p_y, p_modifiers); return true; } #endif @@ -301,7 +308,7 @@ int DisplayServerWeb::_mouse_button_callback(int p_pressed, int p_button, double void DisplayServerWeb::mouse_move_callback(double p_x, double p_y, double p_rel_x, double p_rel_y, int p_modifiers) { #ifdef PROXY_TO_PTHREAD_ENABLED if (!Thread::is_main_thread()) { - callable_mp_static(DisplayServerWeb::_mouse_move_callback).bind(p_x, p_y, p_rel_x, p_rel_y, p_modifiers).call_deferred(); + callable_mp_static(DisplayServerWeb::_mouse_move_callback).call_deferred(p_x, p_y, p_rel_x, p_rel_y, p_modifiers); return; } #endif @@ -394,7 +401,7 @@ void DisplayServerWeb::update_voices_callback(int p_size, const char **p_voice) #ifdef PROXY_TO_PTHREAD_ENABLED if (!Thread::is_main_thread()) { - callable_mp_static(DisplayServerWeb::_update_voices_callback).bind(voices).call_deferred(); + callable_mp_static(DisplayServerWeb::_update_voices_callback).call_deferred(voices); return; } #endif @@ -461,7 +468,7 @@ void DisplayServerWeb::tts_stop() { void DisplayServerWeb::js_utterance_callback(int p_event, int p_id, int p_pos) { #ifdef PROXY_TO_PTHREAD_ENABLED if (!Thread::is_main_thread()) { - callable_mp_static(DisplayServerWeb::_js_utterance_callback).bind(p_event, p_id, p_pos).call_deferred(); + callable_mp_static(DisplayServerWeb::_js_utterance_callback).call_deferred(p_event, p_id, p_pos); return; } #endif @@ -591,7 +598,7 @@ Point2i DisplayServerWeb::mouse_get_position() const { int DisplayServerWeb::mouse_wheel_callback(double p_delta_x, double p_delta_y) { #ifdef PROXY_TO_PTHREAD_ENABLED if (!Thread::is_main_thread()) { - callable_mp_static(DisplayServerWeb::_mouse_wheel_callback).bind(p_delta_x, p_delta_y).call_deferred(); + callable_mp_static(DisplayServerWeb::_mouse_wheel_callback).call_deferred(p_delta_x, p_delta_y); return true; } #endif @@ -654,7 +661,7 @@ int DisplayServerWeb::_mouse_wheel_callback(double p_delta_x, double p_delta_y) void DisplayServerWeb::touch_callback(int p_type, int p_count) { #ifdef PROXY_TO_PTHREAD_ENABLED if (!Thread::is_main_thread()) { - callable_mp_static(DisplayServerWeb::_touch_callback).bind(p_type, p_count).call_deferred(); + callable_mp_static(DisplayServerWeb::_touch_callback).call_deferred(p_type, p_count); return; } #endif @@ -712,7 +719,7 @@ void DisplayServerWeb::vk_input_text_callback(const char *p_text, int p_cursor) #ifdef PROXY_TO_PTHREAD_ENABLED if (!Thread::is_main_thread()) { - callable_mp_static(DisplayServerWeb::_vk_input_text_callback).bind(text, p_cursor).call_deferred(); + callable_mp_static(DisplayServerWeb::_vk_input_text_callback).call_deferred(text, p_cursor); return; } #endif @@ -774,7 +781,7 @@ void DisplayServerWeb::gamepad_callback(int p_index, int p_connected, const char #ifdef PROXY_TO_PTHREAD_ENABLED if (!Thread::is_main_thread()) { - callable_mp_static(DisplayServerWeb::_gamepad_callback).bind(p_index, p_connected, id, guid).call_deferred(); + callable_mp_static(DisplayServerWeb::_gamepad_callback).call_deferred(p_index, p_connected, id, guid); return; } #endif @@ -797,7 +804,7 @@ void DisplayServerWeb::ime_callback(int p_type, const char *p_text) { #ifdef PROXY_TO_PTHREAD_ENABLED if (!Thread::is_main_thread()) { - callable_mp_static(DisplayServerWeb::_ime_callback).bind(p_type, text).call_deferred(); + callable_mp_static(DisplayServerWeb::_ime_callback).call_deferred(p_type, text); return; } #endif @@ -866,6 +873,9 @@ void DisplayServerWeb::_ime_callback(int p_type, const String &p_text) { default: break; } + + ds->process_keys(); + Input::get_singleton()->flush_buffered_events(); } void DisplayServerWeb::window_set_ime_active(const bool p_active, WindowID p_window) { @@ -927,7 +937,7 @@ void DisplayServerWeb::update_clipboard_callback(const char *p_text) { #ifdef PROXY_TO_PTHREAD_ENABLED if (!Thread::is_main_thread()) { - callable_mp_static(DisplayServerWeb::_update_clipboard_callback).bind(text).call_deferred(); + callable_mp_static(DisplayServerWeb::_update_clipboard_callback).call_deferred(text); return; } #endif @@ -953,7 +963,7 @@ String DisplayServerWeb::clipboard_get() const { void DisplayServerWeb::send_window_event_callback(int p_notification) { #ifdef PROXY_TO_PTHREAD_ENABLED if (!Thread::is_main_thread()) { - callable_mp_static(DisplayServerWeb::_send_window_event_callback).bind(p_notification).call_deferred(); + callable_mp_static(DisplayServerWeb::_send_window_event_callback).call_deferred(p_notification); return; } #endif @@ -1022,11 +1032,11 @@ void DisplayServerWeb::_dispatch_input_event(const Ref<InputEvent> &p_event) { } } -DisplayServer *DisplayServerWeb::create_func(const String &p_rendering_driver, WindowMode p_window_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Point2i *p_position, const Size2i &p_resolution, int p_screen, Error &r_error) { - return memnew(DisplayServerWeb(p_rendering_driver, p_window_mode, p_vsync_mode, p_flags, p_position, p_resolution, p_screen, r_error)); +DisplayServer *DisplayServerWeb::create_func(const String &p_rendering_driver, WindowMode p_window_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Point2i *p_position, const Size2i &p_resolution, int p_screen, Context p_context, Error &r_error) { + return memnew(DisplayServerWeb(p_rendering_driver, p_window_mode, p_vsync_mode, p_flags, p_position, p_resolution, p_screen, p_context, r_error)); } -DisplayServerWeb::DisplayServerWeb(const String &p_rendering_driver, WindowMode p_window_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Point2i *p_position, const Size2i &p_resolution, int p_screen, Error &r_error) { +DisplayServerWeb::DisplayServerWeb(const String &p_rendering_driver, WindowMode p_window_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Point2i *p_position, const Size2i &p_resolution, int p_screen, Context p_context, Error &r_error) { r_error = OK; // Always succeeds for now. tts = GLOBAL_GET("audio/general/text_to_speech"); @@ -1353,29 +1363,33 @@ DisplayServer::VSyncMode DisplayServerWeb::window_get_vsync_mode(WindowID p_vsyn } void DisplayServerWeb::process_events() { + process_keys(); Input::get_singleton()->flush_buffered_events(); if (godot_js_input_gamepad_sample() == OK) { process_joypads(); - for (int i = 0; i < key_event_pos; i++) { - const DisplayServerWeb::KeyEvent &ke = key_event_buffer[i]; + } +} - Ref<InputEventKey> ev; - ev.instantiate(); - ev->set_pressed(ke.pressed); - ev->set_echo(ke.echo); - ev->set_keycode(ke.keycode); - ev->set_physical_keycode(ke.physical_keycode); - ev->set_key_label(ke.key_label); - ev->set_unicode(ke.unicode); - ev->set_location(ke.location); - if (ke.raw) { - dom2godot_mod(ev, ke.mod, ke.keycode); - } +void DisplayServerWeb::process_keys() { + for (int i = 0; i < key_event_pos; i++) { + const DisplayServerWeb::KeyEvent &ke = key_event_buffer[i]; - Input::get_singleton()->parse_input_event(ev); + Ref<InputEventKey> ev; + ev.instantiate(); + ev->set_pressed(ke.pressed); + ev->set_echo(ke.echo); + ev->set_keycode(ke.keycode); + ev->set_physical_keycode(ke.physical_keycode); + ev->set_key_label(ke.key_label); + ev->set_unicode(ke.unicode); + ev->set_location(ke.location); + if (ke.raw) { + dom2godot_mod(ev, ke.mod, ke.keycode); } - key_event_pos = 0; + + Input::get_singleton()->parse_input_event(ev); } + key_event_pos = 0; } int DisplayServerWeb::get_current_video_driver() const { diff --git a/platform/web/display_server_web.h b/platform/web/display_server_web.h index 49a017015a..352b3fe523 100644 --- a/platform/web/display_server_web.h +++ b/platform/web/display_server_web.h @@ -145,9 +145,10 @@ private: static void _drop_files_js_callback(const Vector<String> &p_files); void process_joypads(); + void process_keys(); static Vector<String> get_rendering_drivers_func(); - static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_window_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Error &r_error); + static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_window_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error); static void _dispatch_input_event(const Ref<InputEvent> &p_event); @@ -277,7 +278,7 @@ public: virtual void swap_buffers() override; static void register_web_driver(); - DisplayServerWeb(const String &p_rendering_driver, WindowMode p_window_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Point2i *p_position, const Size2i &p_resolution, int p_screen, Error &r_error); + DisplayServerWeb(const String &p_rendering_driver, WindowMode p_window_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Point2i *p_position, const Size2i &p_resolution, int p_screen, Context p_context, Error &r_error); ~DisplayServerWeb(); }; diff --git a/platform/web/emscripten_helpers.py b/platform/web/emscripten_helpers.py index 3ba133c9a1..2cee3e8110 100644 --- a/platform/web/emscripten_helpers.py +++ b/platform/web/emscripten_helpers.py @@ -1,4 +1,5 @@ -import os, json +import json +import os from SCons.Util import WhereIs @@ -24,13 +25,13 @@ def get_build_version(): import version name = "custom_build" - if os.getenv("BUILD_NAME") != None: + if os.getenv("BUILD_NAME") is not None: name = os.getenv("BUILD_NAME") v = "%d.%d" % (version.major, version.minor) if version.patch > 0: v += ".%d" % version.patch status = version.status - if os.getenv("GODOT_VERSION_STATUS") != None: + if os.getenv("GODOT_VERSION_STATUS") is not None: status = str(os.getenv("GODOT_VERSION_STATUS")) v += ".%s.%s" % (status, name) return v @@ -122,7 +123,6 @@ def create_template_zip(env, js, wasm, worker, side): zip_files, ZIPROOT=zip_dir, ZIPSUFFIX="${PROGSUFFIX}${ZIPSUFFIX}", - ZIPCOMSTR="Archiving $SOURCES as $TARGET", ) diff --git a/platform/web/eslint.config.cjs b/platform/web/eslint.config.cjs new file mode 100644 index 0000000000..65ade096a7 --- /dev/null +++ b/platform/web/eslint.config.cjs @@ -0,0 +1,204 @@ +const fs = require('fs'); +const globals = require('globals'); +const htmlParser = require('@html-eslint/parser'); +const htmlPlugin = require('@html-eslint/eslint-plugin'); +const pluginJs = require('@eslint/js'); +const pluginReference = require('eslint-plugin-html'); +const stylistic = require('@stylistic/eslint-plugin'); + +if (process && process.env && process.env.npm_command && !fs.existsSync('./platform/web/eslint.config.cjs')) { + throw Error('eslint must be run from the Godot project root folder'); +} + +const emscriptenGlobals = { + 'Browser': true, + 'ERRNO_CODES': true, + 'FS': true, + 'GL': true, + 'HEAP32': true, + 'HEAP8': true, + 'HEAPF32': true, + 'HEAPU8': true, + 'IDBFS': true, + 'LibraryManager': true, + 'Module': true, + 'UTF8ToString': true, + '_emscripten_webgl_get_current_context': true, + '_free': true, + '_malloc': true, + 'autoAddDeps': true, + 'getValue': true, + 'lengthBytesUTF8': true, + 'mergeInto': true, + 'runtimeKeepalivePop': true, + 'runtimeKeepalivePush': true, + 'setValue': true, + 'stringToUTF8': true, + 'stringToUTF8Array': true, + 'wasmTable': true, +}; + +module.exports = [ + pluginJs.configs.all, + stylistic.configs.customize({ jsx: false }), + + { + rules: { + 'consistent-this': ['error', 'me'], // enforce consistent naming when capturing the current execution context + 'curly': ['error', 'all'], // enforce consistent brace style for all control statements + 'no-else-return': ['error', { 'allowElseIf': true }], // disallow else blocks after return statements in if statements + 'no-param-reassign': ['error', { 'props': false }], // disallow reassigning function parameters + 'no-unused-vars': ['error', { 'args': 'none', 'caughtErrors': 'none' }], // disallow unused variables + + '@stylistic/arrow-parens': ['error', 'always'], // enforces the consistent use of parentheses in arrow functions + '@stylistic/brace-style': ['error', '1tbs', { 'allowSingleLine': false }], // describes the placement of braces relative to their control statement and body + '@stylistic/comma-dangle': ['error', { + 'arrays': 'always-multiline', + 'objects': 'always-multiline', + 'imports': 'always-multiline', + 'exports': 'always-multiline', + 'functions': 'never', + }], // enforces consistent use of trailing commas in object and array literals + '@stylistic/indent': ['error', 'tab', { 'SwitchCase': 0 }], // enforces a consistent indentation style + '@stylistic/indent-binary-ops': ['error', 'tab'], // indentation for binary operators in multiline expressions + '@stylistic/multiline-ternary': ['error', 'always-multiline'], // enforces or disallows newlines between operands of a ternary expression + '@stylistic/no-tabs': ['error', { 'allowIndentationTabs': true }], // looks for tabs anywhere inside a file: code, comments or anything else + '@stylistic/quote-props': ['error', 'consistent'], // requires quotes around object literal property names + '@stylistic/quotes': ['error', 'single'], // enforces the consistent use of either backticks, double, or single quotes + '@stylistic/semi': ['error', 'always'], // enforces consistent use of semicolons + '@stylistic/spaced-comment': ['error', 'always', { 'block': { 'exceptions': ['*'] } }], // enforce consistency of spacing after the start of a comment + + 'camelcase': 'off', // disable: camelcase naming convention + 'capitalized-comments': 'off', // disable: enforce or disallow capitalization of the first letter of a comment + 'complexity': 'off', // disable: enforce a maximum cyclomatic complexity allowed in a program + 'dot-notation': 'off', // disable: enforce dot notation whenever possible + 'eqeqeq': 'off', // disable: require the use of === and !== + 'func-name-matching': 'off', // disable: require function names to match the name of the variable or property to which they are assigned + 'func-names': 'off', // disable: checking named function expressions + 'func-style': 'off', // disable: consistent use of either function declarations or expressions + 'id-length': 'off', // disable: enforce minimum and maximum identifier lengths + 'init-declarations': 'off', // disable: require or disallow initialization in variable declarations + 'line-comment-position': 'off', // disable: enforce position of line comments + 'max-classes-per-file': 'off', // disable: maximum number of classes per file + 'max-lines': 'off', // disable: maximum number of lines per file + 'max-lines-per-function': 'off', // disable: maximum number of lines of code in a function + 'max-params': 'off', // disable: enforce a maximum number of parameters in function definitions + 'max-statements': 'off', // disable: maximum number of statements allowed in function blocks + 'multiline-comment-style': 'off', // disable: enforce a particular style for multiline comments + 'new-cap': 'off', // disable: require constructor names to begin with a capital letter + 'no-bitwise': 'off', // disable: disallow bitwise operators + 'no-continue': 'off', // disable: disallow continue statements + 'no-empty-function': 'off', // disable: disallow empty functions + 'no-eq-null': 'off', // disable: disallow null comparisons without type-checking operators + 'no-implicit-coercion': 'off', // disable: disallow shorthand type conversions + 'no-inline-comments': 'off', // disable: disallow inline comments after code + 'no-magic-numbers': 'off', // disable: disallow magic numbers + 'no-negated-condition': 'off', // disable: disallow negated conditions + 'no-plusplus': 'off', // disable: disallow the unary operators ++ and -- + 'no-self-assign': 'off', // disable: disallow assignments where both sides are exactly the same + 'no-ternary': 'off', // disable: disallow ternary operators + 'no-undefined': 'off', // disable: disallow the use of undefined as an identifier + 'no-underscore-dangle': 'off', // disable: disallow dangling underscores in identifiers + 'no-useless-assignment': 'off', // disable: disallow variable assignments when the value is not used + 'no-warning-comments': 'off', // disable: disallow specified warning terms in comments + 'object-shorthand': 'off', // disable: require or disallow method and property shorthand syntax for object literals + 'one-var': 'off', // disable: enforce variables to be declared either together or separately in functions + 'prefer-arrow-callback': 'off', // disable: require using arrow functions for callbacks + 'prefer-destructuring': 'off', // disable: require destructuring from arrays and/or objects + 'prefer-named-capture-group': 'off', // disable: enforce using named capture group in regular expression + 'prefer-promise-reject-errors': 'off', // disable: require using Error objects as Promise rejection reasons + 'prefer-rest-params': 'off', // disable: require rest parameters instead of arguments + 'prefer-spread': 'off', // disable: require spread operators instead of .apply() + 'require-unicode-regexp': 'off', // disable: enforce the use of u or v flag on RegExp + 'sort-keys': 'off', // disable: require object keys to be sorted + }, + }, + + // jsdoc2rst (node) + { + files: ['js/jsdoc2rst/**/*.js', 'platform/web/js/jsdoc2rst/**/*.js'], + languageOptions: { + globals: globals.node, + }, + }, + + // engine files (browser) + { + files: ['js/engine/**/*.js', 'platform/web/js/engine/**/*.js'], + languageOptions: { + globals: { + ...globals.browser, + 'Features': true, + 'Godot': true, + 'InternalConfig': true, + 'Preloader': true, + }, + }, + }, + + // libraries and modules (browser) + { + files: ['js/libs/**/*.js', 'platform/web/js/libs/**/*.js', 'modules/**/*.js'], + languageOptions: { + globals: { + ...globals.browser, + ...emscriptenGlobals, + 'GodotConfig': true, + 'GodotEventListeners': true, + 'GodotFS': true, + 'GodotOS': true, + 'GodotRuntime': true, + 'IDHandler': true, + 'XRWebGLLayer': true, + }, + }, + }, + + // javascript templates (service workers) + { + files: ['misc/dist/html/**/*.js'], + languageOptions: { + globals: { + ...globals.browser, + '___GODOT_CACHE___': true, + '___GODOT_ENSURE_CROSSORIGIN_ISOLATION_HEADERS___': true, + '___GODOT_OPT_CACHE___': true, + }, + }, + }, + + // html templates + { + files: ['misc/dist/html/**/*.html'], + plugins: { + '@html-eslint': htmlPlugin, + 'eslint-plugin-html': pluginReference, + }, + languageOptions: { + parser: htmlParser, + globals: { + ...globals.browser, + 'Engine': true, + '$GODOT_CONFIG': true, + '$GODOT_PROJECT_NAME': true, + '$GODOT_THREADS_ENABLED': true, + '___GODOT_THREADS_ENABLED___': true, + }, + }, + rules: { + ...htmlPlugin.configs.recommended.rules, + '@html-eslint/indent': ['error', 'tab'], + '@html-eslint/require-closing-tags': ['error', { 'selfClosing': 'never' }], + 'no-alert': 'off', + 'no-console': 'off', + }, + }, + + { + ignores: [ + '**/eslint.config.cjs', + '**/.eslintrc*.js', + '**/*.externs.js', + ], + }, +]; diff --git a/platform/web/javascript_bridge_singleton.cpp b/platform/web/javascript_bridge_singleton.cpp index a2c83d2f2b..c4dbb405a3 100644 --- a/platform/web/javascript_bridge_singleton.cpp +++ b/platform/web/javascript_bridge_singleton.cpp @@ -262,7 +262,7 @@ void JavaScriptObjectImpl::callback(void *p_ref, int p_args_id, int p_argc) { #ifdef PROXY_TO_PTHREAD_ENABLED if (!Thread::is_main_thread()) { - callable_mp_static(JavaScriptObjectImpl::_callback).bind(obj, arg).call_deferred(); + callable_mp_static(JavaScriptObjectImpl::_callback).call_deferred(obj, arg); return; } #endif diff --git a/platform/web/js/engine/features.js b/platform/web/js/engine/features.js index 263ea6ac88..601fa4a117 100644 --- a/platform/web/js/engine/features.js +++ b/platform/web/js/engine/features.js @@ -1,4 +1,4 @@ -const Features = { // eslint-disable-line no-unused-vars +const Features = { /** * Check whether WebGL is available. Optionally, specify a particular version of WebGL to check for. * diff --git a/platform/web/js/libs/library_godot_display.js b/platform/web/js/libs/library_godot_display.js index 99fc429d8f..9445ec7ae5 100644 --- a/platform/web/js/libs/library_godot_display.js +++ b/platform/web/js/libs/library_godot_display.js @@ -279,7 +279,7 @@ const GodotDisplayScreen = { return 0; }, _updateGL: function () { - const gl_context_handle = _emscripten_webgl_get_current_context(); // eslint-disable-line no-undef + const gl_context_handle = _emscripten_webgl_get_current_context(); const gl = GL.getContext(gl_context_handle); if (gl) { GL.resizeOffscreenFramebuffer(gl); @@ -392,19 +392,19 @@ const GodotDisplay = { const func = GodotRuntime.get_func(p_callback); function listener_end(evt) { - evt.currentTarget.cb(1 /*TTS_UTTERANCE_ENDED*/, evt.currentTarget.id, 0); + evt.currentTarget.cb(1 /* TTS_UTTERANCE_ENDED */, evt.currentTarget.id, 0); } function listener_start(evt) { - evt.currentTarget.cb(0 /*TTS_UTTERANCE_STARTED*/, evt.currentTarget.id, 0); + evt.currentTarget.cb(0 /* TTS_UTTERANCE_STARTED */, evt.currentTarget.id, 0); } function listener_error(evt) { - evt.currentTarget.cb(2 /*TTS_UTTERANCE_CANCELED*/, evt.currentTarget.id, 0); + evt.currentTarget.cb(2 /* TTS_UTTERANCE_CANCELED */, evt.currentTarget.id, 0); } function listener_bound(evt) { - evt.currentTarget.cb(3 /*TTS_UTTERANCE_BOUNDARY*/, evt.currentTarget.id, evt.charIndex); + evt.currentTarget.cb(3 /* TTS_UTTERANCE_BOUNDARY */, evt.currentTarget.id, evt.charIndex); } const utterance = new SpeechSynthesisUtterance(GodotRuntime.parseString(p_text)); diff --git a/platform/web/js/libs/library_godot_input.js b/platform/web/js/libs/library_godot_input.js index 1292c468f5..7ea89d553f 100644 --- a/platform/web/js/libs/library_godot_input.js +++ b/platform/web/js/libs/library_godot_input.js @@ -63,8 +63,15 @@ const GodotIME = { ime_position: function (x, y) { if (GodotIME.ime) { - GodotIME.ime.style.left = `${x}px`; - GodotIME.ime.style.top = `${y}px`; + const canvas = GodotConfig.canvas; + const rect = canvas.getBoundingClientRect(); + const rw = canvas.width / rect.width; + const rh = canvas.height / rect.height; + const clx = (x / rw) + rect.x; + const cly = (y / rh) + rect.y; + + GodotIME.ime.style.left = `${clx}px`; + GodotIME.ime.style.top = `${cly}px`; } }, @@ -99,10 +106,12 @@ const GodotIME = { ime.style.background = 'none'; ime.style.opacity = 0.0; ime.style.position = 'fixed'; + ime.style.textAlign = 'left'; + ime.style.fontSize = '1px'; ime.style.left = '0px'; ime.style.top = '0px'; - ime.style.width = '2px'; - ime.style.height = '2px'; + ime.style.width = '100%'; + ime.style.height = '40px'; ime.style.display = 'none'; ime.contentEditable = 'true'; diff --git a/platform/web/js/libs/library_godot_os.js b/platform/web/js/libs/library_godot_os.js index 92635cb6ae..568212275b 100644 --- a/platform/web/js/libs/library_godot_os.js +++ b/platform/web/js/libs/library_godot_os.js @@ -78,7 +78,7 @@ const GodotConfig = { }, locate_file: function (file) { - return Module['locateFile'](file); // eslint-disable-line no-undef + return Module['locateFile'](file); }, clear: function () { GodotConfig.canvas = null; diff --git a/platform/web/js/libs/library_godot_runtime.js b/platform/web/js/libs/library_godot_runtime.js index 73581759b0..4114aa566e 100644 --- a/platform/web/js/libs/library_godot_runtime.js +++ b/platform/web/js/libs/library_godot_runtime.js @@ -34,7 +34,7 @@ const GodotRuntime = { * Functions */ get_func: function (ptr) { - return wasmTable.get(ptr); // eslint-disable-line no-undef + return wasmTable.get(ptr); }, /* @@ -52,19 +52,19 @@ const GodotRuntime = { * Memory */ malloc: function (p_size) { - return _malloc(p_size); // eslint-disable-line no-undef + return _malloc(p_size); }, free: function (p_ptr) { - _free(p_ptr); // eslint-disable-line no-undef + _free(p_ptr); }, getHeapValue: function (p_ptr, p_type) { - return getValue(p_ptr, p_type); // eslint-disable-line no-undef + return getValue(p_ptr, p_type); }, setHeapValue: function (p_ptr, p_value, p_type) { - setValue(p_ptr, p_value, p_type); // eslint-disable-line no-undef + setValue(p_ptr, p_value, p_type); }, heapSub: function (p_heap, p_ptr, p_len) { @@ -86,7 +86,7 @@ const GodotRuntime = { * Strings */ parseString: function (p_ptr) { - return UTF8ToString(p_ptr); // eslint-disable-line no-undef + return UTF8ToString(p_ptr); }, parseStringArray: function (p_ptr, p_size) { @@ -99,13 +99,13 @@ const GodotRuntime = { }, strlen: function (p_str) { - return lengthBytesUTF8(p_str); // eslint-disable-line no-undef + return lengthBytesUTF8(p_str); }, allocString: function (p_str) { const length = GodotRuntime.strlen(p_str) + 1; const c_str = GodotRuntime.malloc(length); - stringToUTF8(p_str, c_str, length); // eslint-disable-line no-undef + stringToUTF8(p_str, c_str, length); return c_str; }, @@ -126,7 +126,7 @@ const GodotRuntime = { }, stringToHeap: function (p_str, p_ptr, p_len) { - return stringToUTF8Array(p_str, HEAP8, p_ptr, p_len); // eslint-disable-line no-undef + return stringToUTF8Array(p_str, HEAP8, p_ptr, p_len); }, }, }; diff --git a/platform/web/js/libs/library_godot_webgl2.js b/platform/web/js/libs/library_godot_webgl2.js index 005b4b7917..4621b2e3c2 100644 --- a/platform/web/js/libs/library_godot_webgl2.js +++ b/platform/web/js/libs/library_godot_webgl2.js @@ -38,7 +38,7 @@ const GodotWebGL2 = { godot_webgl2_glGetBufferSubData__sig: 'vippp', godot_webgl2_glGetBufferSubData__deps: ['$GL', 'emscripten_webgl_get_current_context'], godot_webgl2_glGetBufferSubData: function (target, offset, size, data) { - const gl_context_handle = _emscripten_webgl_get_current_context(); // eslint-disable-line no-undef + const gl_context_handle = _emscripten_webgl_get_current_context(); const gl = GL.getContext(gl_context_handle); if (gl) { gl.GLctx['getBufferSubData'](target, offset, HEAPU8, data, size); diff --git a/platform/web/package-lock.json b/platform/web/package-lock.json index a1615fe591..a2e0fd3b27 100644 --- a/platform/web/package-lock.json +++ b/platform/web/package-lock.json @@ -9,28 +9,21 @@ "version": "1.0.0", "license": "MIT", "devDependencies": { - "@html-eslint/eslint-plugin": "^0.19.1", - "@html-eslint/parser": "^0.19.1", - "eslint": "^8.46.0", - "eslint-config-airbnb-base": "^15.0.0", - "eslint-plugin-html": "^7.1.0", - "eslint-plugin-import": "^2.28.0", - "jsdoc": "^4.0.2" - } - }, - "node_modules/@aashutoshrathi/word-wrap": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", - "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", - "dev": true, - "engines": { - "node": ">=0.10.0" + "@eslint/js": "^9.3.0", + "@html-eslint/eslint-plugin": "^0.24.1", + "@html-eslint/parser": "^0.24.1", + "@stylistic/eslint-plugin": "^2.1.0", + "eslint": "^9.3.0", + "eslint-plugin-html": "^8.1.1", + "espree": "^10.0.1", + "globals": "^15.3.0", + "jsdoc": "^4.0.3" } }, "node_modules/@babel/parser": { - "version": "7.22.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.7.tgz", - "integrity": "sha512-7NF8pOkHP5o2vpmGgNGcfAeCvOYhGLyA3Z4eBQkT1RJlWu47n63bCs93QfJ2hIAFCil7L5P2IWhs1oToVgrL0Q==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.5.tgz", + "integrity": "sha512-EOv5IK8arwh3LI47dz1b0tKUb/1uhHAnHJOrjgtQMIpu1uXd9mlFrJg9IUgGUgZ41Ch0K8REPTYpO7B76b4vJg==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -54,25 +47,37 @@ "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/@eslint-community/regexpp": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.6.2.tgz", - "integrity": "sha512-pPTNuaAG3QMH+buKyBIGJs3g/S5y0caxw0ygM3YyE6yJFySwiGGSzA+mM3KJ8QQvzeLh3blwgSonkFjgQdxzMw==", + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", "dev": true, "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, "node_modules/@eslint/eslintrc": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.1.tgz", - "integrity": "sha512-9t7ZA7NGGK8ckelF0PQCfcxIUzs1Md5rrO6U/c+FIQNanea5UZC0wqKXH4vHBccmu4ZJgZ2idtPeW7+Q2npOEA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz", + "integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==", "dev": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", + "espree": "^10.0.1", + "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", @@ -80,34 +85,46 @@ "strip-json-comments": "^3.1.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@eslint/js": { - "version": "8.46.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.46.0.tgz", - "integrity": "sha512-a8TLtmPi8xzPkCbp/OGFUo5yhRkHM2Ko9kOWP4znJr0WAhWyThaw3PnwX4vOTWOAMsV2uRt32PPDcEz63esSaA==", + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.3.0.tgz", + "integrity": "sha512-niBqk8iwv96+yuTwjM6bWg8ovzAPF9qkICsGtcoa5/dmqcEMfdwNAX7+/OHcJHc7wj7XqPxH98oAHytFYlw6Sw==", "dev": true, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@html-eslint/eslint-plugin": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@html-eslint/eslint-plugin/-/eslint-plugin-0.19.1.tgz", - "integrity": "sha512-Tn+/GWLtNM6NiZFLbfM+vTK0d7gKaDgnw4Pp+DsZi09lFimi4bOPOgy8dSVnLeyFIfP6LkeuwVm4pfBZZM2qbA==", + "version": "0.24.1", + "resolved": "https://registry.npmjs.org/@html-eslint/eslint-plugin/-/eslint-plugin-0.24.1.tgz", + "integrity": "sha512-JwNDQBrNIWEPcxgSpla/2jaUXyQCqL7Xp8CmON4Bk5qg8MwiDLXOgjylfVC+tN52i8JeHWMca34I9DqBGRj9Qg==", "dev": true, "engines": { - "node": ">=8.10.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/@html-eslint/parser": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@html-eslint/parser/-/parser-0.19.1.tgz", - "integrity": "sha512-dpAw6UX0ZSVNnsAzl9ULHZX7CvAGKF5uta4iebbhSDvGE1o9NX6BoOofD/6WucTvs/qnoKojc3Y2LG6vy4afiQ==", + "version": "0.24.1", + "resolved": "https://registry.npmjs.org/@html-eslint/parser/-/parser-0.24.1.tgz", + "integrity": "sha512-O13xX/+Ldh0P7VZMpDDYc3XtWiE1cYm5QhVJ0VB5i7D8Q69HrrGN+5BjS17vkCoLTz+3zWWIiJv4oFmyS5LReA==", "dev": true, "dependencies": { "es-html-parser": "^0.0.9" @@ -117,13 +134,13 @@ } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", - "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==", + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", "dev": true, "dependencies": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", + "@humanwhocodes/object-schema": "^2.0.3", + "debug": "^4.3.1", "minimatch": "^3.0.5" }, "engines": { @@ -144,15 +161,28 @@ } }, "node_modules/@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", "dev": true }, + "node_modules/@humanwhocodes/retry": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.0.tgz", + "integrity": "sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==", + "dev": true, + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, "node_modules/@jsdoc/salty": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/@jsdoc/salty/-/salty-0.2.5.tgz", - "integrity": "sha512-TfRP53RqunNe2HBobVBJ0VLhK1HbfvBYeTC1ahnN64PWvyYyGebmMiPkuwvD9fpw2ZbkoPb8Q7mwy0aR8Z9rvw==", + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/@jsdoc/salty/-/salty-0.2.8.tgz", + "integrity": "sha512-5e+SFVavj1ORKlKaKr2BmTOekmXbelU7dC0cDkQLqag7xfuTPuGMUFx7KWJuv4bYZrTsoL2Z18VVCOKYxzoHcg==", "dev": true, "dependencies": { "lodash": "^4.17.21" @@ -196,218 +226,364 @@ "node": ">= 8" } }, - "node_modules/@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", - "dev": true - }, - "node_modules/@types/linkify-it": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-3.0.2.tgz", - "integrity": "sha512-HZQYqbiFVWufzCwexrvh694SOim8z2d+xJl5UNamcvQFejLY/2YUtzXHYi3cHdI7PMlS8ejH2slRAOJQ32aNbA==", - "dev": true - }, - "node_modules/@types/markdown-it": { - "version": "12.2.3", - "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-12.2.3.tgz", - "integrity": "sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==", + "node_modules/@stylistic/eslint-plugin": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-2.1.0.tgz", + "integrity": "sha512-cBBowKP2u/+uE5CzgH5w8pE9VKqcM7BXdIDPIbGt2rmLJGnA6MJPr9vYGaqgMoJFs7R/FzsMQerMvvEP40g2uw==", "dev": true, "dependencies": { - "@types/linkify-it": "*", - "@types/mdurl": "*" + "@stylistic/eslint-plugin-js": "2.1.0", + "@stylistic/eslint-plugin-jsx": "2.1.0", + "@stylistic/eslint-plugin-plus": "2.1.0", + "@stylistic/eslint-plugin-ts": "2.1.0", + "@types/eslint": "^8.56.10" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "peerDependencies": { + "eslint": ">=8.40.0" } }, - "node_modules/@types/mdurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-1.0.2.tgz", - "integrity": "sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==", - "dev": true + "node_modules/@stylistic/eslint-plugin-js": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-js/-/eslint-plugin-js-2.1.0.tgz", + "integrity": "sha512-gdXUjGNSsnY6nPyqxu6lmDTtVrwCOjun4x8PUn0x04d5ucLI74N3MT1Q0UhdcOR9No3bo5PGDyBgXK+KmD787A==", + "dev": true, + "dependencies": { + "@types/eslint": "^8.56.10", + "acorn": "^8.11.3", + "eslint-visitor-keys": "^4.0.0", + "espree": "^10.0.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "peerDependencies": { + "eslint": ">=8.40.0" + } }, - "node_modules/acorn": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", - "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", + "node_modules/@stylistic/eslint-plugin-jsx": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-jsx/-/eslint-plugin-jsx-2.1.0.tgz", + "integrity": "sha512-mMD7S+IndZo2vxmwpHVTCwx2O1VdtE5tmpeNwgaEcXODzWV1WTWpnsc/PECQKIr/mkLPFWiSIqcuYNhQ/3l6AQ==", "dev": true, - "bin": { - "acorn": "bin/acorn" + "dependencies": { + "@stylistic/eslint-plugin-js": "^2.1.0", + "@types/eslint": "^8.56.10", + "estraverse": "^5.3.0", + "picomatch": "^4.0.2" }, "engines": { - "node": ">=0.4.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "peerDependencies": { + "eslint": ">=8.40.0" } }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "node_modules/@stylistic/eslint-plugin-plus": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-plus/-/eslint-plugin-plus-2.1.0.tgz", + "integrity": "sha512-S5QAlgYXESJaSBFhBSBLZy9o36gXrXQwWSt6QkO+F0SrT9vpV5JF/VKoh+ojO7tHzd8Ckmyouq02TT9Sv2B0zQ==", "dev": true, + "dependencies": { + "@types/eslint": "^8.56.10", + "@typescript-eslint/utils": "^7.8.0" + }, "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + "eslint": "*" } }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "node_modules/@stylistic/eslint-plugin-plus/node_modules/@typescript-eslint/utils": { + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.10.0.tgz", + "integrity": "sha512-olzif1Fuo8R8m/qKkzJqT7qwy16CzPRWBvERS0uvyc+DHd8AKbO4Jb7kpAvVzMmZm8TrHnI7hvjN4I05zow+tg==", "dev": true, "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "7.10.0", + "@typescript-eslint/types": "7.10.0", + "@typescript-eslint/typescript-estree": "7.10.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" } }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "node_modules/@stylistic/eslint-plugin-ts": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-ts/-/eslint-plugin-ts-2.1.0.tgz", + "integrity": "sha512-2ioFibufHYBALx2TBrU4KXovCkN8qCqcb9yIHc0fyOfTaO5jw4d56WW7YRcF3Zgde6qFyXwAN6z/+w4pnmos1g==", "dev": true, + "dependencies": { + "@stylistic/eslint-plugin-js": "2.1.0", + "@types/eslint": "^8.56.10", + "@typescript-eslint/utils": "^7.8.0" + }, "engines": { - "node": ">=8" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "peerDependencies": { + "eslint": ">=8.40.0" } }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/@stylistic/eslint-plugin-ts/node_modules/@typescript-eslint/utils": { + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.10.0.tgz", + "integrity": "sha512-olzif1Fuo8R8m/qKkzJqT7qwy16CzPRWBvERS0uvyc+DHd8AKbO4Jb7kpAvVzMmZm8TrHnI7hvjN4I05zow+tg==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "7.10.0", + "@typescript-eslint/types": "7.10.0", + "@typescript-eslint/typescript-estree": "7.10.0" }, "engines": { - "node": ">=8" + "node": "^18.18.0 || >=20.0.0" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" } }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "node_modules/@types/eslint": { + "version": "8.56.10", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.10.tgz", + "integrity": "sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ==", + "dev": true, + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", "dev": true }, - "node_modules/array-buffer-byte-length": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", - "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, + "node_modules/@types/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==", + "dev": true + }, + "node_modules/@types/markdown-it": { + "version": "14.1.1", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.1.tgz", + "integrity": "sha512-4NpsnpYl2Gt1ljyBGrKMxFYAYvpqbnnkgP/i/g+NLpjEUa3obn1XJCur9YbEXKDAkaXqsR1LbDnGEJ0MmKFxfg==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "is-array-buffer": "^3.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "@types/linkify-it": "^5", + "@types/mdurl": "^2" } }, - "node_modules/array-includes": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.6.tgz", - "integrity": "sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==", + "node_modules/@types/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", + "dev": true + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.10.0.tgz", + "integrity": "sha512-7L01/K8W/VGl7noe2mgH0K7BE29Sq6KAbVmxurj8GGaPDZXPr8EEQ2seOeAS+mEV9DnzxBQB6ax6qQQ5C6P4xg==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "get-intrinsic": "^1.1.3", - "is-string": "^1.0.7" + "@typescript-eslint/types": "7.10.0", + "@typescript-eslint/visitor-keys": "7.10.0" }, "engines": { - "node": ">= 0.4" + "node": "^18.18.0 || >=20.0.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/array.prototype.findlastindex": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.2.tgz", - "integrity": "sha512-tb5thFFlUcp7NdNF6/MpDk/1r/4awWG1FIz3YqDf+/zJSTezBb+/5WViH41obXULHVpDzoiCLpJ/ZO9YbJMsdw==", + "node_modules/@typescript-eslint/types": { + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.10.0.tgz", + "integrity": "sha512-7fNj+Ya35aNyhuqrA1E/VayQX9Elwr8NKZ4WueClR3KwJ7Xx9jcCdOrLW04h51de/+gNbyFMs+IDxh5xIwfbNg==", + "dev": true, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.10.0.tgz", + "integrity": "sha512-LXFnQJjL9XIcxeVfqmNj60YhatpRLt6UhdlFwAkjNc6jSUlK8zQOl1oktAP8PlWFzPQC1jny/8Bai3/HPuvN5g==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "es-shim-unscopables": "^1.0.0", - "get-intrinsic": "^1.1.3" + "@typescript-eslint/types": "7.10.0", + "@typescript-eslint/visitor-keys": "7.10.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" }, "engines": { - "node": ">= 0.4" + "node": "^18.18.0 || >=20.0.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/array.prototype.flat": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz", - "integrity": "sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==", + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "es-shim-unscopables": "^1.0.0" + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" }, "engines": { - "node": ">= 0.4" + "node": ">=16 || 14 >=14.17" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/array.prototype.flatmap": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz", - "integrity": "sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==", + "node_modules/@typescript-eslint/visitor-keys": { + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.10.0.tgz", + "integrity": "sha512-9ntIVgsi6gg6FIq9xjEO4VQJvwOqA3jaBFQJ/6TK5AvEup2+cECI6Fh7QiBxmfMHXU0V0J4RyPeOU1VDNzl9cg==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "es-shim-unscopables": "^1.0.0" + "@typescript-eslint/types": "7.10.0", + "eslint-visitor-keys": "^3.4.3" }, "engines": { - "node": ">= 0.4" + "node": "^18.18.0 || >=20.0.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/arraybuffer.prototype.slice": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.1.tgz", - "integrity": "sha512-09x0ZWFEjj4WD8PDbykUwo3t9arLn8NIzmmYEJFpYekOAQjpkGSyrQhNoRTcwwcFRu+ycWF78QZ63oWTqSjBcw==", + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, - "dependencies": { - "array-buffer-byte-length": "^1.0.0", - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "get-intrinsic": "^1.2.1", - "is-array-buffer": "^3.0.2", - "is-shared-array-buffer": "^1.0.2" + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/acorn": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "dev": true, + "bin": { + "acorn": "bin/acorn" }, "engines": { - "node": ">= 0.4" + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/available-typed-arrays": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", - "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, "engines": { - "node": ">= 0.4" + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" } }, "node_modules/balanced-match": { @@ -432,17 +608,16 @@ "concat-map": "0.0.1" } }, - "node_modules/call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" + "fill-range": "^7.1.1" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=8" } }, "node_modules/callsites": { @@ -506,12 +681,6 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, - "node_modules/confusing-browser-globals": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz", - "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==", - "dev": true - }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -549,32 +718,16 @@ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, - "node_modules/define-properties": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", - "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==", - "dev": true, - "dependencies": { - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", "dev": true, "dependencies": { - "esutils": "^2.0.2" + "path-type": "^4.0.0" }, "engines": { - "node": ">=6.0.0" + "node": ">=8" } }, "node_modules/dom-serializer": { @@ -644,105 +797,12 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, - "node_modules/es-abstract": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.1.tgz", - "integrity": "sha512-ioRRcXMO6OFyRpyzV3kE1IIBd4WG5/kltnzdxSCqoP8CMGs/Li+M1uF5o7lOkZVFjDs+NLesthnF66Pg/0q0Lw==", - "dev": true, - "dependencies": { - "array-buffer-byte-length": "^1.0.0", - "arraybuffer.prototype.slice": "^1.0.1", - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "es-set-tostringtag": "^2.0.1", - "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.5", - "get-intrinsic": "^1.2.1", - "get-symbol-description": "^1.0.0", - "globalthis": "^1.0.3", - "gopd": "^1.0.1", - "has": "^1.0.3", - "has-property-descriptors": "^1.0.0", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.5", - "is-array-buffer": "^3.0.2", - "is-callable": "^1.2.7", - "is-negative-zero": "^2.0.2", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "is-string": "^1.0.7", - "is-typed-array": "^1.1.10", - "is-weakref": "^1.0.2", - "object-inspect": "^1.12.3", - "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.5.0", - "safe-array-concat": "^1.0.0", - "safe-regex-test": "^1.0.0", - "string.prototype.trim": "^1.2.7", - "string.prototype.trimend": "^1.0.6", - "string.prototype.trimstart": "^1.0.6", - "typed-array-buffer": "^1.0.0", - "typed-array-byte-length": "^1.0.0", - "typed-array-byte-offset": "^1.0.0", - "typed-array-length": "^1.0.4", - "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.10" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/es-html-parser": { "version": "0.0.9", "resolved": "https://registry.npmjs.org/es-html-parser/-/es-html-parser-0.0.9.tgz", "integrity": "sha512-oniQMi+466VFsDzcdron9Ry/sqUJpDJg1bbDn0jFJKDdxXhwIOYDr4DgBnO5/yPLGj2xv+n5yy4L1Q0vAC5TYQ==", "dev": true }, - "node_modules/es-set-tostringtag": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", - "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.1.3", - "has": "^1.0.3", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-shim-unscopables": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", - "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", - "dev": true, - "dependencies": { - "has": "^1.0.3" - } - }, - "node_modules/es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, - "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -756,40 +816,37 @@ } }, "node_modules/eslint": { - "version": "8.46.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.46.0.tgz", - "integrity": "sha512-cIO74PvbW0qU8e0mIvk5IV3ToWdCq5FYG6gWPHHkx6gNdjlbAYvtfHmlCMXxjcoVaIdwy/IAt3+mDkZkfvb2Dg==", + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.3.0.tgz", + "integrity": "sha512-5Iv4CsZW030lpUqHBapdPo3MJetAPtejVW8B84GIcIIv8+ohFaddXsrn1Gn8uD9ijDb+kcYKFUVmC8qG8B2ORQ==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.1", - "@eslint/js": "^8.46.0", - "@humanwhocodes/config-array": "^0.11.10", + "@eslint/eslintrc": "^3.1.0", + "@eslint/js": "9.3.0", + "@humanwhocodes/config-array": "^0.13.0", "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.3.0", "@nodelib/fs.walk": "^1.2.8", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", - "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.2", - "espree": "^9.6.1", + "eslint-scope": "^8.0.1", + "eslint-visitor-keys": "^4.0.0", + "espree": "^10.0.1", "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", + "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", "lodash.merge": "^4.6.2", @@ -803,179 +860,64 @@ "eslint": "bin/eslint.js" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint-config-airbnb-base": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-15.0.0.tgz", - "integrity": "sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==", - "dev": true, - "dependencies": { - "confusing-browser-globals": "^1.0.10", - "object.assign": "^4.1.2", - "object.entries": "^1.1.5", - "semver": "^6.3.0" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - }, - "peerDependencies": { - "eslint": "^7.32.0 || ^8.2.0", - "eslint-plugin-import": "^2.25.2" - } - }, - "node_modules/eslint-import-resolver-node": { - "version": "0.3.7", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz", - "integrity": "sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA==", - "dev": true, - "dependencies": { - "debug": "^3.2.7", - "is-core-module": "^2.11.0", - "resolve": "^1.22.1" - } - }, - "node_modules/eslint-import-resolver-node/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-module-utils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz", - "integrity": "sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==", - "dev": true, - "dependencies": { - "debug": "^3.2.7" - }, - "engines": { - "node": ">=4" - }, - "peerDependenciesMeta": { - "eslint": { - "optional": true - } - } - }, - "node_modules/eslint-module-utils/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, "node_modules/eslint-plugin-html": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-html/-/eslint-plugin-html-7.1.0.tgz", - "integrity": "sha512-fNLRraV/e6j8e3XYOC9xgND4j+U7b1Rq+OygMlLcMg+wI/IpVbF+ubQa3R78EjKB9njT6TQOlcK5rFKBVVtdfg==", - "dev": true, - "dependencies": { - "htmlparser2": "^8.0.1" - } - }, - "node_modules/eslint-plugin-import": { - "version": "2.28.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.28.0.tgz", - "integrity": "sha512-B8s/n+ZluN7sxj9eUf7/pRFERX0r5bnFA2dCaLHy2ZeaQEAz0k+ZZkFWRFHJAqxfxQDx6KLv9LeIki7cFdwW+Q==", - "dev": true, - "dependencies": { - "array-includes": "^3.1.6", - "array.prototype.findlastindex": "^1.2.2", - "array.prototype.flat": "^1.3.1", - "array.prototype.flatmap": "^1.3.1", - "debug": "^3.2.7", - "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.7", - "eslint-module-utils": "^2.8.0", - "has": "^1.0.3", - "is-core-module": "^2.12.1", - "is-glob": "^4.0.3", - "minimatch": "^3.1.2", - "object.fromentries": "^2.0.6", - "object.groupby": "^1.0.0", - "object.values": "^1.1.6", - "resolve": "^1.22.3", - "semver": "^6.3.1", - "tsconfig-paths": "^3.14.2" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" - } - }, - "node_modules/eslint-plugin-import/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-plugin-import/node_modules/doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-html/-/eslint-plugin-html-8.1.1.tgz", + "integrity": "sha512-6qmlJsc40D2m3Dn9oEH+0PAOkJhxVu0f5sVItqpCE0YWgYnyP4xCjBc3UWTHaJcY9ARkWOLIIuXLq0ndRnQOHw==", "dev": true, "dependencies": { - "esutils": "^2.0.2" + "htmlparser2": "^9.1.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=16.0.0" } }, "node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.0.1.tgz", + "integrity": "sha512-pL8XjgP4ZOmmwfFE8mEhSxA7ZY4C+LWyqjQ3o4yWkkmD0qcMT9kkW3zWHOczhWcjTSgqycYAgwSlXvZltv65og==", "dev": true, "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, "node_modules/eslint-visitor-keys": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.2.tgz", - "integrity": "sha512-8drBzUEyZ2llkpCA67iYrgEssKDUu68V8ChqqOfFupIaG/LCVPUT+CoGJpT77zJprs4T/W7p07LP7zAIMuweVw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz", + "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==", "dev": true, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, "node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.0.1.tgz", + "integrity": "sha512-MWkrWZbJsL2UwnjxTX3gG8FneachS/Mwg7tdGXce011sJd5b0JG54vat5KHnfSBODZ3Wvzd2WnjxyzsRoVv+ww==", "dev": true, "dependencies": { - "acorn": "^8.9.0", + "acorn": "^8.11.3", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" + "eslint-visitor-keys": "^4.0.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" @@ -1029,6 +971,34 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -1042,24 +1012,36 @@ "dev": true }, "node_modules/fastq": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", - "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", "dev": true, "dependencies": { "reusify": "^1.0.4" } }, "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "dependencies": { - "flat-cache": "^3.0.4" + "to-regex-range": "^5.0.1" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=8" } }, "node_modules/find-up": { @@ -1079,123 +1061,24 @@ } }, "node_modules/flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", "dev": true, "dependencies": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" + "flatted": "^3.2.9", + "keyv": "^4.5.4" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=16" } }, "node_modules/flatted": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", - "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", "dev": true }, - "node_modules/for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", - "dev": true, - "dependencies": { - "is-callable": "^1.1.3" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true - }, - "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "node_modules/function.prototype.name": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", - "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.0", - "functions-have-names": "^1.2.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-intrinsic": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", - "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -1209,45 +1092,35 @@ } }, "node_modules/globals": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", - "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "version": "15.3.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.3.0.tgz", + "integrity": "sha512-cCdyVjIUVTtX8ZsPkq1oCsOsLmGIswqnjZYMJJTGaNApj1yHtLSymKhwH51ttirREn75z3p4k051clwg7rvNKA==", "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, "engines": { - "node": ">=8" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/globalthis": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", - "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "dev": true, "dependencies": { - "define-properties": "^1.1.3" + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.1.3" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/graceful-fs": { @@ -1256,33 +1129,6 @@ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "dev": true }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true - }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -1292,61 +1138,10 @@ "node": ">=8" } }, - "node_modules/has-property-descriptors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", - "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.1.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", - "dev": true, - "dependencies": { - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/htmlparser2": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", - "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-9.1.0.tgz", + "integrity": "sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==", "dev": true, "funding": [ "https://github.com/fb55/htmlparser2?sponsor=1", @@ -1358,14 +1153,14 @@ "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.3", - "domutils": "^3.0.1", - "entities": "^4.4.0" + "domutils": "^3.1.0", + "entities": "^4.5.0" } }, "node_modules/ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", "dev": true, "engines": { "node": ">= 4" @@ -1396,117 +1191,6 @@ "node": ">=0.8.19" } }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "node_modules/internal-slot": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz", - "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.2.0", - "has": "^1.0.3", - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-array-buffer": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", - "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.0", - "is-typed-array": "^1.1.10" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", - "dev": true, - "dependencies": { - "has-bigints": "^1.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-core-module": { - "version": "2.12.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz", - "integrity": "sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==", - "dev": true, - "dependencies": { - "has": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", - "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -1528,31 +1212,13 @@ "node": ">=0.10.0" } }, - "node_modules/is-negative-zero": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", - "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=0.12.0" } }, "node_modules/is-path-inside": { @@ -1564,97 +1230,6 @@ "node": ">=8" } }, - "node_modules/is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-shared-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", - "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", - "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", - "dev": true, - "dependencies": { - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-typed-array": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz", - "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==", - "dev": true, - "dependencies": { - "which-typed-array": "^1.1.11" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true - }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -1683,21 +1258,21 @@ } }, "node_modules/jsdoc": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-4.0.2.tgz", - "integrity": "sha512-e8cIg2z62InH7azBBi3EsSEqrKx+nUtAS5bBcYTSpZFA+vhNPyhv8PTFZ0WsjOPDj04/dOLlm08EDcQJDqaGQg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-4.0.3.tgz", + "integrity": "sha512-Nu7Sf35kXJ1MWDZIMAuATRQTg1iIPdzh7tqJ6jjvaU/GfDf+qi5UV8zJR3Mo+/pYFvm8mzay4+6O5EWigaQBQw==", "dev": true, "dependencies": { "@babel/parser": "^7.20.15", "@jsdoc/salty": "^0.2.1", - "@types/markdown-it": "^12.2.3", + "@types/markdown-it": "^14.1.1", "bluebird": "^3.7.2", "catharsis": "^0.9.0", "escape-string-regexp": "^2.0.0", "js2xmlparser": "^4.0.2", "klaw": "^3.0.0", - "markdown-it": "^12.3.2", - "markdown-it-anchor": "^8.4.1", + "markdown-it": "^14.1.0", + "markdown-it-anchor": "^8.6.7", "marked": "^4.0.10", "mkdirp": "^1.0.4", "requizzle": "^0.2.3", @@ -1720,6 +1295,12 @@ "node": ">=8" } }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -1732,16 +1313,13 @@ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, - "node_modules/json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" + "json-buffer": "3.0.1" } }, "node_modules/klaw": { @@ -1767,12 +1345,12 @@ } }, "node_modules/linkify-it": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz", - "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", "dev": true, "dependencies": { - "uc.micro": "^1.0.1" + "uc.micro": "^2.0.0" } }, "node_modules/locate-path": { @@ -1803,19 +1381,20 @@ "dev": true }, "node_modules/markdown-it": { - "version": "12.3.2", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz", - "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", + "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", "dev": true, "dependencies": { "argparse": "^2.0.1", - "entities": "~2.1.0", - "linkify-it": "^3.0.1", - "mdurl": "^1.0.1", - "uc.micro": "^1.0.5" + "entities": "^4.4.0", + "linkify-it": "^5.0.0", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" }, "bin": { - "markdown-it": "bin/markdown-it.js" + "markdown-it": "bin/markdown-it.mjs" } }, "node_modules/markdown-it-anchor": { @@ -1828,15 +1407,6 @@ "markdown-it": "*" } }, - "node_modules/markdown-it/node_modules/entities": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", - "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==", - "dev": true, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, "node_modules/marked": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", @@ -1850,11 +1420,45 @@ } }, "node_modules/mdurl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", - "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", "dev": true }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", + "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", + "dev": true, + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -1867,15 +1471,6 @@ "node": "*" } }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/mkdirp": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", @@ -1900,123 +1495,18 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, - "node_modules/object-inspect": { - "version": "1.12.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", - "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.assign": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", - "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.entries": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.6.tgz", - "integrity": "sha512-leTPzo4Zvg3pmbQ3rDK69Rl8GQvIqMWubrkxONG9/ojtFE2rD9fjMKfSI5BxW3osRH1m6VdzmqK8oAY9aT4x5w==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.fromentries": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.6.tgz", - "integrity": "sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.groupby": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.0.tgz", - "integrity": "sha512-70MWG6NfRH9GnbZOikuhPPYzpUpof9iW2J9E4dW7FXTqPNb6rllE6u39SKwwiNh8lCwX3DDb5OgcKGiEBrTTyw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.21.2", - "get-intrinsic": "^1.2.1" - } - }, - "node_modules/object.values": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.6.tgz", - "integrity": "sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "dependencies": { - "wrappy": "1" - } - }, "node_modules/optionator": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", - "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, "dependencies": { - "@aashutoshrathi/word-wrap": "^1.2.3", "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", - "type-check": "^0.4.0" + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" }, "engines": { "node": ">= 0.8.0" @@ -2073,15 +1563,6 @@ "node": ">=8" } }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -2091,11 +1572,26 @@ "node": ">=8" } }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } }, "node_modules/prelude-ls": { "version": "1.2.1", @@ -2107,9 +1603,18 @@ } }, "node_modules/punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", "dev": true, "engines": { "node": ">=6" @@ -2135,23 +1640,6 @@ } ] }, - "node_modules/regexp.prototype.flags": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz", - "integrity": "sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "functions-have-names": "^1.2.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/requizzle": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.4.tgz", @@ -2161,23 +1649,6 @@ "lodash": "^4.17.21" } }, - "node_modules/resolve": { - "version": "1.22.3", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.3.tgz", - "integrity": "sha512-P8ur/gp/AmbEzjr729bZnLjXK5Z+4P0zhIJgBgzqRih7hL7BOukHGtSTA3ACMY467GRFz3duQsi0bDZdR7DKdw==", - "dev": true, - "dependencies": { - "is-core-module": "^2.12.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -2197,21 +1668,6 @@ "node": ">=0.10.0" } }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -2235,45 +1691,16 @@ "queue-microtask": "^1.2.2" } }, - "node_modules/safe-array-concat": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.0.tgz", - "integrity": "sha512-9dVEFruWIsnie89yym+xWTAYASdpw3CJV7Li/6zBewGf9z2i1j31rP6jnY0pHEO4QZh6N0K11bFjWmdR8UGdPQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.0", - "has-symbols": "^1.0.3", - "isarray": "^2.0.5" - }, - "engines": { - "node": ">=0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/safe-regex-test": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", - "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", - "is-regex": "^1.1.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", "dev": true, "bin": { "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, "node_modules/shebang-command": { @@ -2297,63 +1724,13 @@ "node": ">=8" } }, - "node_modules/side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trim": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz", - "integrity": "sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==", + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimend": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", - "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz", - "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8" } }, "node_modules/strip-ansi": { @@ -2368,15 +1745,6 @@ "node": ">=8" } }, - "node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -2401,146 +1769,68 @@ "node": ">=8" } }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, - "node_modules/tsconfig-paths": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz", - "integrity": "sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==", - "dev": true, - "dependencies": { - "@types/json5": "^0.0.29", - "json5": "^1.0.2", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - } - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, "dependencies": { - "prelude-ls": "^1.2.1" + "is-number": "^7.0.0" }, "engines": { - "node": ">= 0.8.0" + "node": ">=8.0" } }, - "node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "node_modules/ts-api-utils": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", + "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", "dev": true, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/typed-array-buffer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz", - "integrity": "sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1", - "is-typed-array": "^1.1.10" + "node": ">=16" }, - "engines": { - "node": ">= 0.4" + "peerDependencies": { + "typescript": ">=4.2.0" } }, - "node_modules/typed-array-byte-length": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz", - "integrity": "sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==", + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "has-proto": "^1.0.1", - "is-typed-array": "^1.1.10" + "prelude-ls": "^1.2.1" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 0.8.0" } }, - "node_modules/typed-array-byte-offset": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz", - "integrity": "sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==", + "node_modules/typescript": { + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", "dev": true, - "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "has-proto": "^1.0.1", - "is-typed-array": "^1.1.10" + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-array-length": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", - "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "is-typed-array": "^1.1.9" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=14.17" } }, "node_modules/uc.micro": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", - "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", "dev": true }, - "node_modules/unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/underscore": { "version": "1.13.6", "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz", @@ -2571,47 +1861,15 @@ "node": ">= 8" } }, - "node_modules/which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, - "dependencies": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-typed-array": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.11.tgz", - "integrity": "sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==", - "dev": true, - "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0" - }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=0.10.0" } }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true - }, "node_modules/xmlcreate": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.4.tgz", diff --git a/platform/web/package.json b/platform/web/package.json index 4e17cd530b..588af2ff3b 100644 --- a/platform/web/package.json +++ b/platform/web/package.json @@ -3,32 +3,22 @@ "private": true, "version": "1.0.0", "description": "Development and linting setup for Godot's Web platform code", + "author": "Godot Engine contributors", + "license": "MIT", "scripts": { "docs": "jsdoc --template js/jsdoc2rst/ js/engine/engine.js js/engine/config.js js/engine/features.js --destination ''", - "lint": "npm run lint:engine && npm run lint:libs && npm run lint:modules && npm run lint:tools && npm run lint:sw && npm run lint:html", - "lint:engine": "eslint \"js/engine/*.js\" --no-eslintrc -c .eslintrc.engine.js", - "lint:sw": "eslint \"../../misc/dist/html/service-worker.js\" --no-eslintrc -c .eslintrc.sw.js", - "lint:libs": "eslint \"js/libs/*.js\" --no-eslintrc -c .eslintrc.libs.js", - "lint:modules": "eslint \"../../modules/**/*.js\" --no-eslintrc -c .eslintrc.libs.js", - "lint:tools": "eslint \"js/jsdoc2rst/**/*.js\" --no-eslintrc -c .eslintrc.engine.js", - "lint:html": "eslint \"../../misc/dist/html/*.html\" --no-eslintrc -c .eslintrc.html.js", - "format": "npm run format:engine && npm run format:libs && npm run format:modules && npm run format:tools && format:sw && npm run format:html", - "format:engine": "npm run lint:engine -- --fix", - "format:libs": "npm run lint:libs -- --fix", - "format:modules": "npm run lint:modules -- --fix", - "format:tools": "npm run lint:tools -- --fix", - "format:html": "npm run lint:html -- --fix", - "format:sw": "npm run lint:sw -- --fix" + "lint": "cd ../.. && eslint --no-config-lookup --config ./platform/web/eslint.config.cjs ./platform/web/js ./modules ./misc/dist/html", + "format": "npm run lint -- --fix" }, - "author": "Godot Engine contributors", - "license": "MIT", "devDependencies": { - "@html-eslint/eslint-plugin": "^0.19.1", - "@html-eslint/parser": "^0.19.1", - "eslint": "^8.46.0", - "eslint-config-airbnb-base": "^15.0.0", - "eslint-plugin-import": "^2.28.0", - "jsdoc": "^4.0.2", - "eslint-plugin-html": "^7.1.0" + "@eslint/js": "^9.3.0", + "@html-eslint/eslint-plugin": "^0.24.1", + "@html-eslint/parser": "^0.24.1", + "@stylistic/eslint-plugin": "^2.1.0", + "eslint": "^9.3.0", + "eslint-plugin-html": "^8.1.1", + "espree": "^10.0.1", + "globals": "^15.3.0", + "jsdoc": "^4.0.3" } } diff --git a/platform/web/serve.py b/platform/web/serve.py index 89dff63ca3..f0b0ec9622 100755 --- a/platform/web/serve.py +++ b/platform/web/serve.py @@ -1,13 +1,13 @@ #!/usr/bin/env python3 -from http.server import HTTPServer, SimpleHTTPRequestHandler, test # type: ignore -from pathlib import Path -import os -import sys import argparse import contextlib +import os import socket import subprocess +import sys +from http.server import HTTPServer, SimpleHTTPRequestHandler, test # type: ignore +from pathlib import Path # See cpython GH-17851 and GH-17864. diff --git a/platform/windows/SCsub b/platform/windows/SCsub index 435c501956..f2fb8616ae 100644 --- a/platform/windows/SCsub +++ b/platform/windows/SCsub @@ -4,6 +4,7 @@ Import("env") import os from pathlib import Path + import platform_windows_builders sources = [] @@ -49,6 +50,7 @@ def arrange_program_clean(prog): res_file = "godot_res.rc" res_target = "godot_res" + env["OBJSUFFIX"] res_obj = env.RES(res_target, res_file) +env.Depends(res_obj, "#core/version_generated.gen.h") env.add_source_files(sources, common_win) sources += res_obj @@ -62,6 +64,7 @@ if env["windows_subsystem"] == "gui": res_wrap_file = "godot_res_wrap.rc" res_wrap_target = "godot_res_wrap" + env["OBJSUFFIX"] res_wrap_obj = env_wrap.RES(res_wrap_target, res_wrap_file) + env_wrap.Depends(res_wrap_obj, "#core/version_generated.gen.h") if env.msvc: env_wrap.Append(LINKFLAGS=["/SUBSYSTEM:CONSOLE"]) diff --git a/platform/windows/detect.py b/platform/windows/detect.py index 93eb34001e..0d3e9c606e 100644 --- a/platform/windows/detect.py +++ b/platform/windows/detect.py @@ -1,12 +1,12 @@ -import methods import os import subprocess import sys -from methods import print_warning, print_error -from platform_methods import detect_arch - from typing import TYPE_CHECKING +import methods +from methods import print_error, print_warning +from platform_methods import detect_arch + if TYPE_CHECKING: from SCons.Script.SConscript import SConsEnvironment @@ -178,7 +178,7 @@ def get_opts(): caller_frame = inspect.stack()[1] caller_script_dir = os.path.dirname(os.path.abspath(caller_frame[1])) d3d12_deps_folder = os.path.join(caller_script_dir, "bin", "build_deps") - except: # Give up. + except Exception: # Give up. d3d12_deps_folder = "" return [ @@ -248,10 +248,10 @@ def get_doc_path(): def get_flags(): arch = detect_build_env_arch() or detect_arch() - return [ - ("arch", arch), - ("supported", ["mono"]), - ] + return { + "arch": arch, + "supported": ["mono"], + } def build_res_file(target, source, env: "SConsEnvironment"): @@ -523,7 +523,7 @@ def configure_msvc(env: "SConsEnvironment", vcvars_msvc_config): env.Append(CXXFLAGS=["/bigobj"]) # PIX - if not env["arch"] in ["x86_64", "arm64"] or env["pix_path"] == "" or not os.path.exists(env["pix_path"]): + if env["arch"] not in ["x86_64", "arm64"] or env["pix_path"] == "" or not os.path.exists(env["pix_path"]): env["use_pix"] = False if env["use_pix"]: @@ -750,7 +750,7 @@ def configure_mingw(env: "SConsEnvironment"): env.Append(LIBS=["dxgi", "dxguid"]) # PIX - if not env["arch"] in ["x86_64", "arm64"] or env["pix_path"] == "" or not os.path.exists(env["pix_path"]): + if env["arch"] not in ["x86_64", "arm64"] or env["pix_path"] == "" or not os.path.exists(env["pix_path"]): env["use_pix"] = False if env["use_pix"]: diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp index e79d16629e..62e1a433e2 100644 --- a/platform/windows/display_server_windows.cpp +++ b/platform/windows/display_server_windows.cpp @@ -1832,24 +1832,6 @@ void DisplayServerWindows::window_set_size(const Size2i p_size, WindowID p_windo int w = p_size.width; int h = p_size.height; - - wd.width = w; - wd.height = h; - -#if defined(RD_ENABLED) - if (rendering_context) { - rendering_context->window_set_size(p_window, w, h); - } -#endif -#if defined(GLES3_ENABLED) - if (gl_manager_native) { - gl_manager_native->window_resize(p_window, w, h); - } - if (gl_manager_angle) { - gl_manager_angle->window_resize(p_window, w, h); - } -#endif - RECT rect; GetWindowRect(wd.hWnd, &rect); @@ -3401,6 +3383,21 @@ DisplayServer::VSyncMode DisplayServerWindows::window_get_vsync_mode(WindowID p_ void DisplayServerWindows::set_context(Context p_context) { } +bool DisplayServerWindows::is_window_transparency_available() const { + BOOL dwm_enabled = true; + if (DwmIsCompositionEnabled(&dwm_enabled) == S_OK) { // Note: Always enabled on Windows 8+, this check can be removed after Windows 7 support is dropped. + if (!dwm_enabled) { + return false; + } + } +#if defined(RD_ENABLED) + if (rendering_device && !rendering_device->is_composite_alpha_supported()) { + return false; + } +#endif + return OS::get_singleton()->is_layered_allowed(); +} + #define MI_WP_SIGNATURE 0xFF515700 #define SIGNATURE_MASK 0xFFFFFF00 // Keeping the name suggested by Microsoft, but this macro really answers: @@ -3827,7 +3824,12 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA } } if (system_theme_changed.is_valid()) { - system_theme_changed.call(); + Variant ret; + Callable::CallError ce; + system_theme_changed.callp(nullptr, 0, ret, ce); + if (ce.error != Callable::CallError::CALL_OK) { + ERR_PRINT(vformat("Failed to execute system theme changed callback: %s.", Variant::get_callable_error_text(system_theme_changed, nullptr, 0, ce))); + } } } break; case WM_THEMECHANGED: { @@ -3882,10 +3884,13 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA } else if (indicators[iid].callback.is_valid()) { Variant v_button = mb; Variant v_pos = mouse_get_position(); - Variant *v_args[2] = { &v_button, &v_pos }; + const Variant *v_args[2] = { &v_button, &v_pos }; Variant ret; Callable::CallError ce; indicators[iid].callback.callp((const Variant **)&v_args, 2, ret, ce); + if (ce.error != Callable::CallError::CALL_OK) { + ERR_PRINT(vformat("Failed to execute status indicator callback: %s.", Variant::get_callable_error_text(indicators[iid].callback, v_args, 2, ce))); + } } } return 0; @@ -4626,6 +4631,14 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA rendering_context->window_set_size(window_id, window.width, window.height); } #endif +#if defined(GLES3_ENABLED) + if (gl_manager_native) { + gl_manager_native->window_resize(window_id, window.width, window.height); + } + if (gl_manager_angle) { + gl_manager_angle->window_resize(window_id, window.width, window.height); + } +#endif } if (!window.minimized && (!(window_pos_params->flags & SWP_NOMOVE) || window_pos_params->flags & SWP_FRAMECHANGED)) { @@ -4853,7 +4866,14 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA } if (files.size() && windows[window_id].drop_files_callback.is_valid()) { - windows[window_id].drop_files_callback.call(files); + Variant v_files = files; + const Variant *v_args[1] = { &v_files }; + Variant ret; + Callable::CallError ce; + windows[window_id].drop_files_callback.callp((const Variant **)&v_args, 1, ret, ce); + if (ce.error != Callable::CallError::CALL_OK) { + ERR_PRINT(vformat("Failed to execute drop files callback: %s.", Variant::get_callable_error_text(windows[window_id].drop_files_callback, v_args, 1, ce))); + } } } break; default: { @@ -5501,7 +5521,7 @@ void DisplayServerWindows::tablet_set_current_driver(const String &p_driver) { } } -DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Error &r_error) { +DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error) { KeyMappingWindows::initialize(); drop_events = false; @@ -5861,8 +5881,8 @@ Vector<String> DisplayServerWindows::get_rendering_drivers_func() { return drivers; } -DisplayServer *DisplayServerWindows::create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Error &r_error) { - DisplayServer *ds = memnew(DisplayServerWindows(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_position, p_resolution, p_screen, r_error)); +DisplayServer *DisplayServerWindows::create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error) { + DisplayServer *ds = memnew(DisplayServerWindows(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_position, p_resolution, p_screen, p_context, r_error)); if (r_error != OK) { if (p_rendering_driver == "vulkan") { String executable_name = OS::get_singleton()->get_executable_path().get_file(); diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h index 618b763bc7..9a4eeba486 100644 --- a/platform/windows/display_server_windows.h +++ b/platform/windows/display_server_windows.h @@ -697,11 +697,13 @@ public: virtual void set_context(Context p_context) override; - static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Error &r_error); + virtual bool is_window_transparency_available() const override; + + static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error); static Vector<String> get_rendering_drivers_func(); static void register_windows_driver(); - DisplayServerWindows(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Error &r_error); + DisplayServerWindows(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error); ~DisplayServerWindows(); }; diff --git a/platform/windows/platform_windows_builders.py b/platform/windows/platform_windows_builders.py index 729d55cea6..3fd9e1a581 100644 --- a/platform/windows/platform_windows_builders.py +++ b/platform/windows/platform_windows_builders.py @@ -1,8 +1,8 @@ """Functions used to generate source files during build time""" import os -from detect import get_mingw_bin_prefix -from detect import try_cmd + +from detect import get_mingw_bin_prefix, try_cmd def make_debug_mingw(target, source, env): diff --git a/platform_methods.py b/platform_methods.py index 5326e36077..2b157da22b 100644 --- a/platform_methods.py +++ b/platform_methods.py @@ -1,10 +1,7 @@ import os -import sys -import json import platform -import uuid -import functools import subprocess + import methods # NOTE: The multiprocessing module is not compatible with SCons due to conflict on cPickle @@ -47,14 +44,14 @@ def get_build_version(short): import version name = "custom_build" - if os.getenv("BUILD_NAME") != None: + if os.getenv("BUILD_NAME") is not None: name = os.getenv("BUILD_NAME") v = "%d.%d" % (version.major, version.minor) if version.patch > 0: v += ".%d" % version.patch status = version.status if not short: - if os.getenv("GODOT_VERSION_STATUS") != None: + if os.getenv("GODOT_VERSION_STATUS") is not None: status = str(os.getenv("GODOT_VERSION_STATUS")) v += ".%s.%s" % (status, name) return v @@ -86,7 +83,7 @@ def get_mvk_sdk_path(osname): def int_or_zero(i): try: return int(i) - except: + except (TypeError, ValueError): return 0 def ver_parse(a): @@ -140,9 +137,8 @@ def detect_mvk(env, osname): ) for mvk_path in mvk_list: - if mvk_path and os.path.isfile(os.path.join(mvk_path, osname + "/libMoltenVK.a")): - mvk_found = True - print("MoltenVK found at: " + mvk_path) + if mvk_path and os.path.isfile(os.path.join(mvk_path, f"{osname}/libMoltenVK.a")): + print(f"MoltenVK found at: {mvk_path}") return mvk_path return "" diff --git a/pyproject.toml b/pyproject.toml index f1ea10fbae..34ae075f2b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,6 +11,19 @@ namespace_packages = true explicit_package_bases = true exclude = ["thirdparty/"] -[tool.black] +[tool.ruff] +extend-exclude = ["thirdparty"] +extend-include = ["SConstruct", "SCsub"] line-length = 120 -extend-exclude = ".*thirdparty/.*" +target-version = "py37" + +[tool.ruff.lint] +extend-select = [ + "I", # isort +] + +[tool.ruff.lint.per-file-ignores] +"{SConstruct,SCsub}" = [ + "E402", # Module level import not at top of file + "F821", # Undefined name +] diff --git a/scene/2d/camera_2d.cpp b/scene/2d/camera_2d.cpp index 822f1b58fd..18ef2d8505 100644 --- a/scene/2d/camera_2d.cpp +++ b/scene/2d/camera_2d.cpp @@ -54,16 +54,18 @@ void Camera2D::_update_scroll() { if (is_current()) { ERR_FAIL_COND(custom_viewport && !ObjectDB::get_instance(custom_viewport_id)); + Size2 screen_size = _get_camera_screen_size(); + Transform2D xform; if (is_physics_interpolated_and_enabled()) { xform = _interpolation_data.xform_prev.interpolate_with(_interpolation_data.xform_curr, Engine::get_singleton()->get_physics_interpolation_fraction()); + camera_screen_center = xform.affine_inverse().xform(0.5 * screen_size); } else { xform = get_camera_transform(); } viewport->set_canvas_transform(xform); - Size2 screen_size = _get_camera_screen_size(); Point2 screen_offset = (anchor_mode == ANCHOR_MODE_DRAG_CENTER ? (screen_size * 0.5) : Point2()); Point2 adj_screen_pos = camera_screen_center - (screen_size * 0.5); diff --git a/scene/2d/navigation_region_2d.cpp b/scene/2d/navigation_region_2d.cpp index ab44e57d05..bad9de5daa 100644 --- a/scene/2d/navigation_region_2d.cpp +++ b/scene/2d/navigation_region_2d.cpp @@ -162,8 +162,19 @@ void NavigationRegion2D::_notification(int p_what) { set_physics_process_internal(true); } break; + case NOTIFICATION_VISIBILITY_CHANGED: { +#ifdef DEBUG_ENABLED + if (debug_instance_rid.is_valid()) { + RS::get_singleton()->canvas_item_set_visible(debug_instance_rid, is_visible_in_tree()); + } +#endif // DEBUG_ENABLED + } break; + case NOTIFICATION_EXIT_TREE: { _region_exit_navigation_map(); +#ifdef DEBUG_ENABLED + _free_debug(); +#endif // DEBUG_ENABLED } break; case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { @@ -189,6 +200,9 @@ void NavigationRegion2D::set_navigation_polygon(const Ref<NavigationPolygon> &p_ } navigation_polygon = p_navigation_polygon; +#ifdef DEBUG_ENABLED + debug_mesh_dirty = true; +#endif // DEBUG_ENABLED NavigationServer2D::get_singleton()->region_set_navigation_polygon(region, p_navigation_polygon); if (navigation_polygon.is_valid()) { @@ -420,12 +434,42 @@ void NavigationRegion2D::_region_update_transform() { #ifdef DEBUG_ENABLED void NavigationRegion2D::_update_debug_mesh() { - Vector<Vector2> navigation_polygon_vertices = navigation_polygon->get_vertices(); - if (navigation_polygon_vertices.size() < 3) { + if (!is_inside_tree()) { + _free_debug(); return; } const NavigationServer2D *ns2d = NavigationServer2D::get_singleton(); + RenderingServer *rs = RenderingServer::get_singleton(); + + if (!debug_instance_rid.is_valid()) { + debug_instance_rid = rs->canvas_item_create(); + } + if (!debug_mesh_rid.is_valid()) { + debug_mesh_rid = rs->mesh_create(); + } + + const Transform2D region_gt = get_global_transform(); + + rs->canvas_item_set_parent(debug_instance_rid, get_world_2d()->get_canvas()); + rs->canvas_item_set_transform(debug_instance_rid, region_gt); + + if (!debug_mesh_dirty) { + return; + } + + rs->mesh_clear(debug_mesh_rid); + debug_mesh_dirty = false; + + const Vector<Vector2> &vertices = navigation_polygon->get_vertices(); + if (vertices.size() < 3) { + return; + } + + int polygon_count = navigation_polygon->get_polygon_count(); + if (polygon_count == 0) { + return; + } bool enabled_geometry_face_random_color = ns2d->get_debug_navigation_enable_geometry_face_random_color(); bool enabled_edge_lines = ns2d->get_debug_navigation_enable_edge_lines(); @@ -438,39 +482,109 @@ void NavigationRegion2D::_update_debug_mesh() { debug_edge_color = ns2d->get_debug_navigation_geometry_edge_disabled_color(); } + int vertex_count = 0; + int line_count = 0; + + for (int i = 0; i < polygon_count; i++) { + const Vector<int> &polygon = navigation_polygon->get_polygon(i); + int polygon_size = polygon.size(); + if (polygon_size < 3) { + continue; + } + line_count += polygon_size * 2; + vertex_count += (polygon_size - 2) * 3; + } + + Vector<Vector2> face_vertex_array; + face_vertex_array.resize(vertex_count); + + Vector<Color> face_color_array; + if (enabled_geometry_face_random_color) { + face_color_array.resize(vertex_count); + } + + Vector<Vector2> line_vertex_array; + if (enabled_edge_lines) { + line_vertex_array.resize(line_count); + } + RandomPCG rand; + Color polygon_color = debug_face_color; - for (int i = 0; i < navigation_polygon->get_polygon_count(); i++) { - // An array of vertices for this polygon. - Vector<int> polygon = navigation_polygon->get_polygon(i); - Vector<Vector2> debug_polygon_vertices; - debug_polygon_vertices.resize(polygon.size()); - for (int j = 0; j < polygon.size(); j++) { - ERR_FAIL_INDEX(polygon[j], navigation_polygon_vertices.size()); - debug_polygon_vertices.write[j] = navigation_polygon_vertices[polygon[j]]; + int face_vertex_index = 0; + int line_vertex_index = 0; + + Vector2 *face_vertex_array_ptrw = face_vertex_array.ptrw(); + Color *face_color_array_ptrw = face_color_array.ptrw(); + Vector2 *line_vertex_array_ptrw = line_vertex_array.ptrw(); + + for (int polygon_index = 0; polygon_index < polygon_count; polygon_index++) { + const Vector<int> &polygon_indices = navigation_polygon->get_polygon(polygon_index); + int polygon_indices_size = polygon_indices.size(); + if (polygon_indices_size < 3) { + continue; } - // Generate the polygon color, slightly randomly modified from the settings one. - Color random_variation_color = debug_face_color; if (enabled_geometry_face_random_color) { - random_variation_color.set_hsv( - debug_face_color.get_h() + rand.random(-1.0, 1.0) * 0.1, - debug_face_color.get_s(), - debug_face_color.get_v() + rand.random(-1.0, 1.0) * 0.2); + // Generate the polygon color, slightly randomly modified from the settings one. + polygon_color.set_hsv(debug_face_color.get_h() + rand.random(-1.0, 1.0) * 0.1, debug_face_color.get_s(), debug_face_color.get_v() + rand.random(-1.0, 1.0) * 0.2); + polygon_color.a = debug_face_color.a; } - random_variation_color.a = debug_face_color.a; - Vector<Color> debug_face_colors; - debug_face_colors.push_back(random_variation_color); - RS::get_singleton()->canvas_item_add_polygon(get_canvas_item(), debug_polygon_vertices, debug_face_colors); + for (int polygon_indices_index = 0; polygon_indices_index < polygon_indices_size - 2; polygon_indices_index++) { + face_vertex_array_ptrw[face_vertex_index] = vertices[polygon_indices[0]]; + face_vertex_array_ptrw[face_vertex_index + 1] = vertices[polygon_indices[polygon_indices_index + 1]]; + face_vertex_array_ptrw[face_vertex_index + 2] = vertices[polygon_indices[polygon_indices_index + 2]]; + if (enabled_geometry_face_random_color) { + face_color_array_ptrw[face_vertex_index] = polygon_color; + face_color_array_ptrw[face_vertex_index + 1] = polygon_color; + face_color_array_ptrw[face_vertex_index + 2] = polygon_color; + } + face_vertex_index += 3; + } if (enabled_edge_lines) { - Vector<Color> debug_edge_colors; - debug_edge_colors.push_back(debug_edge_color); - debug_polygon_vertices.push_back(debug_polygon_vertices[0]); // Add first again for closing polyline. - RS::get_singleton()->canvas_item_add_polyline(get_canvas_item(), debug_polygon_vertices, debug_edge_colors); + for (int polygon_indices_index = 0; polygon_indices_index < polygon_indices_size; polygon_indices_index++) { + line_vertex_array_ptrw[line_vertex_index] = vertices[polygon_indices[polygon_indices_index]]; + line_vertex_index += 1; + if (polygon_indices_index + 1 == polygon_indices_size) { + line_vertex_array_ptrw[line_vertex_index] = vertices[polygon_indices[0]]; + line_vertex_index += 1; + } else { + line_vertex_array_ptrw[line_vertex_index] = vertices[polygon_indices[polygon_indices_index + 1]]; + line_vertex_index += 1; + } + } } } + + if (!enabled_geometry_face_random_color) { + face_color_array.resize(face_vertex_array.size()); + face_color_array.fill(debug_face_color); + } + + Array face_mesh_array; + face_mesh_array.resize(Mesh::ARRAY_MAX); + face_mesh_array[Mesh::ARRAY_VERTEX] = face_vertex_array; + face_mesh_array[Mesh::ARRAY_COLOR] = face_color_array; + + rs->mesh_add_surface_from_arrays(debug_mesh_rid, RS::PRIMITIVE_TRIANGLES, face_mesh_array, Array(), Dictionary(), RS::ARRAY_FLAG_USE_2D_VERTICES); + + if (enabled_edge_lines) { + Vector<Color> line_color_array; + line_color_array.resize(line_vertex_array.size()); + line_color_array.fill(debug_edge_color); + + Array line_mesh_array; + line_mesh_array.resize(Mesh::ARRAY_MAX); + line_mesh_array[Mesh::ARRAY_VERTEX] = line_vertex_array; + line_mesh_array[Mesh::ARRAY_COLOR] = line_color_array; + + rs->mesh_add_surface_from_arrays(debug_mesh_rid, RS::PRIMITIVE_LINES, line_mesh_array, Array(), Dictionary(), RS::ARRAY_FLAG_USE_2D_VERTICES); + } + + rs->canvas_item_add_mesh(debug_instance_rid, debug_mesh_rid, Transform2D()); + rs->canvas_item_set_visible(debug_instance_rid, is_visible_in_tree()); } #endif // DEBUG_ENABLED @@ -512,3 +626,19 @@ void NavigationRegion2D::_update_debug_baking_rect() { } } #endif // DEBUG_ENABLED + +#ifdef DEBUG_ENABLED +void NavigationRegion2D::_free_debug() { + RenderingServer *rs = RenderingServer::get_singleton(); + ERR_FAIL_NULL(rs); + if (debug_instance_rid.is_valid()) { + rs->canvas_item_clear(debug_instance_rid); + rs->free(debug_instance_rid); + debug_instance_rid = RID(); + } + if (debug_mesh_rid.is_valid()) { + rs->free(debug_mesh_rid); + debug_mesh_rid = RID(); + } +} +#endif // DEBUG_ENABLED diff --git a/scene/2d/navigation_region_2d.h b/scene/2d/navigation_region_2d.h index 5a86dd607d..52101cb93e 100644 --- a/scene/2d/navigation_region_2d.h +++ b/scene/2d/navigation_region_2d.h @@ -52,6 +52,12 @@ class NavigationRegion2D : public Node2D { #ifdef DEBUG_ENABLED private: + RID debug_mesh_rid; + RID debug_instance_rid; + + bool debug_mesh_dirty = true; + + void _free_debug(); void _update_debug_mesh(); void _update_debug_edge_connections_mesh(); void _update_debug_baking_rect(); diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp index 523d81e874..f7d672620d 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -225,91 +225,6 @@ int TileMap::get_rendering_quadrant_size() const { return rendering_quadrant_size; } -void TileMap::draw_tile(RID p_canvas_item, const Vector2 &p_position, const Ref<TileSet> p_tile_set, int p_atlas_source_id, const Vector2i &p_atlas_coords, int p_alternative_tile, int p_frame, Color p_modulation, const TileData *p_tile_data_override, real_t p_normalized_animation_offset) { - ERR_FAIL_COND(!p_tile_set.is_valid()); - ERR_FAIL_COND(!p_tile_set->has_source(p_atlas_source_id)); - ERR_FAIL_COND(!p_tile_set->get_source(p_atlas_source_id)->has_tile(p_atlas_coords)); - ERR_FAIL_COND(!p_tile_set->get_source(p_atlas_source_id)->has_alternative_tile(p_atlas_coords, p_alternative_tile)); - TileSetSource *source = *p_tile_set->get_source(p_atlas_source_id); - TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); - if (atlas_source) { - // Check for the frame. - if (p_frame >= 0) { - ERR_FAIL_INDEX(p_frame, atlas_source->get_tile_animation_frames_count(p_atlas_coords)); - } - - // Get the texture. - Ref<Texture2D> tex = atlas_source->get_runtime_texture(); - if (!tex.is_valid()) { - return; - } - - // Check if we are in the texture, return otherwise. - Vector2i grid_size = atlas_source->get_atlas_grid_size(); - if (p_atlas_coords.x >= grid_size.x || p_atlas_coords.y >= grid_size.y) { - return; - } - - // Get tile data. - const TileData *tile_data = p_tile_data_override ? p_tile_data_override : atlas_source->get_tile_data(p_atlas_coords, p_alternative_tile); - - // Get the tile modulation. - Color modulate = tile_data->get_modulate() * p_modulation; - - // Compute the offset. - Vector2 tile_offset = tile_data->get_texture_origin(); - - // Get destination rect. - Rect2 dest_rect; - dest_rect.size = atlas_source->get_runtime_tile_texture_region(p_atlas_coords).size; - dest_rect.size.x += FP_ADJUST; - dest_rect.size.y += FP_ADJUST; - - bool transpose = tile_data->get_transpose() ^ bool(p_alternative_tile & TileSetAtlasSource::TRANSFORM_TRANSPOSE); - if (transpose) { - dest_rect.position = (p_position - Vector2(dest_rect.size.y, dest_rect.size.x) / 2 - tile_offset); - } else { - dest_rect.position = (p_position - dest_rect.size / 2 - tile_offset); - } - - if (tile_data->get_flip_h() ^ bool(p_alternative_tile & TileSetAtlasSource::TRANSFORM_FLIP_H)) { - dest_rect.size.x = -dest_rect.size.x; - } - - if (tile_data->get_flip_v() ^ bool(p_alternative_tile & TileSetAtlasSource::TRANSFORM_FLIP_V)) { - dest_rect.size.y = -dest_rect.size.y; - } - - // Draw the tile. - if (p_frame >= 0) { - Rect2i source_rect = atlas_source->get_runtime_tile_texture_region(p_atlas_coords, p_frame); - tex->draw_rect_region(p_canvas_item, dest_rect, source_rect, modulate, transpose, p_tile_set->is_uv_clipping()); - } else if (atlas_source->get_tile_animation_frames_count(p_atlas_coords) == 1) { - Rect2i source_rect = atlas_source->get_runtime_tile_texture_region(p_atlas_coords, 0); - tex->draw_rect_region(p_canvas_item, dest_rect, source_rect, modulate, transpose, p_tile_set->is_uv_clipping()); - } else { - real_t speed = atlas_source->get_tile_animation_speed(p_atlas_coords); - real_t animation_duration = atlas_source->get_tile_animation_total_duration(p_atlas_coords) / speed; - real_t animation_offset = p_normalized_animation_offset * animation_duration; - // Accumulate durations unaffected by the speed to avoid accumulating floating point division errors. - // Aka do `sum(duration[i]) / speed` instead of `sum(duration[i] / speed)`. - real_t time_unscaled = 0.0; - for (int frame = 0; frame < atlas_source->get_tile_animation_frames_count(p_atlas_coords); frame++) { - real_t frame_duration_unscaled = atlas_source->get_tile_animation_frame_duration(p_atlas_coords, frame); - real_t slice_start = time_unscaled / speed; - real_t slice_end = (time_unscaled + frame_duration_unscaled) / speed; - RenderingServer::get_singleton()->canvas_item_add_animation_slice(p_canvas_item, animation_duration, slice_start, slice_end, animation_offset); - - Rect2i source_rect = atlas_source->get_runtime_tile_texture_region(p_atlas_coords, frame); - tex->draw_rect_region(p_canvas_item, dest_rect, source_rect, modulate, transpose, p_tile_set->is_uv_clipping()); - - time_unscaled += frame_duration_unscaled; - } - RenderingServer::get_singleton()->canvas_item_add_animation_slice(p_canvas_item, 1.0, 0.0, 1.0, 0.0); - } - } -} - void TileMap::set_tileset(const Ref<TileSet> &p_tileset) { if (p_tileset == tile_set) { return; diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h index 45604bfb8a..690102f730 100644 --- a/scene/2d/tile_map.h +++ b/scene/2d/tile_map.h @@ -63,8 +63,6 @@ private: // A compatibility enum to specify how is the data if formatted. mutable TileMapDataFormat format = TileMapDataFormat::TILE_MAP_DATA_FORMAT_3; - static constexpr float FP_ADJUST = 0.00001; - // Properties. Ref<TileSet> tile_set; int rendering_quadrant_size = 16; @@ -123,8 +121,6 @@ public: void set_rendering_quadrant_size(int p_size); int get_rendering_quadrant_size() const; - static void draw_tile(RID p_canvas_item, const Vector2 &p_position, const Ref<TileSet> p_tile_set, int p_atlas_source_id, const Vector2i &p_atlas_coords, int p_alternative_tile, int p_frame = -1, Color p_modulation = Color(1.0, 1.0, 1.0, 1.0), const TileData *p_tile_data_override = nullptr, real_t p_normalized_animation_offset = 0.0); - // Accessors. void set_tileset(const Ref<TileSet> &p_tileset); Ref<TileSet> get_tileset() const; diff --git a/scene/2d/tile_map_layer.cpp b/scene/2d/tile_map_layer.cpp index fe51171744..0ac236eaa7 100644 --- a/scene/2d/tile_map_layer.cpp +++ b/scene/2d/tile_map_layer.cpp @@ -340,7 +340,7 @@ void TileMapLayer::_rendering_update(bool p_force_cleanup) { } // Drawing the tile in the canvas item. - TileMap::draw_tile(ci, local_tile_pos - rendering_quadrant->canvas_items_position, tile_set, cell_data.cell.source_id, cell_data.cell.get_atlas_coords(), cell_data.cell.alternative_tile, -1, get_self_modulate(), tile_data, random_animation_offset); + draw_tile(ci, local_tile_pos - rendering_quadrant->canvas_items_position, tile_set, cell_data.cell.source_id, cell_data.cell.get_atlas_coords(), cell_data.cell.alternative_tile, -1, get_self_modulate(), tile_data, random_animation_offset); } // Reset physics interpolation for any recreated canvas items. @@ -1696,18 +1696,18 @@ void TileMapLayer::_notification(int p_what) { _internal_update(true); } break; - case TileMap::NOTIFICATION_ENTER_CANVAS: { + case NOTIFICATION_ENTER_CANVAS: { dirty.flags[DIRTY_FLAGS_LAYER_IN_CANVAS] = true; _queue_internal_update(); } break; - case TileMap::NOTIFICATION_EXIT_CANVAS: { + case NOTIFICATION_EXIT_CANVAS: { dirty.flags[DIRTY_FLAGS_LAYER_IN_CANVAS] = true; // Update immediately on exiting, and force cleanup. _internal_update(true); } break; - case TileMap::NOTIFICATION_VISIBILITY_CHANGED: { + case NOTIFICATION_VISIBILITY_CHANGED: { dirty.flags[DIRTY_FLAGS_LAYER_VISIBILITY] = true; _queue_internal_update(); } break; @@ -2162,6 +2162,91 @@ TileMapCell TileMapLayer::get_cell(const Vector2i &p_coords) const { } } +void TileMapLayer::draw_tile(RID p_canvas_item, const Vector2 &p_position, const Ref<TileSet> p_tile_set, int p_atlas_source_id, const Vector2i &p_atlas_coords, int p_alternative_tile, int p_frame, Color p_modulation, const TileData *p_tile_data_override, real_t p_normalized_animation_offset) { + ERR_FAIL_COND(p_tile_set.is_null()); + ERR_FAIL_COND(!p_tile_set->has_source(p_atlas_source_id)); + ERR_FAIL_COND(!p_tile_set->get_source(p_atlas_source_id)->has_tile(p_atlas_coords)); + ERR_FAIL_COND(!p_tile_set->get_source(p_atlas_source_id)->has_alternative_tile(p_atlas_coords, p_alternative_tile)); + TileSetSource *source = *p_tile_set->get_source(p_atlas_source_id); + TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); + if (atlas_source) { + // Check for the frame. + if (p_frame >= 0) { + ERR_FAIL_INDEX(p_frame, atlas_source->get_tile_animation_frames_count(p_atlas_coords)); + } + + // Get the texture. + Ref<Texture2D> tex = atlas_source->get_runtime_texture(); + if (tex.is_null()) { + return; + } + + // Check if we are in the texture, return otherwise. + Vector2i grid_size = atlas_source->get_atlas_grid_size(); + if (p_atlas_coords.x >= grid_size.x || p_atlas_coords.y >= grid_size.y) { + return; + } + + // Get tile data. + const TileData *tile_data = p_tile_data_override ? p_tile_data_override : atlas_source->get_tile_data(p_atlas_coords, p_alternative_tile); + + // Get the tile modulation. + Color modulate = tile_data->get_modulate() * p_modulation; + + // Compute the offset. + Vector2 tile_offset = tile_data->get_texture_origin(); + + // Get destination rect. + Rect2 dest_rect; + dest_rect.size = atlas_source->get_runtime_tile_texture_region(p_atlas_coords).size; + dest_rect.size.x += FP_ADJUST; + dest_rect.size.y += FP_ADJUST; + + bool transpose = tile_data->get_transpose() ^ bool(p_alternative_tile & TileSetAtlasSource::TRANSFORM_TRANSPOSE); + if (transpose) { + dest_rect.position = (p_position - Vector2(dest_rect.size.y, dest_rect.size.x) / 2 - tile_offset); + } else { + dest_rect.position = (p_position - dest_rect.size / 2 - tile_offset); + } + + if (tile_data->get_flip_h() ^ bool(p_alternative_tile & TileSetAtlasSource::TRANSFORM_FLIP_H)) { + dest_rect.size.x = -dest_rect.size.x; + } + + if (tile_data->get_flip_v() ^ bool(p_alternative_tile & TileSetAtlasSource::TRANSFORM_FLIP_V)) { + dest_rect.size.y = -dest_rect.size.y; + } + + // Draw the tile. + if (p_frame >= 0) { + Rect2i source_rect = atlas_source->get_runtime_tile_texture_region(p_atlas_coords, p_frame); + tex->draw_rect_region(p_canvas_item, dest_rect, source_rect, modulate, transpose, p_tile_set->is_uv_clipping()); + } else if (atlas_source->get_tile_animation_frames_count(p_atlas_coords) == 1) { + Rect2i source_rect = atlas_source->get_runtime_tile_texture_region(p_atlas_coords, 0); + tex->draw_rect_region(p_canvas_item, dest_rect, source_rect, modulate, transpose, p_tile_set->is_uv_clipping()); + } else { + real_t speed = atlas_source->get_tile_animation_speed(p_atlas_coords); + real_t animation_duration = atlas_source->get_tile_animation_total_duration(p_atlas_coords) / speed; + real_t animation_offset = p_normalized_animation_offset * animation_duration; + // Accumulate durations unaffected by the speed to avoid accumulating floating point division errors. + // Aka do `sum(duration[i]) / speed` instead of `sum(duration[i] / speed)`. + real_t time_unscaled = 0.0; + for (int frame = 0; frame < atlas_source->get_tile_animation_frames_count(p_atlas_coords); frame++) { + real_t frame_duration_unscaled = atlas_source->get_tile_animation_frame_duration(p_atlas_coords, frame); + real_t slice_start = time_unscaled / speed; + real_t slice_end = (time_unscaled + frame_duration_unscaled) / speed; + RenderingServer::get_singleton()->canvas_item_add_animation_slice(p_canvas_item, animation_duration, slice_start, slice_end, animation_offset); + + Rect2i source_rect = atlas_source->get_runtime_tile_texture_region(p_atlas_coords, frame); + tex->draw_rect_region(p_canvas_item, dest_rect, source_rect, modulate, transpose, p_tile_set->is_uv_clipping()); + + time_unscaled += frame_duration_unscaled; + } + RenderingServer::get_singleton()->canvas_item_add_animation_slice(p_canvas_item, 1.0, 0.0, 1.0, 0.0); + } + } +} + void TileMapLayer::set_cell(const Vector2i &p_coords, int p_source_id, const Vector2i &p_atlas_coords, int p_alternative_tile) { // Set the current cell tile (using integer position). Vector2i pk(p_coords); @@ -2212,7 +2297,7 @@ void TileMapLayer::erase_cell(const Vector2i &p_coords) { } void TileMapLayer::fix_invalid_tiles() { - ERR_FAIL_COND_MSG(tile_set.is_null(), "Cannot call fix_invalid_tiles() on a TileMap without a valid TileSet."); + ERR_FAIL_COND_MSG(tile_set.is_null(), "Cannot call fix_invalid_tiles() on a TileMapLayer without a valid TileSet."); RBSet<Vector2i> coords; for (const KeyValue<Vector2i, CellData> &E : tile_map_layer_data) { diff --git a/scene/2d/tile_map_layer.h b/scene/2d/tile_map_layer.h index 5861433c8a..57c83d7c4c 100644 --- a/scene/2d/tile_map_layer.h +++ b/scene/2d/tile_map_layer.h @@ -269,6 +269,8 @@ public: }; private: + static constexpr float FP_ADJUST = 0.00001; + // Properties. HashMap<Vector2i, CellData> tile_map_layer_data; @@ -407,6 +409,8 @@ public: // Not exposed to users. TileMapCell get_cell(const Vector2i &p_coords) const; + static void draw_tile(RID p_canvas_item, const Vector2 &p_position, const Ref<TileSet> p_tile_set, int p_atlas_source_id, const Vector2i &p_atlas_coords, int p_alternative_tile, int p_frame = -1, Color p_modulation = Color(1.0, 1.0, 1.0, 1.0), const TileData *p_tile_data_override = nullptr, real_t p_normalized_animation_offset = 0.0); + ////////////// Exposed functions ////////////// // --- Cells manipulation --- diff --git a/scene/3d/navigation_region_3d.cpp b/scene/3d/navigation_region_3d.cpp index 856c52b5d5..40e04f0fb4 100644 --- a/scene/3d/navigation_region_3d.cpp +++ b/scene/3d/navigation_region_3d.cpp @@ -474,7 +474,7 @@ void NavigationRegion3D::_update_debug_mesh() { return; } - if (!NavigationServer3D::get_singleton()->get_debug_navigation_enabled()) { + if (!NavigationServer3D::get_singleton()->get_debug_enabled() || !NavigationServer3D::get_singleton()->get_debug_navigation_enabled()) { if (debug_instance.is_valid()) { RS::get_singleton()->instance_set_visible(debug_instance, false); } @@ -640,7 +640,7 @@ void NavigationRegion3D::_update_debug_mesh() { #ifdef DEBUG_ENABLED void NavigationRegion3D::_update_debug_edge_connections_mesh() { - if (!NavigationServer3D::get_singleton()->get_debug_navigation_enabled()) { + if (!NavigationServer3D::get_singleton()->get_debug_enabled() || !NavigationServer3D::get_singleton()->get_debug_navigation_enabled()) { if (debug_edge_connections_instance.is_valid()) { RS::get_singleton()->instance_set_visible(debug_edge_connections_instance, false); } diff --git a/scene/animation/animation_mixer.cpp b/scene/animation/animation_mixer.cpp index d4c6ca3ea0..e600de6b8b 100644 --- a/scene/animation/animation_mixer.cpp +++ b/scene/animation/animation_mixer.cpp @@ -501,6 +501,7 @@ AnimationMixer::AnimationCallbackModeMethod AnimationMixer::get_callback_mode_me void AnimationMixer::set_callback_mode_discrete(AnimationCallbackModeDiscrete p_mode) { callback_mode_discrete = p_mode; + _clear_caches(); emit_signal(SNAME("mixer_updated")); } @@ -688,7 +689,7 @@ bool AnimationMixer::_update_caches() { track_value->init_value = anim->track_get_key_value(i, 0); track_value->init_value.zero(); - track_value->init_use_continuous = callback_mode_discrete == ANIMATION_CALLBACK_MODE_DISCRETE_FORCE_CONTINUOUS; + track_value->is_init = false; // Can't interpolate them, need to convert. track_value->is_variant_interpolatable = Animation::is_variant_interpolatable(track_value->init_value); @@ -698,7 +699,6 @@ bool AnimationMixer::_update_caches() { int rt = reset_anim->find_track(path, track_src_type); if (rt >= 0) { if (track_src_type == Animation::TYPE_VALUE) { - track_value->init_use_continuous = track_value->init_use_continuous || (reset_anim->value_track_get_update_mode(rt) != Animation::UPDATE_DISCRETE); // Take precedence Force Continuous. if (reset_anim->track_get_key_count(rt) > 0) { track_value->init_value = reset_anim->track_get_key_value(rt, 0); } @@ -1006,7 +1006,7 @@ void AnimationMixer::_blend_init() { TrackCacheValue *t = static_cast<TrackCacheValue *>(track); t->value = Animation::cast_to_blendwise(t->init_value); t->element_size = t->init_value.is_string() ? (real_t)(t->init_value.operator String()).length() : 0; - t->use_continuous = t->init_use_continuous; + t->use_continuous = false; t->use_discrete = false; } break; case Animation::TYPE_AUDIO: { @@ -1462,12 +1462,12 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) { t->value = Animation::blend_variant(t->value, value, blend); } } else { - t->use_discrete = true; if (seeked) { int idx = a->track_find_key(i, time, is_external_seeking ? Animation::FIND_MODE_NEAREST : Animation::FIND_MODE_EXACT, true); if (idx < 0) { continue; } + t->use_discrete = true; Variant value = a->track_get_key_value(i, idx); value = post_process_key_value(a, i, value, t->object_id); Object *t_obj = ObjectDB::get_instance(t->object_id); @@ -1478,6 +1478,7 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) { List<int> indices; a->track_get_key_indices_in_range(i, time, delta, &indices, looped_flag); for (int &F : indices) { + t->use_discrete = true; Variant value = a->track_get_key_value(i, F); value = post_process_key_value(a, i, value, t->object_id); Object *t_obj = ObjectDB::get_instance(t->object_id); @@ -1682,7 +1683,8 @@ void AnimationMixer::_blend_apply() { // Finally, set the tracks. for (const KeyValue<Animation::TypeHash, TrackCache *> &K : track_cache) { TrackCache *track = K.value; - if (!deterministic && Math::is_zero_approx(track->total_weight)) { + bool is_zero_amount = Math::is_zero_approx(track->total_weight); + if (!deterministic && is_zero_amount) { continue; } switch (track->type) { @@ -1742,10 +1744,24 @@ void AnimationMixer::_blend_apply() { case Animation::TYPE_VALUE: { TrackCacheValue *t = static_cast<TrackCacheValue *>(track); - if (!t->is_variant_interpolatable || !t->use_continuous || (callback_mode_discrete == ANIMATION_CALLBACK_MODE_DISCRETE_DOMINANT && t->use_discrete)) { + if (t->use_discrete && !t->use_continuous) { + t->is_init = true; // If only disctere value is applied, no more RESET. + } + + if ((t->is_init && (is_zero_amount || !t->use_continuous)) || + (callback_mode_discrete != ANIMATION_CALLBACK_MODE_DISCRETE_FORCE_CONTINUOUS && + !is_zero_amount && + callback_mode_discrete == ANIMATION_CALLBACK_MODE_DISCRETE_DOMINANT && + t->use_discrete)) { break; // Don't overwrite the value set by UPDATE_DISCRETE. } + if (callback_mode_discrete == ANIMATION_CALLBACK_MODE_DISCRETE_FORCE_CONTINUOUS) { + t->is_init = false; // Always update in Force Continuous. + } else { + t->is_init = !t->use_continuous; // If there is no Continuous in non-Force Continuous type, it means RESET. + } + // Trim unused elements if init array/string is not blended. if (t->value.is_array()) { int actual_blended_size = (int)Math::round(Math::abs(t->element_size.operator real_t())); diff --git a/scene/animation/animation_mixer.h b/scene/animation/animation_mixer.h index b7898cffc9..089a210193 100644 --- a/scene/animation/animation_mixer.h +++ b/scene/animation/animation_mixer.h @@ -126,7 +126,7 @@ protected: /* ---- General settings for animation ---- */ AnimationCallbackModeProcess callback_mode_process = ANIMATION_CALLBACK_MODE_PROCESS_IDLE; AnimationCallbackModeMethod callback_mode_method = ANIMATION_CALLBACK_MODE_METHOD_DEFERRED; - AnimationCallbackModeDiscrete callback_mode_discrete = ANIMATION_CALLBACK_MODE_DISCRETE_DOMINANT; + AnimationCallbackModeDiscrete callback_mode_discrete = ANIMATION_CALLBACK_MODE_DISCRETE_RECESSIVE; int audio_max_polyphony = 32; NodePath root_node; @@ -224,7 +224,7 @@ protected: Vector<StringName> subpath; // TODO: There are many boolean, can be packed into one integer. - bool init_use_continuous = false; + bool is_init = false; bool use_continuous = false; bool use_discrete = false; bool is_using_angle = false; @@ -237,7 +237,7 @@ protected: init_value(p_other.init_value), value(p_other.value), subpath(p_other.subpath), - init_use_continuous(p_other.init_use_continuous), + is_init(p_other.is_init), use_continuous(p_other.use_continuous), use_discrete(p_other.use_discrete), is_using_angle(p_other.is_using_angle), diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp index 435776843c..0c24d79ad7 100644 --- a/scene/animation/animation_player.cpp +++ b/scene/animation/animation_player.cpp @@ -447,10 +447,10 @@ void AnimationPlayer::_play(const StringName &p_name, double p_custom_blend, flo } else { if (p_from_end && c.current.pos == 0) { // Animation reset but played backwards, set position to the end. - c.current.pos = c.current.from->animation->get_length(); + seek(c.current.from->animation->get_length(), true, true); } else if (!p_from_end && c.current.pos == c.current.from->animation->get_length()) { // Animation resumed but already ended, set position to the beginning. - c.current.pos = 0; + seek(0, true, true); } else if (playing) { return; } diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp index 8131fe7aaa..c843bb8c44 100644 --- a/scene/gui/code_edit.cpp +++ b/scene/gui/code_edit.cpp @@ -3050,7 +3050,7 @@ void CodeEdit::_update_delimiter_cache(int p_from_line, int p_to_line) { } int CodeEdit::_is_in_delimiter(int p_line, int p_column, DelimiterType p_type) const { - if (delimiters.size() == 0) { + if (delimiters.size() == 0 || p_line >= delimiter_cache.size()) { return -1; } ERR_FAIL_INDEX_V(p_line, get_line_count(), 0); diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index 1c175f9f95..0d5c69b207 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -211,17 +211,17 @@ void Control::get_argument_options(const StringName &p_function, int p_idx, List const String pf = p_function; Theme::DataType type = Theme::DATA_TYPE_MAX; - if (pf == "add_theme_color_override" || pf == "has_theme_color" || pf == "has_theme_color_override" || pf == "get_theme_color") { + if (pf == "add_theme_color_override" || pf == "has_theme_color" || pf == "has_theme_color_override" || pf == "get_theme_color" || pf == "remove_theme_color_override") { type = Theme::DATA_TYPE_COLOR; - } else if (pf == "add_theme_constant_override" || pf == "has_theme_constant" || pf == "has_theme_constant_override" || pf == "get_theme_constant") { + } else if (pf == "add_theme_constant_override" || pf == "has_theme_constant" || pf == "has_theme_constant_override" || pf == "get_theme_constant" || pf == "remove_theme_constant_override") { type = Theme::DATA_TYPE_CONSTANT; - } else if (pf == "add_theme_font_override" || pf == "has_theme_font" || pf == "has_theme_font_override" || pf == "get_theme_font") { + } else if (pf == "add_theme_font_override" || pf == "has_theme_font" || pf == "has_theme_font_override" || pf == "get_theme_font" || pf == "remove_theme_font_override") { type = Theme::DATA_TYPE_FONT; - } else if (pf == "add_theme_font_size_override" || pf == "has_theme_font_size" || pf == "has_theme_font_size_override" || pf == "get_theme_font_size") { + } else if (pf == "add_theme_font_size_override" || pf == "has_theme_font_size" || pf == "has_theme_font_size_override" || pf == "get_theme_font_size" || pf == "remove_theme_font_size_override") { type = Theme::DATA_TYPE_FONT_SIZE; - } else if (pf == "add_theme_icon_override" || pf == "has_theme_icon" || pf == "has_theme_icon_override" || pf == "get_theme_icon") { + } else if (pf == "add_theme_icon_override" || pf == "has_theme_icon" || pf == "has_theme_icon_override" || pf == "get_theme_icon" || pf == "remove_theme_icon_override") { type = Theme::DATA_TYPE_ICON; - } else if (pf == "add_theme_style_override" || pf == "has_theme_style" || pf == "has_theme_style_override" || pf == "get_theme_style") { + } else if (pf == "add_theme_stylebox_override" || pf == "has_theme_stylebox" || pf == "has_theme_stylebox_override" || pf == "get_theme_stylebox" || pf == "remove_theme_stylebox_override") { type = Theme::DATA_TYPE_STYLEBOX; } diff --git a/scene/gui/dialogs.cpp b/scene/gui/dialogs.cpp index 58961d370c..3d8be38fbd 100644 --- a/scene/gui/dialogs.cpp +++ b/scene/gui/dialogs.cpp @@ -47,7 +47,7 @@ void AcceptDialog::_input_from_window(const Ref<InputEvent> &p_event) { } void AcceptDialog::_parent_focused() { - if (!is_exclusive() && get_flag(FLAG_POPUP)) { + if (popped_up && !is_exclusive() && get_flag(FLAG_POPUP)) { _cancel_pressed(); } } @@ -71,6 +71,7 @@ void AcceptDialog::_notification(int p_what) { parent_visible->connect(SceneStringName(focus_entered), callable_mp(this, &AcceptDialog::_parent_focused)); } } else { + popped_up = false; if (parent_visible) { parent_visible->disconnect(SceneStringName(focus_entered), callable_mp(this, &AcceptDialog::_parent_focused)); parent_visible = nullptr; @@ -78,6 +79,14 @@ void AcceptDialog::_notification(int p_what) { } } break; + case NOTIFICATION_WM_WINDOW_FOCUS_IN: { + if (!is_in_edited_scene_root()) { + if (has_focus()) { + popped_up = true; + } + } + } break; + case NOTIFICATION_THEME_CHANGED: { bg_panel->add_theme_style_override("panel", theme_cache.panel_style); @@ -114,8 +123,14 @@ void AcceptDialog::_text_submitted(const String &p_text) { _ok_pressed(); } +void AcceptDialog::_post_popup() { + Window::_post_popup(); + popped_up = true; +} + void AcceptDialog::_ok_pressed() { if (hide_on_ok) { + popped_up = false; set_visible(false); } ok_pressed(); @@ -124,6 +139,7 @@ void AcceptDialog::_ok_pressed() { } void AcceptDialog::_cancel_pressed() { + popped_up = false; Window *parent_window = parent_visible; if (parent_visible) { parent_visible->disconnect(SceneStringName(focus_entered), callable_mp(this, &AcceptDialog::_parent_focused)); diff --git a/scene/gui/dialogs.h b/scene/gui/dialogs.h index 12b48c903a..404237bfd8 100644 --- a/scene/gui/dialogs.h +++ b/scene/gui/dialogs.h @@ -51,6 +51,7 @@ class AcceptDialog : public Window { HBoxContainer *buttons_hbox = nullptr; Button *ok_button = nullptr; + bool popped_up = false; bool hide_on_ok = true; bool close_on_escape = true; @@ -72,6 +73,7 @@ class AcceptDialog : public Window { protected: virtual Size2 _get_contents_minimum_size() const override; virtual void _input_from_window(const Ref<InputEvent> &p_event) override; + virtual void _post_popup() override; void _notification(int p_what); static void _bind_methods(); diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp index c9f3fc1dfe..6c2a61d255 100644 --- a/scene/gui/graph_edit.cpp +++ b/scene/gui/graph_edit.cpp @@ -1299,18 +1299,26 @@ List<Ref<GraphEdit::Connection>> GraphEdit::get_connections_intersecting_with_re return intersecting_connections; } -void GraphEdit::_draw_minimap_connection_line(CanvasItem *p_where, const Vector2 &p_from, const Vector2 &p_to, const Color &p_from_color, const Color &p_to_color) { - const Vector<Vector2> &points = get_connection_line(p_from, p_to); +void GraphEdit::_draw_minimap_connection_line(const Vector2 &p_from_graph_position, const Vector2 &p_to_graph_position, const Color &p_from_color, const Color &p_to_color) { + Vector<Vector2> points = get_connection_line(p_from_graph_position, p_to_graph_position); + ERR_FAIL_COND_MSG(points.size() < 2, "\"_get_connection_line()\" returned an invalid line."); + // Convert to minimap points. + for (Vector2 &point : points) { + point = minimap->_convert_from_graph_position(point) + minimap->minimap_offset; + } + + // Setup polyline colors. LocalVector<Color> colors; colors.reserve(points.size()); - - float length_inv = 1.0 / (p_from).distance_to(p_to); + const Vector2 &from = points[0]; + const Vector2 &to = points[points.size() - 1]; + float length_inv = 1.0 / (from).distance_to(to); for (const Vector2 &point : points) { - float normalized_curve_position = (p_from).distance_to(point) * length_inv; + float normalized_curve_position = from.distance_to(point) * length_inv; colors.push_back(p_from_color.lerp(p_to_color, normalized_curve_position)); } - p_where->draw_polyline_colors(points, colors, 0.5, lines_antialiased); + minimap->draw_polyline_colors(points, colors, 0.5, lines_antialiased); } void GraphEdit::_update_connections() { @@ -1565,8 +1573,8 @@ void GraphEdit::_minimap_draw() { // Draw node connections. for (const Ref<Connection> &c : connections) { - Vector2 from_position = minimap->_convert_from_graph_position(c->_cache.from_pos * zoom - graph_offset) + minimap_offset; - Vector2 to_position = minimap->_convert_from_graph_position(c->_cache.to_pos * zoom - graph_offset) + minimap_offset; + Vector2 from_graph_position = c->_cache.from_pos * zoom - graph_offset; + Vector2 to_graph_position = c->_cache.to_pos * zoom - graph_offset; Color from_color = c->_cache.from_color; Color to_color = c->_cache.to_color; @@ -1574,7 +1582,8 @@ void GraphEdit::_minimap_draw() { from_color = from_color.lerp(theme_cache.activity_color, c->activity); to_color = to_color.lerp(theme_cache.activity_color, c->activity); } - _draw_minimap_connection_line(minimap, from_position, to_position, from_color, to_color); + + _draw_minimap_connection_line(from_graph_position, to_graph_position, from_color, to_color); } // Draw the "camera" viewport. diff --git a/scene/gui/graph_edit.h b/scene/gui/graph_edit.h index eeda9ae200..20c98c462c 100644 --- a/scene/gui/graph_edit.h +++ b/scene/gui/graph_edit.h @@ -328,7 +328,7 @@ private: void _top_connection_layer_input(const Ref<InputEvent> &p_ev); float _get_shader_line_width(); - void _draw_minimap_connection_line(CanvasItem *p_where, const Vector2 &p_from, const Vector2 &p_to, const Color &p_color, const Color &p_to_color); + void _draw_minimap_connection_line(const Vector2 &p_from_graph_position, const Vector2 &p_to_graph_position, const Color &p_from_color, const Color &p_to_color); void _invalidate_connection_line_cache(); void _update_top_connection_layer(); void _update_connections(); diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp index dbf1137bd6..bdd0102b63 100644 --- a/scene/gui/popup_menu.cpp +++ b/scene/gui/popup_menu.cpp @@ -1014,9 +1014,6 @@ void PopupMenu::_notification(int p_what) { float pm_delay = pm->get_submenu_popup_delay(); set_submenu_popup_delay(pm_delay); } - if (!is_embedded()) { - set_flag(FLAG_NO_FOCUS, true); - } if (system_menu_id != NativeMenu::INVALID_MENU_ID) { bind_global_menu(); } @@ -2829,6 +2826,8 @@ void PopupMenu::popup(const Rect2i &p_bounds) { if (native) { NativeMenu::get_singleton()->popup(global_menu, (p_bounds != Rect2i()) ? p_bounds.position : get_position()); } else { + set_flag(FLAG_NO_FOCUS, !is_embedded()); + moved = Vector2(); popup_time_msec = OS::get_singleton()->get_ticks_msec(); if (!is_embedded()) { @@ -2856,6 +2855,8 @@ void PopupMenu::set_visible(bool p_visible) { NativeMenu::get_singleton()->popup(global_menu, get_position()); } } else { + set_flag(FLAG_NO_FOCUS, !is_embedded()); + Popup::set_visible(p_visible); } } diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index 5122b0a155..f6942ca206 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -1607,8 +1607,34 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V if (p_meta) { int64_t glyph_idx = TS->shaped_text_hit_test_grapheme(rid, p_click.x - rect.position.x); if (glyph_idx >= 0) { + float baseline_y = rect.position.y + TS->shaped_text_get_ascent(rid); const Glyph *glyphs = TS->shaped_text_get_glyphs(rid); - char_pos = glyphs[glyph_idx].start; + if (glyphs[glyph_idx].flags & TextServer::GRAPHEME_IS_EMBEDDED_OBJECT) { + // Emebedded object. + for (int i = 0; i < objects.size(); i++) { + if (TS->shaped_text_get_object_glyph(rid, objects[i]) == glyph_idx) { + Rect2 obj_rect = TS->shaped_text_get_object_rect(rid, objects[i]); + obj_rect.position.y += baseline_y; + if (p_click.y >= obj_rect.position.y && p_click.y <= obj_rect.position.y + obj_rect.size.y) { + char_pos = glyphs[glyph_idx].start; + } + break; + } + } + } else if (glyphs[glyph_idx].font_rid != RID()) { + // Normal glyph. + float fa = TS->font_get_ascent(glyphs[glyph_idx].font_rid, glyphs[glyph_idx].font_size); + float fd = TS->font_get_descent(glyphs[glyph_idx].font_rid, glyphs[glyph_idx].font_size); + if (p_click.y >= baseline_y - fa && p_click.y <= baseline_y + fd) { + char_pos = glyphs[glyph_idx].start; + } + } else if (!(glyphs[glyph_idx].flags & TextServer::GRAPHEME_IS_VIRTUAL)) { + // Hex code box. + Vector2 gl_size = TS->get_hex_code_box_size(glyphs[glyph_idx].font_size, glyphs[glyph_idx].index); + if (p_click.y >= baseline_y - gl_size.y * 0.9 && p_click.y <= baseline_y + gl_size.y * 0.2) { + char_pos = glyphs[glyph_idx].start; + } + } } } else { char_pos = TS->shaped_text_hit_test_position(rid, p_click.x - rect.position.x); diff --git a/scene/gui/subviewport_container.cpp b/scene/gui/subviewport_container.cpp index f6cfe6ab18..c715aceb0b 100644 --- a/scene/gui/subviewport_container.cpp +++ b/scene/gui/subviewport_container.cpp @@ -287,7 +287,7 @@ void SubViewportContainer::_bind_methods() { ClassDB::bind_method(D_METHOD("get_stretch_shrink"), &SubViewportContainer::get_stretch_shrink); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "stretch"), "set_stretch", "is_stretch_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "stretch_shrink"), "set_stretch_shrink", "get_stretch_shrink"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "stretch_shrink", PROPERTY_HINT_RANGE, "1,32,1,or_greater"), "set_stretch_shrink", "get_stretch_shrink"); GDVIRTUAL_BIND(_propagate_input_event, "event"); } diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 69b84da23d..1dd00fab4d 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -1003,23 +1003,12 @@ void TextEdit::_notification(int p_what) { } } - if (str.length() == 0) { - // Draw line background if empty as we won't loop at all. - if (caret_line_wrap_index_map.has(line) && caret_line_wrap_index_map[line].has(line_wrap_index) && highlight_current_line) { - if (rtl) { - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(size.width - ofs_x - xmargin_end, ofs_y, xmargin_end, row_height), theme_cache.current_line_color); - } else { - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(ofs_x, ofs_y, xmargin_end, row_height), theme_cache.current_line_color); - } - } - } else { - // If it has text, then draw current line marker in the margin, as line number etc will draw over it, draw the rest of line marker later. - if (caret_line_wrap_index_map.has(line) && caret_line_wrap_index_map[line].has(line_wrap_index) && highlight_current_line) { - if (rtl) { - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(size.width - ofs_x - xmargin_end, ofs_y, xmargin_end, row_height), theme_cache.current_line_color); - } else { - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(ofs_x, ofs_y, xmargin_end, row_height), theme_cache.current_line_color); - } + // Draw current line highlight. + if (highlight_current_line && caret_line_wrap_index_map.has(line) && caret_line_wrap_index_map[line].has(line_wrap_index)) { + if (rtl) { + RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(size.width - ofs_x - xmargin_end, ofs_y, xmargin_end, row_height), theme_cache.current_line_color); + } else { + RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(ofs_x, ofs_y, xmargin_end, row_height), theme_cache.current_line_color); } } @@ -4248,8 +4237,11 @@ String TextEdit::get_word_at_pos(const Vector2 &p_pos) const { } Point2i TextEdit::get_line_column_at_pos(const Point2i &p_pos, bool p_allow_out_of_bounds) const { - float rows = p_pos.y; - rows -= theme_cache.style_normal->get_margin(SIDE_TOP); + float rows = p_pos.y - theme_cache.style_normal->get_margin(SIDE_TOP); + if (!editable) { + rows -= theme_cache.style_readonly->get_offset().y / 2; + rows += theme_cache.style_normal->get_offset().y / 2; + } rows /= get_line_height(); rows += _get_v_scroll_offset(); int first_vis_line = get_first_visible_line(); @@ -4280,6 +4272,10 @@ Point2i TextEdit::get_line_column_at_pos(const Point2i &p_pos, bool p_allow_out_ int col = 0; int colx = p_pos.x - (theme_cache.style_normal->get_margin(SIDE_LEFT) + gutters_width + gutter_padding); colx += first_visible_col; + if (!editable) { + colx -= theme_cache.style_readonly->get_offset().x / 2; + colx += theme_cache.style_normal->get_offset().x / 2; + } col = _get_char_pos_for_line(colx, row, wrap_index); if (get_line_wrapping_mode() != LineWrappingMode::LINE_WRAPPING_NONE && wrap_index < get_line_wrap_count(row)) { // Move back one if we are at the end of the row. diff --git a/scene/gui/texture_button.cpp b/scene/gui/texture_button.cpp index df90257e03..c267ff93c6 100644 --- a/scene/gui/texture_button.cpp +++ b/scene/gui/texture_button.cpp @@ -178,13 +178,14 @@ void TextureButton::_notification(int p_what) { texdraw = focused; } - if (texdraw.is_valid()) { - size = texdraw->get_size(); - _texture_region = Rect2(Point2(), texdraw->get_size()); + if (texdraw.is_valid() || click_mask.is_valid()) { + const Size2 texdraw_size = texdraw.is_valid() ? texdraw->get_size() : Size2(click_mask->get_size()); + + size = texdraw_size; + _texture_region = Rect2(Point2(), texdraw_size); _tile = false; switch (stretch_mode) { case STRETCH_KEEP: - size = texdraw->get_size(); break; case STRETCH_SCALE: size = get_size(); @@ -194,18 +195,17 @@ void TextureButton::_notification(int p_what) { _tile = true; break; case STRETCH_KEEP_CENTERED: - ofs = (get_size() - texdraw->get_size()) / 2; - size = texdraw->get_size(); + ofs = (get_size() - texdraw_size) / 2; break; case STRETCH_KEEP_ASPECT_CENTERED: case STRETCH_KEEP_ASPECT: { Size2 _size = get_size(); - float tex_width = texdraw->get_width() * _size.height / texdraw->get_height(); + float tex_width = texdraw_size.width * _size.height / texdraw_size.height; float tex_height = _size.height; if (tex_width > _size.width) { tex_width = _size.width; - tex_height = texdraw->get_height() * tex_width / texdraw->get_width(); + tex_height = texdraw_size.height * tex_width / texdraw_size.width; } if (stretch_mode == STRETCH_KEEP_ASPECT_CENTERED) { @@ -217,10 +217,9 @@ void TextureButton::_notification(int p_what) { } break; case STRETCH_KEEP_ASPECT_COVERED: { size = get_size(); - Size2 tex_size = texdraw->get_size(); - Size2 scale_size(size.width / tex_size.width, size.height / tex_size.height); + Size2 scale_size = size / texdraw_size; float scale = scale_size.width > scale_size.height ? scale_size.width : scale_size.height; - Size2 scaled_tex_size = tex_size * scale; + Size2 scaled_tex_size = texdraw_size * scale; Point2 ofs2 = ((scaled_tex_size - size) / scale).abs() / 2.0f; _texture_region = Rect2(ofs2, size / scale); } break; @@ -233,10 +232,12 @@ void TextureButton::_notification(int p_what) { if (draw_focus_only) { // Do nothing, we only needed to calculate the rectangle. - } else if (_tile) { - draw_texture_rect(texdraw, Rect2(ofs, size), _tile); - } else { - draw_texture_rect_region(texdraw, Rect2(ofs, size), _texture_region); + } else if (texdraw.is_valid()) { + if (_tile) { + draw_texture_rect(texdraw, Rect2(ofs, size), _tile); + } else { + draw_texture_rect_region(texdraw, Rect2(ofs, size), _texture_region); + } } } else { _position_rect = Rect2(); diff --git a/scene/main/canvas_item.cpp b/scene/main/canvas_item.cpp index 7ed1f130c8..c0386b056f 100644 --- a/scene/main/canvas_item.cpp +++ b/scene/main/canvas_item.cpp @@ -34,6 +34,7 @@ #include "scene/2d/canvas_group.h" #include "scene/main/canvas_layer.h" #include "scene/main/window.h" +#include "scene/resources/atlas_texture.h" #include "scene/resources/canvas_item_material.h" #include "scene/resources/font.h" #include "scene/resources/multimesh.h" @@ -850,18 +851,28 @@ void CanvasItem::draw_polygon(const Vector<Point2> &p_points, const Vector<Color ERR_THREAD_GUARD; ERR_DRAW_GUARD; - RID rid = p_texture.is_valid() ? p_texture->get_rid() : RID(); + const Ref<AtlasTexture> atlas = p_texture; + if (atlas.is_valid() && atlas->get_atlas().is_valid()) { + const Ref<Texture2D> &texture = atlas->get_atlas(); + const Vector2 atlas_size = texture->get_size(); + + const Vector2 remap_min = atlas->get_region().position / atlas_size; + const Vector2 remap_max = atlas->get_region().get_end() / atlas_size; - RenderingServer::get_singleton()->canvas_item_add_polygon(canvas_item, p_points, p_colors, p_uvs, rid); + PackedVector2Array uvs = p_uvs; + for (Vector2 &p : uvs) { + p.x = Math::remap(p.x, 0, 1, remap_min.x, remap_max.x); + p.y = Math::remap(p.y, 0, 1, remap_min.y, remap_max.y); + } + RenderingServer::get_singleton()->canvas_item_add_polygon(canvas_item, p_points, p_colors, uvs, texture->get_rid()); + } else { + RID texture_rid = p_texture.is_valid() ? p_texture->get_rid() : RID(); + RenderingServer::get_singleton()->canvas_item_add_polygon(canvas_item, p_points, p_colors, p_uvs, texture_rid); + } } void CanvasItem::draw_colored_polygon(const Vector<Point2> &p_points, const Color &p_color, const Vector<Point2> &p_uvs, Ref<Texture2D> p_texture) { - ERR_THREAD_GUARD; - ERR_DRAW_GUARD; - - Vector<Color> colors = { p_color }; - RID rid = p_texture.is_valid() ? p_texture->get_rid() : RID(); - RenderingServer::get_singleton()->canvas_item_add_polygon(canvas_item, p_points, colors, p_uvs, rid); + draw_polygon(p_points, { p_color }, p_uvs, p_texture); } void CanvasItem::draw_mesh(const Ref<Mesh> &p_mesh, const Ref<Texture2D> &p_texture, const Transform2D &p_transform, const Color &p_modulate) { diff --git a/scene/main/instance_placeholder.cpp b/scene/main/instance_placeholder.cpp index fe23ca1800..f36bbe9395 100644 --- a/scene/main/instance_placeholder.cpp +++ b/scene/main/instance_placeholder.cpp @@ -88,16 +88,16 @@ Node *InstancePlaceholder::create_instance(bool p_replace, const Ref<PackedScene if (!ps.is_valid()) { return nullptr; } - Node *scene = ps->instantiate(); - if (!scene) { + Node *instance = ps->instantiate(); + if (!instance) { return nullptr; } - scene->set_name(get_name()); - scene->set_multiplayer_authority(get_multiplayer_authority()); + instance->set_name(get_name()); + instance->set_multiplayer_authority(get_multiplayer_authority()); int pos = get_index(); for (const PropSet &E : stored_values) { - scene->set(E.name, E.value); + set_value_on_instance(this, instance, E); } if (p_replace) { @@ -105,10 +105,125 @@ Node *InstancePlaceholder::create_instance(bool p_replace, const Ref<PackedScene base->remove_child(this); } - base->add_child(scene); - base->move_child(scene, pos); + base->add_child(instance); + base->move_child(instance, pos); - return scene; + return instance; +} + +// This method will attempt to set the correct values on the placeholder instance +// for regular types this is trivial and unnecessary. +// For nodes however this becomes a bit tricky because they might now have existed until the instantiation, +// so this method will try to find the correct nodes and resolve them. +void InstancePlaceholder::set_value_on_instance(InstancePlaceholder *p_placeholder, Node *p_instance, const PropSet &p_set) { + bool is_valid; + + // If we don't have any info, we can't do anything, + // so try setting the value directly. + Variant current = p_instance->get(p_set.name, &is_valid); + if (!is_valid) { + p_instance->set(p_set.name, p_set.value, &is_valid); + return; + } + + Variant::Type current_type = current.get_type(); + Variant::Type placeholder_type = p_set.value.get_type(); + + // Arrays are a special case, because their containing type might be different. + if (current_type != Variant::Type::ARRAY) { + // Check if the variant types match. + if (Variant::evaluate(Variant::OP_EQUAL, current_type, placeholder_type)) { + p_instance->set(p_set.name, p_set.value, &is_valid); + if (is_valid) { + return; + } + // Types match but setting failed? This is strange, so let's print a warning! + WARN_PRINT(vformat("Property '%s' with type '%s' could not be set when creating instance of '%s'.", p_set.name, Variant::get_type_name(current_type), p_placeholder->get_name())); + return; + } + } else { + // We are dealing with an Array. + // Let's check if the subtype of the array matches first. + // This is needed because the set method of ScriptInstance checks for type, + // but the ClassDB set method doesn't! So we cannot reliably know what actually happens. + Array current_array = current; + Array placeholder_array = p_set.value; + if (current_array.is_same_typed(placeholder_array)) { + p_instance->set(p_set.name, p_set.value, &is_valid); + if (is_valid) { + return; + } + // Internal array types match but setting failed? This is strange, so let's print a warning! + WARN_PRINT(vformat("Array Property '%s' with type '%s' could not be set when creating instance of '%s'.", p_set.name, Variant::get_type_name(Variant::Type(current_array.get_typed_builtin())), p_placeholder->get_name())); + } + // Arrays are not the same internal type. This should be happening because we have a NodePath Array, + // but the instance wants a Node Array. + } + + switch (current_type) { + case Variant::Type::NIL: + if (placeholder_type != Variant::Type::NODE_PATH) { + break; + } + // If it's nil but we have a NodePath, we guess what works. + + p_instance->set(p_set.name, p_set.value, &is_valid); + if (is_valid) { + break; + } + + p_instance->set(p_set.name, try_get_node(p_placeholder, p_instance, p_set.value), &is_valid); + break; + case Variant::Type::OBJECT: + if (placeholder_type != Variant::Type::NODE_PATH) { + break; + } + // Easiest case, we want a node, but we have a deferred NodePath. + p_instance->set(p_set.name, try_get_node(p_placeholder, p_instance, p_set.value)); + break; + case Variant::Type::ARRAY: { + // If we have reached here it means our array types don't match, + // so we will convert the placeholder array into the correct type + // and resolve nodes if necessary. + Array current_array = current; + Array converted_array; + Array placeholder_array = p_set.value; + converted_array = current_array.duplicate(); + converted_array.resize(placeholder_array.size()); + + if (Variant::evaluate(Variant::OP_EQUAL, current_array.get_typed_builtin(), Variant::Type::NODE_PATH)) { + // We want a typed NodePath array. + for (int i = 0; i < placeholder_array.size(); i++) { + converted_array.set(i, placeholder_array[i]); + } + } else { + // We want Nodes, convert NodePaths. + for (int i = 0; i < placeholder_array.size(); i++) { + converted_array.set(i, try_get_node(p_placeholder, p_instance, placeholder_array[i])); + } + } + + p_instance->set(p_set.name, converted_array, &is_valid); + if (!is_valid) { + WARN_PRINT(vformat("Property '%s' with type '%s' could not be set when creating instance of '%s'.", p_set.name, Variant::get_type_name(current_type), p_placeholder->get_name())); + } + break; + } + default: + WARN_PRINT(vformat("Property '%s' with type '%s' could not be set when creating instance of '%s'.", p_set.name, Variant::get_type_name(current_type), p_placeholder->get_name())); + break; + } +} + +Node *InstancePlaceholder::try_get_node(InstancePlaceholder *p_placeholder, Node *p_instance, const NodePath &p_path) { + // First try to resolve internally, + // if that fails try resolving externally. + Node *node = p_instance->get_node_or_null(p_path); + if (node == nullptr) { + node = p_placeholder->get_node_or_null(p_path); + } + + return node; } Dictionary InstancePlaceholder::get_stored_values(bool p_with_order) { diff --git a/scene/main/instance_placeholder.h b/scene/main/instance_placeholder.h index 480474d0bd..ccf1e63a16 100644 --- a/scene/main/instance_placeholder.h +++ b/scene/main/instance_placeholder.h @@ -46,6 +46,10 @@ class InstancePlaceholder : public Node { List<PropSet> stored_values; +private: + void set_value_on_instance(InstancePlaceholder *p_placeholder, Node *p_instance, const PropSet &p_set); + Node *try_get_node(InstancePlaceholder *p_placeholder, Node *p_instance, const NodePath &p_path); + protected: bool _set(const StringName &p_name, const Variant &p_value); bool _get(const StringName &p_name, Variant &r_ret) const; diff --git a/scene/main/window.cpp b/scene/main/window.cpp index e9a7123da0..addbd6078a 100644 --- a/scene/main/window.cpp +++ b/scene/main/window.cpp @@ -306,10 +306,21 @@ String Window::get_title() const { return title; } +void Window::_settings_changed() { + if (visible && initial_position != WINDOW_INITIAL_POSITION_ABSOLUTE && is_in_edited_scene_root()) { + Size2 screen_size = Size2(GLOBAL_GET("display/window/size/viewport_width"), GLOBAL_GET("display/window/size/viewport_height")); + position = (screen_size - size) / 2; + if (embedder) { + embedder->_sub_window_update(this); + } + } +} + void Window::set_initial_position(Window::WindowInitialPosition p_initial_position) { ERR_MAIN_THREAD_GUARD; initial_position = p_initial_position; + _settings_changed(); notify_property_list_changed(); } @@ -829,7 +840,12 @@ void Window::set_visible(bool p_visible) { if (visible) { embedder = embedder_vp; if (initial_position != WINDOW_INITIAL_POSITION_ABSOLUTE) { - position = (embedder->get_visible_rect().size - size) / 2; + if (is_in_edited_scene_root()) { + Size2 screen_size = Size2(GLOBAL_GET("display/window/size/viewport_width"), GLOBAL_GET("display/window/size/viewport_height")); + position = (screen_size - size) / 2; + } else { + position = (embedder->get_visible_rect().size - size) / 2; + } } embedder->_sub_window_register(this); RS::get_singleton()->viewport_set_update_mode(get_viewport_rid(), RS::VIEWPORT_UPDATE_WHEN_PARENT_VISIBLE); @@ -1265,6 +1281,12 @@ void Window::_notification(int p_what) { } break; case NOTIFICATION_ENTER_TREE: { + if (is_in_edited_scene_root()) { + if (!ProjectSettings::get_singleton()->is_connected("settings_changed", callable_mp(this, &Window::_settings_changed))) { + ProjectSettings::get_singleton()->connect("settings_changed", callable_mp(this, &Window::_settings_changed)); + } + } + bool embedded = false; { embedder = get_embedder(); @@ -1280,7 +1302,12 @@ void Window::_notification(int p_what) { // Create as embedded. if (embedder) { if (initial_position != WINDOW_INITIAL_POSITION_ABSOLUTE) { - position = (embedder->get_visible_rect().size - size) / 2; + if (is_in_edited_scene_root()) { + Size2 screen_size = Size2(GLOBAL_GET("display/window/size/viewport_width"), GLOBAL_GET("display/window/size/viewport_height")); + position = (screen_size - size) / 2; + } else { + position = (embedder->get_visible_rect().size - size) / 2; + } } embedder->_sub_window_register(this); RS::get_singleton()->viewport_set_update_mode(get_viewport_rid(), RS::VIEWPORT_UPDATE_WHEN_PARENT_VISIBLE); @@ -1377,6 +1404,10 @@ void Window::_notification(int p_what) { } break; case NOTIFICATION_EXIT_TREE: { + if (ProjectSettings::get_singleton()->is_connected("settings_changed", callable_mp(this, &Window::_settings_changed))) { + ProjectSettings::get_singleton()->disconnect("settings_changed", callable_mp(this, &Window::_settings_changed)); + } + set_theme_context(nullptr, false); if (transient) { diff --git a/scene/main/window.h b/scene/main/window.h index ffcf50ccdd..33d593711f 100644 --- a/scene/main/window.h +++ b/scene/main/window.h @@ -214,6 +214,8 @@ private: int resize_margin = 0; } theme_cache; + void _settings_changed(); + Viewport *embedder = nullptr; Transform2D window_transform; diff --git a/scene/resources/2d/tile_set.cpp b/scene/resources/2d/tile_set.cpp index 0f73577768..6c3356a205 100644 --- a/scene/resources/2d/tile_set.cpp +++ b/scene/resources/2d/tile_set.cpp @@ -6378,7 +6378,7 @@ Ref<ConvexPolygonShape2D> TileData::get_collision_polygon_shape(int p_layer_id, for (int i = 0; i < size; i++) { Ref<ConvexPolygonShape2D> transformed_polygon; transformed_polygon.instantiate(); - transformed_polygon->set_points(get_transformed_vertices(shapes_data.shapes[shape_index]->get_points(), p_flip_h, p_flip_v, p_transpose)); + transformed_polygon->set_points(get_transformed_vertices(shapes_data.shapes[i]->get_points(), p_flip_h, p_flip_v, p_transpose)); shapes_data.transformed_shapes[key][i] = transformed_polygon; } return shapes_data.transformed_shapes[key][shape_index]; diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp index 27da825bfe..8e49a8b56f 100644 --- a/scene/resources/material.cpp +++ b/scene/resources/material.cpp @@ -822,7 +822,18 @@ uniform float distance_fade_max : hint_range(0.0, 4096.0, 0.01); )"; } - if (flags[FLAG_ALBEDO_TEXTURE_MSDF]) { + if (flags[FLAG_ALBEDO_TEXTURE_MSDF] && flags[FLAG_UV1_USE_TRIPLANAR]) { + String msg = "MSDF is not supported on triplanar materials. Ignoring MSDF in favor of triplanar mapping."; + if (textures[TEXTURE_ALBEDO].is_valid()) { + WARN_PRINT(vformat("%s (albedo %s): " + msg, get_path(), textures[TEXTURE_ALBEDO]->get_path())); + } else if (!get_path().is_empty()) { + WARN_PRINT(vformat("%s: " + msg, get_path())); + } else { + WARN_PRINT(msg); + } + } + + if (flags[FLAG_ALBEDO_TEXTURE_MSDF] && !flags[FLAG_UV1_USE_TRIPLANAR]) { code += R"( uniform float msdf_pixel_range : hint_range(1.0, 100.0, 1.0); uniform float msdf_outline_size : hint_range(0.0, 250.0, 1.0); @@ -1271,7 +1282,7 @@ void vertex() {)"; code += "}\n"; - if (flags[FLAG_ALBEDO_TEXTURE_MSDF]) { + if (flags[FLAG_ALBEDO_TEXTURE_MSDF] && !flags[FLAG_UV1_USE_TRIPLANAR]) { code += R"( float msdf_median(float r, float g, float b, float a) { return min(max(min(r, g), min(max(r, g), b)), a); @@ -1414,7 +1425,7 @@ void fragment() {)"; } } - if (flags[FLAG_ALBEDO_TEXTURE_MSDF]) { + if (flags[FLAG_ALBEDO_TEXTURE_MSDF] && !flags[FLAG_UV1_USE_TRIPLANAR]) { code += R"( { // Albedo Texture MSDF: Enabled @@ -1427,11 +1438,7 @@ void fragment() {)"; if (flags[FLAG_USE_POINT_SIZE]) { code += " vec2 dest_size = vec2(1.0) / fwidth(POINT_COORD);\n"; } else { - if (flags[FLAG_UV1_USE_TRIPLANAR]) { - code += " vec2 dest_size = vec2(1.0) / fwidth(uv1_triplanar_pos);\n"; - } else { - code += " vec2 dest_size = vec2(1.0) / fwidth(base_uv);\n"; - } + code += " vec2 dest_size = vec2(1.0) / fwidth(base_uv);\n"; } code += R"( float px_size = max(0.5 * dot(msdf_size, dest_size), 1.0); diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp index 5e50b9a240..0c57c6b7ba 100644 --- a/scene/resources/packed_scene.cpp +++ b/scene/resources/packed_scene.cpp @@ -314,6 +314,16 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { ERR_FAIL_INDEX_V(nprops[j].value, prop_count, nullptr); if (nprops[j].name & FLAG_PATH_PROPERTY_IS_NODE) { + if (!Engine::get_singleton()->is_editor_hint() && node->get_scene_instance_load_placeholder()) { + // We cannot know if the referenced nodes exist yet, so instead of deferring, we write the NodePaths directly. + + uint32_t name_idx = nprops[j].name & (FLAG_PATH_PROPERTY_IS_NODE - 1); + ERR_FAIL_UNSIGNED_INDEX_V(name_idx, (uint32_t)sname_count, nullptr); + + node->set(snames[name_idx], props[nprops[j].value], &valid); + continue; + } + uint32_t name_idx = nprops[j].name & (FLAG_PATH_PROPERTY_IS_NODE - 1); ERR_FAIL_UNSIGNED_INDEX_V(name_idx, (uint32_t)sname_count, nullptr); diff --git a/scene/resources/portable_compressed_texture.cpp b/scene/resources/portable_compressed_texture.cpp index 918b5c0b41..002db30379 100644 --- a/scene/resources/portable_compressed_texture.cpp +++ b/scene/resources/portable_compressed_texture.cpp @@ -153,14 +153,14 @@ void PortableCompressedTexture2D::create_from_image(const Ref<Image> &p_image, C for (int i = 0; i < p_image->get_mipmap_count() + 1; i++) { Vector<uint8_t> data; if (p_compression_mode == COMPRESSION_MODE_LOSSY) { - data = Image::webp_lossy_packer(p_image->get_image_from_mipmap(i), p_lossy_quality); + data = Image::webp_lossy_packer(i ? p_image->get_image_from_mipmap(i) : p_image, p_lossy_quality); encode_uint16(DATA_FORMAT_WEBP, buffer.ptrw() + 2); } else { if (use_webp) { - data = Image::webp_lossless_packer(p_image->get_image_from_mipmap(i)); + data = Image::webp_lossless_packer(i ? p_image->get_image_from_mipmap(i) : p_image); encode_uint16(DATA_FORMAT_WEBP, buffer.ptrw() + 2); } else { - data = Image::png_packer(p_image->get_image_from_mipmap(i)); + data = Image::png_packer(i ? p_image->get_image_from_mipmap(i) : p_image); encode_uint16(DATA_FORMAT_PNG, buffer.ptrw() + 2); } } diff --git a/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp index 2e27ac9198..7c13e623c2 100644 --- a/scene/resources/resource_format_text.cpp +++ b/scene/resources/resource_format_text.cpp @@ -33,9 +33,7 @@ #include "core/config/project_settings.h" #include "core/io/dir_access.h" #include "core/io/missing_resource.h" -#include "core/io/resource_format_binary.h" #include "core/object/script_language.h" -#include "core/version.h" // Version 2: Changed names for Basis, AABB, Vectors, etc. // Version 3: New string ID for ext/subresources, breaks forward compat. @@ -44,11 +42,6 @@ // For compat, save as version 3 if not using PackedVector4Array or no big PackedByteArray. #define FORMAT_VERSION_COMPAT 3 -#define BINARY_FORMAT_VERSION 4 - -#include "core/io/dir_access.h" -#include "core/version.h" - #define _printerr() ERR_PRINT(String(res_path + ":" + itos(lines) + " - Parse Error: " + error_text).utf8().get_data()); /// @@ -1127,298 +1120,6 @@ void ResourceLoaderText::open(Ref<FileAccess> p_f, bool p_skip_first_tag) { rp.userdata = this; } -static void bs_save_unicode_string(Ref<FileAccess> p_f, const String &p_string, bool p_bit_on_len = false) { - CharString utf8 = p_string.utf8(); - if (p_bit_on_len) { - p_f->store_32((utf8.length() + 1) | 0x80000000); - } else { - p_f->store_32(utf8.length() + 1); - } - p_f->store_buffer((const uint8_t *)utf8.get_data(), utf8.length() + 1); -} - -Error ResourceLoaderText::save_as_binary(const String &p_path) { - if (error) { - return error; - } - - Ref<FileAccess> wf = FileAccess::open(p_path, FileAccess::WRITE); - if (wf.is_null()) { - return ERR_CANT_OPEN; - } - - //save header compressed - static const uint8_t header[4] = { 'R', 'S', 'R', 'C' }; - wf->store_buffer(header, 4); - - wf->store_32(0); //endianness, little endian - wf->store_32(0); //64 bits file, false for now - wf->store_32(VERSION_MAJOR); - wf->store_32(VERSION_MINOR); - static const int save_format_version = BINARY_FORMAT_VERSION; - wf->store_32(save_format_version); - - bs_save_unicode_string(wf, is_scene ? "PackedScene" : resource_type); - wf->store_64(0); //offset to import metadata, this is no longer used - - wf->store_32(ResourceFormatSaverBinaryInstance::FORMAT_FLAG_NAMED_SCENE_IDS | ResourceFormatSaverBinaryInstance::FORMAT_FLAG_UIDS); - - wf->store_64(res_uid); - - for (int i = 0; i < ResourceFormatSaverBinaryInstance::RESERVED_FIELDS; i++) { - wf->store_32(0); // reserved - } - - wf->store_32(0); //string table size, will not be in use - uint64_t ext_res_count_pos = wf->get_position(); - - wf->store_32(0); //zero ext resources, still parsing them - - //go with external resources - - DummyReadData dummy_read; - VariantParser::ResourceParser rp_new; - rp_new.ext_func = _parse_ext_resource_dummys; - rp_new.sub_func = _parse_sub_resource_dummys; - rp_new.userdata = &dummy_read; - - while (next_tag.name == "ext_resource") { - if (!next_tag.fields.has("path")) { - error = ERR_FILE_CORRUPT; - error_text = "Missing 'path' in external resource tag"; - _printerr(); - return error; - } - - if (!next_tag.fields.has("type")) { - error = ERR_FILE_CORRUPT; - error_text = "Missing 'type' in external resource tag"; - _printerr(); - return error; - } - - if (!next_tag.fields.has("id")) { - error = ERR_FILE_CORRUPT; - error_text = "Missing 'id' in external resource tag"; - _printerr(); - return error; - } - - String path = next_tag.fields["path"]; - String type = next_tag.fields["type"]; - String id = next_tag.fields["id"]; - ResourceUID::ID uid = ResourceUID::INVALID_ID; - if (next_tag.fields.has("uid")) { - String uidt = next_tag.fields["uid"]; - uid = ResourceUID::get_singleton()->text_to_id(uidt); - } - - bs_save_unicode_string(wf, type); - bs_save_unicode_string(wf, path); - wf->store_64(uid); - - int lindex = dummy_read.external_resources.size(); - Ref<DummyResource> dr; - dr.instantiate(); - dr->set_path("res://dummy" + itos(lindex)); //anything is good to detect it for saving as external - dummy_read.external_resources[dr] = lindex; - dummy_read.rev_external_resources[id] = dr; - - error = VariantParser::parse_tag(&stream, lines, error_text, next_tag, &rp_new); - - if (error) { - _printerr(); - return error; - } - } - - // save external resource table - wf->seek(ext_res_count_pos); - wf->store_32(dummy_read.external_resources.size()); - wf->seek_end(); - - //now, save resources to a separate file, for now - - uint64_t sub_res_count_pos = wf->get_position(); - wf->store_32(0); //zero sub resources, still parsing them - - String temp_file = p_path + ".temp"; - Vector<uint64_t> local_offsets; - Vector<uint64_t> local_pointers_pos; - { - Ref<FileAccess> wf2 = FileAccess::open(temp_file, FileAccess::WRITE); - if (wf2.is_null()) { - return ERR_CANT_OPEN; - } - - while (next_tag.name == "sub_resource" || next_tag.name == "resource") { - String type; - String id; - bool main_res; - - if (next_tag.name == "sub_resource") { - if (!next_tag.fields.has("type")) { - error = ERR_FILE_CORRUPT; - error_text = "Missing 'type' in external resource tag"; - _printerr(); - return error; - } - - if (!next_tag.fields.has("id")) { - error = ERR_FILE_CORRUPT; - error_text = "Missing 'id' in external resource tag"; - _printerr(); - return error; - } - - type = next_tag.fields["type"]; - id = next_tag.fields["id"]; - main_res = false; - - if (!dummy_read.resource_map.has(id)) { - Ref<DummyResource> dr; - dr.instantiate(); - dr->set_scene_unique_id(id); - dummy_read.resource_map[id] = dr; - uint32_t im_size = dummy_read.resource_index_map.size(); - dummy_read.resource_index_map.insert(dr, im_size); - } - - } else { - type = res_type; - String uid_text = ResourceUID::get_singleton()->id_to_text(res_uid); - id = type + "_" + uid_text.replace("uid://", "").replace("<invalid>", "0"); - main_res = true; - } - - local_offsets.push_back(wf2->get_position()); - - bs_save_unicode_string(wf, "local://" + id); - local_pointers_pos.push_back(wf->get_position()); - wf->store_64(0); //temp local offset - - bs_save_unicode_string(wf2, type); - uint64_t propcount_ofs = wf2->get_position(); - wf2->store_32(0); - - int prop_count = 0; - - while (true) { - String assign; - Variant value; - - error = VariantParser::parse_tag_assign_eof(&stream, lines, error_text, next_tag, assign, value, &rp_new); - - if (error) { - if (main_res && error == ERR_FILE_EOF) { - next_tag.name = ""; //exit - break; - } - - _printerr(); - return error; - } - - if (!assign.is_empty()) { - HashMap<StringName, int> empty_string_map; //unused - bs_save_unicode_string(wf2, assign, true); - ResourceFormatSaverBinaryInstance::write_variant(wf2, value, dummy_read.resource_index_map, dummy_read.external_resources, empty_string_map); - prop_count++; - - } else if (!next_tag.name.is_empty()) { - error = OK; - break; - } else { - error = ERR_FILE_CORRUPT; - error_text = "Premature end of file while parsing [sub_resource]"; - _printerr(); - return error; - } - } - - wf2->seek(propcount_ofs); - wf2->store_32(prop_count); - wf2->seek_end(); - } - - if (next_tag.name == "node") { - // This is a node, must save one more! - - if (!is_scene) { - error_text += "found the 'node' tag on a resource file!"; - _printerr(); - error = ERR_FILE_CORRUPT; - return error; - } - - Ref<PackedScene> packed_scene = _parse_node_tag(rp_new); - - if (!packed_scene.is_valid()) { - return error; - } - - error = OK; - //get it here - List<PropertyInfo> props; - packed_scene->get_property_list(&props); - - String id = "PackedScene_" + ResourceUID::get_singleton()->id_to_text(res_uid).replace("uid://", "").replace("<invalid>", "0"); - bs_save_unicode_string(wf, "local://" + id); - local_pointers_pos.push_back(wf->get_position()); - wf->store_64(0); //temp local offset - - local_offsets.push_back(wf2->get_position()); - bs_save_unicode_string(wf2, "PackedScene"); - uint64_t propcount_ofs = wf2->get_position(); - wf2->store_32(0); - - int prop_count = 0; - - for (const PropertyInfo &E : props) { - if (!(E.usage & PROPERTY_USAGE_STORAGE)) { - continue; - } - - String name = E.name; - Variant value = packed_scene->get(name); - - HashMap<StringName, int> empty_string_map; //unused - bs_save_unicode_string(wf2, name, true); - ResourceFormatSaverBinaryInstance::write_variant(wf2, value, dummy_read.resource_index_map, dummy_read.external_resources, empty_string_map); - prop_count++; - } - - wf2->seek(propcount_ofs); - wf2->store_32(prop_count); - wf2->seek_end(); - } - } - - uint64_t offset_from = wf->get_position(); - wf->seek(sub_res_count_pos); //plus one because the saved one - wf->store_32(local_offsets.size()); - - for (int i = 0; i < local_offsets.size(); i++) { - wf->seek(local_pointers_pos[i]); - wf->store_64(local_offsets[i] + offset_from); - } - - wf->seek_end(); - - Vector<uint8_t> data = FileAccess::get_file_as_bytes(temp_file); - wf->store_buffer(data.ptr(), data.size()); - { - Ref<DirAccess> dar = DirAccess::open(temp_file.get_base_dir()); - ERR_FAIL_COND_V(dar.is_null(), FAILED); - - dar->remove(temp_file); - } - - wf->store_buffer((const uint8_t *)"RSRC", 4); //magic at end - - return OK; -} - Error ResourceLoaderText::get_classes_used(HashSet<StringName> *r_classes) { if (error) { return error; @@ -1835,29 +1536,6 @@ Error ResourceFormatLoaderText::rename_dependencies(const String &p_path, const ResourceFormatLoaderText *ResourceFormatLoaderText::singleton = nullptr; -Error ResourceFormatLoaderText::convert_file_to_binary(const String &p_src_path, const String &p_dst_path) { - Error err; - Ref<FileAccess> f = FileAccess::open(p_src_path, FileAccess::READ, &err); - - ERR_FAIL_COND_V_MSG(err != OK, ERR_CANT_OPEN, "Cannot open file '" + p_src_path + "'."); - - ResourceLoaderText loader; - const String &path = p_src_path; - loader.local_path = ProjectSettings::get_singleton()->localize_path(path); - loader.res_path = loader.local_path; - loader.open(f); - return loader.save_as_binary(p_dst_path); -} - -/*****************************************************************************************************/ -/*****************************************************************************************************/ -/*****************************************************************************************************/ -/*****************************************************************************************************/ -/*****************************************************************************************************/ -/*****************************************************************************************************/ -/*****************************************************************************************************/ -/*****************************************************************************************************/ -/*****************************************************************************************************/ /*****************************************************************************************************/ String ResourceFormatSaverTextInstance::_write_resources(void *ud, const Ref<Resource> &p_resource) { diff --git a/scene/resources/resource_format_text.h b/scene/resources/resource_format_text.h index 41363fd975..b5542f77ba 100644 --- a/scene/resources/resource_format_text.h +++ b/scene/resources/resource_format_text.h @@ -87,11 +87,6 @@ class ResourceLoaderText { Error _parse_sub_resource(VariantParser::Stream *p_stream, Ref<Resource> &r_res, int &line, String &r_err_str); Error _parse_ext_resource(VariantParser::Stream *p_stream, Ref<Resource> &r_res, int &line, String &r_err_str); - // for converter - class DummyResource : public Resource { - public: - }; - struct DummyReadData { bool no_placeholders = false; HashMap<Ref<Resource>, int> external_resources; @@ -133,7 +128,6 @@ public: Error rename_dependencies(Ref<FileAccess> p_f, const String &p_path, const HashMap<String, String> &p_map); Error get_classes_used(HashSet<StringName> *r_classes); - Error save_as_binary(const String &p_path); ResourceLoaderText(); }; @@ -152,8 +146,6 @@ public: virtual void get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types = false) override; virtual Error rename_dependencies(const String &p_path, const HashMap<String, String> &p_map) override; - static Error convert_file_to_binary(const String &p_src_path, const String &p_dst_path); - ResourceFormatLoaderText() { singleton = this; } }; diff --git a/scene/theme/SCsub b/scene/theme/SCsub index 5f62ae4b05..2372d1820a 100644 --- a/scene/theme/SCsub +++ b/scene/theme/SCsub @@ -4,7 +4,6 @@ Import("env") import default_theme_builders - env.add_source_files(env.scene_sources, "*.cpp") SConscript("icons/SCsub") diff --git a/scene/theme/icons/SCsub b/scene/theme/icons/SCsub index 46133ccceb..1f3b7f6d17 100644 --- a/scene/theme/icons/SCsub +++ b/scene/theme/icons/SCsub @@ -4,7 +4,6 @@ Import("env") import default_theme_icons_builders - env["BUILDERS"]["MakeDefaultThemeIconsBuilder"] = Builder( action=env.Run(default_theme_icons_builders.make_default_theme_icons_action), suffix=".h", diff --git a/scu_builders.py b/scu_builders.py index a9ae428222..fc5461196f 100644 --- a/scu_builders.py +++ b/scu_builders.py @@ -1,11 +1,11 @@ -"""Functions used to generate scu build source files during build time -""" +"""Functions used to generate scu build source files during build time""" -import glob, os +import glob import math -from methods import print_error +import os from pathlib import Path -from os.path import normpath, basename + +from methods import print_error base_folder_path = str(Path(__file__).parent) + "/" base_folder_only = os.path.basename(os.path.normpath(base_folder_path)) @@ -25,7 +25,7 @@ def clear_out_stale_files(output_folder, extension, fresh_files): for file in glob.glob(output_folder + "/*." + extension): file = Path(file) - if not file in fresh_files: + if file not in fresh_files: # print("removed stale file: " + str(file)) os.remove(file) @@ -56,7 +56,7 @@ def find_files_in_folder(folder, sub_folder, include_list, extension, sought_exc li = '#include "' + folder + "/" + sub_folder_slashed + file + '"' - if not simple_name in sought_exceptions: + if simple_name not in sought_exceptions: include_list.append(li) else: found_exceptions.append(li) @@ -78,9 +78,9 @@ def write_output_file(file_count, include_list, start_line, end_line, output_fol file_text = "" - for l in range(start_line, end_line): - if l < len(include_list): - line = include_list[l] + for i in range(start_line, end_line): + if i < len(include_list): + line = include_list[i] li = line + "\n" file_text += li @@ -221,7 +221,6 @@ def process_folder(folders, sought_exceptions=[], includes_per_scu=0, extension= lines_per_file = max(lines_per_file, 1) start_line = 0 - file_number = 0 # These do not vary throughout the loop output_folder = abs_main_folder + "/scu/" diff --git a/servers/display_server.cpp b/servers/display_server.cpp index fcbedbc1f8..2150a3038e 100644 --- a/servers/display_server.cpp +++ b/servers/display_server.cpp @@ -996,6 +996,8 @@ void DisplayServer::_bind_methods() { ClassDB::bind_method(D_METHOD("tablet_get_current_driver"), &DisplayServer::tablet_get_current_driver); ClassDB::bind_method(D_METHOD("tablet_set_current_driver", "name"), &DisplayServer::tablet_set_current_driver); + ClassDB::bind_method(D_METHOD("is_window_transparency_available"), &DisplayServer::is_window_transparency_available); + #ifndef DISABLE_DEPRECATED BIND_ENUM_CONSTANT(FEATURE_GLOBAL_MENU); #endif @@ -1183,9 +1185,9 @@ Vector<String> DisplayServer::get_create_function_rendering_drivers(int p_index) return server_create_functions[p_index].get_rendering_drivers_function(); } -DisplayServer *DisplayServer::create(int p_index, const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Error &r_error) { +DisplayServer *DisplayServer::create(int p_index, const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error) { ERR_FAIL_INDEX_V(p_index, server_create_count, nullptr); - return server_create_functions[p_index].create_function(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_position, p_resolution, p_screen, r_error); + return server_create_functions[p_index].create_function(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_position, p_resolution, p_screen, p_context, r_error); } void DisplayServer::_input_set_mouse_mode(Input::MouseMode p_mode) { diff --git a/servers/display_server.h b/servers/display_server.h index 30f6ee5ccf..5224d59c04 100644 --- a/servers/display_server.h +++ b/servers/display_server.h @@ -82,7 +82,13 @@ public: OPENGL_CONTEXT, }; - typedef DisplayServer *(*CreateFunction)(const String &, WindowMode, VSyncMode, uint32_t, const Point2i *, const Size2i &, int p_screen, Error &r_error); + enum Context { + CONTEXT_EDITOR, + CONTEXT_PROJECTMAN, + CONTEXT_ENGINE, + }; + + typedef DisplayServer *(*CreateFunction)(const String &, WindowMode, VSyncMode, uint32_t, const Point2i *, const Size2i &, int p_screen, Context, Error &r_error); typedef Vector<String> (*GetRenderingDriversFunction)(); private: @@ -572,19 +578,15 @@ public: virtual Rect2 status_indicator_get_rect(IndicatorID p_id) const; virtual void delete_status_indicator(IndicatorID p_id); - enum Context { - CONTEXT_EDITOR, - CONTEXT_PROJECTMAN, - CONTEXT_ENGINE, - }; - virtual void set_context(Context p_context); + virtual bool is_window_transparency_available() const { return false; } + static void register_create_function(const char *p_name, CreateFunction p_function, GetRenderingDriversFunction p_get_drivers); static int get_create_function_count(); static const char *get_create_function_name(int p_index); static Vector<String> get_create_function_rendering_drivers(int p_index); - static DisplayServer *create(int p_index, const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Error &r_error); + static DisplayServer *create(int p_index, const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error); DisplayServer(); ~DisplayServer(); diff --git a/servers/display_server_headless.h b/servers/display_server_headless.h index 38baa6a4fa..51755ddfbd 100644 --- a/servers/display_server_headless.h +++ b/servers/display_server_headless.h @@ -45,7 +45,7 @@ private: return drivers; } - static DisplayServer *create_func(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Error &r_error) { + static DisplayServer *create_func(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error) { r_error = OK; RasterizerDummy::make_current(); return memnew(DisplayServerHeadless()); diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp index 068f9d9ef2..aae32f0b3e 100644 --- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp +++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp @@ -972,14 +972,14 @@ void RenderForwardClustered::_fill_render_list(RenderListType p_render_list, con float distance = 0.0; // Check if camera is NOT inside the mesh AABB. - if (!inst->transformed_aabb.has_point(p_render_data->scene_data->cam_transform.origin)) { + if (!inst->transformed_aabb.has_point(p_render_data->scene_data->main_cam_transform.origin)) { // Get the LOD support points on the mesh AABB. - Vector3 lod_support_min = inst->transformed_aabb.get_support(p_render_data->scene_data->cam_transform.basis.get_column(Vector3::AXIS_Z)); - Vector3 lod_support_max = inst->transformed_aabb.get_support(-p_render_data->scene_data->cam_transform.basis.get_column(Vector3::AXIS_Z)); + Vector3 lod_support_min = inst->transformed_aabb.get_support(p_render_data->scene_data->main_cam_transform.basis.get_column(Vector3::AXIS_Z)); + Vector3 lod_support_max = inst->transformed_aabb.get_support(-p_render_data->scene_data->main_cam_transform.basis.get_column(Vector3::AXIS_Z)); // Get the distances to those points on the AABB from the camera origin. - float distance_min = (float)p_render_data->scene_data->cam_transform.origin.distance_to(lod_support_min); - float distance_max = (float)p_render_data->scene_data->cam_transform.origin.distance_to(lod_support_max); + float distance_min = (float)p_render_data->scene_data->main_cam_transform.origin.distance_to(lod_support_min); + float distance_max = (float)p_render_data->scene_data->main_cam_transform.origin.distance_to(lod_support_max); if (distance_min * distance_max < 0.0) { //crossing plane @@ -1388,7 +1388,6 @@ void RenderForwardClustered::_pre_opaque_render(RenderDataRD *p_render_data, boo p_render_data->shadows.clear(); p_render_data->directional_shadows.clear(); - Plane camera_plane(-p_render_data->scene_data->cam_transform.basis.get_column(Vector3::AXIS_Z), p_render_data->scene_data->cam_transform.origin); float lod_distance_multiplier = p_render_data->scene_data->cam_projection.get_lod_multiplier(); { for (int i = 0; i < p_render_data->render_shadow_count; i++) { @@ -1407,7 +1406,7 @@ void RenderForwardClustered::_pre_opaque_render(RenderDataRD *p_render_data, boo RENDER_TIMESTAMP("Render OmniLight Shadows"); // Cube shadows are rendered in their own way. for (const int &index : p_render_data->cube_shadows) { - _render_shadow_pass(p_render_data->render_shadows[index].light, p_render_data->shadow_atlas, p_render_data->render_shadows[index].pass, p_render_data->render_shadows[index].instances, camera_plane, lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, true, true, true, p_render_data->render_info, viewport_size, p_render_data->scene_data->cam_transform); + _render_shadow_pass(p_render_data->render_shadows[index].light, p_render_data->shadow_atlas, p_render_data->render_shadows[index].pass, p_render_data->render_shadows[index].instances, lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, true, true, true, p_render_data->render_info, viewport_size, p_render_data->scene_data->cam_transform); } if (p_render_data->directional_shadows.size()) { @@ -1437,11 +1436,11 @@ void RenderForwardClustered::_pre_opaque_render(RenderDataRD *p_render_data, boo //render directional shadows for (uint32_t i = 0; i < p_render_data->directional_shadows.size(); i++) { - _render_shadow_pass(p_render_data->render_shadows[p_render_data->directional_shadows[i]].light, p_render_data->shadow_atlas, p_render_data->render_shadows[p_render_data->directional_shadows[i]].pass, p_render_data->render_shadows[p_render_data->directional_shadows[i]].instances, camera_plane, lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, false, i == p_render_data->directional_shadows.size() - 1, false, p_render_data->render_info, viewport_size, p_render_data->scene_data->cam_transform); + _render_shadow_pass(p_render_data->render_shadows[p_render_data->directional_shadows[i]].light, p_render_data->shadow_atlas, p_render_data->render_shadows[p_render_data->directional_shadows[i]].pass, p_render_data->render_shadows[p_render_data->directional_shadows[i]].instances, lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, false, i == p_render_data->directional_shadows.size() - 1, false, p_render_data->render_info, viewport_size, p_render_data->scene_data->cam_transform); } //render positional shadows for (uint32_t i = 0; i < p_render_data->shadows.size(); i++) { - _render_shadow_pass(p_render_data->render_shadows[p_render_data->shadows[i]].light, p_render_data->shadow_atlas, p_render_data->render_shadows[p_render_data->shadows[i]].pass, p_render_data->render_shadows[p_render_data->shadows[i]].instances, camera_plane, lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, i == 0, i == p_render_data->shadows.size() - 1, true, p_render_data->render_info, viewport_size, p_render_data->scene_data->cam_transform); + _render_shadow_pass(p_render_data->render_shadows[p_render_data->shadows[i]].light, p_render_data->shadow_atlas, p_render_data->render_shadows[p_render_data->shadows[i]].pass, p_render_data->render_shadows[p_render_data->shadows[i]].instances, lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, i == 0, i == p_render_data->shadows.size() - 1, true, p_render_data->render_info, viewport_size, p_render_data->scene_data->cam_transform); } _render_shadow_process(); @@ -2371,7 +2370,7 @@ void RenderForwardClustered::_render_buffers_debug_draw(const RenderDataRD *p_re } } -void RenderForwardClustered::_render_shadow_pass(RID p_light, RID p_shadow_atlas, int p_pass, const PagedArray<RenderGeometryInstance *> &p_instances, const Plane &p_camera_plane, float p_lod_distance_multiplier, float p_screen_mesh_lod_threshold, bool p_open_pass, bool p_close_pass, bool p_clear_region, RenderingMethod::RenderInfo *p_render_info, const Size2i &p_viewport_size, const Transform3D &p_main_cam_transform) { +void RenderForwardClustered::_render_shadow_pass(RID p_light, RID p_shadow_atlas, int p_pass, const PagedArray<RenderGeometryInstance *> &p_instances, float p_lod_distance_multiplier, float p_screen_mesh_lod_threshold, bool p_open_pass, bool p_close_pass, bool p_clear_region, RenderingMethod::RenderInfo *p_render_info, const Size2i &p_viewport_size, const Transform3D &p_main_cam_transform) { RendererRD::LightStorage *light_storage = RendererRD::LightStorage::get_singleton(); ERR_FAIL_COND(!light_storage->owns_light_instance(p_light)); @@ -2526,7 +2525,7 @@ void RenderForwardClustered::_render_shadow_pass(RID p_light, RID p_shadow_atlas if (render_cubemap) { //rendering to cubemap - _render_shadow_append(render_fb, p_instances, light_projection, light_transform, zfar, 0, 0, reverse_cull_face, false, false, use_pancake, p_camera_plane, p_lod_distance_multiplier, p_screen_mesh_lod_threshold, Rect2(), false, true, true, true, p_render_info, p_viewport_size, p_main_cam_transform); + _render_shadow_append(render_fb, p_instances, light_projection, light_transform, zfar, 0, 0, reverse_cull_face, false, false, use_pancake, p_lod_distance_multiplier, p_screen_mesh_lod_threshold, Rect2(), false, true, true, true, p_render_info, p_viewport_size, p_main_cam_transform); if (finalize_cubemap) { _render_shadow_process(); _render_shadow_end(); @@ -2544,7 +2543,7 @@ void RenderForwardClustered::_render_shadow_pass(RID p_light, RID p_shadow_atlas } else { //render shadow - _render_shadow_append(render_fb, p_instances, light_projection, light_transform, zfar, 0, 0, reverse_cull_face, using_dual_paraboloid, using_dual_paraboloid_flip, use_pancake, p_camera_plane, p_lod_distance_multiplier, p_screen_mesh_lod_threshold, atlas_rect, flip_y, p_clear_region, p_open_pass, p_close_pass, p_render_info, p_viewport_size, p_main_cam_transform); + _render_shadow_append(render_fb, p_instances, light_projection, light_transform, zfar, 0, 0, reverse_cull_face, using_dual_paraboloid, using_dual_paraboloid_flip, use_pancake, p_lod_distance_multiplier, p_screen_mesh_lod_threshold, atlas_rect, flip_y, p_clear_region, p_open_pass, p_close_pass, p_render_info, p_viewport_size, p_main_cam_transform); } } @@ -2557,7 +2556,7 @@ void RenderForwardClustered::_render_shadow_begin() { scene_state.instance_data[RENDER_LIST_SECONDARY].clear(); } -void RenderForwardClustered::_render_shadow_append(RID p_framebuffer, const PagedArray<RenderGeometryInstance *> &p_instances, const Projection &p_projection, const Transform3D &p_transform, float p_zfar, float p_bias, float p_normal_bias, bool p_reverse_cull_face, bool p_use_dp, bool p_use_dp_flip, bool p_use_pancake, const Plane &p_camera_plane, float p_lod_distance_multiplier, float p_screen_mesh_lod_threshold, const Rect2i &p_rect, bool p_flip_y, bool p_clear_region, bool p_begin, bool p_end, RenderingMethod::RenderInfo *p_render_info, const Size2i &p_viewport_size, const Transform3D &p_main_cam_transform) { +void RenderForwardClustered::_render_shadow_append(RID p_framebuffer, const PagedArray<RenderGeometryInstance *> &p_instances, const Projection &p_projection, const Transform3D &p_transform, float p_zfar, float p_bias, float p_normal_bias, bool p_reverse_cull_face, bool p_use_dp, bool p_use_dp_flip, bool p_use_pancake, float p_lod_distance_multiplier, float p_screen_mesh_lod_threshold, const Rect2i &p_rect, bool p_flip_y, bool p_clear_region, bool p_begin, bool p_end, RenderingMethod::RenderInfo *p_render_info, const Size2i &p_viewport_size, const Transform3D &p_main_cam_transform) { uint32_t shadow_pass_index = scene_state.shadow_passes.size(); SceneState::ShadowPass shadow_pass; @@ -2615,7 +2614,6 @@ void RenderForwardClustered::_render_shadow_append(RID p_framebuffer, const Page shadow_pass.pass_mode = pass_mode; shadow_pass.rp_uniform_set = RID(); //will be filled later when instance buffer is complete - shadow_pass.camera_plane = p_camera_plane; shadow_pass.screen_mesh_lod_threshold = scene_data.screen_mesh_lod_threshold; shadow_pass.lod_distance_multiplier = scene_data.lod_distance_multiplier; diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h index 1f12d92754..ae9e5e7c10 100644 --- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h +++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h @@ -347,7 +347,6 @@ class RenderForwardClustered : public RendererSceneRenderRD { PassMode pass_mode; RID rp_uniform_set; - Plane camera_plane; float lod_distance_multiplier; float screen_mesh_lod_threshold; @@ -594,9 +593,9 @@ class RenderForwardClustered : public RendererSceneRenderRD { /* Render shadows */ - void _render_shadow_pass(RID p_light, RID p_shadow_atlas, int p_pass, const PagedArray<RenderGeometryInstance *> &p_instances, const Plane &p_camera_plane = Plane(), float p_lod_distance_multiplier = 0, float p_screen_mesh_lod_threshold = 0.0, bool p_open_pass = true, bool p_close_pass = true, bool p_clear_region = true, RenderingMethod::RenderInfo *p_render_info = nullptr, const Size2i &p_viewport_size = Size2i(1, 1), const Transform3D &p_main_cam_transform = Transform3D()); + void _render_shadow_pass(RID p_light, RID p_shadow_atlas, int p_pass, const PagedArray<RenderGeometryInstance *> &p_instances, float p_lod_distance_multiplier = 0, float p_screen_mesh_lod_threshold = 0.0, bool p_open_pass = true, bool p_close_pass = true, bool p_clear_region = true, RenderingMethod::RenderInfo *p_render_info = nullptr, const Size2i &p_viewport_size = Size2i(1, 1), const Transform3D &p_main_cam_transform = Transform3D()); void _render_shadow_begin(); - void _render_shadow_append(RID p_framebuffer, const PagedArray<RenderGeometryInstance *> &p_instances, const Projection &p_projection, const Transform3D &p_transform, float p_zfar, float p_bias, float p_normal_bias, bool p_reverse_cull_face, bool p_use_dp, bool p_use_dp_flip, bool p_use_pancake, const Plane &p_camera_plane = Plane(), float p_lod_distance_multiplier = 0.0, float p_screen_mesh_lod_threshold = 0.0, const Rect2i &p_rect = Rect2i(), bool p_flip_y = false, bool p_clear_region = true, bool p_begin = true, bool p_end = true, RenderingMethod::RenderInfo *p_render_info = nullptr, const Size2i &p_viewport_size = Size2i(1, 1), const Transform3D &p_main_cam_transform = Transform3D()); + void _render_shadow_append(RID p_framebuffer, const PagedArray<RenderGeometryInstance *> &p_instances, const Projection &p_projection, const Transform3D &p_transform, float p_zfar, float p_bias, float p_normal_bias, bool p_reverse_cull_face, bool p_use_dp, bool p_use_dp_flip, bool p_use_pancake, float p_lod_distance_multiplier = 0.0, float p_screen_mesh_lod_threshold = 0.0, const Rect2i &p_rect = Rect2i(), bool p_flip_y = false, bool p_clear_region = true, bool p_begin = true, bool p_end = true, RenderingMethod::RenderInfo *p_render_info = nullptr, const Size2i &p_viewport_size = Size2i(1, 1), const Transform3D &p_main_cam_transform = Transform3D()); void _render_shadow_process(); void _render_shadow_end(); diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp index 878ea7bcfb..194a70dc22 100644 --- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp +++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp @@ -590,7 +590,6 @@ void RenderForwardMobile::_pre_opaque_render(RenderDataRD *p_render_data) { p_render_data->shadows.clear(); p_render_data->directional_shadows.clear(); - Plane camera_plane(-p_render_data->scene_data->cam_transform.basis.get_column(Vector3::AXIS_Z), p_render_data->scene_data->cam_transform.origin); float lod_distance_multiplier = p_render_data->scene_data->cam_projection.get_lod_multiplier(); { for (int i = 0; i < p_render_data->render_shadow_count; i++) { @@ -608,7 +607,7 @@ void RenderForwardMobile::_pre_opaque_render(RenderDataRD *p_render_data) { //cube shadows are rendered in their own way for (const int &index : p_render_data->cube_shadows) { - _render_shadow_pass(p_render_data->render_shadows[index].light, p_render_data->shadow_atlas, p_render_data->render_shadows[index].pass, p_render_data->render_shadows[index].instances, camera_plane, lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, true, true, true, p_render_data->render_info, p_render_data->scene_data->cam_transform); + _render_shadow_pass(p_render_data->render_shadows[index].light, p_render_data->shadow_atlas, p_render_data->render_shadows[index].pass, p_render_data->render_shadows[index].instances, lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, true, true, true, p_render_data->render_info, p_render_data->scene_data->cam_transform); } if (p_render_data->directional_shadows.size()) { @@ -629,11 +628,11 @@ void RenderForwardMobile::_pre_opaque_render(RenderDataRD *p_render_data) { //render directional shadows for (uint32_t i = 0; i < p_render_data->directional_shadows.size(); i++) { - _render_shadow_pass(p_render_data->render_shadows[p_render_data->directional_shadows[i]].light, p_render_data->shadow_atlas, p_render_data->render_shadows[p_render_data->directional_shadows[i]].pass, p_render_data->render_shadows[p_render_data->directional_shadows[i]].instances, camera_plane, lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, false, i == p_render_data->directional_shadows.size() - 1, false, p_render_data->render_info, p_render_data->scene_data->cam_transform); + _render_shadow_pass(p_render_data->render_shadows[p_render_data->directional_shadows[i]].light, p_render_data->shadow_atlas, p_render_data->render_shadows[p_render_data->directional_shadows[i]].pass, p_render_data->render_shadows[p_render_data->directional_shadows[i]].instances, lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, false, i == p_render_data->directional_shadows.size() - 1, false, p_render_data->render_info, p_render_data->scene_data->cam_transform); } //render positional shadows for (uint32_t i = 0; i < p_render_data->shadows.size(); i++) { - _render_shadow_pass(p_render_data->render_shadows[p_render_data->shadows[i]].light, p_render_data->shadow_atlas, p_render_data->render_shadows[p_render_data->shadows[i]].pass, p_render_data->render_shadows[p_render_data->shadows[i]].instances, camera_plane, lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, i == 0, i == p_render_data->shadows.size() - 1, true, p_render_data->render_info, p_render_data->scene_data->cam_transform); + _render_shadow_pass(p_render_data->render_shadows[p_render_data->shadows[i]].light, p_render_data->shadow_atlas, p_render_data->render_shadows[p_render_data->shadows[i]].pass, p_render_data->render_shadows[p_render_data->shadows[i]].instances, lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, i == 0, i == p_render_data->shadows.size() - 1, true, p_render_data->render_info, p_render_data->scene_data->cam_transform); } _render_shadow_process(); @@ -1115,7 +1114,7 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color /* these are being called from RendererSceneRenderRD::_pre_opaque_render */ -void RenderForwardMobile::_render_shadow_pass(RID p_light, RID p_shadow_atlas, int p_pass, const PagedArray<RenderGeometryInstance *> &p_instances, const Plane &p_camera_plane, float p_lod_distance_multiplier, float p_screen_mesh_lod_threshold, bool p_open_pass, bool p_close_pass, bool p_clear_region, RenderingMethod::RenderInfo *p_render_info, const Transform3D &p_main_cam_transform) { +void RenderForwardMobile::_render_shadow_pass(RID p_light, RID p_shadow_atlas, int p_pass, const PagedArray<RenderGeometryInstance *> &p_instances, float p_lod_distance_multiplier, float p_screen_mesh_lod_threshold, bool p_open_pass, bool p_close_pass, bool p_clear_region, RenderingMethod::RenderInfo *p_render_info, const Transform3D &p_main_cam_transform) { RendererRD::LightStorage *light_storage = RendererRD::LightStorage::get_singleton(); ERR_FAIL_COND(!light_storage->owns_light_instance(p_light)); @@ -1269,7 +1268,7 @@ void RenderForwardMobile::_render_shadow_pass(RID p_light, RID p_shadow_atlas, i if (render_cubemap) { //rendering to cubemap - _render_shadow_append(render_fb, p_instances, light_projection, light_transform, zfar, 0, 0, false, false, use_pancake, p_camera_plane, p_lod_distance_multiplier, p_screen_mesh_lod_threshold, Rect2(), false, true, true, true, p_render_info, p_main_cam_transform); + _render_shadow_append(render_fb, p_instances, light_projection, light_transform, zfar, 0, 0, false, false, use_pancake, p_lod_distance_multiplier, p_screen_mesh_lod_threshold, Rect2(), false, true, true, true, p_render_info, p_main_cam_transform); if (finalize_cubemap) { _render_shadow_process(); _render_shadow_end(); @@ -1288,7 +1287,7 @@ void RenderForwardMobile::_render_shadow_pass(RID p_light, RID p_shadow_atlas, i } else { //render shadow - _render_shadow_append(render_fb, p_instances, light_projection, light_transform, zfar, 0, 0, using_dual_paraboloid, using_dual_paraboloid_flip, use_pancake, p_camera_plane, p_lod_distance_multiplier, p_screen_mesh_lod_threshold, atlas_rect, flip_y, p_clear_region, p_open_pass, p_close_pass, p_render_info, p_main_cam_transform); + _render_shadow_append(render_fb, p_instances, light_projection, light_transform, zfar, 0, 0, using_dual_paraboloid, using_dual_paraboloid_flip, use_pancake, p_lod_distance_multiplier, p_screen_mesh_lod_threshold, atlas_rect, flip_y, p_clear_region, p_open_pass, p_close_pass, p_render_info, p_main_cam_transform); } } @@ -1300,7 +1299,7 @@ void RenderForwardMobile::_render_shadow_begin() { render_list[RENDER_LIST_SECONDARY].clear(); } -void RenderForwardMobile::_render_shadow_append(RID p_framebuffer, const PagedArray<RenderGeometryInstance *> &p_instances, const Projection &p_projection, const Transform3D &p_transform, float p_zfar, float p_bias, float p_normal_bias, bool p_use_dp, bool p_use_dp_flip, bool p_use_pancake, const Plane &p_camera_plane, float p_lod_distance_multiplier, float p_screen_mesh_lod_threshold, const Rect2i &p_rect, bool p_flip_y, bool p_clear_region, bool p_begin, bool p_end, RenderingMethod::RenderInfo *p_render_info, const Transform3D &p_main_cam_transform) { +void RenderForwardMobile::_render_shadow_append(RID p_framebuffer, const PagedArray<RenderGeometryInstance *> &p_instances, const Projection &p_projection, const Transform3D &p_transform, float p_zfar, float p_bias, float p_normal_bias, bool p_use_dp, bool p_use_dp_flip, bool p_use_pancake, float p_lod_distance_multiplier, float p_screen_mesh_lod_threshold, const Rect2i &p_rect, bool p_flip_y, bool p_clear_region, bool p_begin, bool p_end, RenderingMethod::RenderInfo *p_render_info, const Transform3D &p_main_cam_transform) { uint32_t shadow_pass_index = scene_state.shadow_passes.size(); SceneState::ShadowPass shadow_pass; @@ -1357,7 +1356,6 @@ void RenderForwardMobile::_render_shadow_append(RID p_framebuffer, const PagedAr shadow_pass.pass_mode = pass_mode; shadow_pass.rp_uniform_set = RID(); //will be filled later when instance buffer is complete - shadow_pass.camera_plane = p_camera_plane; shadow_pass.screen_mesh_lod_threshold = scene_data.screen_mesh_lod_threshold; shadow_pass.lod_distance_multiplier = scene_data.lod_distance_multiplier; @@ -1879,25 +1877,27 @@ void RenderForwardMobile::_fill_render_list(RenderListType p_render_list, const // LOD if (p_render_data->scene_data->screen_mesh_lod_threshold > 0.0 && mesh_storage->mesh_surface_has_lod(surf->surface)) { - // Get the LOD support points on the mesh AABB. - Vector3 lod_support_min = inst->transformed_aabb.get_support(p_render_data->scene_data->cam_transform.basis.get_column(Vector3::AXIS_Z)); - Vector3 lod_support_max = inst->transformed_aabb.get_support(-p_render_data->scene_data->cam_transform.basis.get_column(Vector3::AXIS_Z)); - - // Get the distances to those points on the AABB from the camera origin. - float distance_min = (float)p_render_data->scene_data->cam_transform.origin.distance_to(lod_support_min); - float distance_max = (float)p_render_data->scene_data->cam_transform.origin.distance_to(lod_support_max); - float distance = 0.0; - if (distance_min * distance_max < 0.0) { - //crossing plane - distance = 0.0; - } else if (distance_min >= 0.0) { - distance = distance_min; - } else if (distance_max <= 0.0) { - distance = -distance_max; + // Check if camera is NOT inside the mesh AABB. + if (!inst->transformed_aabb.has_point(p_render_data->scene_data->main_cam_transform.origin)) { + // Get the LOD support points on the mesh AABB. + Vector3 lod_support_min = inst->transformed_aabb.get_support(p_render_data->scene_data->main_cam_transform.basis.get_column(Vector3::AXIS_Z)); + Vector3 lod_support_max = inst->transformed_aabb.get_support(-p_render_data->scene_data->main_cam_transform.basis.get_column(Vector3::AXIS_Z)); + + // Get the distances to those points on the AABB from the camera origin. + float distance_min = (float)p_render_data->scene_data->main_cam_transform.origin.distance_to(lod_support_min); + float distance_max = (float)p_render_data->scene_data->main_cam_transform.origin.distance_to(lod_support_max); + + if (distance_min * distance_max < 0.0) { + //crossing plane + distance = 0.0; + } else if (distance_min >= 0.0) { + distance = distance_min; + } else if (distance_max <= 0.0) { + distance = -distance_max; + } } - if (p_render_data->scene_data->cam_orthogonal) { distance = 1.0; } diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h index f29503e5ec..aa1b8f34b2 100644 --- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h +++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h @@ -178,9 +178,9 @@ private: /* Render shadows */ - void _render_shadow_pass(RID p_light, RID p_shadow_atlas, int p_pass, const PagedArray<RenderGeometryInstance *> &p_instances, const Plane &p_camera_plane = Plane(), float p_lod_distance_multiplier = 0, float p_screen_mesh_lod_threshold = 0.0, bool p_open_pass = true, bool p_close_pass = true, bool p_clear_region = true, RenderingMethod::RenderInfo *p_render_info = nullptr, const Transform3D &p_main_cam_transform = Transform3D()); + void _render_shadow_pass(RID p_light, RID p_shadow_atlas, int p_pass, const PagedArray<RenderGeometryInstance *> &p_instances, float p_lod_distance_multiplier = 0, float p_screen_mesh_lod_threshold = 0.0, bool p_open_pass = true, bool p_close_pass = true, bool p_clear_region = true, RenderingMethod::RenderInfo *p_render_info = nullptr, const Transform3D &p_main_cam_transform = Transform3D()); void _render_shadow_begin(); - void _render_shadow_append(RID p_framebuffer, const PagedArray<RenderGeometryInstance *> &p_instances, const Projection &p_projection, const Transform3D &p_transform, float p_zfar, float p_bias, float p_normal_bias, bool p_use_dp, bool p_use_dp_flip, bool p_use_pancake, const Plane &p_camera_plane = Plane(), float p_lod_distance_multiplier = 0.0, float p_screen_mesh_lod_threshold = 0.0, const Rect2i &p_rect = Rect2i(), bool p_flip_y = false, bool p_clear_region = true, bool p_begin = true, bool p_end = true, RenderingMethod::RenderInfo *p_render_info = nullptr, const Transform3D &p_main_cam_transform = Transform3D()); + void _render_shadow_append(RID p_framebuffer, const PagedArray<RenderGeometryInstance *> &p_instances, const Projection &p_projection, const Transform3D &p_transform, float p_zfar, float p_bias, float p_normal_bias, bool p_use_dp, bool p_use_dp_flip, bool p_use_pancake, float p_lod_distance_multiplier = 0.0, float p_screen_mesh_lod_threshold = 0.0, const Rect2i &p_rect = Rect2i(), bool p_flip_y = false, bool p_clear_region = true, bool p_begin = true, bool p_end = true, RenderingMethod::RenderInfo *p_render_info = nullptr, const Transform3D &p_main_cam_transform = Transform3D()); void _render_shadow_process(); void _render_shadow_end(); @@ -270,7 +270,6 @@ private: PassMode pass_mode; RID rp_uniform_set; - Plane camera_plane; float lod_distance_multiplier; float screen_mesh_lod_threshold; diff --git a/servers/rendering/renderer_scene_cull.cpp b/servers/rendering/renderer_scene_cull.cpp index 96c0479ac3..b02d3def88 100644 --- a/servers/rendering/renderer_scene_cull.cpp +++ b/servers/rendering/renderer_scene_cull.cpp @@ -958,6 +958,8 @@ void RendererSceneCull::instance_set_blend_shape_weight(RID p_instance, int p_sh if (instance->mesh_instance.is_valid()) { RSG::mesh_storage->mesh_instance_set_blend_shape_weight(instance->mesh_instance, p_shape, p_weight); } + + _instance_queue_update(instance, false, false); } void RendererSceneCull::instance_set_surface_override_material(RID p_instance, int p_surface, RID p_material) { diff --git a/servers/rendering/rendering_device.cpp b/servers/rendering/rendering_device.cpp index 15e1731823..0227472d0e 100644 --- a/servers/rendering/rendering_device.cpp +++ b/servers/rendering/rendering_device.cpp @@ -3332,12 +3332,15 @@ Error RenderingDevice::_draw_list_setup_framebuffer(Framebuffer *p_framebuffer, } Error RenderingDevice::_draw_list_render_pass_begin(Framebuffer *p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_colors, float p_clear_depth, uint32_t p_clear_stencil, Point2i p_viewport_offset, Point2i p_viewport_size, RDD::FramebufferID p_framebuffer_driver_id, RDD::RenderPassID p_render_pass) { - LocalVector<RDD::RenderPassClearValue> clear_values; - LocalVector<RDG::ResourceTracker *> resource_trackers; - LocalVector<RDG::ResourceUsage> resource_usages; + thread_local LocalVector<RDD::RenderPassClearValue> clear_values; + thread_local LocalVector<RDG::ResourceTracker *> resource_trackers; + thread_local LocalVector<RDG::ResourceUsage> resource_usages; bool uses_color = false; bool uses_depth = false; + clear_values.clear(); clear_values.resize(p_framebuffer->texture_ids.size()); + resource_trackers.clear(); + resource_usages.clear(); int clear_values_count = 0; { int color_index = 0; @@ -4734,6 +4737,10 @@ String RenderingDevice::get_device_api_name() const { return driver->get_api_name(); } +bool RenderingDevice::is_composite_alpha_supported() const { + return driver->is_composite_alpha_supported(main_queue); +} + String RenderingDevice::get_device_api_version() const { return driver->get_api_version(); } diff --git a/servers/rendering/rendering_device.h b/servers/rendering/rendering_device.h index 42773fc347..d0fa4ab1fa 100644 --- a/servers/rendering/rendering_device.h +++ b/servers/rendering/rendering_device.h @@ -1360,6 +1360,8 @@ public: String get_device_api_version() const; String get_device_pipeline_cache_uuid() const; + bool is_composite_alpha_supported() const; + uint64_t get_driver_resource(DriverResource p_resource, RID p_rid = RID(), uint64_t p_index = 0); static RenderingDevice *get_singleton(); diff --git a/servers/rendering/rendering_device_driver.h b/servers/rendering/rendering_device_driver.h index e9464ba321..f9a861426a 100644 --- a/servers/rendering/rendering_device_driver.h +++ b/servers/rendering/rendering_device_driver.h @@ -769,6 +769,8 @@ public: virtual String get_pipeline_cache_uuid() const = 0; virtual const Capabilities &get_capabilities() const = 0; + virtual bool is_composite_alpha_supported(CommandQueueID p_queue) const { return false; } + /******************/ virtual ~RenderingDeviceDriver(); diff --git a/servers/rendering/shader_compiler.cpp b/servers/rendering/shader_compiler.cpp index 87f608bfe6..a4ee33ecc0 100644 --- a/servers/rendering/shader_compiler.cpp +++ b/servers/rendering/shader_compiler.cpp @@ -362,7 +362,7 @@ void ShaderCompiler::_dump_function_deps(const SL::ShaderNode *p_node, const Str } header += _constr(fnode->arguments[i].is_const); if (fnode->arguments[i].type == SL::TYPE_STRUCT) { - header += _qualstr(fnode->arguments[i].qualifier) + _mkid(fnode->arguments[i].type_str) + " " + _mkid(fnode->arguments[i].name); + header += _qualstr(fnode->arguments[i].qualifier) + _mkid(fnode->arguments[i].struct_name) + " " + _mkid(fnode->arguments[i].name); } else { header += _qualstr(fnode->arguments[i].qualifier) + _prestr(fnode->arguments[i].precision) + _typestr(fnode->arguments[i].type) + " " + _mkid(fnode->arguments[i].name); } @@ -743,7 +743,7 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene gcode += _constr(true); gcode += _prestr(cnode.precision, ShaderLanguage::is_float_type(cnode.type)); if (cnode.type == SL::TYPE_STRUCT) { - gcode += _mkid(cnode.type_str); + gcode += _mkid(cnode.struct_name); } else { gcode += _typestr(cnode.type); } diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp index 5a02980929..1a4c8ea742 100644 --- a/servers/rendering/shader_language.cpp +++ b/servers/rendering/shader_language.cpp @@ -1389,7 +1389,7 @@ bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_rea *r_data_type = function->arguments[i].type; } if (r_struct_name) { - *r_struct_name = function->arguments[i].type_str; + *r_struct_name = function->arguments[i].struct_name; } if (r_array_size) { *r_array_size = function->arguments[i].array_size; @@ -1442,7 +1442,7 @@ bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_rea *r_array_size = shader->constants[p_identifier].array_size; } if (r_struct_name) { - *r_struct_name = shader->constants[p_identifier].type_str; + *r_struct_name = shader->constants[p_identifier].struct_name; } if (r_constant_value) { if (shader->constants[p_identifier].initializer && shader->constants[p_identifier].initializer->values.size() == 1) { @@ -3432,7 +3432,7 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionI } String func_arg_name; if (pfunc->arguments[j].type == TYPE_STRUCT) { - func_arg_name = pfunc->arguments[j].type_str; + func_arg_name = pfunc->arguments[j].struct_name; } else { func_arg_name = get_datatype_name(pfunc->arguments[j].type); } @@ -3455,10 +3455,10 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionI for (int j = 0; j < args.size(); j++) { if (get_scalar_type(args[j]) == args[j] && p_func->arguments[j + 1]->type == Node::NODE_TYPE_CONSTANT && args3[j] == 0 && convert_constant(static_cast<ConstantNode *>(p_func->arguments[j + 1]), pfunc->arguments[j].type)) { //all good, but it needs implicit conversion later - } else if (args[j] != pfunc->arguments[j].type || (args[j] == TYPE_STRUCT && args2[j] != pfunc->arguments[j].type_str) || args3[j] != pfunc->arguments[j].array_size) { + } else if (args[j] != pfunc->arguments[j].type || (args[j] == TYPE_STRUCT && args2[j] != pfunc->arguments[j].struct_name) || args3[j] != pfunc->arguments[j].array_size) { String func_arg_name; if (pfunc->arguments[j].type == TYPE_STRUCT) { - func_arg_name = pfunc->arguments[j].type_str; + func_arg_name = pfunc->arguments[j].struct_name; } else { func_arg_name = get_datatype_name(pfunc->arguments[j].type); } @@ -9228,7 +9228,7 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f ShaderNode::Constant constant; constant.name = name; constant.type = is_struct ? TYPE_STRUCT : type; - constant.type_str = struct_name; + constant.struct_name = struct_name; constant.precision = precision; constant.initializer = nullptr; constant.array_size = array_size; @@ -9407,7 +9407,7 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f expr->datatype = constant.type; - expr->struct_name = constant.type_str; + expr->struct_name = constant.struct_name; expr->array_size = constant.array_size; @@ -9748,7 +9748,7 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f FunctionNode::Argument arg; arg.type = param_type; arg.name = param_name; - arg.type_str = param_struct_name; + arg.struct_name = param_struct_name; arg.precision = param_precision; arg.qualifier = param_qualifier; arg.tex_argument_check = false; @@ -10371,7 +10371,11 @@ Error ShaderLanguage::complete(const String &p_code, const ShaderCompileInfo &p_ if (shader->vfunctions[i].name == completion_function) { String calltip; - calltip += get_datatype_name(shader->vfunctions[i].function->return_type); + if (shader->vfunctions[i].function->return_type == TYPE_STRUCT) { + calltip += String(shader->vfunctions[i].function->return_struct_name); + } else { + calltip += get_datatype_name(shader->vfunctions[i].function->return_type); + } if (shader->vfunctions[i].function->return_array_size > 0) { calltip += "["; @@ -10406,7 +10410,11 @@ Error ShaderLanguage::complete(const String &p_code, const ShaderCompileInfo &p_ } } - calltip += get_datatype_name(shader->vfunctions[i].function->arguments[j].type); + if (shader->vfunctions[i].function->arguments[j].type == TYPE_STRUCT) { + calltip += String(shader->vfunctions[i].function->arguments[j].struct_name); + } else { + calltip += get_datatype_name(shader->vfunctions[i].function->arguments[j].type); + } calltip += " "; calltip += shader->vfunctions[i].function->arguments[j].name; diff --git a/servers/rendering/shader_language.h b/servers/rendering/shader_language.h index 816a202b50..38f304ff31 100644 --- a/servers/rendering/shader_language.h +++ b/servers/rendering/shader_language.h @@ -583,7 +583,7 @@ public: ArgumentQualifier qualifier; StringName name; DataType type; - StringName type_str; + StringName struct_name; DataPrecision precision; //for passing textures as arguments bool tex_argument_check; @@ -618,7 +618,7 @@ public: struct Constant { StringName name; DataType type; - StringName type_str; + StringName struct_name; DataPrecision precision; ConstantNode *initializer = nullptr; int array_size; diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp index 5cfd8d3cd2..7637d4e7da 100644 --- a/servers/rendering_server.cpp +++ b/servers/rendering_server.cpp @@ -2229,6 +2229,7 @@ void RenderingServer::_bind_methods() { BIND_CONSTANT(MAX_GLOW_LEVELS); BIND_CONSTANT(MAX_CURSORS); BIND_CONSTANT(MAX_2D_DIRECTIONAL_LIGHTS); + BIND_CONSTANT(MAX_MESH_SURFACES); /* TEXTURE */ diff --git a/servers/xr_server.cpp b/servers/xr_server.cpp index 2cfe98ea1e..a4e68afee0 100644 --- a/servers/xr_server.cpp +++ b/servers/xr_server.cpp @@ -98,6 +98,8 @@ void XRServer::_bind_methods() { BIND_ENUM_CONSTANT(RESET_BUT_KEEP_TILT); BIND_ENUM_CONSTANT(DONT_RESET_ROTATION); + ADD_SIGNAL(MethodInfo("reference_frame_changed")); + ADD_SIGNAL(MethodInfo("interface_added", PropertyInfo(Variant::STRING_NAME, "interface_name"))); ADD_SIGNAL(MethodInfo("interface_removed", PropertyInfo(Variant::STRING_NAME, "interface_name"))); @@ -213,11 +215,13 @@ void XRServer::center_on_hmd(RotationMode p_rotation_mode, bool p_keep_height) { reference_frame = new_reference_frame.inverse(); set_render_reference_frame(reference_frame); + emit_signal(SNAME("reference_frame_changed")); } void XRServer::clear_reference_frame() { reference_frame = Transform3D(); set_render_reference_frame(reference_frame); + emit_signal(SNAME("reference_frame_changed")); } void XRServer::_set_render_reference_frame(const Transform3D &p_reference_frame) { diff --git a/tests/core/math/test_transform_2d.h b/tests/core/math/test_transform_2d.h index 36d27ce7a9..6d3c80e5ca 100644 --- a/tests/core/math/test_transform_2d.h +++ b/tests/core/math/test_transform_2d.h @@ -45,48 +45,132 @@ Transform2D identity() { return Transform2D(); } +TEST_CASE("[Transform2D] Default constructor") { + Transform2D default_constructor = Transform2D(); + CHECK(default_constructor == Transform2D(Vector2(1, 0), Vector2(0, 1), Vector2(0, 0))); +} + +TEST_CASE("[Transform2D] Copy constructor") { + Transform2D T = create_dummy_transform(); + Transform2D copy_constructor = Transform2D(T); + CHECK(T == copy_constructor); +} + +TEST_CASE("[Transform2D] Constructor from angle and position") { + constexpr float ROTATION = Math_PI / 4; + const Vector2 TRANSLATION = Vector2(20, -20); + + const Transform2D test = Transform2D(ROTATION, TRANSLATION); + const Transform2D expected = Transform2D().rotated(ROTATION).translated(TRANSLATION); + CHECK(test == expected); +} + +TEST_CASE("[Transform2D] Constructor from angle, scale, skew and position") { + constexpr float ROTATION = Math_PI / 2; + const Vector2 SCALE = Vector2(2, 0.5); + constexpr float SKEW = Math_PI / 4; + const Vector2 TRANSLATION = Vector2(30, 0); + + const Transform2D test = Transform2D(ROTATION, SCALE, SKEW, TRANSLATION); + Transform2D expected = Transform2D().scaled(SCALE).rotated(ROTATION).translated(TRANSLATION); + expected.set_skew(SKEW); + + CHECK(test.is_equal_approx(expected)); +} + +TEST_CASE("[Transform2D] Constructor from raw values") { + const Transform2D test = Transform2D(1, 2, 3, 4, 5, 6); + const Transform2D expected = Transform2D(Vector2(1, 2), Vector2(3, 4), Vector2(5, 6)); + CHECK(test == expected); +} + +TEST_CASE("[Transform2D] xform") { + const Vector2 v = Vector2(2, 3); + const Transform2D T = Transform2D(Vector2(1, 2), Vector2(3, 4), Vector2(5, 6)); + const Vector2 expected = Vector2(1 * 2 + 3 * 3 + 5 * 1, 2 * 2 + 4 * 3 + 6 * 1); + CHECK(T.xform(v) == expected); +} + +TEST_CASE("[Transform2D] Basis xform") { + const Vector2 v = Vector2(2, 2); + const Transform2D T1 = Transform2D(Vector2(1, 2), Vector2(3, 4), Vector2(0, 0)); + + // Both versions should be the same when the origin is (0,0). + CHECK(T1.basis_xform(v) == T1.xform(v)); + + const Transform2D T2 = Transform2D(Vector2(1, 2), Vector2(3, 4), Vector2(5, 6)); + + // Each version should be different when the origin is not (0,0). + CHECK_FALSE(T2.basis_xform(v) == T2.xform(v)); +} + +TEST_CASE("[Transform2D] Affine inverse") { + const Transform2D orig = create_dummy_transform(); + const Transform2D affine_inverted = orig.affine_inverse(); + const Transform2D affine_inverted_again = affine_inverted.affine_inverse(); + CHECK(affine_inverted_again == orig); +} + +TEST_CASE("[Transform2D] Orthonormalized") { + const Transform2D T = create_dummy_transform(); + const Transform2D orthonormalized_T = T.orthonormalized(); + + // Check each basis has length 1. + CHECK(Math::is_equal_approx(orthonormalized_T[0].length_squared(), 1)); + CHECK(Math::is_equal_approx(orthonormalized_T[1].length_squared(), 1)); + + const Vector2 vx = Vector2(orthonormalized_T[0].x, orthonormalized_T[1].x); + const Vector2 vy = Vector2(orthonormalized_T[0].y, orthonormalized_T[1].y); + + // Check the basis are orthogonal. + CHECK(Math::is_equal_approx(orthonormalized_T.tdotx(vx), 1)); + CHECK(Math::is_equal_approx(orthonormalized_T.tdotx(vy), 0)); + CHECK(Math::is_equal_approx(orthonormalized_T.tdoty(vx), 0)); + CHECK(Math::is_equal_approx(orthonormalized_T.tdoty(vy), 1)); +} + TEST_CASE("[Transform2D] translation") { - Vector2 offset = Vector2(1, 2); + const Vector2 offset = Vector2(1, 2); // Both versions should give the same result applied to identity. CHECK(identity().translated(offset) == identity().translated_local(offset)); // Check both versions against left and right multiplications. - Transform2D orig = create_dummy_transform(); - Transform2D T = identity().translated(offset); + const Transform2D orig = create_dummy_transform(); + const Transform2D T = identity().translated(offset); CHECK(orig.translated(offset) == T * orig); CHECK(orig.translated_local(offset) == orig * T); } TEST_CASE("[Transform2D] scaling") { - Vector2 scaling = Vector2(1, 2); + const Vector2 scaling = Vector2(1, 2); // Both versions should give the same result applied to identity. CHECK(identity().scaled(scaling) == identity().scaled_local(scaling)); // Check both versions against left and right multiplications. - Transform2D orig = create_dummy_transform(); - Transform2D S = identity().scaled(scaling); + const Transform2D orig = create_dummy_transform(); + const Transform2D S = identity().scaled(scaling); CHECK(orig.scaled(scaling) == S * orig); CHECK(orig.scaled_local(scaling) == orig * S); } TEST_CASE("[Transform2D] rotation") { - real_t phi = 1.0; + constexpr real_t phi = 1.0; // Both versions should give the same result applied to identity. CHECK(identity().rotated(phi) == identity().rotated_local(phi)); // Check both versions against left and right multiplications. - Transform2D orig = create_dummy_transform(); - Transform2D R = identity().rotated(phi); + const Transform2D orig = create_dummy_transform(); + const Transform2D R = identity().rotated(phi); CHECK(orig.rotated(phi) == R * orig); CHECK(orig.rotated_local(phi) == orig * R); } TEST_CASE("[Transform2D] Interpolation") { - Transform2D rotate_scale_skew_pos = Transform2D(Math::deg_to_rad(170.0), Vector2(3.6, 8.0), Math::deg_to_rad(20.0), Vector2(2.4, 6.8)); - Transform2D rotate_scale_skew_pos_halfway = Transform2D(Math::deg_to_rad(85.0), Vector2(2.3, 4.5), Math::deg_to_rad(10.0), Vector2(1.2, 3.4)); + const Transform2D rotate_scale_skew_pos = Transform2D(Math::deg_to_rad(170.0), Vector2(3.6, 8.0), Math::deg_to_rad(20.0), Vector2(2.4, 6.8)); + const Transform2D rotate_scale_skew_pos_halfway = Transform2D(Math::deg_to_rad(85.0), Vector2(2.3, 4.5), Math::deg_to_rad(10.0), Vector2(1.2, 3.4)); Transform2D interpolated = Transform2D().interpolate_with(rotate_scale_skew_pos, 0.5); CHECK(interpolated.get_origin().is_equal_approx(rotate_scale_skew_pos_halfway.get_origin())); CHECK(interpolated.get_rotation() == doctest::Approx(rotate_scale_skew_pos_halfway.get_rotation())); @@ -98,8 +182,8 @@ TEST_CASE("[Transform2D] Interpolation") { } TEST_CASE("[Transform2D] Finite number checks") { - const Vector2 x(0, 1); - const Vector2 infinite(NAN, NAN); + const Vector2 x = Vector2(0, 1); + const Vector2 infinite = Vector2(NAN, NAN); CHECK_MESSAGE( Transform2D(x, x, x).is_finite(), diff --git a/tests/display_server_mock.h b/tests/display_server_mock.h index fd79a46c5c..e4946995a7 100644 --- a/tests/display_server_mock.h +++ b/tests/display_server_mock.h @@ -56,7 +56,7 @@ private: return drivers; } - static DisplayServer *create_func(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Error &r_error) { + static DisplayServer *create_func(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error) { r_error = OK; RasterizerDummy::make_current(); return memnew(DisplayServerMock()); diff --git a/tests/python_build/test_gles3_builder.py b/tests/python_build/test_gles3_builder.py index 6f16139eb9..b34d33bde7 100644 --- a/tests/python_build/test_gles3_builder.py +++ b/tests/python_build/test_gles3_builder.py @@ -2,7 +2,7 @@ import json import pytest -from gles3_builders import build_gles3_header, GLES3HeaderStruct +from gles3_builders import GLES3HeaderStruct, build_gles3_header @pytest.mark.parametrize( diff --git a/tests/python_build/test_glsl_builder.py b/tests/python_build/test_glsl_builder.py index 348ef8441c..9f548855ff 100644 --- a/tests/python_build/test_glsl_builder.py +++ b/tests/python_build/test_glsl_builder.py @@ -2,7 +2,7 @@ import json import pytest -from glsl_builders import build_raw_header, RAWHeaderStruct, build_rd_header, RDHeaderStruct +from glsl_builders import RAWHeaderStruct, RDHeaderStruct, build_raw_header, build_rd_header @pytest.mark.parametrize( diff --git a/tests/scene/test_instance_placeholder.h b/tests/scene/test_instance_placeholder.h new file mode 100644 index 0000000000..8e8cf7c9df --- /dev/null +++ b/tests/scene/test_instance_placeholder.h @@ -0,0 +1,532 @@ +/**************************************************************************/ +/* test_instance_placeholder.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef TEST_INSTANCE_PLACEHOLDER_H +#define TEST_INSTANCE_PLACEHOLDER_H + +#include "scene/main/instance_placeholder.h" +#include "scene/resources/packed_scene.h" + +#include "tests/test_macros.h" + +class _TestInstancePlaceholderNode : public Node { + GDCLASS(_TestInstancePlaceholderNode, Node); + +protected: + static void _bind_methods() { + ClassDB::bind_method(D_METHOD("set_int_property", "int_property"), &_TestInstancePlaceholderNode::set_int_property); + ClassDB::bind_method(D_METHOD("get_int_property"), &_TestInstancePlaceholderNode::get_int_property); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "int_property"), "set_int_property", "get_int_property"); + + ClassDB::bind_method(D_METHOD("set_reference_property", "reference_property"), &_TestInstancePlaceholderNode::set_reference_property); + ClassDB::bind_method(D_METHOD("get_reference_property"), &_TestInstancePlaceholderNode::get_reference_property); + + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "reference_property", PROPERTY_HINT_NODE_TYPE), "set_reference_property", "get_reference_property"); + + ClassDB::bind_method(D_METHOD("set_reference_array_property", "reference_array_property"), &_TestInstancePlaceholderNode::set_reference_array_property); + ClassDB::bind_method(D_METHOD("get_reference_array_property"), &_TestInstancePlaceholderNode::get_reference_array_property); + + // The hint string value "24/34:Node" is determined from existing PackedScenes with typed Array properties. + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "reference_array_property", PROPERTY_HINT_TYPE_STRING, "24/34:Node"), "set_reference_array_property", "get_reference_array_property"); + } + +public: + int int_property = 0; + + void set_int_property(int p_int) { + int_property = p_int; + } + + int get_int_property() const { + return int_property; + } + + Variant reference_property; + + void set_reference_property(const Variant &p_node) { + reference_property = p_node; + } + + Variant get_reference_property() const { + return reference_property; + } + + Array reference_array_property; + + void set_reference_array_property(const Array &p_array) { + reference_array_property = p_array; + } + + Array get_reference_array_property() const { + return reference_array_property; + } + + _TestInstancePlaceholderNode() { + reference_array_property.set_typed(Variant::OBJECT, "Node", Variant()); + } +}; + +namespace TestInstancePlaceholder { + +TEST_CASE("[SceneTree][InstancePlaceholder] Instantiate from placeholder with no overrides") { + GDREGISTER_CLASS(_TestInstancePlaceholderNode); + + SUBCASE("with non-node values") { + InstancePlaceholder *ip = memnew(InstancePlaceholder); + ip->set_name("TestScene"); + Node *root = memnew(Node); + SceneTree::get_singleton()->get_root()->add_child(root); + + root->add_child(ip); + // Create a scene to instance. + _TestInstancePlaceholderNode *scene = memnew(_TestInstancePlaceholderNode); + scene->set_int_property(12); + + // Pack the scene. + PackedScene *packed_scene = memnew(PackedScene); + const Error err = packed_scene->pack(scene); + REQUIRE(err == OK); + + // Instantiate the scene. + _TestInstancePlaceholderNode *created = Object::cast_to<_TestInstancePlaceholderNode>(ip->create_instance(true, packed_scene)); + REQUIRE(created != nullptr); + CHECK(created->get_name() == "TestScene"); + CHECK(created->get_int_property() == 12); + + root->queue_free(); + memdelete(scene); + } + + SUBCASE("with node value") { + InstancePlaceholder *ip = memnew(InstancePlaceholder); + ip->set_name("TestScene"); + Node *root = memnew(Node); + SceneTree::get_singleton()->get_root()->add_child(root); + + root->add_child(ip); + // Create a scene to instance. + _TestInstancePlaceholderNode *scene = memnew(_TestInstancePlaceholderNode); + Node *referenced = memnew(Node); + scene->add_child(referenced); + referenced->set_owner(scene); + scene->set_reference_property(referenced); + // Pack the scene. + PackedScene *packed_scene = memnew(PackedScene); + const Error err = packed_scene->pack(scene); + REQUIRE(err == OK); + + // Instantiate the scene. + _TestInstancePlaceholderNode *created = Object::cast_to<_TestInstancePlaceholderNode>(ip->create_instance(true, packed_scene)); + REQUIRE(created != nullptr); + CHECK(created->get_name() == "TestScene"); + CHECK(created->get_child_count() == 1); + CHECK(created->get_reference_property().identity_compare(created->get_child(0, false))); + CHECK_FALSE(created->get_reference_property().identity_compare(referenced)); + + root->queue_free(); + memdelete(scene); + } + + SUBCASE("with node-array value") { + InstancePlaceholder *ip = memnew(InstancePlaceholder); + ip->set_name("TestScene"); + Node *root = memnew(Node); + SceneTree::get_singleton()->get_root()->add_child(root); + + root->add_child(ip); + // Create a scene to instance. + _TestInstancePlaceholderNode *scene = memnew(_TestInstancePlaceholderNode); + Node *referenced1 = memnew(Node); + Node *referenced2 = memnew(Node); + scene->add_child(referenced1); + scene->add_child(referenced2); + referenced1->set_owner(scene); + referenced2->set_owner(scene); + Array node_array; + node_array.set_typed(Variant::OBJECT, "Node", Variant()); + node_array.push_back(referenced1); + node_array.push_back(referenced2); + scene->set_reference_array_property(node_array); + // Pack the scene. + PackedScene *packed_scene = memnew(PackedScene); + const Error err = packed_scene->pack(scene); + REQUIRE(err == OK); + + // Instantiate the scene. + _TestInstancePlaceholderNode *created = Object::cast_to<_TestInstancePlaceholderNode>(ip->create_instance(true, packed_scene)); + REQUIRE(created != nullptr); + CHECK(created->get_name() == "TestScene"); + CHECK(created->get_child_count() == 2); + Array created_array = created->get_reference_array_property(); + REQUIRE(created_array.size() == node_array.size()); + REQUIRE(created_array.size() == created->get_child_count()); + + // Iterate over all nodes, since the ordering is not guaranteed. + for (int i = 0; i < node_array.size(); i++) { + bool node_found = false; + for (int j = 0; j < created->get_child_count(); j++) { + if (created_array[i].identity_compare(created->get_child(j, true))) { + node_found = true; + } + } + CHECK(node_found); + } + root->queue_free(); + memdelete(scene); + } +} + +TEST_CASE("[SceneTree][InstancePlaceholder] Instantiate from placeholder with overrides") { + GDREGISTER_CLASS(_TestInstancePlaceholderNode); + + SUBCASE("with non-node values") { + InstancePlaceholder *ip = memnew(InstancePlaceholder); + Node *root = memnew(Node); + SceneTree::get_singleton()->get_root()->add_child(root); + + root->add_child(ip); + ip->set_name("TestScene"); + ip->set("int_property", 45); + // Create a scene to pack. + _TestInstancePlaceholderNode *scene = memnew(_TestInstancePlaceholderNode); + scene->set_int_property(12); + + // Pack the scene. + PackedScene *packed_scene = memnew(PackedScene); + packed_scene->pack(scene); + + // Instantiate the scene. + _TestInstancePlaceholderNode *created = Object::cast_to<_TestInstancePlaceholderNode>(ip->create_instance(true, packed_scene)); + REQUIRE(created != nullptr); + CHECK(created->get_int_property() == 45); + + root->queue_free(); + memdelete(scene); + } + + SUBCASE("with node values") { + InstancePlaceholder *ip = memnew(InstancePlaceholder); + ip->set_name("TestScene"); + Node *root = memnew(Node); + Node *overriding = memnew(Node); + SceneTree::get_singleton()->get_root()->add_child(root); + + root->add_child(ip); + root->add_child(overriding); + ip->set("reference_property", overriding); + // Create a scene to instance. + _TestInstancePlaceholderNode *scene = memnew(_TestInstancePlaceholderNode); + Node *referenced = memnew(Node); + scene->add_child(referenced); + referenced->set_owner(scene); + scene->set_reference_property(referenced); + // Pack the scene. + PackedScene *packed_scene = memnew(PackedScene); + const Error err = packed_scene->pack(scene); + REQUIRE(err == OK); + + // Instantiate the scene. + _TestInstancePlaceholderNode *created = Object::cast_to<_TestInstancePlaceholderNode>(ip->create_instance(true, packed_scene)); + REQUIRE(created != nullptr); + CHECK(created->get_name() == "TestScene"); + CHECK(created->get_child_count() == 1); + CHECK(created->get_reference_property().identity_compare(overriding)); + CHECK_FALSE(created->get_reference_property().identity_compare(referenced)); + + root->queue_free(); + memdelete(scene); + } + + SUBCASE("with node-array value") { + InstancePlaceholder *ip = memnew(InstancePlaceholder); + ip->set_name("TestScene"); + Node *root = memnew(Node); + SceneTree::get_singleton()->get_root()->add_child(root); + + Node *override1 = memnew(Node); + Node *override2 = memnew(Node); + Node *override3 = memnew(Node); + root->add_child(ip); + root->add_child(override1); + root->add_child(override2); + root->add_child(override3); + + Array override_node_array; + override_node_array.set_typed(Variant::OBJECT, "Node", Variant()); + override_node_array.push_back(override1); + override_node_array.push_back(override2); + override_node_array.push_back(override3); + + ip->set("reference_array_property", override_node_array); + + // Create a scene to instance. + _TestInstancePlaceholderNode *scene = memnew(_TestInstancePlaceholderNode); + Node *referenced1 = memnew(Node); + Node *referenced2 = memnew(Node); + + scene->add_child(referenced1); + scene->add_child(referenced2); + + referenced1->set_owner(scene); + referenced2->set_owner(scene); + Array referenced_array; + referenced_array.set_typed(Variant::OBJECT, "Node", Variant()); + referenced_array.push_back(referenced1); + referenced_array.push_back(referenced2); + + scene->set_reference_array_property(referenced_array); + // Pack the scene. + PackedScene *packed_scene = memnew(PackedScene); + const Error err = packed_scene->pack(scene); + REQUIRE(err == OK); + + // Instantiate the scene. + _TestInstancePlaceholderNode *created = Object::cast_to<_TestInstancePlaceholderNode>(ip->create_instance(true, packed_scene)); + REQUIRE(created != nullptr); + CHECK(created->get_name() == "TestScene"); + CHECK(created->get_child_count() == 2); + Array created_array = created->get_reference_array_property(); + REQUIRE_FALSE(created_array.size() == referenced_array.size()); + REQUIRE(created_array.size() == override_node_array.size()); + REQUIRE_FALSE(created_array.size() == created->get_child_count()); + + // Iterate over all nodes, since the ordering is not guaranteed. + for (int i = 0; i < override_node_array.size(); i++) { + bool node_found = false; + for (int j = 0; j < created_array.size(); j++) { + if (override_node_array[i].identity_compare(created_array[j])) { + node_found = true; + } + } + CHECK(node_found); + } + root->queue_free(); + memdelete(scene); + } +} + +TEST_CASE("[SceneTree][InstancePlaceholder] Instance a PackedScene containing an InstancePlaceholder with no overrides") { + GDREGISTER_CLASS(_TestInstancePlaceholderNode); + + // Create the internal scene. + _TestInstancePlaceholderNode *internal = memnew(_TestInstancePlaceholderNode); + internal->set_name("InternalNode"); + Node *referenced = memnew(Node); + referenced->set_name("OriginalReference"); + internal->add_child(referenced); + referenced->set_owner(internal); + internal->set_reference_property(referenced); + + // Pack the internal scene. + PackedScene *internal_scene = memnew(PackedScene); + Error err = internal_scene->pack(internal); + REQUIRE(err == OK); + + const String internal_path = OS::get_singleton()->get_cache_path().path_join("instance_placeholder_test_internal.tscn"); + err = ResourceSaver::save(internal_scene, internal_path); + REQUIRE(err == OK); + + Ref<PackedScene> internal_scene_loaded = ResourceLoader::load(internal_path, "PackedScene", ResourceFormatLoader::CacheMode::CACHE_MODE_IGNORE, &err); + REQUIRE(err == OK); + + // Create the main scene. + Node *root = memnew(Node); + root->set_name("MainNode"); + Node *overriding = memnew(Node); + overriding->set_name("OverridingReference"); + + _TestInstancePlaceholderNode *internal_created = Object::cast_to<_TestInstancePlaceholderNode>(internal_scene_loaded->instantiate(PackedScene::GEN_EDIT_STATE_MAIN_INHERITED)); + internal_created->set_scene_instance_load_placeholder(true); + root->add_child(internal_created); + internal_created->set_owner(root); + + root->add_child(overriding); + overriding->set_owner(root); + // Here we introduce an error, we override the property with an internal node to the instance placeholder. + // The InstancePlaceholder is now forced to properly resolve the Node. + internal_created->set("reference_property", NodePath("OriginalReference")); + + // Pack the main scene. + PackedScene *main_scene = memnew(PackedScene); + err = main_scene->pack(root); + REQUIRE(err == OK); + + const String main_path = OS::get_singleton()->get_cache_path().path_join("instance_placeholder_test_main.tscn"); + err = ResourceSaver::save(main_scene, main_path); + REQUIRE(err == OK); + + // // Instantiate the scene. + Ref<PackedScene> main_scene_loaded = ResourceLoader::load(main_path, "PackedScene", ResourceFormatLoader::CacheMode::CACHE_MODE_IGNORE, &err); + REQUIRE(err == OK); + + Node *instanced_main_node = main_scene_loaded->instantiate(); + REQUIRE(instanced_main_node != nullptr); + SceneTree::get_singleton()->get_root()->add_child(instanced_main_node); + CHECK(instanced_main_node->get_name() == "MainNode"); + REQUIRE(instanced_main_node->get_child_count() == 2); + InstancePlaceholder *instanced_placeholder = Object::cast_to<InstancePlaceholder>(instanced_main_node->get_child(0, true)); + REQUIRE(instanced_placeholder != nullptr); + + _TestInstancePlaceholderNode *final_node = Object::cast_to<_TestInstancePlaceholderNode>(instanced_placeholder->create_instance(true)); + REQUIRE(final_node != nullptr); + REQUIRE(final_node->get_child_count() == 1); + REQUIRE(final_node->get_reference_property().identity_compare(final_node->get_child(0, true))); + + instanced_main_node->queue_free(); + memdelete(overriding); + memdelete(root); + memdelete(internal); + DirAccess::remove_file_or_error(internal_path); + DirAccess::remove_file_or_error(main_path); +} + +TEST_CASE("[SceneTree][InstancePlaceholder] Instance a PackedScene containing an InstancePlaceholder with overrides") { + GDREGISTER_CLASS(_TestInstancePlaceholderNode); + + // Create the internal scene. + _TestInstancePlaceholderNode *internal = memnew(_TestInstancePlaceholderNode); + internal->set_name("InternalNode"); + Node *referenced = memnew(Node); + referenced->set_name("OriginalReference"); + internal->add_child(referenced); + referenced->set_owner(internal); + internal->set_reference_property(referenced); + + Node *array_ref1 = memnew(Node); + array_ref1->set_name("ArrayRef1"); + internal->add_child(array_ref1); + array_ref1->set_owner(internal); + Node *array_ref2 = memnew(Node); + array_ref2->set_name("ArrayRef2"); + internal->add_child(array_ref2); + array_ref2->set_owner(internal); + Array referenced_array; + referenced_array.set_typed(Variant::OBJECT, "Node", Variant()); + referenced_array.push_back(array_ref1); + referenced_array.push_back(array_ref2); + internal->set_reference_array_property(referenced_array); + + // Pack the internal scene. + PackedScene *internal_scene = memnew(PackedScene); + Error err = internal_scene->pack(internal); + REQUIRE(err == OK); + + const String internal_path = OS::get_singleton()->get_cache_path().path_join("instance_placeholder_test_internal_override.tscn"); + err = ResourceSaver::save(internal_scene, internal_path); + REQUIRE(err == OK); + + Ref<PackedScene> internal_scene_loaded = ResourceLoader::load(internal_path, "PackedScene", ResourceFormatLoader::CacheMode::CACHE_MODE_IGNORE, &err); + REQUIRE(err == OK); + + // Create the main scene. + Node *root = memnew(Node); + root->set_name("MainNode"); + Node *overriding = memnew(Node); + overriding->set_name("OverridingReference"); + Node *array_ext = memnew(Node); + array_ext->set_name("ExternalArrayMember"); + + _TestInstancePlaceholderNode *internal_created = Object::cast_to<_TestInstancePlaceholderNode>(internal_scene_loaded->instantiate(PackedScene::GEN_EDIT_STATE_MAIN_INHERITED)); + internal_created->set_scene_instance_load_placeholder(true); + root->add_child(internal_created); + internal_created->set_owner(root); + + root->add_child(overriding); + overriding->set_owner(root); + root->add_child(array_ext); + array_ext->set_owner(root); + // Here we introduce an error, we override the property with an internal node to the instance placeholder. + // The InstancePlaceholder is now forced to properly resolve the Node. + internal_created->set_reference_property(overriding); + Array internal_array = internal_created->get_reference_array_property(); + Array override_array; + override_array.set_typed(Variant::OBJECT, "Node", Variant()); + for (int i = 0; i < internal_array.size(); i++) { + override_array.push_back(internal_array[i]); + } + override_array.push_back(array_ext); + internal_created->set_reference_array_property(override_array); + + // Pack the main scene. + PackedScene *main_scene = memnew(PackedScene); + err = main_scene->pack(root); + REQUIRE(err == OK); + + const String main_path = OS::get_singleton()->get_cache_path().path_join("instance_placeholder_test_main_override.tscn"); + err = ResourceSaver::save(main_scene, main_path); + REQUIRE(err == OK); + + // // Instantiate the scene. + Ref<PackedScene> main_scene_loaded = ResourceLoader::load(main_path, "PackedScene", ResourceFormatLoader::CacheMode::CACHE_MODE_IGNORE, &err); + REQUIRE(err == OK); + + Node *instanced_main_node = main_scene_loaded->instantiate(); + REQUIRE(instanced_main_node != nullptr); + SceneTree::get_singleton()->get_root()->add_child(instanced_main_node); + CHECK(instanced_main_node->get_name() == "MainNode"); + REQUIRE(instanced_main_node->get_child_count() == 3); + InstancePlaceholder *instanced_placeholder = Object::cast_to<InstancePlaceholder>(instanced_main_node->get_child(0, true)); + REQUIRE(instanced_placeholder != nullptr); + + _TestInstancePlaceholderNode *final_node = Object::cast_to<_TestInstancePlaceholderNode>(instanced_placeholder->create_instance(true)); + REQUIRE(final_node != nullptr); + REQUIRE(final_node->get_child_count() == 3); + REQUIRE(final_node->get_reference_property().identity_compare(instanced_main_node->get_child(1, true))); + Array final_array = final_node->get_reference_array_property(); + REQUIRE(final_array.size() == 3); + Array wanted_node_array; + wanted_node_array.push_back(instanced_main_node->get_child(2, true)); // ExternalArrayMember + wanted_node_array.push_back(final_node->get_child(1, true)); // ArrayRef1 + wanted_node_array.push_back(final_node->get_child(2, true)); // ArrayRef2 + + // Iterate over all nodes, since the ordering is not guaranteed. + for (int i = 0; i < wanted_node_array.size(); i++) { + bool node_found = false; + for (int j = 0; j < final_array.size(); j++) { + if (wanted_node_array[i].identity_compare(final_array[j])) { + node_found = true; + } + } + CHECK(node_found); + } + + instanced_main_node->queue_free(); + memdelete(array_ext); + memdelete(overriding); + memdelete(root); + memdelete(internal); + DirAccess::remove_file_or_error(internal_path); + DirAccess::remove_file_or_error(main_path); +} + +} //namespace TestInstancePlaceholder + +#endif // TEST_INSTANCE_PLACEHOLDER_H diff --git a/tests/test_main.cpp b/tests/test_main.cpp index 41fc21b469..041231888b 100644 --- a/tests/test_main.cpp +++ b/tests/test_main.cpp @@ -111,6 +111,7 @@ #include "tests/scene/test_curve_3d.h" #include "tests/scene/test_gradient.h" #include "tests/scene/test_image_texture.h" +#include "tests/scene/test_instance_placeholder.h" #include "tests/scene/test_node.h" #include "tests/scene/test_node_2d.h" #include "tests/scene/test_packed_scene.h" @@ -252,7 +253,7 @@ struct GodotTestCaseListener : public doctest::IReporter { OS::get_singleton()->set_has_server_feature_callback(nullptr); for (int i = 0; i < DisplayServer::get_create_function_count(); i++) { if (String("mock") == DisplayServer::get_create_function_name(i)) { - DisplayServer::create(i, "", DisplayServer::WindowMode::WINDOW_MODE_MINIMIZED, DisplayServer::VSyncMode::VSYNC_ENABLED, 0, nullptr, Vector2i(0, 0), DisplayServer::SCREEN_PRIMARY, err); + DisplayServer::create(i, "", DisplayServer::WindowMode::WINDOW_MODE_MINIMIZED, DisplayServer::VSyncMode::VSYNC_ENABLED, 0, nullptr, Vector2i(0, 0), DisplayServer::SCREEN_PRIMARY, DisplayServer::CONTEXT_EDITOR, err); break; } } |
