diff options
413 files changed, 7171 insertions, 3182 deletions
diff --git a/.github/actions/godot-deps/action.yml b/.github/actions/godot-deps/action.yml index 881eb7d1f6..cac72c436e 100644 --- a/.github/actions/godot-deps/action.yml +++ b/.github/actions/godot-deps/action.yml @@ -23,5 +23,5 @@ runs: shell: bash run: | python -c "import sys; print(sys.version)" - python -m pip install scons==4.4.0 + python -m pip install scons==4.7.0 scons --version diff --git a/.github/workflows/first_interaction.yml b/.github/workflows/first_interaction.yml deleted file mode 100644 index 1475e07249..0000000000 --- a/.github/workflows/first_interaction.yml +++ /dev/null @@ -1,48 +0,0 @@ -name: 🔗 GHA - -on: - issues: - types: [opened] - pull_request: - branches: [master] - types: [opened] - -jobs: - check_for_first_issue: - name: 🌱 Check for first interaction - runs-on: ubuntu-latest - steps: - - uses: actions/first-interaction@v1 - with: - repo-token: ${{ secrets.GITHUB_TOKEN }} - issue-message: | - Hello from the maintainer team! - - First of all, thank you for taking the time to report an issue here. - - In case you haven't already, be sure to attach an MRP (minimal - reproduction project) if **specific** steps or assets are needed in - order to reproduce the issue. Contributors will use the MRP to - validate that the fix is working as intended. - - The time it will take to assess and fix your issue may vary. - Follow the blog about our - [new releases](https://godotengine.org/blog/release/) and - [pre-releases](https://godotengine.org/blog/pre-release/) to track - new fixes or go to - [our forum](https://forum.godotengine.org/) where users could help - you in the meantime. - - Thank you for your patience. - pr-message: | - Hi there, fellow Godot contributor! - - Thank you for your first PR. - - Be sure to join us in the - [developers chat](https://chat.godotengine.org) to talk about your - PR, especially if you need help or guidance. Don't forget to consult - the [contributing docs](https://docs.godotengine.org/en/stable/contributing/development/index.html) - too! - - Thank you for your patience. diff --git a/.gitignore b/.gitignore index 46dcf84b43..3946cc96e5 100644 --- a/.gitignore +++ b/.gitignore @@ -38,6 +38,7 @@ platform/windows/godot_res.res # Ninja build files build.ninja .ninja +run_ninja_env.bat # Generated by Godot binary .import/ diff --git a/SConstruct b/SConstruct index 73ef420a0d..2cce05a20b 100644 --- a/SConstruct +++ b/SConstruct @@ -303,11 +303,6 @@ else: selected_platform = "macos" elif sys.platform == "win32": selected_platform = "windows" - else: - print("Could not detect platform automatically. Supported platforms:") - for x in platform_list: - print("\t" + x) - print("\nPlease run SCons again and select a valid platform: platform=<string>") if selected_platform != "": print("Automatically detected platform: " + selected_platform) @@ -334,6 +329,16 @@ if selected_platform == "javascript": print('Platform "javascript" has been renamed to "web" in Godot 4. Building for platform "web".') selected_platform = "web" +if selected_platform not in platform_list: + if selected_platform == "": + print("Could not detect platform automatically.") + elif selected_platform != "list": + print(f'Invalid target platform "{selected_platform}".') + + print("The following platforms are available:\n\t{}\n".format("\n\t".join(platform_list))) + print("Please run SCons again and select a valid platform: platform=<string>.") + Exit(0 if selected_platform == "list" else 255) + # Make sure to update this to the found, valid platform as it's used through the buildsystem as the reference. # It should always be re-set after calling `opts.Update()` otherwise it uses the original input value. env_base["platform"] = selected_platform @@ -480,571 +485,551 @@ if not env_base["deprecated"]: if env_base["precision"] == "double": env_base.Append(CPPDEFINES=["REAL_T_IS_DOUBLE"]) -if selected_platform in platform_list: - tmppath = "./platform/" + selected_platform - sys.path.insert(0, tmppath) - import detect - - env = env_base.Clone() - - # Default num_jobs to local cpu count if not user specified. - # SCons has a peculiarity where user-specified options won't be overridden - # by SetOption, so we can rely on this to know if we should use our default. - initial_num_jobs = env.GetOption("num_jobs") - altered_num_jobs = initial_num_jobs + 1 - env.SetOption("num_jobs", altered_num_jobs) - if env.GetOption("num_jobs") == altered_num_jobs: - cpu_count = os.cpu_count() - if cpu_count is None: - print("Couldn't auto-detect CPU count to configure build parallelism. Specify it with the -j argument.") - else: - safer_cpu_count = cpu_count if cpu_count <= 4 else cpu_count - 1 - print( - "Auto-detected %d CPU cores available for build parallelism. Using %d cores by default. You can override it with the -j argument." - % (cpu_count, safer_cpu_count) - ) - env.SetOption("num_jobs", safer_cpu_count) - - env.extra_suffix = "" - - if env["extra_suffix"] != "": - env.extra_suffix += "." + env["extra_suffix"] - - # Environment flags - env.Append(CPPDEFINES=env.get("cppdefines", "").split()) - env.Append(CCFLAGS=env.get("ccflags", "").split()) - env.Append(CXXFLAGS=env.get("cxxflags", "").split()) - env.Append(CFLAGS=env.get("cflags", "").split()) - env.Append(LINKFLAGS=env.get("linkflags", "").split()) - - # Feature build profile - disabled_classes = [] - if env["build_profile"] != "": - print("Using feature build profile: " + env["build_profile"]) - import json +tmppath = "./platform/" + selected_platform +sys.path.insert(0, tmppath) +import detect + +env = env_base.Clone() + +# Default num_jobs to local cpu count if not user specified. +# SCons has a peculiarity where user-specified options won't be overridden +# by SetOption, so we can rely on this to know if we should use our default. +initial_num_jobs = env.GetOption("num_jobs") +altered_num_jobs = initial_num_jobs + 1 +env.SetOption("num_jobs", altered_num_jobs) +if env.GetOption("num_jobs") == altered_num_jobs: + cpu_count = os.cpu_count() + if cpu_count is None: + print("Couldn't auto-detect CPU count to configure build parallelism. Specify it with the -j argument.") + else: + safer_cpu_count = cpu_count if cpu_count <= 4 else cpu_count - 1 + print( + "Auto-detected %d CPU cores available for build parallelism. Using %d cores by default. You can override it with the -j argument." + % (cpu_count, safer_cpu_count) + ) + env.SetOption("num_jobs", safer_cpu_count) + +env.extra_suffix = "" + +if env["extra_suffix"] != "": + env.extra_suffix += "." + env["extra_suffix"] + +# Environment flags +env.Append(CPPDEFINES=env.get("cppdefines", "").split()) +env.Append(CCFLAGS=env.get("ccflags", "").split()) +env.Append(CXXFLAGS=env.get("cxxflags", "").split()) +env.Append(CFLAGS=env.get("cflags", "").split()) +env.Append(LINKFLAGS=env.get("linkflags", "").split()) + +# Feature build profile +disabled_classes = [] +if env["build_profile"] != "": + print("Using feature build profile: " + env["build_profile"]) + import json - try: - ft = json.load(open(env["build_profile"])) - if "disabled_classes" in ft: - disabled_classes = ft["disabled_classes"] - if "disabled_build_options" in ft: - dbo = ft["disabled_build_options"] - for c in dbo: - env[c] = dbo[c] - except: - print("Error opening feature build profile: " + env["build_profile"]) - Exit(255) - methods.write_disabled_classes(disabled_classes) - - # Platform specific flags. - # These can sometimes override default options. - flag_list = platform_flags[selected_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] - - # 'dev_mode' and 'production' are aliases to set default options if they haven't been - # set manually by the user. - # These need to be checked *after* platform specific flags so that different - # default values can be set (e.g. to keep LTO off for `production` on some platforms). - if env["dev_mode"]: - env["verbose"] = methods.get_cmdline_bool("verbose", True) - env["warnings"] = ARGUMENTS.get("warnings", "extra") - env["werror"] = methods.get_cmdline_bool("werror", True) - env["tests"] = methods.get_cmdline_bool("tests", True) - if env["production"]: - env["use_static_cpp"] = methods.get_cmdline_bool("use_static_cpp", True) - env["debug_symbols"] = methods.get_cmdline_bool("debug_symbols", False) - # LTO "auto" means we handle the preferred option in each platform detect.py. - env["lto"] = ARGUMENTS.get("lto", "auto") - - # Run SCU file generation script if in a SCU build. - if env["scu_build"]: - max_includes_per_scu = 8 - if env_base.dev_build == True: - max_includes_per_scu = 1024 - - read_scu_limit = int(env["scu_limit"]) - read_scu_limit = max(0, min(read_scu_limit, 1024)) - if read_scu_limit != 0: - max_includes_per_scu = read_scu_limit - - methods.set_scu_folders(scu_builders.generate_scu_files(max_includes_per_scu)) - - # Must happen after the flags' definition, as configure is when most flags - # are actually handled to change compile options, etc. - detect.configure(env) - - print(f'Building for platform "{selected_platform}", architecture "{env["arch"]}", target "{env["target"]}".') - if env.dev_build: - print("NOTE: Developer build, with debug optimization level and debug symbols (unless overridden).") - - # Set optimize and debug_symbols flags. - # "custom" means do nothing and let users set their own optimization flags. - # Needs to happen after configure to have `env.msvc` defined. - if env.msvc: - if env["debug_symbols"]: - env.Append(CCFLAGS=["/Zi", "/FS"]) - env.Append(LINKFLAGS=["/DEBUG:FULL"]) - else: - env.Append(LINKFLAGS=["/DEBUG:NONE"]) - - if env["optimize"] == "speed": - env.Append(CCFLAGS=["/O2"]) - env.Append(LINKFLAGS=["/OPT:REF"]) - elif env["optimize"] == "speed_trace": - env.Append(CCFLAGS=["/O2"]) - env.Append(LINKFLAGS=["/OPT:REF", "/OPT:NOICF"]) - elif env["optimize"] == "size": - env.Append(CCFLAGS=["/O1"]) - env.Append(LINKFLAGS=["/OPT:REF"]) - elif env["optimize"] == "debug" or env["optimize"] == "none": - env.Append(CCFLAGS=["/Od"]) + try: + ft = json.load(open(env["build_profile"])) + if "disabled_classes" in ft: + disabled_classes = ft["disabled_classes"] + if "disabled_build_options" in ft: + dbo = ft["disabled_build_options"] + for c in dbo: + env[c] = dbo[c] + except: + print("Error opening feature build profile: " + env["build_profile"]) + Exit(255) +methods.write_disabled_classes(disabled_classes) + +# Platform specific flags. +# These can sometimes override default options. +flag_list = platform_flags[selected_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] + +# 'dev_mode' and 'production' are aliases to set default options if they haven't been +# set manually by the user. +# These need to be checked *after* platform specific flags so that different +# default values can be set (e.g. to keep LTO off for `production` on some platforms). +if env["dev_mode"]: + env["verbose"] = methods.get_cmdline_bool("verbose", True) + env["warnings"] = ARGUMENTS.get("warnings", "extra") + env["werror"] = methods.get_cmdline_bool("werror", True) + env["tests"] = methods.get_cmdline_bool("tests", True) +if env["production"]: + env["use_static_cpp"] = methods.get_cmdline_bool("use_static_cpp", True) + env["debug_symbols"] = methods.get_cmdline_bool("debug_symbols", False) + # LTO "auto" means we handle the preferred option in each platform detect.py. + env["lto"] = ARGUMENTS.get("lto", "auto") + +# Run SCU file generation script if in a SCU build. +if env["scu_build"]: + max_includes_per_scu = 8 + if env_base.dev_build == True: + max_includes_per_scu = 1024 + + read_scu_limit = int(env["scu_limit"]) + read_scu_limit = max(0, min(read_scu_limit, 1024)) + if read_scu_limit != 0: + max_includes_per_scu = read_scu_limit + + methods.set_scu_folders(scu_builders.generate_scu_files(max_includes_per_scu)) + +# Must happen after the flags' definition, as configure is when most flags +# are actually handled to change compile options, etc. +detect.configure(env) + +print(f'Building for platform "{selected_platform}", architecture "{env["arch"]}", target "{env["target"]}".') +if env.dev_build: + print("NOTE: Developer build, with debug optimization level and debug symbols (unless overridden).") + +# Set optimize and debug_symbols flags. +# "custom" means do nothing and let users set their own optimization flags. +# Needs to happen after configure to have `env.msvc` defined. +if env.msvc: + if env["debug_symbols"]: + env.Append(CCFLAGS=["/Zi", "/FS"]) + env.Append(LINKFLAGS=["/DEBUG:FULL"]) else: - if env["debug_symbols"]: - # Adding dwarf-4 explicitly makes stacktraces work with clang builds, - # otherwise addr2line doesn't understand them - env.Append(CCFLAGS=["-gdwarf-4"]) - if env.dev_build: - env.Append(CCFLAGS=["-g3"]) - else: - env.Append(CCFLAGS=["-g2"]) + env.Append(LINKFLAGS=["/DEBUG:NONE"]) + + if env["optimize"] == "speed": + env.Append(CCFLAGS=["/O2"]) + env.Append(LINKFLAGS=["/OPT:REF"]) + elif env["optimize"] == "speed_trace": + env.Append(CCFLAGS=["/O2"]) + env.Append(LINKFLAGS=["/OPT:REF", "/OPT:NOICF"]) + elif env["optimize"] == "size": + env.Append(CCFLAGS=["/O1"]) + env.Append(LINKFLAGS=["/OPT:REF"]) + elif env["optimize"] == "debug" or env["optimize"] == "none": + env.Append(CCFLAGS=["/Od"]) +else: + if env["debug_symbols"]: + # Adding dwarf-4 explicitly makes stacktraces work with clang builds, + # otherwise addr2line doesn't understand them + env.Append(CCFLAGS=["-gdwarf-4"]) + if env.dev_build: + env.Append(CCFLAGS=["-g3"]) else: - if methods.using_clang(env) and not methods.is_vanilla_clang(env): - # Apple Clang, its linker doesn't like -s. - env.Append(LINKFLAGS=["-Wl,-S", "-Wl,-x", "-Wl,-dead_strip"]) - else: - env.Append(LINKFLAGS=["-s"]) - - if env["optimize"] == "speed": - env.Append(CCFLAGS=["-O3"]) - # `-O2` is friendlier to debuggers than `-O3`, leading to better crash backtraces. - elif env["optimize"] == "speed_trace": - env.Append(CCFLAGS=["-O2"]) - elif env["optimize"] == "size": - env.Append(CCFLAGS=["-Os"]) - elif env["optimize"] == "debug": - env.Append(CCFLAGS=["-Og"]) - elif env["optimize"] == "none": - env.Append(CCFLAGS=["-O0"]) - - # Needs to happen after configure to handle "auto". - if env["lto"] != "none": - print("Using LTO: " + env["lto"]) - - # Set our C and C++ standard requirements. - # C++17 is required as we need guaranteed copy elision as per GH-36436. - # Prepending to make it possible to override. - # This needs to come after `configure`, otherwise we don't have env.msvc. - if not env.msvc: - # Specifying GNU extensions support explicitly, which are supported by - # both GCC and Clang. Both currently default to gnu11 and gnu++14. - env.Prepend(CFLAGS=["-std=gnu11"]) - env.Prepend(CXXFLAGS=["-std=gnu++17"]) + env.Append(CCFLAGS=["-g2"]) else: - # MSVC doesn't have clear C standard support, /std only covers C++. - # We apply it to CCFLAGS (both C and C++ code) in case it impacts C features. - env.Prepend(CCFLAGS=["/std:c++17"]) - - # Enforce our minimal compiler version requirements - cc_version = methods.get_compiler_version(env) or { - "major": None, - "minor": None, - "patch": None, - "metadata1": None, - "metadata2": None, - "date": None, - } - cc_version_major = int(cc_version["major"] or -1) - cc_version_minor = int(cc_version["minor"] or -1) - cc_version_metadata1 = cc_version["metadata1"] or "" - - if methods.using_gcc(env): - if cc_version_major == -1: - print( - "Couldn't detect compiler version, skipping version checks. " - "Build may fail if the compiler doesn't support C++17 fully." - ) - # GCC 8 before 8.4 has a regression in the support of guaranteed copy elision - # which causes a build failure: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=86521 - elif cc_version_major == 8 and cc_version_minor < 4: - print( - "Detected GCC 8 version < 8.4, which is not supported due to a " - "regression in its C++17 guaranteed copy elision support. Use a " - 'newer GCC version, or Clang 6 or later by passing "use_llvm=yes" ' - "to the SCons command line." - ) - Exit(255) - elif cc_version_major < 7: - print( - "Detected GCC version older than 7, which does not fully support " - "C++17. Supported versions are GCC 7, 9 and later. Use a newer GCC " - 'version, or Clang 6 or later by passing "use_llvm=yes" to the ' - "SCons command line." - ) - Exit(255) - elif cc_version_metadata1 == "win32": + if methods.using_clang(env) and not methods.is_vanilla_clang(env): + # Apple Clang, its linker doesn't like -s. + env.Append(LINKFLAGS=["-Wl,-S", "-Wl,-x", "-Wl,-dead_strip"]) + else: + env.Append(LINKFLAGS=["-s"]) + + if env["optimize"] == "speed": + env.Append(CCFLAGS=["-O3"]) + # `-O2` is friendlier to debuggers than `-O3`, leading to better crash backtraces. + elif env["optimize"] == "speed_trace": + env.Append(CCFLAGS=["-O2"]) + elif env["optimize"] == "size": + env.Append(CCFLAGS=["-Os"]) + elif env["optimize"] == "debug": + env.Append(CCFLAGS=["-Og"]) + elif env["optimize"] == "none": + env.Append(CCFLAGS=["-O0"]) + +# Needs to happen after configure to handle "auto". +if env["lto"] != "none": + print("Using LTO: " + env["lto"]) + +# Set our C and C++ standard requirements. +# C++17 is required as we need guaranteed copy elision as per GH-36436. +# Prepending to make it possible to override. +# This needs to come after `configure`, otherwise we don't have env.msvc. +if not env.msvc: + # Specifying GNU extensions support explicitly, which are supported by + # both GCC and Clang. Both currently default to gnu11 and gnu++14. + env.Prepend(CFLAGS=["-std=gnu11"]) + env.Prepend(CXXFLAGS=["-std=gnu++17"]) +else: + # MSVC doesn't have clear C standard support, /std only covers C++. + # We apply it to CCFLAGS (both C and C++ code) in case it impacts C features. + env.Prepend(CCFLAGS=["/std:c++17"]) + +# Enforce our minimal compiler version requirements +cc_version = methods.get_compiler_version(env) or { + "major": None, + "minor": None, + "patch": None, + "metadata1": None, + "metadata2": None, + "date": None, +} +cc_version_major = int(cc_version["major"] or -1) +cc_version_minor = int(cc_version["minor"] or -1) +cc_version_metadata1 = cc_version["metadata1"] or "" + +if methods.using_gcc(env): + if cc_version_major == -1: + print( + "Couldn't detect compiler version, skipping version checks. " + "Build may fail if the compiler doesn't support C++17 fully." + ) + # GCC 8 before 8.4 has a regression in the support of guaranteed copy elision + # which causes a build failure: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=86521 + elif cc_version_major == 8 and cc_version_minor < 4: + print( + "Detected GCC 8 version < 8.4, which is not supported due to a " + "regression in its C++17 guaranteed copy elision support. Use a " + 'newer GCC version, or Clang 6 or later by passing "use_llvm=yes" ' + "to the SCons command line." + ) + Exit(255) + elif cc_version_major < 7: + print( + "Detected GCC version older than 7, which does not fully support " + "C++17. Supported versions are GCC 7, 9 and later. Use a newer GCC " + 'version, or Clang 6 or later by passing "use_llvm=yes" to the ' + "SCons command line." + ) + Exit(255) + elif cc_version_metadata1 == "win32": + print( + "Detected mingw version is not using posix threads. Only posix " + "version of mingw is supported. " + 'Use "update-alternatives --config x86_64-w64-mingw32-g++" ' + "to switch to posix threads." + ) + Exit(255) +elif methods.using_clang(env): + if cc_version_major == -1: + print( + "Couldn't detect compiler version, skipping version checks. " + "Build may fail if the compiler doesn't support C++17 fully." + ) + # Apple LLVM versions differ from upstream LLVM version \o/, compare + # in https://en.wikipedia.org/wiki/Xcode#Toolchain_versions + elif env["platform"] == "macos" or env["platform"] == "ios": + vanilla = methods.is_vanilla_clang(env) + if vanilla and cc_version_major < 6: print( - "Detected mingw version is not using posix threads. Only posix " - "version of mingw is supported. " - 'Use "update-alternatives --config x86_64-w64-mingw32-g++" ' - "to switch to posix threads." + "Detected Clang version older than 6, which does not fully support " + "C++17. Supported versions are Clang 6 and later." ) Exit(255) - elif methods.using_clang(env): - if cc_version_major == -1: - print( - "Couldn't detect compiler version, skipping version checks. " - "Build may fail if the compiler doesn't support C++17 fully." - ) - # Apple LLVM versions differ from upstream LLVM version \o/, compare - # in https://en.wikipedia.org/wiki/Xcode#Toolchain_versions - elif env["platform"] == "macos" or env["platform"] == "ios": - vanilla = methods.is_vanilla_clang(env) - if vanilla and cc_version_major < 6: - print( - "Detected Clang version older than 6, which does not fully support " - "C++17. Supported versions are Clang 6 and later." - ) - Exit(255) - elif not vanilla and cc_version_major < 10: - print( - "Detected Apple Clang version older than 10, which does not fully " - "support C++17. Supported versions are Apple Clang 10 and later." - ) - Exit(255) - elif cc_version_major < 6: + elif not vanilla and cc_version_major < 10: print( - "Detected Clang version older than 6, which does not fully support " - "C++17. Supported versions are Clang 6 and later." + "Detected Apple Clang version older than 10, which does not fully " + "support C++17. Supported versions are Apple Clang 10 and later." ) Exit(255) + elif cc_version_major < 6: + print( + "Detected Clang version older than 6, which does not fully support " + "C++17. Supported versions are Clang 6 and later." + ) + Exit(255) - # Disable exception handling. Godot doesn't use exceptions anywhere, and this - # saves around 20% of binary size and very significant build time (GH-80513). - if env["disable_exceptions"]: - if env.msvc: - env.Append(CPPDEFINES=[("_HAS_EXCEPTIONS", 0)]) - else: - env.Append(CXXFLAGS=["-fno-exceptions"]) - elif env.msvc: - env.Append(CXXFLAGS=["/EHsc"]) - - # Configure compiler warnings - if env.msvc: # MSVC - if env["warnings"] == "no": - env.Append(CCFLAGS=["/w"]) - else: - if env["warnings"] == "extra": - env.Append(CCFLAGS=["/W4"]) - elif env["warnings"] == "all": - # C4458 is like -Wshadow. Part of /W4 but let's apply it for the default /W3 too. - env.Append(CCFLAGS=["/W3", "/w34458"]) - elif env["warnings"] == "moderate": - env.Append(CCFLAGS=["/W2"]) - # Disable warnings which we don't plan to fix. +# Disable exception handling. Godot doesn't use exceptions anywhere, and this +# saves around 20% of binary size and very significant build time (GH-80513). +if env["disable_exceptions"]: + if env.msvc: + env.Append(CPPDEFINES=[("_HAS_EXCEPTIONS", 0)]) + else: + env.Append(CXXFLAGS=["-fno-exceptions"]) +elif env.msvc: + env.Append(CXXFLAGS=["/EHsc"]) + +# Configure compiler warnings +if env.msvc: # MSVC + if env["warnings"] == "no": + env.Append(CCFLAGS=["/w"]) + else: + if env["warnings"] == "extra": + env.Append(CCFLAGS=["/W4"]) + elif env["warnings"] == "all": + # C4458 is like -Wshadow. Part of /W4 but let's apply it for the default /W3 too. + env.Append(CCFLAGS=["/W3", "/w34458"]) + elif env["warnings"] == "moderate": + env.Append(CCFLAGS=["/W2"]) + # Disable warnings which we don't plan to fix. + + env.Append( + CCFLAGS=[ + "/wd4100", # C4100 (unreferenced formal parameter): Doesn't play nice with polymorphism. + "/wd4127", # C4127 (conditional expression is constant) + "/wd4201", # C4201 (non-standard nameless struct/union): Only relevant for C89. + "/wd4244", # C4244 C4245 C4267 (narrowing conversions): Unavoidable at this scale. + "/wd4245", + "/wd4267", + "/wd4305", # C4305 (truncation): double to float or real_t, too hard to avoid. + "/wd4514", # C4514 (unreferenced inline function has been removed) + "/wd4714", # C4714 (function marked as __forceinline not inlined) + "/wd4820", # C4820 (padding added after construct) + ] + ) + + if env["werror"]: + env.Append(CCFLAGS=["/WX"]) + env.Append(LINKFLAGS=["/WX"]) +else: # GCC, Clang + common_warnings = [] + if methods.using_gcc(env): + common_warnings += ["-Wshadow", "-Wno-misleading-indentation"] + if cc_version_major == 7: # Bogus warning fixed in 8+. + common_warnings += ["-Wno-strict-overflow"] + if cc_version_major < 11: + # Regression in GCC 9/10, spams so much in our variadic templates + # that we need to outright disable it. + common_warnings += ["-Wno-type-limits"] + if cc_version_major >= 12: # False positives in our error macros, see GH-58747. + common_warnings += ["-Wno-return-type"] + elif methods.using_clang(env) or methods.using_emcc(env): + common_warnings += ["-Wshadow-field-in-constructor", "-Wshadow-uncaptured-local"] + # We often implement `operator<` for structs of pointers as a requirement + # for putting them in `Set` or `Map`. We don't mind about unreliable ordering. + common_warnings += ["-Wno-ordered-compare-function-pointers"] + + if env["warnings"] == "extra": + env.Append(CCFLAGS=["-Wall", "-Wextra", "-Wwrite-strings", "-Wno-unused-parameter"] + common_warnings) + env.Append(CXXFLAGS=["-Wctor-dtor-privacy", "-Wnon-virtual-dtor"]) + if methods.using_gcc(env): env.Append( CCFLAGS=[ - "/wd4100", # C4100 (unreferenced formal parameter): Doesn't play nice with polymorphism. - "/wd4127", # C4127 (conditional expression is constant) - "/wd4201", # C4201 (non-standard nameless struct/union): Only relevant for C89. - "/wd4244", # C4244 C4245 C4267 (narrowing conversions): Unavoidable at this scale. - "/wd4245", - "/wd4267", - "/wd4305", # C4305 (truncation): double to float or real_t, too hard to avoid. - "/wd4514", # C4514 (unreferenced inline function has been removed) - "/wd4714", # C4714 (function marked as __forceinline not inlined) - "/wd4820", # C4820 (padding added after construct) + "-Walloc-zero", + "-Wduplicated-branches", + "-Wduplicated-cond", + "-Wstringop-overflow=4", ] ) - - if env["werror"]: - env.Append(CCFLAGS=["/WX"]) - env.Append(LINKFLAGS=["/WX"]) - else: # GCC, Clang - common_warnings = [] - - if methods.using_gcc(env): - common_warnings += ["-Wshadow", "-Wno-misleading-indentation"] - if cc_version_major == 7: # Bogus warning fixed in 8+. - common_warnings += ["-Wno-strict-overflow"] - if cc_version_major < 11: - # Regression in GCC 9/10, spams so much in our variadic templates - # that we need to outright disable it. - common_warnings += ["-Wno-type-limits"] - if cc_version_major >= 12: # False positives in our error macros, see GH-58747. - common_warnings += ["-Wno-return-type"] + env.Append(CXXFLAGS=["-Wplacement-new=1"]) + # Need to fix a warning with AudioServer lambdas before enabling. + # if cc_version_major != 9: # GCC 9 had a regression (GH-36325). + # env.Append(CXXFLAGS=["-Wnoexcept"]) + if cc_version_major >= 9: + env.Append(CCFLAGS=["-Wattribute-alias=2"]) + if cc_version_major >= 11: # Broke on MethodBind templates before GCC 11. + env.Append(CCFLAGS=["-Wlogical-op"]) elif methods.using_clang(env) or methods.using_emcc(env): - common_warnings += ["-Wshadow-field-in-constructor", "-Wshadow-uncaptured-local"] - # We often implement `operator<` for structs of pointers as a requirement - # for putting them in `Set` or `Map`. We don't mind about unreliable ordering. - common_warnings += ["-Wno-ordered-compare-function-pointers"] - - if env["warnings"] == "extra": - env.Append(CCFLAGS=["-Wall", "-Wextra", "-Wwrite-strings", "-Wno-unused-parameter"] + common_warnings) - env.Append(CXXFLAGS=["-Wctor-dtor-privacy", "-Wnon-virtual-dtor"]) - if methods.using_gcc(env): - env.Append( - CCFLAGS=[ - "-Walloc-zero", - "-Wduplicated-branches", - "-Wduplicated-cond", - "-Wstringop-overflow=4", - ] - ) - env.Append(CXXFLAGS=["-Wplacement-new=1"]) - # Need to fix a warning with AudioServer lambdas before enabling. - # if cc_version_major != 9: # GCC 9 had a regression (GH-36325). - # env.Append(CXXFLAGS=["-Wnoexcept"]) - if cc_version_major >= 9: - env.Append(CCFLAGS=["-Wattribute-alias=2"]) - if cc_version_major >= 11: # Broke on MethodBind templates before GCC 11. - env.Append(CCFLAGS=["-Wlogical-op"]) - elif methods.using_clang(env) or methods.using_emcc(env): - env.Append(CCFLAGS=["-Wimplicit-fallthrough"]) - elif env["warnings"] == "all": - env.Append(CCFLAGS=["-Wall"] + common_warnings) - elif env["warnings"] == "moderate": - env.Append(CCFLAGS=["-Wall", "-Wno-unused"] + common_warnings) - else: # 'no' - env.Append(CCFLAGS=["-w"]) - - if env["werror"]: - env.Append(CCFLAGS=["-Werror"]) + env.Append(CCFLAGS=["-Wimplicit-fallthrough"]) + elif env["warnings"] == "all": + env.Append(CCFLAGS=["-Wall"] + common_warnings) + elif env["warnings"] == "moderate": + env.Append(CCFLAGS=["-Wall", "-Wno-unused"] + common_warnings) + else: # 'no' + env.Append(CCFLAGS=["-w"]) + + if env["werror"]: + env.Append(CCFLAGS=["-Werror"]) + +if hasattr(detect, "get_program_suffix"): + suffix = "." + detect.get_program_suffix() +else: + suffix = "." + selected_platform - if hasattr(detect, "get_program_suffix"): - suffix = "." + detect.get_program_suffix() - else: - suffix = "." + selected_platform +suffix += "." + env["target"] +if env.dev_build: + suffix += ".dev" - suffix += "." + env["target"] - if env.dev_build: - suffix += ".dev" +if env_base["precision"] == "double": + suffix += ".double" - if env_base["precision"] == "double": - suffix += ".double" +suffix += "." + env["arch"] - suffix += "." + env["arch"] +if not env["threads"]: + suffix += ".nothreads" - if not env["threads"]: - suffix += ".nothreads" +suffix += env.extra_suffix - suffix += env.extra_suffix +sys.path.remove(tmppath) +sys.modules.pop("detect") - sys.path.remove(tmppath) - sys.modules.pop("detect") +modules_enabled = OrderedDict() +env.module_dependencies = {} +env.module_icons_paths = [] +env.doc_class_path = platform_doc_class_path - modules_enabled = OrderedDict() - env.module_dependencies = {} - env.module_icons_paths = [] - env.doc_class_path = platform_doc_class_path +for name, path in modules_detected.items(): + if not env["module_" + name + "_enabled"]: + continue + sys.path.insert(0, path) + env.current_module = name + import config - for name, path in modules_detected.items(): - if not env["module_" + name + "_enabled"]: + if config.can_build(env, selected_platform): + # Disable it if a required dependency is missing. + if not env.module_check_dependencies(name): continue - sys.path.insert(0, path) - env.current_module = name - import config - - if config.can_build(env, selected_platform): - # Disable it if a required dependency is missing. - if not env.module_check_dependencies(name): - continue - - config.configure(env) - # Get doc classes paths (if present) - try: - doc_classes = config.get_doc_classes() - doc_path = config.get_doc_path() - for c in doc_classes: - env.doc_class_path[c] = path + "/" + doc_path - except Exception: - pass - # Get icon paths (if present) - try: - icons_path = config.get_icons_path() - env.module_icons_paths.append(path + "/" + icons_path) - except Exception: - # Default path for module icons - env.module_icons_paths.append(path + "/" + "icons") - modules_enabled[name] = path - - sys.path.remove(path) - sys.modules.pop("config") - - env.module_list = modules_enabled - methods.sort_module_list(env) - if env.editor_build: - # Add editor-specific dependencies to the dependency graph. - env.module_add_dependencies("editor", ["freetype", "svg"]) + config.configure(env) + # Get doc classes paths (if present) + try: + doc_classes = config.get_doc_classes() + doc_path = config.get_doc_path() + for c in doc_classes: + env.doc_class_path[c] = path + "/" + doc_path + except Exception: + pass + # Get icon paths (if present) + try: + icons_path = config.get_icons_path() + env.module_icons_paths.append(path + "/" + icons_path) + except Exception: + # Default path for module icons + env.module_icons_paths.append(path + "/" + "icons") + modules_enabled[name] = path - # And check if they are met. - if not env.module_check_dependencies("editor"): - print("Not all modules required by editor builds are enabled.") - Exit(255) + sys.path.remove(path) + sys.modules.pop("config") - methods.generate_version_header(env.module_version_string) - - env["PROGSUFFIX_WRAP"] = suffix + env.module_version_string + ".console" + env["PROGSUFFIX"] - env["PROGSUFFIX"] = suffix + env.module_version_string + env["PROGSUFFIX"] - env["OBJSUFFIX"] = suffix + env["OBJSUFFIX"] - # (SH)LIBSUFFIX will be used for our own built libraries - # LIBSUFFIXES contains LIBSUFFIX and SHLIBSUFFIX by default, - # so we need to append the default suffixes to keep the ability - # to link against thirdparty libraries (.a, .so, .lib, etc.). - if os.name == "nt": - # On Windows, only static libraries and import libraries can be - # statically linked - both using .lib extension - env["LIBSUFFIXES"] += [env["LIBSUFFIX"]] - else: - env["LIBSUFFIXES"] += [env["LIBSUFFIX"], env["SHLIBSUFFIX"]] - env["LIBSUFFIX"] = suffix + env["LIBSUFFIX"] - env["SHLIBSUFFIX"] = suffix + env["SHLIBSUFFIX"] +env.module_list = modules_enabled +methods.sort_module_list(env) - env["OBJPREFIX"] = env["object_prefix"] - env["SHOBJPREFIX"] = env["object_prefix"] +if env.editor_build: + # Add editor-specific dependencies to the dependency graph. + env.module_add_dependencies("editor", ["freetype", "svg"]) - if env["disable_3d"]: - if env.editor_build: - print("Build option 'disable_3d=yes' cannot be used for editor builds, only for export template builds.") - Exit(255) - else: - env.Append(CPPDEFINES=["_3D_DISABLED"]) - if env["disable_advanced_gui"]: - if env.editor_build: - print( - "Build option 'disable_advanced_gui=yes' cannot be used for editor builds, " - "only for export template builds." - ) - Exit(255) - else: - env.Append(CPPDEFINES=["ADVANCED_GUI_DISABLED"]) - if env["minizip"]: - env.Append(CPPDEFINES=["MINIZIP_ENABLED"]) - if env["brotli"]: - env.Append(CPPDEFINES=["BROTLI_ENABLED"]) - - if not env["verbose"]: - methods.no_verbose(sys, env) - - GLSL_BUILDERS = { - "RD_GLSL": env.Builder( - action=env.Run(glsl_builders.build_rd_headers), - suffix="glsl.gen.h", - src_suffix=".glsl", - ), - "GLSL_HEADER": env.Builder( - action=env.Run(glsl_builders.build_raw_headers), - suffix="glsl.gen.h", - src_suffix=".glsl", - ), - "GLES3_GLSL": env.Builder( - action=env.Run(gles3_builders.build_gles3_headers), - suffix="glsl.gen.h", - src_suffix=".glsl", - ), - } - env.Append(BUILDERS=GLSL_BUILDERS) - - scons_cache_path = os.environ.get("SCONS_CACHE") - if scons_cache_path != None: - CacheDir(scons_cache_path) - print("Scons cache enabled... (path: '" + scons_cache_path + "')") - - if env["vsproj"]: - env.vs_incs = [] - env.vs_srcs = [] - - # CompileDB and Ninja are only available with certain SCons versions which - # not everybody might have yet, so we have to check. - from SCons import __version__ as scons_raw_version - - scons_ver = env._get_major_minor_revision(scons_raw_version) - if env["compiledb"] and scons_ver < (4, 0, 0): - # Generating the compilation DB (`compile_commands.json`) requires SCons 4.0.0 or later. - print("The `compiledb=yes` option requires SCons 4.0 or later, but your version is %s." % scons_raw_version) + # And check if they are met. + if not env.module_check_dependencies("editor"): + print("Not all modules required by editor builds are enabled.") Exit(255) - if scons_ver >= (4, 0, 0): - env.Tool("compilation_db") - env.Alias("compiledb", env.CompilationDatabase()) - if env["ninja"]: - if scons_ver < (4, 2, 0): - print("The `ninja=yes` option requires SCons 4.2 or later, but your version is %s." % scons_raw_version) - Exit(255) - - SetOption("experimental", "ninja") - - # By setting this we allow the user to run ninja by themselves with all - # the flags they need, as apparently automatically running from scons - # is way slower. - SetOption("disable_execute_ninja", True) - - env.Tool("ninja") - - # Threads - if env["threads"]: - env.Append(CPPDEFINES=["THREADS_ENABLED"]) +methods.generate_version_header(env.module_version_string) + +env["PROGSUFFIX_WRAP"] = suffix + env.module_version_string + ".console" + env["PROGSUFFIX"] +env["PROGSUFFIX"] = suffix + env.module_version_string + env["PROGSUFFIX"] +env["OBJSUFFIX"] = suffix + env["OBJSUFFIX"] +# (SH)LIBSUFFIX will be used for our own built libraries +# LIBSUFFIXES contains LIBSUFFIX and SHLIBSUFFIX by default, +# so we need to append the default suffixes to keep the ability +# to link against thirdparty libraries (.a, .so, .lib, etc.). +if os.name == "nt": + # On Windows, only static libraries and import libraries can be + # statically linked - both using .lib extension + env["LIBSUFFIXES"] += [env["LIBSUFFIX"]] +else: + env["LIBSUFFIXES"] += [env["LIBSUFFIX"], env["SHLIBSUFFIX"]] +env["LIBSUFFIX"] = suffix + env["LIBSUFFIX"] +env["SHLIBSUFFIX"] = suffix + env["SHLIBSUFFIX"] - # Build subdirs, the build order is dependent on link order. - Export("env") +env["OBJPREFIX"] = env["object_prefix"] +env["SHOBJPREFIX"] = env["object_prefix"] - SConscript("core/SCsub") - SConscript("servers/SCsub") - SConscript("scene/SCsub") +if env["disable_3d"]: if env.editor_build: - SConscript("editor/SCsub") - SConscript("drivers/SCsub") - - SConscript("platform/SCsub") - SConscript("modules/SCsub") - if env["tests"]: - SConscript("tests/SCsub") - SConscript("main/SCsub") - - SConscript("platform/" + selected_platform + "/SCsub") # Build selected platform. - - # Microsoft Visual Studio Project Generation - if env["vsproj"]: - env["CPPPATH"] = [Dir(path) for path in env["CPPPATH"]] - methods.generate_vs_project(env, ARGUMENTS, env["vsproj_name"]) - methods.generate_cpp_hint_file("cpp.hint") - - # Check for the existence of headers - conf = Configure(env) - if "check_c_headers" in env: - headers = env["check_c_headers"] - for header in headers: - if conf.CheckCHeader(header): - env.AppendUnique(CPPDEFINES=[headers[header]]) - -elif selected_platform != "": - if selected_platform == "list": - print("The following platforms are available:\n") + print("Build option 'disable_3d=yes' cannot be used for editor builds, only for export template builds.") + Exit(255) else: - print('Invalid target platform "' + selected_platform + '".') - print("The following platforms were detected:\n") - - for x in platform_list: - print("\t" + x) - - print("\nPlease run SCons again and select a valid platform: platform=<string>") - - if selected_platform == "list": - # Exit early to suppress the rest of the built-in SCons messages - Exit() + env.Append(CPPDEFINES=["_3D_DISABLED"]) +if env["disable_advanced_gui"]: + if env.editor_build: + print( + "Build option 'disable_advanced_gui=yes' cannot be used for editor builds, " + "only for export template builds." + ) + Exit(255) else: + env.Append(CPPDEFINES=["ADVANCED_GUI_DISABLED"]) +if env["minizip"]: + env.Append(CPPDEFINES=["MINIZIP_ENABLED"]) +if env["brotli"]: + env.Append(CPPDEFINES=["BROTLI_ENABLED"]) + +if not env["verbose"]: + methods.no_verbose(sys, env) + +GLSL_BUILDERS = { + "RD_GLSL": env.Builder( + action=env.Run(glsl_builders.build_rd_headers), + suffix="glsl.gen.h", + src_suffix=".glsl", + ), + "GLSL_HEADER": env.Builder( + action=env.Run(glsl_builders.build_raw_headers), + suffix="glsl.gen.h", + src_suffix=".glsl", + ), + "GLES3_GLSL": env.Builder( + action=env.Run(gles3_builders.build_gles3_headers), + suffix="glsl.gen.h", + src_suffix=".glsl", + ), +} +env.Append(BUILDERS=GLSL_BUILDERS) + +scons_cache_path = os.environ.get("SCONS_CACHE") +if scons_cache_path != None: + CacheDir(scons_cache_path) + print("Scons cache enabled... (path: '" + scons_cache_path + "')") + +if env["vsproj"]: + env.vs_incs = [] + env.vs_srcs = [] + +# CompileDB and Ninja are only available with certain SCons versions which +# not everybody might have yet, so we have to check. +from SCons import __version__ as scons_raw_version + +scons_ver = env._get_major_minor_revision(scons_raw_version) +if env["compiledb"] and scons_ver < (4, 0, 0): + # Generating the compilation DB (`compile_commands.json`) requires SCons 4.0.0 or later. + print("The `compiledb=yes` option requires SCons 4.0 or later, but your version is %s." % scons_raw_version) + Exit(255) +if scons_ver >= (4, 0, 0): + env.Tool("compilation_db") + env.Alias("compiledb", env.CompilationDatabase()) + +if env["ninja"]: + if scons_ver < (4, 2, 0): + print("The `ninja=yes` option requires SCons 4.2 or later, but your version is %s." % scons_raw_version) Exit(255) -# The following only makes sense when the 'env' is defined, and assumes it is. -if "env" in locals(): - # FIXME: This method mixes both cosmetic progress stuff and cache handling... - methods.show_progress(env) - # TODO: replace this with `env.Dump(format="json")` - # once we start requiring SCons 4.0 as min version. - methods.dump(env) + SetOption("experimental", "ninja") + + # By setting this we allow the user to run ninja by themselves with all + # the flags they need, as apparently automatically running from scons + # is way slower. + SetOption("disable_execute_ninja", True) + + env.Tool("ninja") + +# Threads +if env["threads"]: + env.Append(CPPDEFINES=["THREADS_ENABLED"]) + +# Build subdirs, the build order is dependent on link order. +Export("env") + +SConscript("core/SCsub") +SConscript("servers/SCsub") +SConscript("scene/SCsub") +if env.editor_build: + SConscript("editor/SCsub") +SConscript("drivers/SCsub") + +SConscript("platform/SCsub") +SConscript("modules/SCsub") +if env["tests"]: + SConscript("tests/SCsub") +SConscript("main/SCsub") + +SConscript("platform/" + selected_platform + "/SCsub") # Build selected platform. + +# Microsoft Visual Studio Project Generation +if env["vsproj"]: + env["CPPPATH"] = [Dir(path) for path in env["CPPPATH"]] + methods.generate_vs_project(env, ARGUMENTS, env["vsproj_name"]) + methods.generate_cpp_hint_file("cpp.hint") + +# Check for the existence of headers +conf = Configure(env) +if "check_c_headers" in env: + headers = env["check_c_headers"] + for header in headers: + if conf.CheckCHeader(header): + env.AppendUnique(CPPDEFINES=[headers[header]]) + + +# FIXME: This method mixes both cosmetic progress stuff and cache handling... +methods.show_progress(env) +# TODO: replace this with `env.Dump(format="json")` +# once we start requiring SCons 4.0 as min version. +methods.dump(env) def print_elapsed_time(): diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp index 14023c5c75..352486bd09 100644 --- a/core/config/project_settings.cpp +++ b/core/config/project_settings.cpp @@ -1438,6 +1438,7 @@ ProjectSettings::ProjectSettings() { GLOBAL_DEF("application/run/disable_stdout", false); GLOBAL_DEF("application/run/disable_stderr", false); GLOBAL_DEF("application/run/print_header", true); + GLOBAL_DEF("application/run/enable_alt_space_menu", false); GLOBAL_DEF_RST("application/config/use_hidden_project_data_directory", true); GLOBAL_DEF("application/config/use_custom_user_dir", false); GLOBAL_DEF("application/config/custom_user_dir_name", ""); @@ -1538,6 +1539,7 @@ ProjectSettings::ProjectSettings() { GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/rendering_device/staging_buffer/block_size_kb", PROPERTY_HINT_RANGE, "4,2048,1,or_greater"), 256); GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/rendering_device/staging_buffer/max_size_mb", PROPERTY_HINT_RANGE, "1,1024,1,or_greater"), 128); GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/rendering_device/staging_buffer/texture_upload_region_size_px", PROPERTY_HINT_RANGE, "1,256,1,or_greater"), 64); + GLOBAL_DEF_RST(PropertyInfo(Variant::BOOL, "rendering/rendering_device/pipeline_cache/enable"), true); GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "rendering/rendering_device/pipeline_cache/save_chunk_size_mb", PROPERTY_HINT_RANGE, "0.000001,64.0,0.001,or_greater"), 3.0); GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/rendering_device/vulkan/max_descriptors_per_pool", PROPERTY_HINT_RANGE, "1,256,1,or_greater"), 64); diff --git a/core/crypto/SCsub b/core/crypto/SCsub index ac79e10d19..a6defdfdab 100644 --- a/core/crypto/SCsub +++ b/core/crypto/SCsub @@ -21,9 +21,9 @@ if is_builtin or not has_module: # to make a "light" build with only the necessary mbedtls files. if not has_module: # Minimal mbedTLS config file - env_crypto.Append( - CPPDEFINES=[("MBEDTLS_CONFIG_FILE", '\\"thirdparty/mbedtls/include/godot_core_mbedtls_config.h\\"')] - ) + config_path = "thirdparty/mbedtls/include/godot_core_mbedtls_config.h" + config_path = f"<{config_path}>" if env_crypto["ninja"] and env_crypto.msvc else f'\\"{config_path}\\"' + env_crypto.Append(CPPDEFINES=[("MBEDTLS_CONFIG_FILE", config_path)]) # Build minimal mbedTLS library (MD5/SHA/Base64/AES). env_thirdparty = env_crypto.Clone() env_thirdparty.disable_warnings() @@ -46,9 +46,9 @@ if not has_module: env.core_sources += thirdparty_obj elif is_builtin: # Module mbedTLS config file - env_crypto.Append( - CPPDEFINES=[("MBEDTLS_CONFIG_FILE", '\\"thirdparty/mbedtls/include/godot_module_mbedtls_config.h\\"')] - ) + config_path = "thirdparty/mbedtls/include/godot_module_mbedtls_config.h" + config_path = f"<{config_path}>" if env_crypto["ninja"] and env_crypto.msvc else f'\\"{config_path}\\"' + env_crypto.Append(CPPDEFINES=[("MBEDTLS_CONFIG_FILE", config_path)]) # Needed to force rebuilding the core files when the configuration file is updated. thirdparty_obj = ["#thirdparty/mbedtls/include/godot_module_mbedtls_config.h"] diff --git a/core/io/marshalls.cpp b/core/io/marshalls.cpp index b25fcccd7f..f0bbf00a1d 100644 --- a/core/io/marshalls.cpp +++ b/core/io/marshalls.cpp @@ -687,7 +687,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int (*r_len) += used; } - if (str == "script") { + if (str == "script" && value.get_type() != Variant::NIL) { ERR_FAIL_COND_V_MSG(value.get_type() != Variant::STRING, ERR_INVALID_DATA, "Invalid value for \"script\" property, expected script path as String."); String path = value; ERR_FAIL_COND_V_MSG(path.is_empty() || !path.begins_with("res://") || !ResourceLoader::exists(path, "Script"), ERR_INVALID_DATA, "Invalid script path: '" + path + "'."); diff --git a/core/io/resource.cpp b/core/io/resource.cpp index dc974a545a..74f18ceee1 100644 --- a/core/io/resource.cpp +++ b/core/io/resource.cpp @@ -424,8 +424,7 @@ RID Resource::get_rid() const { } } if (_get_extension() && _get_extension()->get_rid) { - RID ret; - ret.from_uint64(_get_extension()->get_rid(_get_extension_instance())); + RID ret = RID::from_uint64(_get_extension()->get_rid(_get_extension_instance())); if (ret.is_valid()) { return ret; } diff --git a/core/math/convex_hull.cpp b/core/math/convex_hull.cpp index 478fde3a64..80662c1b07 100644 --- a/core/math/convex_hull.cpp +++ b/core/math/convex_hull.cpp @@ -77,15 +77,17 @@ subject to the following restrictions: #ifdef DEBUG_ENABLED #define CHULL_ASSERT(m_cond) \ - do { \ + if constexpr (true) { \ if (unlikely(!(m_cond))) { \ ERR_PRINT("Assertion \"" _STR(m_cond) "\" failed."); \ } \ - } while (0) + } else \ + ((void)0) #else #define CHULL_ASSERT(m_cond) \ - do { \ - } while (0) + if constexpr (true) { \ + } else \ + ((void)0) #endif #if defined(DEBUG_CONVEX_HULL) || defined(SHOW_ITERATIONS) diff --git a/core/os/pool_allocator.cpp b/core/os/pool_allocator.cpp index acbaed4ce8..9a993cd14f 100644 --- a/core/os/pool_allocator.cpp +++ b/core/os/pool_allocator.cpp @@ -36,12 +36,13 @@ #include "core/string/print_string.h" #define COMPACT_CHUNK(m_entry, m_to_pos) \ - do { \ + if constexpr (true) { \ void *_dst = &((unsigned char *)pool)[m_to_pos]; \ void *_src = &((unsigned char *)pool)[(m_entry).pos]; \ memmove(_dst, _src, aligned((m_entry).len)); \ (m_entry).pos = m_to_pos; \ - } while (0); + } else \ + ((void)0) void PoolAllocator::mt_lock() const { } diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp index bdef1b9bbe..2b62b72a51 100644 --- a/core/string/ustring.cpp +++ b/core/string/ustring.cpp @@ -2110,12 +2110,12 @@ CharString String::utf8() const { String String::utf16(const char16_t *p_utf16, int p_len) { String ret; - ret.parse_utf16(p_utf16, p_len); + ret.parse_utf16(p_utf16, p_len, true); return ret; } -Error String::parse_utf16(const char16_t *p_utf16, int p_len) { +Error String::parse_utf16(const char16_t *p_utf16, int p_len, bool p_default_little_endian) { if (!p_utf16) { return ERR_INVALID_DATA; } @@ -2125,8 +2125,12 @@ Error String::parse_utf16(const char16_t *p_utf16, int p_len) { int cstr_size = 0; int str_size = 0; +#ifdef BIG_ENDIAN_ENABLED + bool byteswap = p_default_little_endian; +#else + bool byteswap = !p_default_little_endian; +#endif /* HANDLE BOM (Byte Order Mark) */ - bool byteswap = false; // assume correct endianness if no BOM found if (p_len < 0 || p_len >= 1) { bool has_bom = false; if (uint16_t(p_utf16[0]) == 0xfeff) { // correct BOM, read as is diff --git a/core/string/ustring.h b/core/string/ustring.h index fa904c8200..693df6dcba 100644 --- a/core/string/ustring.h +++ b/core/string/ustring.h @@ -393,7 +393,7 @@ public: static String utf8(const char *p_utf8, int p_len = -1); Char16String utf16() const; - Error parse_utf16(const char16_t *p_utf16, int p_len = -1); + Error parse_utf16(const char16_t *p_utf16, int p_len = -1, bool p_default_little_endian = true); static String utf16(const char16_t *p_utf16, int p_len = -1); static uint32_t hash(const char32_t *p_cstr, int p_len); /* hash the string */ diff --git a/core/variant/variant_op.cpp b/core/variant/variant_op.cpp index 60ae09c6f1..dcf4b287d1 100644 --- a/core/variant/variant_op.cpp +++ b/core/variant/variant_op.cpp @@ -230,18 +230,20 @@ public: }; #define register_string_op(m_op_type, m_op_code) \ - do { \ + if constexpr (true) { \ register_op<m_op_type<String, String>>(m_op_code, Variant::STRING, Variant::STRING); \ register_op<m_op_type<String, StringName>>(m_op_code, Variant::STRING, Variant::STRING_NAME); \ register_op<m_op_type<StringName, String>>(m_op_code, Variant::STRING_NAME, Variant::STRING); \ register_op<m_op_type<StringName, StringName>>(m_op_code, Variant::STRING_NAME, Variant::STRING_NAME); \ - } while (false) + } else \ + ((void)0) #define register_string_modulo_op(m_class, m_type) \ - do { \ + if constexpr (true) { \ register_op<OperatorEvaluatorStringFormat<String, m_class>>(Variant::OP_MODULE, Variant::STRING, m_type); \ register_op<OperatorEvaluatorStringFormat<StringName, m_class>>(Variant::OP_MODULE, Variant::STRING_NAME, m_type); \ - } while (false) + } else \ + ((void)0) void Variant::_register_variant_operators() { memset(operator_return_type_table, 0, sizeof(operator_return_type_table)); diff --git a/core/variant/variant_parser.cpp b/core/variant/variant_parser.cpp index 46c450d9f8..fa91758fff 100644 --- a/core/variant/variant_parser.cpp +++ b/core/variant/variant_parser.cpp @@ -1027,7 +1027,7 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream, Error err = p_res_parser->ext_func(p_res_parser->userdata, p_stream, res, line, r_err_str); if (err) { // If the file is missing, the error can be ignored. - if (err != ERR_FILE_NOT_FOUND && err != ERR_CANT_OPEN) { + if (err != ERR_FILE_NOT_FOUND && err != ERR_CANT_OPEN && err != ERR_FILE_CANT_OPEN) { return err; } } diff --git a/doc/classes/AnimatableBody3D.xml b/doc/classes/AnimatableBody3D.xml index f5c6217477..f888ca1416 100644 --- a/doc/classes/AnimatableBody3D.xml +++ b/doc/classes/AnimatableBody3D.xml @@ -8,9 +8,9 @@ When [AnimatableBody3D] is moved, its linear and angular velocity are estimated and used to affect other physics bodies in its path. This makes it useful for moving platforms, doors, and other moving objects. </description> <tutorials> - <link title="3D Physics Tests Demo">https://godotengine.org/asset-library/asset/675</link> - <link title="Third Person Shooter Demo">https://godotengine.org/asset-library/asset/678</link> - <link title="3D Voxel Demo">https://godotengine.org/asset-library/asset/676</link> + <link title="3D Physics Tests Demo">https://godotengine.org/asset-library/asset/2747</link> + <link title="Third Person Shooter (TPS) Demo">https://godotengine.org/asset-library/asset/2710</link> + <link title="3D Voxel Demo">https://godotengine.org/asset-library/asset/2755</link> </tutorials> <members> <member name="sync_to_physics" type="bool" setter="set_sync_to_physics" getter="is_sync_to_physics_enabled" default="true"> diff --git a/doc/classes/AnimatedSprite2D.xml b/doc/classes/AnimatedSprite2D.xml index 4b38773505..012ae4fe29 100644 --- a/doc/classes/AnimatedSprite2D.xml +++ b/doc/classes/AnimatedSprite2D.xml @@ -8,7 +8,7 @@ </description> <tutorials> <link title="2D Sprite animation">$DOCS_URL/tutorials/2d/2d_sprite_animation.html</link> - <link title="2D Dodge The Creeps Demo">https://godotengine.org/asset-library/asset/515</link> + <link title="2D Dodge The Creeps Demo">https://godotengine.org/asset-library/asset/2712</link> </tutorials> <methods> <method name="get_playing_speed" qualifiers="const"> diff --git a/doc/classes/AnimationMixer.xml b/doc/classes/AnimationMixer.xml index 9ccaa385db..a77e9e28c6 100644 --- a/doc/classes/AnimationMixer.xml +++ b/doc/classes/AnimationMixer.xml @@ -80,7 +80,7 @@ <param index="0" name="name" type="StringName" /> <description> Returns the first [AnimationLibrary] with key [param name] or [code]null[/code] if not found. - To get the [AnimationPlayer]'s global animation library, use [code]get_animation_library("")[/code]. + To get the [AnimationMixer]'s global animation library, use [code]get_animation_library("")[/code]. </description> </method> <method name="get_animation_library_list" qualifiers="const"> @@ -239,14 +239,14 @@ <return type="bool" /> <param index="0" name="name" type="StringName" /> <description> - Returns [code]true[/code] if the [AnimationPlayer] stores an [Animation] with key [param name]. + Returns [code]true[/code] if the [AnimationMixer] stores an [Animation] with key [param name]. </description> </method> <method name="has_animation_library" qualifiers="const"> <return type="bool" /> <param index="0" name="name" type="StringName" /> <description> - Returns [code]true[/code] if the [AnimationPlayer] stores an [AnimationLibrary] with key [param name]. + Returns [code]true[/code] if the [AnimationMixer] stores an [AnimationLibrary] with key [param name]. </description> </method> <method name="remove_animation_library"> @@ -333,9 +333,14 @@ Notifies when the caches have been cleared, either automatically, or manually via [method clear_caches]. </description> </signal> + <signal name="mixer_applied"> + <description> + Notifies when the blending result related have been applied to the target objects. + </description> + </signal> <signal name="mixer_updated"> <description> - Editor only. Notifies when the property have been updated to update dummy [AnimationPlayer] in animation player editor. + Notifies when the property related process have been updated. </description> </signal> </signals> diff --git a/doc/classes/AnimationNodeAdd3.xml b/doc/classes/AnimationNodeAdd3.xml index 12f00849cc..edd733ad34 100644 --- a/doc/classes/AnimationNodeAdd3.xml +++ b/doc/classes/AnimationNodeAdd3.xml @@ -13,6 +13,6 @@ </description> <tutorials> <link title="Using AnimationTree">$DOCS_URL/tutorials/animation/animation_tree.html</link> - <link title="Third Person Shooter Demo">https://godotengine.org/asset-library/asset/678</link> + <link title="Third Person Shooter (TPS) Demo">https://godotengine.org/asset-library/asset/2710</link> </tutorials> </class> diff --git a/doc/classes/AnimationNodeAnimation.xml b/doc/classes/AnimationNodeAnimation.xml index 5683371182..0c85e8e670 100644 --- a/doc/classes/AnimationNodeAnimation.xml +++ b/doc/classes/AnimationNodeAnimation.xml @@ -8,8 +8,8 @@ </description> <tutorials> <link title="Using AnimationTree">$DOCS_URL/tutorials/animation/animation_tree.html</link> - <link title="3D Platformer Demo">https://godotengine.org/asset-library/asset/125</link> - <link title="Third Person Shooter Demo">https://godotengine.org/asset-library/asset/678</link> + <link title="3D Platformer Demo">https://godotengine.org/asset-library/asset/2748</link> + <link title="Third Person Shooter (TPS) Demo">https://godotengine.org/asset-library/asset/2710</link> </tutorials> <members> <member name="animation" type="StringName" setter="set_animation" getter="get_animation" default="&"""> diff --git a/doc/classes/AnimationNodeBlend2.xml b/doc/classes/AnimationNodeBlend2.xml index 61df0b26b3..50d785fee7 100644 --- a/doc/classes/AnimationNodeBlend2.xml +++ b/doc/classes/AnimationNodeBlend2.xml @@ -9,7 +9,7 @@ </description> <tutorials> <link title="Using AnimationTree">$DOCS_URL/tutorials/animation/animation_tree.html</link> - <link title="3D Platformer Demo">https://godotengine.org/asset-library/asset/125</link> - <link title="Third Person Shooter Demo">https://godotengine.org/asset-library/asset/678</link> + <link title="3D Platformer Demo">https://godotengine.org/asset-library/asset/2748</link> + <link title="Third Person Shooter (TPS) Demo">https://godotengine.org/asset-library/asset/2710</link> </tutorials> </class> diff --git a/doc/classes/AnimationNodeBlendSpace2D.xml b/doc/classes/AnimationNodeBlendSpace2D.xml index 47933a1fd0..596767599c 100644 --- a/doc/classes/AnimationNodeBlendSpace2D.xml +++ b/doc/classes/AnimationNodeBlendSpace2D.xml @@ -10,7 +10,7 @@ </description> <tutorials> <link title="Using AnimationTree">$DOCS_URL/tutorials/animation/animation_tree.html</link> - <link title="Third Person Shooter Demo">https://godotengine.org/asset-library/asset/678</link> + <link title="Third Person Shooter (TPS) Demo">https://godotengine.org/asset-library/asset/2710</link> </tutorials> <methods> <method name="add_blend_point"> diff --git a/doc/classes/AnimationNodeOneShot.xml b/doc/classes/AnimationNodeOneShot.xml index 6ff2d6f6db..b2a8002d74 100644 --- a/doc/classes/AnimationNodeOneShot.xml +++ b/doc/classes/AnimationNodeOneShot.xml @@ -53,7 +53,7 @@ </description> <tutorials> <link title="Using AnimationTree">$DOCS_URL/tutorials/animation/animation_tree.html</link> - <link title="Third Person Shooter Demo">https://godotengine.org/asset-library/asset/678</link> + <link title="Third Person Shooter (TPS) Demo">https://godotengine.org/asset-library/asset/2710</link> </tutorials> <members> <member name="autorestart" type="bool" setter="set_autorestart" getter="has_autorestart" default="false"> diff --git a/doc/classes/AnimationNodeOutput.xml b/doc/classes/AnimationNodeOutput.xml index f957650294..6186fdd8e1 100644 --- a/doc/classes/AnimationNodeOutput.xml +++ b/doc/classes/AnimationNodeOutput.xml @@ -8,7 +8,7 @@ </description> <tutorials> <link title="Using AnimationTree">$DOCS_URL/tutorials/animation/animation_tree.html</link> - <link title="3D Platformer Demo">https://godotengine.org/asset-library/asset/125</link> - <link title="Third Person Shooter Demo">https://godotengine.org/asset-library/asset/678</link> + <link title="3D Platformer Demo">https://godotengine.org/asset-library/asset/2748</link> + <link title="Third Person Shooter (TPS) Demo">https://godotengine.org/asset-library/asset/2710</link> </tutorials> </class> diff --git a/doc/classes/AnimationNodeTimeScale.xml b/doc/classes/AnimationNodeTimeScale.xml index 4cb8ccb962..9bf7529124 100644 --- a/doc/classes/AnimationNodeTimeScale.xml +++ b/doc/classes/AnimationNodeTimeScale.xml @@ -8,6 +8,6 @@ </description> <tutorials> <link title="Using AnimationTree">$DOCS_URL/tutorials/animation/animation_tree.html</link> - <link title="3D Platformer Demo">https://godotengine.org/asset-library/asset/125</link> + <link title="3D Platformer Demo">https://godotengine.org/asset-library/asset/2748</link> </tutorials> </class> diff --git a/doc/classes/AnimationNodeTransition.xml b/doc/classes/AnimationNodeTransition.xml index 775a208735..382166d823 100644 --- a/doc/classes/AnimationNodeTransition.xml +++ b/doc/classes/AnimationNodeTransition.xml @@ -38,8 +38,8 @@ </description> <tutorials> <link title="Using AnimationTree">$DOCS_URL/tutorials/animation/animation_tree.html</link> - <link title="3D Platformer Demo">https://godotengine.org/asset-library/asset/125</link> - <link title="Third Person Shooter Demo">https://godotengine.org/asset-library/asset/678</link> + <link title="3D Platformer Demo">https://godotengine.org/asset-library/asset/2748</link> + <link title="Third Person Shooter (TPS) Demo">https://godotengine.org/asset-library/asset/2710</link> </tutorials> <methods> <method name="is_input_loop_broken_at_end" qualifiers="const"> diff --git a/doc/classes/AnimationPlayer.xml b/doc/classes/AnimationPlayer.xml index 9d2d93b0f0..1b742bea28 100644 --- a/doc/classes/AnimationPlayer.xml +++ b/doc/classes/AnimationPlayer.xml @@ -12,7 +12,7 @@ <tutorials> <link title="2D Sprite animation">$DOCS_URL/tutorials/2d/2d_sprite_animation.html</link> <link title="Animation documentation index">$DOCS_URL/tutorials/animation/index.html</link> - <link title="Third Person Shooter Demo">https://godotengine.org/asset-library/asset/678</link> + <link title="Third Person Shooter (TPS) Demo">https://godotengine.org/asset-library/asset/2710</link> </tutorials> <methods> <method name="animation_get_next" qualifiers="const"> diff --git a/doc/classes/AnimationTree.xml b/doc/classes/AnimationTree.xml index d240a4967e..4a63b35ba0 100644 --- a/doc/classes/AnimationTree.xml +++ b/doc/classes/AnimationTree.xml @@ -9,7 +9,7 @@ </description> <tutorials> <link title="Using AnimationTree">$DOCS_URL/tutorials/animation/animation_tree.html</link> - <link title="Third Person Shooter Demo">https://godotengine.org/asset-library/asset/678</link> + <link title="Third Person Shooter (TPS) Demo">https://godotengine.org/asset-library/asset/2710</link> </tutorials> <methods> <method name="get_process_callback" qualifiers="const" deprecated="Use [member AnimationMixer.callback_mode_process] instead."> diff --git a/doc/classes/Area2D.xml b/doc/classes/Area2D.xml index a52aa80606..4ad5db2b67 100644 --- a/doc/classes/Area2D.xml +++ b/doc/classes/Area2D.xml @@ -10,9 +10,9 @@ </description> <tutorials> <link title="Using Area2D">$DOCS_URL/tutorials/physics/using_area_2d.html</link> - <link title="2D Dodge The Creeps Demo">https://godotengine.org/asset-library/asset/515</link> - <link title="2D Pong Demo">https://godotengine.org/asset-library/asset/121</link> - <link title="2D Platformer Demo">https://godotengine.org/asset-library/asset/120</link> + <link title="2D Dodge The Creeps Demo">https://godotengine.org/asset-library/asset/2712</link> + <link title="2D Pong Demo">https://godotengine.org/asset-library/asset/2728</link> + <link title="2D Platformer Demo">https://godotengine.org/asset-library/asset/2727</link> </tutorials> <methods> <method name="get_overlapping_areas" qualifiers="const"> diff --git a/doc/classes/Area3D.xml b/doc/classes/Area3D.xml index 671c824c30..4ee16d499d 100644 --- a/doc/classes/Area3D.xml +++ b/doc/classes/Area3D.xml @@ -11,8 +11,8 @@ </description> <tutorials> <link title="Using Area2D">$DOCS_URL/tutorials/physics/using_area_2d.html</link> - <link title="3D Platformer Demo">https://godotengine.org/asset-library/asset/125</link> - <link title="GUI in 3D Demo">https://godotengine.org/asset-library/asset/127</link> + <link title="3D Platformer Demo">https://godotengine.org/asset-library/asset/2748</link> + <link title="GUI in 3D Viewport Demo">https://godotengine.org/asset-library/asset/2807</link> </tutorials> <methods> <method name="get_overlapping_areas" qualifiers="const"> diff --git a/doc/classes/Array.xml b/doc/classes/Array.xml index fd5ba57615..a72ac60536 100644 --- a/doc/classes/Array.xml +++ b/doc/classes/Array.xml @@ -40,6 +40,7 @@ [/codeblocks] [b]Note:[/b] Arrays are always passed by reference. To get a copy of an array that can be modified independently of the original array, use [method duplicate]. [b]Note:[/b] Erasing elements while iterating over arrays is [b]not[/b] supported and will result in unpredictable behavior. + [b]Differences between packed arrays, typed arrays, and untyped arrays:[/b] Packed arrays are generally faster to iterate on and modify compared to a typed array of the same type (e.g. [PackedInt64Array] versus [code]Array[int][/code]). Also, packed arrays consume less memory. As a downside, packed arrays are less flexible as they don't offer as many convenience methods such as [method Array.map]. Typed arrays are in turn faster to iterate on and modify than untyped arrays. </description> <tutorials> </tutorials> @@ -57,7 +58,30 @@ <param index="2" name="class_name" type="StringName" /> <param index="3" name="script" type="Variant" /> <description> - Creates a typed array from the [param base] array. + Creates a typed array from the [param base] array. All arguments are required. + - [param type] is the built-in type as a [enum Variant.Type] constant, for example [constant TYPE_INT]. + - [param class_name] is the [b]native[/b] class name, for example [Node]. If [param type] is not [constant TYPE_OBJECT], must be an empty string. + - [param script] is the associated script. Must be a [Script] instance or [code]null[/code]. + Examples: + [codeblock] + class_name MyNode + extends Node + + class MyClass: + pass + + func _ready(): + var a = Array([], TYPE_INT, &"", null) # Array[int] + var b = Array([], TYPE_OBJECT, &"Node", null) # Array[Node] + var c = Array([], TYPE_OBJECT, &"Node", MyNode) # Array[MyNode] + var d = Array([], TYPE_OBJECT, &"RefCounted", MyClass) # Array[MyClass] + [/codeblock] + [b]Note:[/b] This constructor can be useful if you want to create a typed array on the fly, but you are not required to use it. In GDScript you can use a temporary variable with the static type you need and then pass it: + [codeblock] + func _ready(): + var a: Array[int] = [] + some_func(a) + [/codeblock] </description> </constructor> <constructor name="Array"> @@ -323,19 +347,19 @@ <method name="get_typed_builtin" qualifiers="const"> <return type="int" /> <description> - Returns the [enum Variant.Type] constant for a typed array. If the [Array] is not typed, returns [constant TYPE_NIL]. + Returns the built-in type of the typed array as a [enum Variant.Type] constant. If the array is not typed, returns [constant TYPE_NIL]. </description> </method> <method name="get_typed_class_name" qualifiers="const"> <return type="StringName" /> <description> - Returns a class name of a typed [Array] of type [constant TYPE_OBJECT]. + Returns the [b]native[/b] class name of the typed array if the built-in type is [constant TYPE_OBJECT]. Otherwise, this method returns an empty string. </description> </method> <method name="get_typed_script" qualifiers="const"> <return type="Variant" /> <description> - Returns the script associated with a typed array tied to a class name. + Returns the script associated with the typed array. This method returns a [Script] instance or [code]null[/code]. </description> </method> <method name="has" qualifiers="const" keywords="includes, contains"> diff --git a/doc/classes/AudioEffect.xml b/doc/classes/AudioEffect.xml index 4fe47ac011..bd31603517 100644 --- a/doc/classes/AudioEffect.xml +++ b/doc/classes/AudioEffect.xml @@ -9,7 +9,7 @@ </description> <tutorials> <link title="Audio buses">$DOCS_URL/tutorials/audio/audio_buses.html</link> - <link title="Audio Mic Record Demo">https://godotengine.org/asset-library/asset/527</link> + <link title="Audio Microphone Record Demo">https://godotengine.org/asset-library/asset/2760</link> </tutorials> <methods> <method name="_instantiate" qualifiers="virtual"> diff --git a/doc/classes/AudioEffectRecord.xml b/doc/classes/AudioEffectRecord.xml index 5b43c5ebaa..ed0a0c5b08 100644 --- a/doc/classes/AudioEffectRecord.xml +++ b/doc/classes/AudioEffectRecord.xml @@ -11,7 +11,7 @@ </description> <tutorials> <link title="Recording with microphone">$DOCS_URL/tutorials/audio/recording_with_microphone.html</link> - <link title="Audio Mic Record Demo">https://godotengine.org/asset-library/asset/527</link> + <link title="Audio Microphone Record Demo">https://godotengine.org/asset-library/asset/2760</link> </tutorials> <methods> <method name="get_recording" qualifiers="const"> diff --git a/doc/classes/AudioEffectReverb.xml b/doc/classes/AudioEffectReverb.xml index 2c055f3825..4ac9672a8f 100644 --- a/doc/classes/AudioEffectReverb.xml +++ b/doc/classes/AudioEffectReverb.xml @@ -8,7 +8,7 @@ </description> <tutorials> <link title="Audio buses">$DOCS_URL/tutorials/audio/audio_buses.html</link> - <link title="Third Person Shooter Demo">https://godotengine.org/asset-library/asset/678</link> + <link title="Third Person Shooter (TPS) Demo">https://godotengine.org/asset-library/asset/2710</link> </tutorials> <members> <member name="damping" type="float" setter="set_damping" getter="get_damping" default="0.5"> diff --git a/doc/classes/AudioEffectSpectrumAnalyzer.xml b/doc/classes/AudioEffectSpectrumAnalyzer.xml index eb70396433..d9312cc87d 100644 --- a/doc/classes/AudioEffectSpectrumAnalyzer.xml +++ b/doc/classes/AudioEffectSpectrumAnalyzer.xml @@ -8,7 +8,7 @@ See also [AudioStreamGenerator] for procedurally generating sounds. </description> <tutorials> - <link title="Audio Spectrum Demo">https://godotengine.org/asset-library/asset/528</link> + <link title="Audio Spectrum Visualizer Demo">https://godotengine.org/asset-library/asset/2762</link> <link title="Godot 3.2 will get new audio features">https://godotengine.org/article/godot-32-will-get-new-audio-features</link> </tutorials> <members> diff --git a/doc/classes/AudioServer.xml b/doc/classes/AudioServer.xml index 993aa581dc..b3cf53367d 100644 --- a/doc/classes/AudioServer.xml +++ b/doc/classes/AudioServer.xml @@ -8,9 +8,9 @@ </description> <tutorials> <link title="Audio buses">$DOCS_URL/tutorials/audio/audio_buses.html</link> - <link title="Audio Device Changer Demo">https://godotengine.org/asset-library/asset/525</link> - <link title="Audio Mic Record Demo">https://godotengine.org/asset-library/asset/527</link> - <link title="Audio Spectrum Demo">https://godotengine.org/asset-library/asset/528</link> + <link title="Audio Device Changer Demo">https://godotengine.org/asset-library/asset/2758</link> + <link title="Audio Microphone Record Demo">https://godotengine.org/asset-library/asset/2760</link> + <link title="Audio Spectrum Visualizer Demo">https://godotengine.org/asset-library/asset/2762</link> </tutorials> <methods> <method name="add_bus"> diff --git a/doc/classes/AudioStream.xml b/doc/classes/AudioStream.xml index 9813c2f251..4abce3f1da 100644 --- a/doc/classes/AudioStream.xml +++ b/doc/classes/AudioStream.xml @@ -8,9 +8,9 @@ </description> <tutorials> <link title="Audio streams">$DOCS_URL/tutorials/audio/audio_streams.html</link> - <link title="Audio Generator Demo">https://godotengine.org/asset-library/asset/526</link> - <link title="Audio Mic Record Demo">https://godotengine.org/asset-library/asset/527</link> - <link title="Audio Spectrum Demo">https://godotengine.org/asset-library/asset/528</link> + <link title="Audio Generator Demo">https://godotengine.org/asset-library/asset/2759</link> + <link title="Audio Microphone Record Demo">https://godotengine.org/asset-library/asset/2760</link> + <link title="Audio Spectrum Visualizer Demo">https://godotengine.org/asset-library/asset/2762</link> </tutorials> <methods> <method name="_get_beat_count" qualifiers="virtual const"> diff --git a/doc/classes/AudioStreamGenerator.xml b/doc/classes/AudioStreamGenerator.xml index 9e91d5c450..f618e69631 100644 --- a/doc/classes/AudioStreamGenerator.xml +++ b/doc/classes/AudioStreamGenerator.xml @@ -63,7 +63,7 @@ [b]Note:[/b] Due to performance constraints, this class is best used from C# or from a compiled language via GDExtension. If you still want to use this class from GDScript, consider using a lower [member mix_rate] such as 11,025 Hz or 22,050 Hz. </description> <tutorials> - <link title="Audio Generator Demo">https://godotengine.org/asset-library/asset/526</link> + <link title="Audio Generator Demo">https://godotengine.org/asset-library/asset/2759</link> </tutorials> <members> <member name="buffer_length" type="float" setter="set_buffer_length" getter="get_buffer_length" default="0.5"> diff --git a/doc/classes/AudioStreamGeneratorPlayback.xml b/doc/classes/AudioStreamGeneratorPlayback.xml index 88c5cf6dbe..2f86eaf9e9 100644 --- a/doc/classes/AudioStreamGeneratorPlayback.xml +++ b/doc/classes/AudioStreamGeneratorPlayback.xml @@ -7,7 +7,7 @@ This class is meant to be used with [AudioStreamGenerator] to play back the generated audio in real-time. </description> <tutorials> - <link title="Audio Generator Demo">https://godotengine.org/asset-library/asset/526</link> + <link title="Audio Generator Demo">https://godotengine.org/asset-library/asset/2759</link> <link title="Godot 3.2 will get new audio features">https://godotengine.org/article/godot-32-will-get-new-audio-features</link> </tutorials> <methods> diff --git a/doc/classes/AudioStreamPlayback.xml b/doc/classes/AudioStreamPlayback.xml index 460a7050c8..9f87b76a2b 100644 --- a/doc/classes/AudioStreamPlayback.xml +++ b/doc/classes/AudioStreamPlayback.xml @@ -7,7 +7,7 @@ Can play, loop, pause a scroll through audio. See [AudioStream] and [AudioStreamOggVorbis] for usage. </description> <tutorials> - <link title="Audio Generator Demo">https://godotengine.org/asset-library/asset/526</link> + <link title="Audio Generator Demo">https://godotengine.org/asset-library/asset/2759</link> </tutorials> <methods> <method name="_get_loop_count" qualifiers="virtual const"> diff --git a/doc/classes/AudioStreamPlayer.xml b/doc/classes/AudioStreamPlayer.xml index fbe2508da3..a7d0a10073 100644 --- a/doc/classes/AudioStreamPlayer.xml +++ b/doc/classes/AudioStreamPlayer.xml @@ -1,100 +1,104 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="AudioStreamPlayer" inherits="Node" keywords="sound, music, song" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> - Plays back audio non-positionally. + A node for audio playback. </brief_description> <description> - Plays an audio stream non-positionally. - To play audio positionally, use [AudioStreamPlayer2D] or [AudioStreamPlayer3D] instead of [AudioStreamPlayer]. + The [AudioStreamPlayer] node plays an audio stream non-positionally. It is ideal for user interfaces, menus, or background music. + To use this node, [member stream] needs to be set to a valid [AudioStream] resource. Playing more than one sound at the time is also supported, see [member max_polyphony]. + If you need to play audio at a specific position, use [AudioStreamPlayer2D] or [AudioStreamPlayer3D] instead. </description> <tutorials> <link title="Audio streams">$DOCS_URL/tutorials/audio/audio_streams.html</link> - <link title="2D Dodge The Creeps Demo">https://godotengine.org/asset-library/asset/515</link> - <link title="Audio Device Changer Demo">https://godotengine.org/asset-library/asset/525</link> - <link title="Audio Generator Demo">https://godotengine.org/asset-library/asset/526</link> - <link title="Audio Mic Record Demo">https://godotengine.org/asset-library/asset/527</link> - <link title="Audio Spectrum Demo">https://godotengine.org/asset-library/asset/528</link> + <link title="2D Dodge The Creeps Demo">https://godotengine.org/asset-library/asset/2712</link> + <link title="Audio Device Changer Demo">https://godotengine.org/asset-library/asset/2758</link> + <link title="Audio Generator Demo">https://godotengine.org/asset-library/asset/2759</link> + <link title="Audio Microphone Record Demo">https://godotengine.org/asset-library/asset/2760</link> + <link title="Audio Spectrum Visualizer Demo">https://godotengine.org/asset-library/asset/2762</link> </tutorials> <methods> <method name="get_playback_position"> <return type="float" /> <description> - Returns the position in the [AudioStream] in seconds. + Returns the position in the [AudioStream] of the latest sound, in seconds. Returns [code]0.0[/code] if no sounds are playing. + [b]Note:[/b] The position is not always accurate, as the [AudioServer] does not mix audio every processed frame. To get more accurate results, add [method AudioServer.get_time_since_last_mix] to the returned position. </description> </method> <method name="get_stream_playback"> <return type="AudioStreamPlayback" /> <description> - Returns the [AudioStreamPlayback] object associated with this [AudioStreamPlayer]. + Returns the latest [AudioStreamPlayback] of this node, usually the most recently created by [method play]. If no sounds are playing, this method fails and returns an empty playback. </description> </method> <method name="has_stream_playback"> <return type="bool" /> <description> - Returns whether the [AudioStreamPlayer] can return the [AudioStreamPlayback] object or not. + Returns [code]true[/code] if any sound is active, even if [member stream_paused] is set to [code]true[/code]. See also [member playing] and [method get_stream_playback]. </description> </method> <method name="play"> <return type="void" /> <param index="0" name="from_position" type="float" default="0.0" /> <description> - Plays the audio from the given [param from_position], in seconds. + Plays a sound from the beginning, or the given [param from_position] in seconds. </description> </method> <method name="seek"> <return type="void" /> <param index="0" name="to_position" type="float" /> <description> - Sets the position from which audio will be played, in seconds. + Restarts all sounds to be played from the given [param to_position], in seconds. Does nothing if no sounds are playing. </description> </method> <method name="stop"> <return type="void" /> <description> - Stops the audio. + Stops all sounds from this node. </description> </method> </methods> <members> <member name="autoplay" type="bool" setter="set_autoplay" getter="is_autoplay_enabled" default="false"> - If [code]true[/code], audio plays when added to scene tree. + If [code]true[/code], this node calls [method play] when entering the tree. </member> <member name="bus" type="StringName" setter="set_bus" getter="get_bus" default="&"Master""> - Bus on which this audio is playing. - [b]Note:[/b] When setting this property, keep in mind that no validation is performed to see if the given name matches an existing bus. This is because audio bus layouts might be loaded after this property is set. If this given name can't be resolved at runtime, it will fall back to [code]"Master"[/code]. + The target bus name. All sounds from this node will be playing on this bus. + [b]Note:[/b] At runtime, if no bus with the given name exists, all sounds will fall back on [code]"Master"[/code]. See also [method AudioServer.get_bus_name]. </member> <member name="max_polyphony" type="int" setter="set_max_polyphony" getter="get_max_polyphony" default="1"> - The maximum number of sounds this node can play at the same time. Playing additional sounds after this value is reached will cut off the oldest sounds. + The maximum number of sounds this node can play at the same time. Calling [method play] after this value is reached will cut off the oldest sounds. </member> <member name="mix_target" type="int" setter="set_mix_target" getter="get_mix_target" enum="AudioStreamPlayer.MixTarget" default="0"> - If the audio configuration has more than two speakers, this sets the target channels. See [enum MixTarget] constants. + The mix target channels, as one of the [enum MixTarget] constants. Has no effect when two speakers or less are detected (see [enum AudioServer.SpeakerMode]). </member> <member name="pitch_scale" type="float" setter="set_pitch_scale" getter="get_pitch_scale" default="1.0"> - The pitch and the tempo of the audio, as a multiplier of the audio sample's sample rate. + The audio's pitch and tempo, as a multiplier of the [member stream]'s sample rate. A value of [code]2.0[/code] doubles the audio's pitch, while a value of [code]0.5[/code] halves the pitch. </member> <member name="playing" type="bool" setter="_set_playing" getter="is_playing" default="false"> - If [code]true[/code], audio is playing. + If [code]true[/code], this node is playing sounds. Setting this property has the same effect as [method play] and [method stop]. </member> <member name="stream" type="AudioStream" setter="set_stream" getter="get_stream"> - The [AudioStream] object to be played. + The [AudioStream] resource to be played. Setting this property stops all currently playing sounds. If left empty, the [AudioStreamPlayer] does not work. </member> <member name="stream_paused" type="bool" setter="set_stream_paused" getter="get_stream_paused" default="false"> - If [code]true[/code], the playback is paused. You can resume it by setting [member stream_paused] to [code]false[/code]. + If [code]true[/code], the sounds are paused. Setting [member stream_paused] to [code]false[/code] resumes all sounds. + [b]Note:[/b] This property is automatically changed when exiting or entering the tree, or this node is paused (see [member Node.process_mode]). </member> <member name="volume_db" type="float" setter="set_volume_db" getter="get_volume_db" default="0.0"> - Volume of sound, in dB. + Volume of sound, in decibel. This is an offset of the [member stream]'s volume. + [b]Note:[/b] To convert between decibel and linear energy (like most volume sliders do), use [method @GlobalScope.db_to_linear] and [method @GlobalScope.linear_to_db]. </member> </members> <signals> <signal name="finished"> <description> - Emitted when the audio stops playing. + Emitted when a sound finishes playing without interruptions. This signal is [i]not[/i] emitted when calling [method stop], or when exiting the tree while sounds are playing. </description> </signal> </signals> <constants> <constant name="MIX_TARGET_STEREO" value="0" enum="MixTarget"> - The audio will be played only on the first channel. + The audio will be played only on the first channel. This is the default. </constant> <constant name="MIX_TARGET_SURROUND" value="1" enum="MixTarget"> The audio will be played on all surround channels. diff --git a/doc/classes/Basis.xml b/doc/classes/Basis.xml index 32077c0b89..338d9523fa 100644 --- a/doc/classes/Basis.xml +++ b/doc/classes/Basis.xml @@ -15,10 +15,10 @@ <link title="Math documentation index">$DOCS_URL/tutorials/math/index.html</link> <link title="Matrices and transforms">$DOCS_URL/tutorials/math/matrices_and_transforms.html</link> <link title="Using 3D transforms">$DOCS_URL/tutorials/3d/using_transforms.html</link> - <link title="Matrix Transform Demo">https://godotengine.org/asset-library/asset/584</link> - <link title="3D Platformer Demo">https://godotengine.org/asset-library/asset/125</link> - <link title="3D Voxel Demo">https://godotengine.org/asset-library/asset/676</link> - <link title="2.5D Demo">https://godotengine.org/asset-library/asset/583</link> + <link title="Matrix Transform Demo">https://godotengine.org/asset-library/asset/2787</link> + <link title="3D Platformer Demo">https://godotengine.org/asset-library/asset/2748</link> + <link title="3D Voxel Demo">https://godotengine.org/asset-library/asset/2755</link> + <link title="2.5D Game Demo">https://godotengine.org/asset-library/asset/2783</link> </tutorials> <constructors> <constructor name="Basis"> diff --git a/doc/classes/BoneAttachment3D.xml b/doc/classes/BoneAttachment3D.xml index 227f6817cc..bafa463f82 100644 --- a/doc/classes/BoneAttachment3D.xml +++ b/doc/classes/BoneAttachment3D.xml @@ -52,6 +52,7 @@ </member> <member name="override_pose" type="bool" setter="set_override_pose" getter="get_override_pose" default="false"> Whether the BoneAttachment3D node will override the bone pose of the bone it is attached to. When set to [code]true[/code], the BoneAttachment3D node can change the pose of the bone. When set to [code]false[/code], the BoneAttachment3D will always be set to the bone's transform. + [b]Note:[/b] This override performs interruptively in the skeleton update process using signals due to the old design. It may cause unintended behavior when used at the same time with [SkeletonModifier3D]. </member> </members> </class> diff --git a/doc/classes/BoxShape3D.xml b/doc/classes/BoxShape3D.xml index 5190e6e759..ead5afd89c 100644 --- a/doc/classes/BoxShape3D.xml +++ b/doc/classes/BoxShape3D.xml @@ -8,9 +8,9 @@ [b]Performance:[/b] [BoxShape3D] is fast to check collisions against. It is faster than [CapsuleShape3D] and [CylinderShape3D], but slower than [SphereShape3D]. </description> <tutorials> - <link title="3D Physics Tests Demo">https://godotengine.org/asset-library/asset/675</link> - <link title="3D Kinematic Character Demo">https://godotengine.org/asset-library/asset/126</link> - <link title="3D Platformer Demo">https://godotengine.org/asset-library/asset/125</link> + <link title="3D Physics Tests Demo">https://godotengine.org/asset-library/asset/2747</link> + <link title="3D Kinematic Character Demo">https://godotengine.org/asset-library/asset/2739</link> + <link title="3D Platformer Demo">https://godotengine.org/asset-library/asset/2748</link> </tutorials> <members> <member name="size" type="Vector3" setter="set_size" getter="get_size" default="Vector3(1, 1, 1)"> diff --git a/doc/classes/Button.xml b/doc/classes/Button.xml index e5b47ffb89..30df4fd10d 100644 --- a/doc/classes/Button.xml +++ b/doc/classes/Button.xml @@ -36,8 +36,8 @@ [b]Note:[/b] Buttons do not interpret touch input and therefore don't support multitouch, since mouse emulation can only press one button at a given time. Use [TouchScreenButton] for buttons that trigger gameplay movement or actions. </description> <tutorials> - <link title="2D Dodge The Creeps Demo">https://godotengine.org/asset-library/asset/515</link> - <link title="OS Test Demo">https://godotengine.org/asset-library/asset/677</link> + <link title="2D Dodge The Creeps Demo">https://godotengine.org/asset-library/asset/2712</link> + <link title="Operating System Testing Demo">https://godotengine.org/asset-library/asset/2789</link> </tutorials> <members> <member name="alignment" type="int" setter="set_text_alignment" getter="get_text_alignment" enum="HorizontalAlignment" default="1"> diff --git a/doc/classes/Camera2D.xml b/doc/classes/Camera2D.xml index 6319bfb3d1..da98e6c26a 100644 --- a/doc/classes/Camera2D.xml +++ b/doc/classes/Camera2D.xml @@ -10,9 +10,8 @@ Note that the [Camera2D] node's [code]position[/code] doesn't represent the actual position of the screen, which may differ due to applied smoothing or limits. You can use [method get_screen_center_position] to get the real position. </description> <tutorials> - <link title="2D Platformer Demo">https://godotengine.org/asset-library/asset/120</link> - <link title="2D Isometric Demo">https://godotengine.org/asset-library/asset/112</link> - <link title="2D HDR Demo">https://godotengine.org/asset-library/asset/110</link> + <link title="2D Platformer Demo">https://godotengine.org/asset-library/asset/2727</link> + <link title="2D Isometric Demo">https://godotengine.org/asset-library/asset/2718</link> </tutorials> <methods> <method name="align"> diff --git a/doc/classes/Camera3D.xml b/doc/classes/Camera3D.xml index 01890b471c..27194122d5 100644 --- a/doc/classes/Camera3D.xml +++ b/doc/classes/Camera3D.xml @@ -7,7 +7,7 @@ [Camera3D] is a special node that displays what is visible from its current location. Cameras register themselves in the nearest [Viewport] node (when ascending the tree). Only one camera can be active per viewport. If no viewport is available ascending the tree, the camera will register in the global viewport. In other words, a camera just provides 3D display capabilities to a [Viewport], and, without one, a scene registered in that [Viewport] (or higher viewports) can't be displayed. </description> <tutorials> - <link title="Third Person Shooter Demo">https://godotengine.org/asset-library/asset/678</link> + <link title="Third Person Shooter (TPS) Demo">https://godotengine.org/asset-library/asset/2710</link> </tutorials> <methods> <method name="clear_current"> diff --git a/doc/classes/CanvasItem.xml b/doc/classes/CanvasItem.xml index 72783bc5d6..1cfd44467f 100644 --- a/doc/classes/CanvasItem.xml +++ b/doc/classes/CanvasItem.xml @@ -12,7 +12,7 @@ <tutorials> <link title="Viewport and canvas transforms">$DOCS_URL/tutorials/2d/2d_transforms.html</link> <link title="Custom drawing in 2D">$DOCS_URL/tutorials/2d/custom_drawing_in_2d.html</link> - <link title="Audio Spectrum Demo">https://godotengine.org/asset-library/asset/528</link> + <link title="Audio Spectrum Visualizer Demo">https://godotengine.org/asset-library/asset/2762</link> </tutorials> <methods> <method name="_draw" qualifiers="virtual"> @@ -606,14 +606,15 @@ [b]Note:[/b] For controls that inherit [Popup], the correct way to make them visible is to call one of the multiple [code]popup*()[/code] functions instead. </member> <member name="y_sort_enabled" type="bool" setter="set_y_sort_enabled" getter="is_y_sort_enabled" default="false"> - If [code]true[/code], this [CanvasItem] and its [CanvasItem] child nodes are sorted according to the Y position. Nodes with a lower Y position are drawn before those with a higher Y position. If [code]false[/code], Y-sorting is disabled. - You can nest nodes with Y-sorting. Child Y-sorted nodes are sorted in the same space as the parent Y-sort. This feature allows you to organize a scene better or divide it into multiple ones without changing your scene tree. + If [code]true[/code], this and child [CanvasItem] nodes with a lower Y position are rendered in front of nodes with a higher Y position. If [code]false[/code], this and child [CanvasItem] nodes are rendered normally in scene tree order. + With Y-sorting enabled on a parent node ('A') but disabled on a child node ('B'), the child node ('B') is sorted but its children ('C1', 'C2', etc) render together on the same Y position as the child node 'B'. This allows you to organize the render order of a scene without changing the scene tree. + Nodes sort relative to each other only if they are on the same [member z_index]. </member> <member name="z_as_relative" type="bool" setter="set_z_as_relative" getter="is_z_relative" default="true"> If [code]true[/code], the node's Z index is relative to its parent's Z index. If this node's Z index is 2 and its parent's effective Z index is 3, then this node's effective Z index will be 2 + 3 = 5. </member> <member name="z_index" type="int" setter="set_z_index" getter="get_z_index" default="0"> - Z index. Controls the order in which the nodes render. A node with a higher Z index will display in front of others. Must be between [constant RenderingServer.CANVAS_ITEM_Z_MIN] and [constant RenderingServer.CANVAS_ITEM_Z_MAX] (inclusive). + Controls the order in which the nodes render. A node with a higher Z index will display in front of others. Must be between [constant RenderingServer.CANVAS_ITEM_Z_MIN] and [constant RenderingServer.CANVAS_ITEM_Z_MAX] (inclusive). [b]Note:[/b] Changing the Z index of a [Control] only affects the drawing order, not the order in which input events are handled. This can be useful to implement certain UI animations, e.g. a menu where hovered items are scaled and should overlap others. </member> </members> diff --git a/doc/classes/CanvasLayer.xml b/doc/classes/CanvasLayer.xml index a91b003d79..597ec78089 100644 --- a/doc/classes/CanvasLayer.xml +++ b/doc/classes/CanvasLayer.xml @@ -12,7 +12,7 @@ <tutorials> <link title="Viewport and canvas transforms">$DOCS_URL/tutorials/2d/2d_transforms.html</link> <link title="Canvas layers">$DOCS_URL/tutorials/2d/canvas_layers.html</link> - <link title="2D Dodge The Creeps Demo">https://godotengine.org/asset-library/asset/515</link> + <link title="2D Dodge The Creeps Demo">https://godotengine.org/asset-library/asset/2712</link> </tutorials> <methods> <method name="get_canvas" qualifiers="const"> diff --git a/doc/classes/CapsuleShape3D.xml b/doc/classes/CapsuleShape3D.xml index 2c8c2cef9e..4c6b3a870f 100644 --- a/doc/classes/CapsuleShape3D.xml +++ b/doc/classes/CapsuleShape3D.xml @@ -8,7 +8,7 @@ [b]Performance:[/b] [CapsuleShape3D] is fast to check collisions against. It is faster than [CylinderShape3D], but slower than [SphereShape3D] and [BoxShape3D]. </description> <tutorials> - <link title="3D Physics Tests Demo">https://godotengine.org/asset-library/asset/675</link> + <link title="3D Physics Tests Demo">https://godotengine.org/asset-library/asset/2747</link> </tutorials> <members> <member name="height" type="float" setter="set_height" getter="get_height" default="2.0"> diff --git a/doc/classes/CharacterBody2D.xml b/doc/classes/CharacterBody2D.xml index b66a01a282..df409cb3c4 100644 --- a/doc/classes/CharacterBody2D.xml +++ b/doc/classes/CharacterBody2D.xml @@ -10,8 +10,8 @@ <tutorials> <link title="Kinematic character (2D)">$DOCS_URL/tutorials/physics/kinematic_character_2d.html</link> <link title="Using CharacterBody2D">$DOCS_URL/tutorials/physics/using_character_body_2d.html</link> - <link title="2D Kinematic Character Demo">https://godotengine.org/asset-library/asset/113</link> - <link title="2D Platformer Demo">https://godotengine.org/asset-library/asset/120</link> + <link title="2D Kinematic Character Demo">https://godotengine.org/asset-library/asset/2719</link> + <link title="2D Platformer Demo">https://godotengine.org/asset-library/asset/2727</link> </tutorials> <methods> <method name="apply_floor_snap"> diff --git a/doc/classes/CharacterBody3D.xml b/doc/classes/CharacterBody3D.xml index 2382c77a12..498149b9c0 100644 --- a/doc/classes/CharacterBody3D.xml +++ b/doc/classes/CharacterBody3D.xml @@ -9,10 +9,10 @@ </description> <tutorials> <link title="Kinematic character (2D)">$DOCS_URL/tutorials/physics/kinematic_character_2d.html</link> - <link title="3D Kinematic Character Demo">https://godotengine.org/asset-library/asset/126</link> - <link title="3D Platformer Demo">https://godotengine.org/asset-library/asset/125</link> - <link title="3D Voxel Demo">https://godotengine.org/asset-library/asset/676</link> - <link title="Third Person Shooter Demo">https://godotengine.org/asset-library/asset/678</link> + <link title="3D Kinematic Character Demo">https://godotengine.org/asset-library/asset/2739</link> + <link title="3D Platformer Demo">https://godotengine.org/asset-library/asset/2748</link> + <link title="3D Voxel Demo">https://godotengine.org/asset-library/asset/2755</link> + <link title="Third Person Shooter (TPS) Demo">https://godotengine.org/asset-library/asset/2710</link> </tutorials> <methods> <method name="apply_floor_snap"> @@ -31,7 +31,8 @@ <method name="get_floor_normal" qualifiers="const"> <return type="Vector3" /> <description> - Returns the surface normal of the floor at the last collision point. Only valid after calling [method move_and_slide] and when [method is_on_floor] returns [code]true[/code]. + Returns the collision normal of the floor at the last collision point. Only valid after calling [method move_and_slide] and when [method is_on_floor] returns [code]true[/code]. + [b]Warning:[/b] The collision normal is not always the same as the surface normal. </description> </method> <method name="get_last_motion" qualifiers="const"> diff --git a/doc/classes/CollisionPolygon2D.xml b/doc/classes/CollisionPolygon2D.xml index 29535ffd64..1805683de5 100644 --- a/doc/classes/CollisionPolygon2D.xml +++ b/doc/classes/CollisionPolygon2D.xml @@ -26,7 +26,6 @@ <member name="polygon" type="PackedVector2Array" setter="set_polygon" getter="get_polygon" default="PackedVector2Array()"> The polygon's list of vertices. Each point will be connected to the next, and the final point will be connected to the first. [b]Note:[/b] The returned vertices are in the local coordinate space of the given [CollisionPolygon2D]. - [b]Warning:[/b] The returned value is a clone of the [PackedVector2Array], not a reference. </member> </members> <constants> diff --git a/doc/classes/CollisionPolygon3D.xml b/doc/classes/CollisionPolygon3D.xml index 16090c203e..4f5866c348 100644 --- a/doc/classes/CollisionPolygon3D.xml +++ b/doc/classes/CollisionPolygon3D.xml @@ -21,7 +21,6 @@ </member> <member name="polygon" type="PackedVector2Array" setter="set_polygon" getter="get_polygon" default="PackedVector2Array()"> Array of vertices which define the 2D polygon in the local XY plane. - [b]Note:[/b] The returned value is a copy of the original. Methods which mutate the size or properties of the return value will not impact the original polygon. To change properties of the polygon, assign it to a temporary variable and make changes before reassigning the class property. </member> </members> </class> diff --git a/doc/classes/CollisionShape2D.xml b/doc/classes/CollisionShape2D.xml index 1320982376..dd04bf7b82 100644 --- a/doc/classes/CollisionShape2D.xml +++ b/doc/classes/CollisionShape2D.xml @@ -8,9 +8,9 @@ </description> <tutorials> <link title="Physics introduction">$DOCS_URL/tutorials/physics/physics_introduction.html</link> - <link title="2D Dodge The Creeps Demo">https://godotengine.org/asset-library/asset/515</link> - <link title="2D Pong Demo">https://godotengine.org/asset-library/asset/121</link> - <link title="2D Kinematic Character Demo">https://godotengine.org/asset-library/asset/113</link> + <link title="2D Dodge The Creeps Demo">https://godotengine.org/asset-library/asset/2712</link> + <link title="2D Pong Demo">https://godotengine.org/asset-library/asset/2728</link> + <link title="2D Kinematic Character Demo">https://godotengine.org/asset-library/asset/2719</link> </tutorials> <members> <member name="debug_color" type="Color" setter="set_debug_color" getter="get_debug_color" default="Color(0, 0, 0, 1)"> diff --git a/doc/classes/CollisionShape3D.xml b/doc/classes/CollisionShape3D.xml index f6c0c323f4..a4e0ed0b28 100644 --- a/doc/classes/CollisionShape3D.xml +++ b/doc/classes/CollisionShape3D.xml @@ -9,9 +9,9 @@ </description> <tutorials> <link title="Physics introduction">$DOCS_URL/tutorials/physics/physics_introduction.html</link> - <link title="3D Kinematic Character Demo">https://godotengine.org/asset-library/asset/126</link> - <link title="3D Platformer Demo">https://godotengine.org/asset-library/asset/125</link> - <link title="Third Person Shooter Demo">https://godotengine.org/asset-library/asset/678</link> + <link title="3D Kinematic Character Demo">https://godotengine.org/asset-library/asset/2739</link> + <link title="3D Platformer Demo">https://godotengine.org/asset-library/asset/2748</link> + <link title="Third Person Shooter (TPS) Demo">https://godotengine.org/asset-library/asset/2710</link> </tutorials> <methods> <method name="make_convex_from_siblings"> diff --git a/doc/classes/Color.xml b/doc/classes/Color.xml index 942d3bc80d..37beca5f81 100644 --- a/doc/classes/Color.xml +++ b/doc/classes/Color.xml @@ -10,9 +10,9 @@ [url=https://raw.githubusercontent.com/godotengine/godot-docs/master/img/color_constants.png]Color constants cheatsheet[/url] </description> <tutorials> - <link title="2D GD Paint Demo">https://godotengine.org/asset-library/asset/517</link> - <link title="Tween Demo">https://godotengine.org/asset-library/asset/146</link> - <link title="GUI Drag And Drop Demo">https://godotengine.org/asset-library/asset/133</link> + <link title="2D GD Paint Demo">https://godotengine.org/asset-library/asset/2768</link> + <link title="Tween Interpolation Demo">https://godotengine.org/asset-library/asset/2733</link> + <link title="GUI Drag And Drop Demo">https://godotengine.org/asset-library/asset/2767</link> </tutorials> <constructors> <constructor name="Color"> diff --git a/doc/classes/ColorPicker.xml b/doc/classes/ColorPicker.xml index 9aa0122ed2..cf26f917e1 100644 --- a/doc/classes/ColorPicker.xml +++ b/doc/classes/ColorPicker.xml @@ -8,7 +8,7 @@ [b]Note:[/b] This control is the color picker widget itself. You can use a [ColorPickerButton] instead if you need a button that brings up a [ColorPicker] in a popup. </description> <tutorials> - <link title="Tween Demo">https://godotengine.org/asset-library/asset/146</link> + <link title="Tween Interpolation Demo">https://godotengine.org/asset-library/asset/2733</link> </tutorials> <methods> <method name="add_preset"> diff --git a/doc/classes/ColorPickerButton.xml b/doc/classes/ColorPickerButton.xml index c53d61f036..bec2520397 100644 --- a/doc/classes/ColorPickerButton.xml +++ b/doc/classes/ColorPickerButton.xml @@ -9,8 +9,8 @@ [b]Note:[/b] By default, the button may not be wide enough for the color preview swatch to be visible. Make sure to set [member Control.custom_minimum_size] to a big enough value to give the button enough space. </description> <tutorials> - <link title="GUI Drag And Drop Demo">https://godotengine.org/asset-library/asset/133</link> - <link title="2D GD Paint Demo">https://godotengine.org/asset-library/asset/517</link> + <link title="2D GD Paint Demo">https://godotengine.org/asset-library/asset/2768</link> + <link title="GUI Drag And Drop Demo">https://godotengine.org/asset-library/asset/2767</link> </tutorials> <methods> <method name="get_picker"> diff --git a/doc/classes/ColorRect.xml b/doc/classes/ColorRect.xml index 413d51db72..6b2ddd748e 100644 --- a/doc/classes/ColorRect.xml +++ b/doc/classes/ColorRect.xml @@ -7,7 +7,7 @@ Displays a rectangle filled with a solid [member color]. If you need to display the border alone, consider using a [Panel] instead. </description> <tutorials> - <link title="2D Dodge The Creeps Demo">https://godotengine.org/asset-library/asset/515</link> + <link title="2D Dodge The Creeps Demo">https://godotengine.org/asset-library/asset/2712</link> </tutorials> <members> <member name="color" type="Color" setter="set_color" getter="get_color" default="Color(1, 1, 1, 1)" keywords="colour"> diff --git a/doc/classes/ConcavePolygonShape3D.xml b/doc/classes/ConcavePolygonShape3D.xml index 7e4df2073f..5c93325b63 100644 --- a/doc/classes/ConcavePolygonShape3D.xml +++ b/doc/classes/ConcavePolygonShape3D.xml @@ -11,7 +11,7 @@ [b]Performance:[/b] Due to its complexity, [ConcavePolygonShape3D] is the slowest 3D collision shape to check collisions against. Its use should generally be limited to level geometry. For convex geometry, [ConvexPolygonShape3D] should be used. For dynamic physics bodies that need concave collision, several [ConvexPolygonShape3D]s can be used to represent its collision by using convex decomposition; see [ConvexPolygonShape3D]'s documentation for instructions. </description> <tutorials> - <link title="3D Physics Tests Demo">https://godotengine.org/asset-library/asset/675</link> + <link title="3D Physics Tests Demo">https://godotengine.org/asset-library/asset/2747</link> </tutorials> <methods> <method name="get_faces" qualifiers="const"> diff --git a/doc/classes/ConvexPolygonShape3D.xml b/doc/classes/ConvexPolygonShape3D.xml index 280cadde78..98c3459289 100644 --- a/doc/classes/ConvexPolygonShape3D.xml +++ b/doc/classes/ConvexPolygonShape3D.xml @@ -10,7 +10,7 @@ [b]Performance:[/b] [ConvexPolygonShape3D] is faster to check collisions against compared to [ConcavePolygonShape3D], but it is slower than primitive collision shapes such as [SphereShape3D] and [BoxShape3D]. Its use should generally be limited to medium-sized objects that cannot have their collision accurately represented by primitive shapes. </description> <tutorials> - <link title="3D Physics Tests Demo">https://godotengine.org/asset-library/asset/675</link> + <link title="3D Physics Tests Demo">https://godotengine.org/asset-library/asset/2747</link> </tutorials> <members> <member name="points" type="PackedVector3Array" setter="set_points" getter="get_points" default="PackedVector3Array()"> diff --git a/doc/classes/CylinderShape3D.xml b/doc/classes/CylinderShape3D.xml index 8bec199ab6..db98cac6e3 100644 --- a/doc/classes/CylinderShape3D.xml +++ b/doc/classes/CylinderShape3D.xml @@ -9,9 +9,9 @@ [b]Performance:[/b] [CylinderShape3D] is fast to check collisions against, but it is slower than [CapsuleShape3D], [BoxShape3D], and [SphereShape3D]. </description> <tutorials> - <link title="Third Person Shooter Demo">https://godotengine.org/asset-library/asset/678</link> - <link title="3D Physics Tests Demo">https://godotengine.org/asset-library/asset/675</link> - <link title="3D Voxel Demo">https://godotengine.org/asset-library/asset/676</link> + <link title="Third Person Shooter (TPS) Demo">https://godotengine.org/asset-library/asset/2710</link> + <link title="3D Physics Tests Demo">https://godotengine.org/asset-library/asset/2747</link> + <link title="3D Voxel Demo">https://godotengine.org/asset-library/asset/2755</link> </tutorials> <members> <member name="height" type="float" setter="set_height" getter="get_height" default="2.0"> diff --git a/doc/classes/Dictionary.xml b/doc/classes/Dictionary.xml index 14ce7c894f..7f0fdddcdd 100644 --- a/doc/classes/Dictionary.xml +++ b/doc/classes/Dictionary.xml @@ -138,8 +138,8 @@ </description> <tutorials> <link title="GDScript basics: Dictionary">$DOCS_URL/tutorials/scripting/gdscript/gdscript_basics.html#dictionary</link> - <link title="3D Voxel Demo">https://godotengine.org/asset-library/asset/676</link> - <link title="OS Test Demo">https://godotengine.org/asset-library/asset/677</link> + <link title="3D Voxel Demo">https://godotengine.org/asset-library/asset/2755</link> + <link title="Operating System Testing Demo">https://godotengine.org/asset-library/asset/2789</link> </tutorials> <constructors> <constructor name="Dictionary"> diff --git a/doc/classes/EditorExportPlatformPC.xml b/doc/classes/EditorExportPlatformPC.xml index 3c2a27deab..def14e5955 100644 --- a/doc/classes/EditorExportPlatformPC.xml +++ b/doc/classes/EditorExportPlatformPC.xml @@ -4,7 +4,10 @@ Base class for the desktop platform exporter (Windows and Linux/BSD). </brief_description> <description> + The base class for the desktop platform exporters. These include Windows and Linux/BSD, but not macOS. See the classes inheriting this one for more details. </description> <tutorials> + <link title="Exporting for Windows">$DOCS_URL/tutorials/export/exporting_for_windows.html</link> + <link title="Exporting for Linux">$DOCS_URL/tutorials/export/exporting_for_linux.html</link> </tutorials> </class> diff --git a/doc/classes/EditorSettings.xml b/doc/classes/EditorSettings.xml index 573171b7e1..87ca0536b8 100644 --- a/doc/classes/EditorSettings.xml +++ b/doc/classes/EditorSettings.xml @@ -830,7 +830,7 @@ <member name="interface/theme/icon_and_font_color" type="int" setter="" getter=""> The icon and font color scheme to use in the editor. - [b]Auto[/b] determines the color scheme to use automatically based on [member interface/theme/base_color]. - - [b]Dark[/b] makes fonts and icons dark (suitable for light themes). Icon colors are automatically converted by the editor following the set of rules defined in [url=https://github.com/godotengine/godot/blob/master/editor/editor_themes.cpp]this file[/url]. + - [b]Dark[/b] makes fonts and icons dark (suitable for light themes). Icon colors are automatically converted by the editor following the set of rules defined in [url=https://github.com/godotengine/godot/blob/master/editor/themes/editor_theme_manager.cpp]this file[/url]. - [b]Light[/b] makes fonts and icons light (suitable for dark themes). </member> <member name="interface/theme/icon_saturation" type="float" setter="" getter=""> diff --git a/doc/classes/Environment.xml b/doc/classes/Environment.xml index 189517c392..c28476aa3c 100644 --- a/doc/classes/Environment.xml +++ b/doc/classes/Environment.xml @@ -13,9 +13,8 @@ <tutorials> <link title="Environment and post-processing">$DOCS_URL/tutorials/3d/environment_and_post_processing.html</link> <link title="High dynamic range lighting">$DOCS_URL/tutorials/3d/high_dynamic_range.html</link> - <link title="3D Material Testers Demo">https://godotengine.org/asset-library/asset/123</link> - <link title="2D HDR Demo">https://godotengine.org/asset-library/asset/110</link> - <link title="Third Person Shooter Demo">https://godotengine.org/asset-library/asset/678</link> + <link title="3D Material Testers Demo">https://godotengine.org/asset-library/asset/2742</link> + <link title="Third Person Shooter (TPS) Demo">https://godotengine.org/asset-library/asset/2710</link> </tutorials> <methods> <method name="get_glow_level" qualifiers="const"> diff --git a/doc/classes/FileAccess.xml b/doc/classes/FileAccess.xml index fad6cbcc93..f27fdcc623 100644 --- a/doc/classes/FileAccess.xml +++ b/doc/classes/FileAccess.xml @@ -40,7 +40,7 @@ <tutorials> <link title="File system">$DOCS_URL/tutorials/scripting/filesystem.html</link> <link title="Runtime file loading and saving">$DOCS_URL/tutorials/io/runtime_file_loading_and_saving.html</link> - <link title="3D Voxel Demo">https://godotengine.org/asset-library/asset/676</link> + <link title="3D Voxel Demo">https://godotengine.org/asset-library/asset/2755</link> </tutorials> <methods> <method name="close"> diff --git a/doc/classes/GPUParticles2D.xml b/doc/classes/GPUParticles2D.xml index f4ba305f8b..a7d89e8596 100644 --- a/doc/classes/GPUParticles2D.xml +++ b/doc/classes/GPUParticles2D.xml @@ -10,8 +10,8 @@ </description> <tutorials> <link title="Particle systems (2D)">$DOCS_URL/tutorials/2d/particle_systems_2d.html</link> - <link title="2D Particles Demo">https://godotengine.org/asset-library/asset/118</link> - <link title="2D Dodge The Creeps Demo (uses GPUParticles2D for the trail behind the player)">https://godotengine.org/asset-library/asset/515</link> + <link title="2D Particles Demo">https://godotengine.org/asset-library/asset/2724</link> + <link title="2D Dodge The Creeps Demo (uses GPUParticles2D for the trail behind the player)">https://godotengine.org/asset-library/asset/2712</link> </tutorials> <methods> <method name="capture_rect" qualifiers="const"> @@ -43,7 +43,8 @@ <method name="restart"> <return type="void" /> <description> - Restarts all the existing particles. + Restarts the particle emission cycle, clearing existing particles. To avoid particles vanishing from the viewport, wait for the [signal finished] signal before calling. + [b]Note:[/b] The [signal finished] signal is only emitted by [member one_shot] emitters. </description> </method> </methods> @@ -64,7 +65,9 @@ Particle draw order. Uses [enum DrawOrder] values. </member> <member name="emitting" type="bool" setter="set_emitting" getter="is_emitting" default="true"> - If [code]true[/code], particles are being emitted. [member emitting] can be used to start and stop particles from emitting. However, if [member one_shot] is [code]true[/code] setting [member emitting] to [code]true[/code] will not restart the emission cycle until after all active particles finish processing. You can use the [signal finished] signal to be notified once all active particles finish processing. + If [code]true[/code], particles are being emitted. [member emitting] can be used to start and stop particles from emitting. However, if [member one_shot] is [code]true[/code] setting [member emitting] to [code]true[/code] will not restart the emission cycle unless all active particles have finished processing. Use the [signal finished] signal to be notified once all active particles finish processing. + [b]Note:[/b] For [member one_shot] emitters, due to the particles being computed on the GPU, there may be a short period after receiving the [signal finished] signal during which setting this to [code]true[/code] will not restart the emission cycle. + [b]Tip:[/b] If your [member one_shot] emitter needs to immediately restart emitting particles once [signal finished] signal is received, consider calling [method restart] instead of setting [member emitting]. </member> <member name="explosiveness" type="float" setter="set_explosiveness_ratio" getter="get_explosiveness_ratio" default="0.0"> How rapidly particles in an emission cycle are emitted. If greater than [code]0[/code], there will be a gap in emissions before the next cycle begins. @@ -132,8 +135,9 @@ <signals> <signal name="finished"> <description> - Emitted when all active particles have finished processing. When [member one_shot] is disabled, particles will process continuously, so this is never emitted. - [b]Note:[/b] Due to the particles being computed on the GPU there might be a delay before the signal gets emitted. + Emitted when all active particles have finished processing. To immediately restart the emission cycle, call [method restart]. + Never emitted when [member one_shot] is disabled, as particles will be emitted and processed continuously. + [b]Note:[/b] For [member one_shot] emitters, due to the particles being computed on the GPU, there may be a short period after receiving the signal during which setting [member emitting] to [code]true[/code] will not restart the emission cycle. This delay is avoided by instead calling [method restart]. </description> </signal> </signals> diff --git a/doc/classes/GPUParticles3D.xml b/doc/classes/GPUParticles3D.xml index d1903b85cd..61a3b467f1 100644 --- a/doc/classes/GPUParticles3D.xml +++ b/doc/classes/GPUParticles3D.xml @@ -10,7 +10,7 @@ <tutorials> <link title="Particle systems (3D)">$DOCS_URL/tutorials/3d/particles/index.html</link> <link title="Controlling thousands of fish with Particles">$DOCS_URL/tutorials/performance/vertex_animation/controlling_thousands_of_fish.html</link> - <link title="Third Person Shooter Demo">https://godotengine.org/asset-library/asset/678</link> + <link title="Third Person Shooter (TPS) Demo">https://godotengine.org/asset-library/asset/2710</link> </tutorials> <methods> <method name="capture_aabb" qualifiers="const"> @@ -48,7 +48,8 @@ <method name="restart"> <return type="void" /> <description> - Restarts the particle emission, clearing existing particles. + Restarts the particle emission cycle, clearing existing particles. To avoid particles vanishing from the viewport, wait for the [signal finished] signal before calling. + [b]Note:[/b] The [signal finished] signal is only emitted by [member one_shot] emitters. </description> </method> <method name="set_draw_pass_mesh"> @@ -95,7 +96,9 @@ <member name="draw_skin" type="Skin" setter="set_skin" getter="get_skin"> </member> <member name="emitting" type="bool" setter="set_emitting" getter="is_emitting" default="true"> - If [code]true[/code], particles are being emitted. [member emitting] can be used to start and stop particles from emitting. However, if [member one_shot] is [code]true[/code] setting [member emitting] to [code]true[/code] will not restart the emission cycle until after all active particles finish processing. You can use the [signal finished] signal to be notified once all active particles finish processing. + If [code]true[/code], particles are being emitted. [member emitting] can be used to start and stop particles from emitting. However, if [member one_shot] is [code]true[/code] setting [member emitting] to [code]true[/code] will not restart the emission cycle unless all active particles have finished processing. Use the [signal finished] signal to be notified once all active particles finish processing. + [b]Note:[/b] For [member one_shot] emitters, due to the particles being computed on the GPU, there may be a short period after receiving the [signal finished] signal during which setting this to [code]true[/code] will not restart the emission cycle. + [b]Tip:[/b] If your [member one_shot] emitter needs to immediately restart emitting particles once [signal finished] signal is received, consider calling [method restart] instead of setting [member emitting]. </member> <member name="explosiveness" type="float" setter="set_explosiveness_ratio" getter="get_explosiveness_ratio" default="0.0"> Time ratio between each emission. If [code]0[/code], particles are emitted continuously. If [code]1[/code], all particles are emitted simultaneously. @@ -157,8 +160,9 @@ <signals> <signal name="finished"> <description> - Emitted when all active particles have finished processing. When [member one_shot] is disabled, particles will process continuously, so this is never emitted. - [b]Note:[/b] Due to the particles being computed on the GPU there might be a delay before the signal gets emitted. + Emitted when all active particles have finished processing. To immediately emit new particles, call [method restart]. + Never emitted when [member one_shot] is disabled, as particles will be emitted and processed continuously. + [b]Note:[/b] For [member one_shot] emitters, due to the particles being computed on the GPU, there may be a short period after receiving the signal during which setting [member emitting] to [code]true[/code] will not restart the emission cycle. This delay is avoided by instead calling [method restart]. </description> </signal> </signals> diff --git a/doc/classes/Gradient.xml b/doc/classes/Gradient.xml index c7fa1f40e0..c85b3acafa 100644 --- a/doc/classes/Gradient.xml +++ b/doc/classes/Gradient.xml @@ -78,8 +78,8 @@ </methods> <members> <member name="colors" type="PackedColorArray" setter="set_colors" getter="get_colors" default="PackedColorArray(0, 0, 0, 1, 1, 1, 1, 1)"> - Gradient's colors returned as a [PackedColorArray]. - [b]Note:[/b] This property returns a copy, modifying the return value does not update the gradient. To update the gradient use [method set_color] method (for updating colors individually) or assign to this property directly (for bulk-updating all colors at once). + Gradient's colors as a [PackedColorArray]. + [b]Note:[/b] Setting this property updates all colors at once. To update any color individually use [method set_color]. </member> <member name="interpolation_color_space" type="int" setter="set_interpolation_color_space" getter="get_interpolation_color_space" enum="Gradient.ColorSpace" default="0"> The color space used to interpolate between points of the gradient. It does not affect the returned colors, which will always be in sRGB space. See [enum ColorSpace] for available modes. @@ -89,8 +89,8 @@ The algorithm used to interpolate between points of the gradient. See [enum InterpolationMode] for available modes. </member> <member name="offsets" type="PackedFloat32Array" setter="set_offsets" getter="get_offsets" default="PackedFloat32Array(0, 1)"> - Gradient's offsets returned as a [PackedFloat32Array]. - [b]Note:[/b] This property returns a copy, modifying the return value does not update the gradient. To update the gradient use [method set_offset] method (for updating offsets individually) or assign to this property directly (for bulk-updating all offsets at once). + Gradient's offsets as a [PackedFloat32Array]. + [b]Note:[/b] Setting this property updates all offsets at once. To update any offset individually use [method set_offset]. </member> </members> <constants> diff --git a/doc/classes/GridContainer.xml b/doc/classes/GridContainer.xml index d69f4a10d2..76f789a058 100644 --- a/doc/classes/GridContainer.xml +++ b/doc/classes/GridContainer.xml @@ -9,7 +9,7 @@ </description> <tutorials> <link title="Using Containers">$DOCS_URL/tutorials/ui/gui_containers.html</link> - <link title="OS Test Demo">https://godotengine.org/asset-library/asset/677</link> + <link title="Operating System Testing Demo">https://godotengine.org/asset-library/asset/2789</link> </tutorials> <members> <member name="columns" type="int" setter="set_columns" getter="get_columns" default="1"> diff --git a/doc/classes/Input.xml b/doc/classes/Input.xml index e622a6bce3..119ecb7f0e 100644 --- a/doc/classes/Input.xml +++ b/doc/classes/Input.xml @@ -9,8 +9,8 @@ </description> <tutorials> <link title="Inputs documentation index">$DOCS_URL/tutorials/inputs/index.html</link> - <link title="2D Dodge The Creeps Demo">https://godotengine.org/asset-library/asset/515</link> - <link title="3D Voxel Demo">https://godotengine.org/asset-library/asset/676</link> + <link title="2D Dodge The Creeps Demo">https://godotengine.org/asset-library/asset/2712</link> + <link title="3D Voxel Demo">https://godotengine.org/asset-library/asset/2755</link> </tutorials> <methods> <method name="action_press"> diff --git a/doc/classes/InputEvent.xml b/doc/classes/InputEvent.xml index 96a4612466..6f2e6aac20 100644 --- a/doc/classes/InputEvent.xml +++ b/doc/classes/InputEvent.xml @@ -9,8 +9,8 @@ <tutorials> <link title="Using InputEvent">$DOCS_URL/tutorials/inputs/inputevent.html</link> <link title="Viewport and canvas transforms">$DOCS_URL/tutorials/2d/2d_transforms.html</link> - <link title="2D Dodge The Creeps Demo">https://godotengine.org/asset-library/asset/515</link> - <link title="3D Voxel Demo">https://godotengine.org/asset-library/asset/676</link> + <link title="2D Dodge The Creeps Demo">https://godotengine.org/asset-library/asset/2712</link> + <link title="3D Voxel Demo">https://godotengine.org/asset-library/asset/2755</link> </tutorials> <methods> <method name="accumulate"> diff --git a/doc/classes/InputEventAction.xml b/doc/classes/InputEventAction.xml index 1fe4cfcd79..4715f9fe95 100644 --- a/doc/classes/InputEventAction.xml +++ b/doc/classes/InputEventAction.xml @@ -9,8 +9,8 @@ </description> <tutorials> <link title="Using InputEvent: Actions">$DOCS_URL/tutorials/inputs/inputevent.html#actions</link> - <link title="2D Dodge The Creeps Demo">https://godotengine.org/asset-library/asset/515</link> - <link title="3D Voxel Demo">https://godotengine.org/asset-library/asset/676</link> + <link title="2D Dodge The Creeps Demo">https://godotengine.org/asset-library/asset/2712</link> + <link title="3D Voxel Demo">https://godotengine.org/asset-library/asset/2755</link> </tutorials> <members> <member name="action" type="StringName" setter="set_action" getter="get_action" default="&"""> diff --git a/doc/classes/InputEventMouseMotion.xml b/doc/classes/InputEventMouseMotion.xml index e6ec674975..98a0221fe8 100644 --- a/doc/classes/InputEventMouseMotion.xml +++ b/doc/classes/InputEventMouseMotion.xml @@ -10,7 +10,7 @@ <tutorials> <link title="Using InputEvent">$DOCS_URL/tutorials/inputs/inputevent.html</link> <link title="Mouse and input coordinates">$DOCS_URL/tutorials/inputs/mouse_and_input_coordinates.html</link> - <link title="3D Voxel Demo">https://godotengine.org/asset-library/asset/676</link> + <link title="3D Voxel Demo">https://godotengine.org/asset-library/asset/2755</link> </tutorials> <members> <member name="pen_inverted" type="bool" setter="set_pen_inverted" getter="get_pen_inverted" default="false"> diff --git a/doc/classes/JSONRPC.xml b/doc/classes/JSONRPC.xml index 348688f7f8..e5ee93cb93 100644 --- a/doc/classes/JSONRPC.xml +++ b/doc/classes/JSONRPC.xml @@ -79,15 +79,19 @@ </methods> <constants> <constant name="PARSE_ERROR" value="-32700" enum="ErrorCode"> + The request could not be parsed as it was not valid by JSON standard ([method JSON.parse] failed). </constant> <constant name="INVALID_REQUEST" value="-32600" enum="ErrorCode"> + A method call was requested but the request's format is not valid. </constant> <constant name="METHOD_NOT_FOUND" value="-32601" enum="ErrorCode"> A method call was requested but no function of that name existed in the JSONRPC subclass. </constant> <constant name="INVALID_PARAMS" value="-32602" enum="ErrorCode"> + A method call was requested but the given method parameters are not valid. Not used by the built-in JSONRPC. </constant> <constant name="INTERNAL_ERROR" value="-32603" enum="ErrorCode"> + An internal error occurred while processing the request. Not used by the built-in JSONRPC. </constant> </constants> </class> diff --git a/doc/classes/JavaClass.xml b/doc/classes/JavaClass.xml index 541f23013d..ecfcaa8781 100644 --- a/doc/classes/JavaClass.xml +++ b/doc/classes/JavaClass.xml @@ -1,8 +1,12 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="JavaClass" inherits="RefCounted" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> + Represents an object from the Java Native Interface. </brief_description> <description> + Represents an object from the Java Native Interface. It is returned from [method JavaClassWrapper.wrap]. + [b]Note:[/b] This class only works on Android. For any other build, this class does nothing. + [b]Note:[/b] This class is not to be confused with [JavaScriptObject]. </description> <tutorials> </tutorials> diff --git a/doc/classes/JavaClassWrapper.xml b/doc/classes/JavaClassWrapper.xml index e197b1e97e..01c3392b04 100644 --- a/doc/classes/JavaClassWrapper.xml +++ b/doc/classes/JavaClassWrapper.xml @@ -1,8 +1,11 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="JavaClassWrapper" inherits="Object" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> + Provides access to the Java Native Interface. </brief_description> <description> + The JavaClassWrapper singleton provides a way for the Godot application to send and receive data through the [url=https://developer.android.com/training/articles/perf-jni]Java Native Interface[/url] (JNI). + [b]Note:[/b] This singleton is only available in Android builds. </description> <tutorials> </tutorials> @@ -11,6 +14,8 @@ <return type="JavaClass" /> <param index="0" name="name" type="String" /> <description> + Wraps a class defined in Java, and returns it as a [JavaClass] [Object] type that Godot can interact with. + [b]Note:[/b] This method only works on Android. On every other platform, this method does nothing and returns an empty [JavaClass]. </description> </method> </methods> diff --git a/doc/classes/Joint3D.xml b/doc/classes/Joint3D.xml index 9e0b753701..ea0dda881a 100644 --- a/doc/classes/Joint3D.xml +++ b/doc/classes/Joint3D.xml @@ -7,7 +7,7 @@ Abstract base class for all joints in 3D physics. 3D joints bind together two physics bodies and apply a constraint. </description> <tutorials> - <link title="3D Truck Town Demo">https://godotengine.org/asset-library/asset/524</link> + <link title="3D Truck Town Demo">https://godotengine.org/asset-library/asset/2752</link> </tutorials> <methods> <method name="get_rid" qualifiers="const"> diff --git a/doc/classes/Label.xml b/doc/classes/Label.xml index f39f5616f0..8acd05cbd1 100644 --- a/doc/classes/Label.xml +++ b/doc/classes/Label.xml @@ -7,7 +7,7 @@ A control for displaying plain text. It gives you control over the horizontal and vertical alignment and can wrap the text inside the node's bounding rectangle. It doesn't support bold, italics, or other rich text formatting. For that, use [RichTextLabel] instead. </description> <tutorials> - <link title="2D Dodge The Creeps Demo">https://godotengine.org/asset-library/asset/515</link> + <link title="2D Dodge The Creeps Demo">https://godotengine.org/asset-library/asset/2712</link> </tutorials> <methods> <method name="get_character_bounds" qualifiers="const"> diff --git a/doc/classes/Light3D.xml b/doc/classes/Light3D.xml index bffa20bf23..c1fc49cf9f 100644 --- a/doc/classes/Light3D.xml +++ b/doc/classes/Light3D.xml @@ -9,7 +9,7 @@ <tutorials> <link title="3D lights and shadows">$DOCS_URL/tutorials/3d/lights_and_shadows.html</link> <link title="Faking global illumination">$DOCS_URL/tutorials/3d/global_illumination/faking_global_illumination.html</link> - <link title="Third Person Shooter Demo">https://godotengine.org/asset-library/asset/678</link> + <link title="Third Person Shooter (TPS) Demo">https://godotengine.org/asset-library/asset/2710</link> </tutorials> <methods> <method name="get_correlated_color" qualifiers="const"> diff --git a/doc/classes/Line2D.xml b/doc/classes/Line2D.xml index 283d847a93..a553e79746 100644 --- a/doc/classes/Line2D.xml +++ b/doc/classes/Line2D.xml @@ -9,8 +9,8 @@ [b]Note:[/b] [Line2D] is drawn using a 2D mesh. </description> <tutorials> - <link title="Matrix Transform Demo">https://godotengine.org/asset-library/asset/584</link> - <link title="2.5D Demo">https://godotengine.org/asset-library/asset/583</link> + <link title="Matrix Transform Demo">https://godotengine.org/asset-library/asset/2787</link> + <link title="2.5D Game Demo">https://godotengine.org/asset-library/asset/2783</link> </tutorials> <methods> <method name="add_point"> diff --git a/doc/classes/Material.xml b/doc/classes/Material.xml index 87fa3fd676..4a73bc2271 100644 --- a/doc/classes/Material.xml +++ b/doc/classes/Material.xml @@ -8,8 +8,8 @@ Importantly, you can inherit from [Material] to create your own custom material type in script or in GDExtension. </description> <tutorials> - <link title="3D Material Testers Demo">https://godotengine.org/asset-library/asset/123</link> - <link title="Third Person Shooter Demo">https://godotengine.org/asset-library/asset/678</link> + <link title="3D Material Testers Demo">https://godotengine.org/asset-library/asset/2742</link> + <link title="Third Person Shooter (TPS) Demo">https://godotengine.org/asset-library/asset/2710</link> </tutorials> <methods> <method name="_can_do_next_pass" qualifiers="virtual const"> diff --git a/doc/classes/Mesh.xml b/doc/classes/Mesh.xml index 3f8ed91ad7..966e870940 100644 --- a/doc/classes/Mesh.xml +++ b/doc/classes/Mesh.xml @@ -7,10 +7,10 @@ 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. </description> <tutorials> - <link title="3D Material Testers Demo">https://godotengine.org/asset-library/asset/123</link> - <link title="3D Kinematic Character Demo">https://godotengine.org/asset-library/asset/126</link> - <link title="3D Platformer Demo">https://godotengine.org/asset-library/asset/125</link> - <link title="Third Person Shooter Demo">https://godotengine.org/asset-library/asset/678</link> + <link title="3D Material Testers Demo">https://godotengine.org/asset-library/asset/2742</link> + <link title="3D Kinematic Character Demo">https://godotengine.org/asset-library/asset/2739</link> + <link title="3D Platformer Demo">https://godotengine.org/asset-library/asset/2748</link> + <link title="Third Person Shooter (TPS) Demo">https://godotengine.org/asset-library/asset/2710</link> </tutorials> <methods> <method name="_get_aabb" qualifiers="virtual const"> diff --git a/doc/classes/MeshInstance3D.xml b/doc/classes/MeshInstance3D.xml index 4645435f55..f56b03bdc4 100644 --- a/doc/classes/MeshInstance3D.xml +++ b/doc/classes/MeshInstance3D.xml @@ -7,10 +7,10 @@ MeshInstance3D is a node that takes a [Mesh] resource and adds it to the current scenario by creating an instance of it. This is the class most often used render 3D geometry and can be used to instance a single [Mesh] in many places. This allows reusing geometry, which can save on resources. When a [Mesh] has to be instantiated more than thousands of times at close proximity, consider using a [MultiMesh] in a [MultiMeshInstance3D] instead. </description> <tutorials> - <link title="3D Material Testers Demo">https://godotengine.org/asset-library/asset/123</link> - <link title="3D Kinematic Character Demo">https://godotengine.org/asset-library/asset/126</link> - <link title="3D Platformer Demo">https://godotengine.org/asset-library/asset/125</link> - <link title="Third Person Shooter Demo">https://godotengine.org/asset-library/asset/678</link> + <link title="3D Material Testers Demo">https://godotengine.org/asset-library/asset/2742</link> + <link title="3D Kinematic Character Demo">https://godotengine.org/asset-library/asset/2739</link> + <link title="3D Platformer Demo">https://godotengine.org/asset-library/asset/2748</link> + <link title="Third Person Shooter (TPS) Demo">https://godotengine.org/asset-library/asset/2710</link> </tutorials> <methods> <method name="create_convex_collision"> diff --git a/doc/classes/MeshLibrary.xml b/doc/classes/MeshLibrary.xml index f7099e569f..c5e8d8cbe7 100644 --- a/doc/classes/MeshLibrary.xml +++ b/doc/classes/MeshLibrary.xml @@ -7,8 +7,8 @@ A library of meshes. Contains a list of [Mesh] resources, each with a name and ID. Each item can also include collision and navigation shapes. This resource is used in [GridMap]. </description> <tutorials> - <link title="3D Kinematic Character Demo">https://godotengine.org/asset-library/asset/126</link> - <link title="3D Platformer Demo">https://godotengine.org/asset-library/asset/125</link> + <link title="3D Kinematic Character Demo">https://godotengine.org/asset-library/asset/2739</link> + <link title="3D Platformer Demo">https://godotengine.org/asset-library/asset/2748</link> </tutorials> <methods> <method name="clear"> diff --git a/doc/classes/MultiplayerPeer.xml b/doc/classes/MultiplayerPeer.xml index 04fd282457..edb2c39e24 100644 --- a/doc/classes/MultiplayerPeer.xml +++ b/doc/classes/MultiplayerPeer.xml @@ -10,7 +10,6 @@ </description> <tutorials> <link title="High-level multiplayer">$DOCS_URL/tutorials/networking/high_level_multiplayer.html</link> - <link title="WebRTC Signaling Demo">https://godotengine.org/asset-library/asset/537</link> </tutorials> <methods> <method name="close"> diff --git a/doc/classes/NavigationMesh.xml b/doc/classes/NavigationMesh.xml index 42ef354bc8..8be8a89543 100644 --- a/doc/classes/NavigationMesh.xml +++ b/doc/classes/NavigationMesh.xml @@ -8,7 +8,7 @@ </description> <tutorials> <link title="Using NavigationMeshes">$DOCS_URL/tutorials/navigation/navigation_using_navigationmeshes.html</link> - <link title="3D Navmesh Demo">https://godotengine.org/asset-library/asset/124</link> + <link title="3D Navigation Demo">https://godotengine.org/asset-library/asset/2743</link> </tutorials> <methods> <method name="add_polygon"> diff --git a/doc/classes/NavigationPolygon.xml b/doc/classes/NavigationPolygon.xml index 9c4e8169b0..eebdc817a7 100644 --- a/doc/classes/NavigationPolygon.xml +++ b/doc/classes/NavigationPolygon.xml @@ -43,8 +43,8 @@ [/codeblocks] </description> <tutorials> - <link title="2D Navigation Demo">https://godotengine.org/asset-library/asset/117</link> <link title="Using NavigationMeshes">$DOCS_URL/tutorials/navigation/navigation_using_navigationmeshes.html</link> + <link title="Navigation Polygon 2D Demo">https://godotengine.org/asset-library/asset/2722</link> </tutorials> <methods> <method name="add_outline"> diff --git a/doc/classes/NavigationServer2D.xml b/doc/classes/NavigationServer2D.xml index 91d69edf29..baef7dc02d 100644 --- a/doc/classes/NavigationServer2D.xml +++ b/doc/classes/NavigationServer2D.xml @@ -14,8 +14,8 @@ This server keeps tracks of any call and executes them during the sync phase. This means that you can request any change to the map, using any thread, without worrying. </description> <tutorials> - <link title="2D Navigation Demo">https://godotengine.org/asset-library/asset/117</link> <link title="Using NavigationServer">$DOCS_URL/tutorials/navigation/navigation_using_navigationservers.html</link> + <link title="Navigation Polygon 2D Demo">https://godotengine.org/asset-library/asset/2722</link> </tutorials> <methods> <method name="agent_create"> diff --git a/doc/classes/NavigationServer3D.xml b/doc/classes/NavigationServer3D.xml index 29d9f5424a..6be9d5165f 100644 --- a/doc/classes/NavigationServer3D.xml +++ b/doc/classes/NavigationServer3D.xml @@ -14,8 +14,8 @@ This server keeps tracks of any call and executes them during the sync phase. This means that you can request any change to the map, using any thread, without worrying. </description> <tutorials> - <link title="3D Navmesh Demo">https://godotengine.org/asset-library/asset/124</link> <link title="Using NavigationServer">$DOCS_URL/tutorials/navigation/navigation_using_navigationservers.html</link> + <link title="3D Navigation Demo">https://godotengine.org/asset-library/asset/2743</link> </tutorials> <methods> <method name="agent_create"> diff --git a/doc/classes/NodePath.xml b/doc/classes/NodePath.xml index 6e0799e796..f294b64576 100644 --- a/doc/classes/NodePath.xml +++ b/doc/classes/NodePath.xml @@ -34,7 +34,7 @@ [b]Note:[/b] In a boolean context, a [NodePath] will evaluate to [code]false[/code] if it is empty ([code]NodePath("")[/code]). Otherwise, a [NodePath] will always evaluate to [code]true[/code]. </description> <tutorials> - <link title="2D Role Playing Game Demo">https://godotengine.org/asset-library/asset/520</link> + <link title="2D Role Playing Game (RPG) Demo">https://godotengine.org/asset-library/asset/2729</link> </tutorials> <constructors> <constructor name="NodePath"> diff --git a/doc/classes/OS.xml b/doc/classes/OS.xml index bd1bd9afa7..de39901133 100644 --- a/doc/classes/OS.xml +++ b/doc/classes/OS.xml @@ -8,7 +8,7 @@ [b]Note:[/b] In Godot 4, [OS] functions related to window management, clipboard, and TTS were moved to the [DisplayServer] singleton (and the [Window] class). Functions related to time were removed and are only available in the [Time] class. </description> <tutorials> - <link title="OS Test Demo">https://godotengine.org/asset-library/asset/677</link> + <link title="Operating System Testing Demo">https://godotengine.org/asset-library/asset/2789</link> </tutorials> <methods> <method name="alert"> diff --git a/doc/classes/OccluderPolygon2D.xml b/doc/classes/OccluderPolygon2D.xml index a78375ad01..36e1540b39 100644 --- a/doc/classes/OccluderPolygon2D.xml +++ b/doc/classes/OccluderPolygon2D.xml @@ -17,7 +17,6 @@ </member> <member name="polygon" type="PackedVector2Array" setter="set_polygon" getter="get_polygon" default="PackedVector2Array()"> A [Vector2] array with the index for polygon's vertices positions. - [b]Note:[/b] The returned value is a copy of the underlying array, rather than a reference. </member> </members> <constants> diff --git a/doc/classes/PackedByteArray.xml b/doc/classes/PackedByteArray.xml index 0c1532f61a..e179c111a2 100644 --- a/doc/classes/PackedByteArray.xml +++ b/doc/classes/PackedByteArray.xml @@ -6,6 +6,7 @@ <description> An array specifically designed to hold bytes. Packs data tightly, so it saves memory for large array sizes. [PackedByteArray] also provides methods to encode/decode various types to/from bytes. The way values are encoded is an implementation detail and shouldn't be relied upon when interacting with external apps. + [b]Note:[/b] Packed arrays are always passed by reference. To get a copy of an array that can be modified independently of the original array, use [method duplicate]. This is [i]not[/i] the case for built-in properties and methods. The returned packed array of these are a copies, and changing it will [i]not[/i] affect the original value. To update a built-in property you need to modify the returned array, and then assign it to the property again. </description> <tutorials> </tutorials> diff --git a/doc/classes/PackedColorArray.xml b/doc/classes/PackedColorArray.xml index ad96ba2490..57295cb1e3 100644 --- a/doc/classes/PackedColorArray.xml +++ b/doc/classes/PackedColorArray.xml @@ -5,6 +5,8 @@ </brief_description> <description> An array specifically designed to hold [Color]. Packs data tightly, so it saves memory for large array sizes. + [b]Differences between packed arrays, typed arrays, and untyped arrays:[/b] Packed arrays are generally faster to iterate on and modify compared to a typed array of the same type (e.g. [PackedColorArray] versus [code]Array[Color][/code]). Also, packed arrays consume less memory. As a downside, packed arrays are less flexible as they don't offer as many convenience methods such as [method Array.map]. Typed arrays are in turn faster to iterate on and modify than untyped arrays. + [b]Note:[/b] Packed arrays are always passed by reference. To get a copy of an array that can be modified independently of the original array, use [method duplicate]. This is [i]not[/i] the case for built-in properties and methods. The returned packed array of these are a copies, and changing it will [i]not[/i] affect the original value. To update a built-in property you need to modify the returned array, and then assign it to the property again. </description> <tutorials> </tutorials> diff --git a/doc/classes/PackedFloat32Array.xml b/doc/classes/PackedFloat32Array.xml index 6f1ecacca4..2db1386fd0 100644 --- a/doc/classes/PackedFloat32Array.xml +++ b/doc/classes/PackedFloat32Array.xml @@ -6,6 +6,7 @@ <description> An array specifically designed to hold 32-bit floating-point values (float). Packs data tightly, so it saves memory for large array sizes. If you need to pack 64-bit floats tightly, see [PackedFloat64Array]. + [b]Note:[/b] Packed arrays are always passed by reference. To get a copy of an array that can be modified independently of the original array, use [method duplicate]. This is [i]not[/i] the case for built-in properties and methods. The returned packed array of these are a copies, and changing it will [i]not[/i] affect the original value. To update a built-in property you need to modify the returned array, and then assign it to the property again. </description> <tutorials> </tutorials> diff --git a/doc/classes/PackedFloat64Array.xml b/doc/classes/PackedFloat64Array.xml index aef5ab90ac..0bcee918ed 100644 --- a/doc/classes/PackedFloat64Array.xml +++ b/doc/classes/PackedFloat64Array.xml @@ -6,6 +6,8 @@ <description> An array specifically designed to hold 64-bit floating-point values (double). Packs data tightly, so it saves memory for large array sizes. If you only need to pack 32-bit floats tightly, see [PackedFloat32Array] for a more memory-friendly alternative. + [b]Differences between packed arrays, typed arrays, and untyped arrays:[/b] Packed arrays are generally faster to iterate on and modify compared to a typed array of the same type (e.g. [PackedFloat64Array] versus [code]Array[float][/code]). Also, packed arrays consume less memory. As a downside, packed arrays are less flexible as they don't offer as many convenience methods such as [method Array.map]. Typed arrays are in turn faster to iterate on and modify than untyped arrays. + [b]Note:[/b] Packed arrays are always passed by reference. To get a copy of an array that can be modified independently of the original array, use [method duplicate]. This is [i]not[/i] the case for built-in properties and methods. The returned packed array of these are a copies, and changing it will [i]not[/i] affect the original value. To update a built-in property you need to modify the returned array, and then assign it to the property again. </description> <tutorials> </tutorials> diff --git a/doc/classes/PackedInt32Array.xml b/doc/classes/PackedInt32Array.xml index e6396e2a93..93b2ae7394 100644 --- a/doc/classes/PackedInt32Array.xml +++ b/doc/classes/PackedInt32Array.xml @@ -6,6 +6,7 @@ <description> An array specifically designed to hold 32-bit integer values. Packs data tightly, so it saves memory for large array sizes. [b]Note:[/b] This type stores signed 32-bit integers, which means it can take values in the interval [code][-2^31, 2^31 - 1][/code], i.e. [code][-2147483648, 2147483647][/code]. Exceeding those bounds will wrap around. In comparison, [int] uses signed 64-bit integers which can hold much larger values. If you need to pack 64-bit integers tightly, see [PackedInt64Array]. + [b]Note:[/b] Packed arrays are always passed by reference. To get a copy of an array that can be modified independently of the original array, use [method duplicate]. This is [i]not[/i] the case for built-in properties and methods. The returned packed array of these are a copies, and changing it will [i]not[/i] affect the original value. To update a built-in property you need to modify the returned array, and then assign it to the property again. </description> <tutorials> </tutorials> diff --git a/doc/classes/PackedInt64Array.xml b/doc/classes/PackedInt64Array.xml index 55024341c1..3d34165915 100644 --- a/doc/classes/PackedInt64Array.xml +++ b/doc/classes/PackedInt64Array.xml @@ -6,6 +6,8 @@ <description> An array specifically designed to hold 64-bit integer values. Packs data tightly, so it saves memory for large array sizes. [b]Note:[/b] This type stores signed 64-bit integers, which means it can take values in the interval [code][-2^63, 2^63 - 1][/code], i.e. [code][-9223372036854775808, 9223372036854775807][/code]. Exceeding those bounds will wrap around. If you only need to pack 32-bit integers tightly, see [PackedInt32Array] for a more memory-friendly alternative. + [b]Differences between packed arrays, typed arrays, and untyped arrays:[/b] Packed arrays are generally faster to iterate on and modify compared to a typed array of the same type (e.g. [PackedInt32Array] versus [code]Array[int][/code]). Also, packed arrays consume less memory. As a downside, packed arrays are less flexible as they don't offer as many convenience methods such as [method Array.map]. Typed arrays are in turn faster to iterate on and modify than untyped arrays. + [b]Note:[/b] Packed arrays are always passed by reference. To get a copy of an array that can be modified independently of the original array, use [method duplicate]. This is [i]not[/i] the case for built-in properties and methods. The returned packed array of these are a copies, and changing it will [i]not[/i] affect the original value. To update a built-in property you need to modify the returned array, and then assign it to the property again. </description> <tutorials> </tutorials> diff --git a/doc/classes/PackedScene.xml b/doc/classes/PackedScene.xml index 9324f99535..579b4c5b9f 100644 --- a/doc/classes/PackedScene.xml +++ b/doc/classes/PackedScene.xml @@ -73,7 +73,7 @@ [/codeblocks] </description> <tutorials> - <link title="2D Role Playing Game Demo">https://godotengine.org/asset-library/asset/520</link> + <link title="2D Role Playing Game (RPG) Demo">https://godotengine.org/asset-library/asset/2729</link> </tutorials> <methods> <method name="can_instantiate" qualifiers="const"> diff --git a/doc/classes/PackedStringArray.xml b/doc/classes/PackedStringArray.xml index f1b02272f3..621831c7a3 100644 --- a/doc/classes/PackedStringArray.xml +++ b/doc/classes/PackedStringArray.xml @@ -11,9 +11,11 @@ var string = " ".join(string_array) print(string) # "hello world" [/codeblock] + [b]Differences between packed arrays, typed arrays, and untyped arrays:[/b] Packed arrays are generally faster to iterate on and modify compared to a typed array of the same type (e.g. [PackedStringArray] versus [code]Array[String][/code]). Also, packed arrays consume less memory. As a downside, packed arrays are less flexible as they don't offer as many convenience methods such as [method Array.map]. Typed arrays are in turn faster to iterate on and modify than untyped arrays. + [b]Note:[/b] Packed arrays are always passed by reference. To get a copy of an array that can be modified independently of the original array, use [method duplicate]. This is [i]not[/i] the case for built-in properties and methods. The returned packed array of these are a copies, and changing it will [i]not[/i] affect the original value. To update a built-in property you need to modify the returned array, and then assign it to the property again. </description> <tutorials> - <link title="OS Test Demo">https://godotengine.org/asset-library/asset/677</link> + <link title="Operating System Testing Demo">https://godotengine.org/asset-library/asset/2789</link> </tutorials> <constructors> <constructor name="PackedStringArray"> diff --git a/doc/classes/PackedVector2Array.xml b/doc/classes/PackedVector2Array.xml index c73fea9114..14a3816353 100644 --- a/doc/classes/PackedVector2Array.xml +++ b/doc/classes/PackedVector2Array.xml @@ -5,9 +5,11 @@ </brief_description> <description> An array specifically designed to hold [Vector2]. Packs data tightly, so it saves memory for large array sizes. + [b]Differences between packed arrays, typed arrays, and untyped arrays:[/b] Packed arrays are generally faster to iterate on and modify compared to a typed array of the same type (e.g. [PackedVector3Array] versus [code]Array[Vector2][/code]). Also, packed arrays consume less memory. As a downside, packed arrays are less flexible as they don't offer as many convenience methods such as [method Array.map]. Typed arrays are in turn faster to iterate on and modify than untyped arrays. + [b]Note:[/b] Packed arrays are always passed by reference. To get a copy of an array that can be modified independently of the original array, use [method duplicate]. This is [i]not[/i] the case for built-in properties and methods. The returned packed array of these are a copies, and changing it will [i]not[/i] affect the original value. To update a built-in property you need to modify the returned array, and then assign it to the property again. </description> <tutorials> - <link title="2D Navigation Astar Demo">https://godotengine.org/asset-library/asset/519</link> + <link title="Grid-based Navigation with AStarGrid2D Demo">https://godotengine.org/asset-library/asset/2723</link> </tutorials> <constructors> <constructor name="PackedVector2Array"> diff --git a/doc/classes/PackedVector3Array.xml b/doc/classes/PackedVector3Array.xml index 89f258eaea..49220c6fd6 100644 --- a/doc/classes/PackedVector3Array.xml +++ b/doc/classes/PackedVector3Array.xml @@ -5,6 +5,8 @@ </brief_description> <description> An array specifically designed to hold [Vector3]. Packs data tightly, so it saves memory for large array sizes. + [b]Differences between packed arrays, typed arrays, and untyped arrays:[/b] Packed arrays are generally faster to iterate on and modify compared to a typed array of the same type (e.g. [PackedVector3Array] versus [code]Array[Vector3][/code]). Also, packed arrays consume less memory. As a downside, packed arrays are less flexible as they don't offer as many convenience methods such as [method Array.map]. Typed arrays are in turn faster to iterate on and modify than untyped arrays. + [b]Note:[/b] Packed arrays are always passed by reference. To get a copy of an array that can be modified independently of the original array, use [method duplicate]. This is [i]not[/i] the case for built-in properties and methods. The returned packed array of these are a copies, and changing it will [i]not[/i] affect the original value. To update a built-in property you need to modify the returned array, and then assign it to the property again. </description> <tutorials> </tutorials> diff --git a/doc/classes/Panel.xml b/doc/classes/Panel.xml index db1fa1ccda..993c383c70 100644 --- a/doc/classes/Panel.xml +++ b/doc/classes/Panel.xml @@ -7,9 +7,8 @@ [Panel] is a GUI control that displays a [StyleBox]. See also [PanelContainer]. </description> <tutorials> - <link title="2D Role Playing Game Demo">https://godotengine.org/asset-library/asset/520</link> - <link title="2D Finite State Machine Demo">https://godotengine.org/asset-library/asset/516</link> - <link title="3D Inverse Kinematics Demo">https://godotengine.org/asset-library/asset/523</link> + <link title="2D Role Playing Game (RPG) Demo">https://godotengine.org/asset-library/asset/2729</link> + <link title="Hierarchical Finite State Machine Demo">https://godotengine.org/asset-library/asset/2714</link> </tutorials> <theme_items> <theme_item name="panel" data_type="style" type="StyleBox"> diff --git a/doc/classes/PanelContainer.xml b/doc/classes/PanelContainer.xml index c6b3604cc4..c2972abc14 100644 --- a/doc/classes/PanelContainer.xml +++ b/doc/classes/PanelContainer.xml @@ -8,7 +8,7 @@ </description> <tutorials> <link title="Using Containers">$DOCS_URL/tutorials/ui/gui_containers.html</link> - <link title="2D Role Playing Game Demo">https://godotengine.org/asset-library/asset/520</link> + <link title="2D Role Playing Game (RPG) Demo">https://godotengine.org/asset-library/asset/2729</link> </tutorials> <members> <member name="mouse_filter" type="int" setter="set_mouse_filter" getter="get_mouse_filter" overrides="Control" enum="Control.MouseFilter" default="0" /> diff --git a/doc/classes/PhysicalBone3D.xml b/doc/classes/PhysicalBone3D.xml index c3b202e0a5..bce1a80526 100644 --- a/doc/classes/PhysicalBone3D.xml +++ b/doc/classes/PhysicalBone3D.xml @@ -13,7 +13,7 @@ <return type="void" /> <param index="0" name="state" type="PhysicsDirectBodyState3D" /> <description> - Called during physics processing, allowing you to read and safely modify the simulation state for the object. By default, it works in addition to the usual physics behavior, but the [member custom_integrator] property allows you to disable the default behavior and do fully custom force integration for a body. + Called during physics processing, allowing you to read and safely modify the simulation state for the object. By default, it is called before the standard force integration, but the [member custom_integrator] property allows you to disable the standard force integration and do fully custom force integration for a body. </description> </method> <method name="apply_central_impulse"> @@ -67,7 +67,8 @@ If [code]true[/code], the body is deactivated when there is no movement, so it will not take part in the simulation until it is awakened by an external force. </member> <member name="custom_integrator" type="bool" setter="set_use_custom_integrator" getter="is_using_custom_integrator" default="false"> - If [code]true[/code], internal force integration will be disabled (like gravity or air friction) for this body. Other than collision response, the body will only move as determined by the [method _integrate_forces] function, if defined. + If [code]true[/code], the standard force integration (like gravity or damping) will be disabled for this body. Other than collision response, the body will only move as determined by the [method _integrate_forces] method, if that virtual method is overridden. + Setting this property will call the method [method PhysicsServer3D.body_set_omit_force_integration] internally. </member> <member name="friction" type="float" setter="set_friction" getter="get_friction" default="1.0"> The body's friction, from [code]0[/code] (frictionless) to [code]1[/code] (max friction). diff --git a/doc/classes/PhysicalBoneSimulator3D.xml b/doc/classes/PhysicalBoneSimulator3D.xml new file mode 100644 index 0000000000..149993e1e2 --- /dev/null +++ b/doc/classes/PhysicalBoneSimulator3D.xml @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="PhysicalBoneSimulator3D" inherits="SkeletonModifier3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> + <brief_description> + Node that can be the parent of [PhysicalBone3D] and can apply the simulation results to [Skeleton3D]. + </brief_description> + <description> + Node that can be the parent of [PhysicalBone3D] and can apply the simulation results to [Skeleton3D]. + </description> + <tutorials> + </tutorials> + <methods> + <method name="is_simulating_physics" qualifiers="const"> + <return type="bool" /> + <description> + Returns a boolean that indicates whether the [PhysicalBoneSimulator3D] is running and simulating. + </description> + </method> + <method name="physical_bones_add_collision_exception"> + <return type="void" /> + <param index="0" name="exception" type="RID" /> + <description> + Adds a collision exception to the physical bone. + Works just like the [RigidBody3D] node. + </description> + </method> + <method name="physical_bones_remove_collision_exception"> + <return type="void" /> + <param index="0" name="exception" type="RID" /> + <description> + Removes a collision exception to the physical bone. + Works just like the [RigidBody3D] node. + </description> + </method> + <method name="physical_bones_start_simulation"> + <return type="void" /> + <param index="0" name="bones" type="StringName[]" default="[]" /> + <description> + Tells the [PhysicalBone3D] nodes in the Skeleton to start simulating and reacting to the physics world. + Optionally, a list of bone names can be passed-in, allowing only the passed-in bones to be simulated. + </description> + </method> + <method name="physical_bones_stop_simulation"> + <return type="void" /> + <description> + Tells the [PhysicalBone3D] nodes in the Skeleton to stop simulating. + </description> + </method> + </methods> +</class> diff --git a/doc/classes/PhysicsDirectBodyState2D.xml b/doc/classes/PhysicsDirectBodyState2D.xml index 7051663a78..d60cc1ee6b 100644 --- a/doc/classes/PhysicsDirectBodyState2D.xml +++ b/doc/classes/PhysicsDirectBodyState2D.xml @@ -202,7 +202,7 @@ <method name="integrate_forces"> <return type="void" /> <description> - Calls the built-in force integration code. + Updates the body's linear and angular velocity by applying gravity and damping for the equivalent of one physics tick. </description> </method> <method name="set_constant_force"> diff --git a/doc/classes/PhysicsDirectBodyState2DExtension.xml b/doc/classes/PhysicsDirectBodyState2DExtension.xml index 04612b461e..932c1c8352 100644 --- a/doc/classes/PhysicsDirectBodyState2DExtension.xml +++ b/doc/classes/PhysicsDirectBodyState2DExtension.xml @@ -14,6 +14,7 @@ <return type="void" /> <param index="0" name="force" type="Vector2" /> <description> + Overridable version of [method PhysicsDirectBodyState2D.add_constant_central_force]. </description> </method> <method name="_add_constant_force" qualifiers="virtual"> @@ -21,24 +22,28 @@ <param index="0" name="force" type="Vector2" /> <param index="1" name="position" type="Vector2" /> <description> + Overridable version of [method PhysicsDirectBodyState2D.add_constant_force]. </description> </method> <method name="_add_constant_torque" qualifiers="virtual"> <return type="void" /> <param index="0" name="torque" type="float" /> <description> + Overridable version of [method PhysicsDirectBodyState2D.add_constant_torque]. </description> </method> <method name="_apply_central_force" qualifiers="virtual"> <return type="void" /> <param index="0" name="force" type="Vector2" /> <description> + Overridable version of [method PhysicsDirectBodyState2D.apply_central_force]. </description> </method> <method name="_apply_central_impulse" qualifiers="virtual"> <return type="void" /> <param index="0" name="impulse" type="Vector2" /> <description> + Overridable version of [method PhysicsDirectBodyState2D.apply_central_impulse]. </description> </method> <method name="_apply_force" qualifiers="virtual"> @@ -46,6 +51,7 @@ <param index="0" name="force" type="Vector2" /> <param index="1" name="position" type="Vector2" /> <description> + Overridable version of [method PhysicsDirectBodyState2D.apply_force]. </description> </method> <method name="_apply_impulse" qualifiers="virtual"> @@ -53,211 +59,249 @@ <param index="0" name="impulse" type="Vector2" /> <param index="1" name="position" type="Vector2" /> <description> + Overridable version of [method PhysicsDirectBodyState2D.apply_impulse]. </description> </method> <method name="_apply_torque" qualifiers="virtual"> <return type="void" /> <param index="0" name="torque" type="float" /> <description> + Overridable version of [method PhysicsDirectBodyState2D.apply_torque]. </description> </method> <method name="_apply_torque_impulse" qualifiers="virtual"> <return type="void" /> <param index="0" name="impulse" type="float" /> <description> + Overridable version of [method PhysicsDirectBodyState2D.apply_torque_impulse]. </description> </method> <method name="_get_angular_velocity" qualifiers="virtual const"> <return type="float" /> <description> + Implement to override the behavior of [member PhysicsDirectBodyState2D.angular_velocity] and its respective getter. </description> </method> <method name="_get_center_of_mass" qualifiers="virtual const"> <return type="Vector2" /> <description> + Implement to override the behavior of [member PhysicsDirectBodyState2D.center_of_mass] and its respective getter. </description> </method> <method name="_get_center_of_mass_local" qualifiers="virtual const"> <return type="Vector2" /> <description> + Implement to override the behavior of [member PhysicsDirectBodyState2D.center_of_mass_local] and its respective getter. </description> </method> <method name="_get_constant_force" qualifiers="virtual const"> <return type="Vector2" /> <description> + Overridable version of [method PhysicsDirectBodyState2D.get_constant_force]. </description> </method> <method name="_get_constant_torque" qualifiers="virtual const"> <return type="float" /> <description> + Overridable version of [method PhysicsDirectBodyState2D.get_constant_torque]. </description> </method> <method name="_get_contact_collider" qualifiers="virtual const"> <return type="RID" /> <param index="0" name="contact_idx" type="int" /> <description> + Overridable version of [method PhysicsDirectBodyState2D.get_contact_collider]. </description> </method> <method name="_get_contact_collider_id" qualifiers="virtual const"> <return type="int" /> <param index="0" name="contact_idx" type="int" /> <description> + Overridable version of [method PhysicsDirectBodyState2D.get_contact_collider_id]. </description> </method> <method name="_get_contact_collider_object" qualifiers="virtual const"> <return type="Object" /> <param index="0" name="contact_idx" type="int" /> <description> + Overridable version of [method PhysicsDirectBodyState2D.get_contact_collider_object]. </description> </method> <method name="_get_contact_collider_position" qualifiers="virtual const"> <return type="Vector2" /> <param index="0" name="contact_idx" type="int" /> <description> + Overridable version of [method PhysicsDirectBodyState2D.get_contact_collider_position]. </description> </method> <method name="_get_contact_collider_shape" qualifiers="virtual const"> <return type="int" /> <param index="0" name="contact_idx" type="int" /> <description> + Overridable version of [method PhysicsDirectBodyState2D.get_contact_collider_shape]. </description> </method> <method name="_get_contact_collider_velocity_at_position" qualifiers="virtual const"> <return type="Vector2" /> <param index="0" name="contact_idx" type="int" /> <description> + Overridable version of [method PhysicsDirectBodyState2D.get_contact_collider_velocity_at_position]. </description> </method> <method name="_get_contact_count" qualifiers="virtual const"> <return type="int" /> <description> + Overridable version of [method PhysicsDirectBodyState2D.get_contact_count]. </description> </method> <method name="_get_contact_impulse" qualifiers="virtual const"> <return type="Vector2" /> <param index="0" name="contact_idx" type="int" /> <description> + Overridable version of [method PhysicsDirectBodyState2D.get_contact_impulse]. </description> </method> <method name="_get_contact_local_normal" qualifiers="virtual const"> <return type="Vector2" /> <param index="0" name="contact_idx" type="int" /> <description> + Overridable version of [method PhysicsDirectBodyState2D.get_contact_local_normal]. </description> </method> <method name="_get_contact_local_position" qualifiers="virtual const"> <return type="Vector2" /> <param index="0" name="contact_idx" type="int" /> <description> + Overridable version of [method PhysicsDirectBodyState2D.get_contact_local_position]. </description> </method> <method name="_get_contact_local_shape" qualifiers="virtual const"> <return type="int" /> <param index="0" name="contact_idx" type="int" /> <description> + Overridable version of [method PhysicsDirectBodyState2D.get_contact_local_shape]. </description> </method> <method name="_get_contact_local_velocity_at_position" qualifiers="virtual const"> <return type="Vector2" /> <param index="0" name="contact_idx" type="int" /> <description> + Overridable version of [method PhysicsDirectBodyState2D.get_contact_local_velocity_at_position]. </description> </method> <method name="_get_inverse_inertia" qualifiers="virtual const"> <return type="float" /> <description> + Implement to override the behavior of [member PhysicsDirectBodyState2D.inverse_inertia] and its respective getter. </description> </method> <method name="_get_inverse_mass" qualifiers="virtual const"> <return type="float" /> <description> + Implement to override the behavior of [member PhysicsDirectBodyState2D.inverse_mass] and its respective getter. </description> </method> <method name="_get_linear_velocity" qualifiers="virtual const"> <return type="Vector2" /> <description> + Implement to override the behavior of [member PhysicsDirectBodyState2D.linear_velocity] and its respective getter. </description> </method> <method name="_get_space_state" qualifiers="virtual"> <return type="PhysicsDirectSpaceState2D" /> <description> + Overridable version of [method PhysicsDirectBodyState2D.get_space_state]. </description> </method> <method name="_get_step" qualifiers="virtual const"> <return type="float" /> <description> + Implement to override the behavior of [member PhysicsDirectBodyState2D.step] and its respective getter. </description> </method> <method name="_get_total_angular_damp" qualifiers="virtual const"> <return type="float" /> <description> + Implement to override the behavior of [member PhysicsDirectBodyState2D.total_angular_damp] and its respective getter. </description> </method> <method name="_get_total_gravity" qualifiers="virtual const"> <return type="Vector2" /> <description> + Implement to override the behavior of [member PhysicsDirectBodyState2D.total_gravity] and its respective getter. </description> </method> <method name="_get_total_linear_damp" qualifiers="virtual const"> <return type="float" /> <description> + Implement to override the behavior of [member PhysicsDirectBodyState2D.total_linear_damp] and its respective getter. </description> </method> <method name="_get_transform" qualifiers="virtual const"> <return type="Transform2D" /> <description> + Implement to override the behavior of [member PhysicsDirectBodyState2D.transform] and its respective getter. </description> </method> <method name="_get_velocity_at_local_position" qualifiers="virtual const"> <return type="Vector2" /> <param index="0" name="local_position" type="Vector2" /> <description> + Overridable version of [method PhysicsDirectBodyState2D.get_velocity_at_local_position]. </description> </method> <method name="_integrate_forces" qualifiers="virtual"> <return type="void" /> <description> + Overridable version of [method PhysicsDirectBodyState2D.integrate_forces]. </description> </method> <method name="_is_sleeping" qualifiers="virtual const"> <return type="bool" /> <description> + Implement to override the behavior of [member PhysicsDirectBodyState2D.sleeping] and its respective getter. </description> </method> <method name="_set_angular_velocity" qualifiers="virtual"> <return type="void" /> <param index="0" name="velocity" type="float" /> <description> + Implement to override the behavior of [member PhysicsDirectBodyState2D.angular_velocity] and its respective setter. </description> </method> <method name="_set_constant_force" qualifiers="virtual"> <return type="void" /> <param index="0" name="force" type="Vector2" /> <description> + Overridable version of [method PhysicsDirectBodyState2D.set_constant_force]. </description> </method> <method name="_set_constant_torque" qualifiers="virtual"> <return type="void" /> <param index="0" name="torque" type="float" /> <description> + Overridable version of [method PhysicsDirectBodyState2D.set_constant_torque]. </description> </method> <method name="_set_linear_velocity" qualifiers="virtual"> <return type="void" /> <param index="0" name="velocity" type="Vector2" /> <description> + Implement to override the behavior of [member PhysicsDirectBodyState2D.linear_velocity] and its respective setter. </description> </method> <method name="_set_sleep_state" qualifiers="virtual"> <return type="void" /> <param index="0" name="enabled" type="bool" /> <description> + Implement to override the behavior of [member PhysicsDirectBodyState2D.sleeping] and its respective setter. </description> </method> <method name="_set_transform" qualifiers="virtual"> <return type="void" /> <param index="0" name="transform" type="Transform2D" /> <description> + Implement to override the behavior of [member PhysicsDirectBodyState2D.transform] and its respective setter. </description> </method> </methods> diff --git a/doc/classes/PhysicsDirectBodyState3D.xml b/doc/classes/PhysicsDirectBodyState3D.xml index 42c65763aa..e8c3f3f89d 100644 --- a/doc/classes/PhysicsDirectBodyState3D.xml +++ b/doc/classes/PhysicsDirectBodyState3D.xml @@ -202,7 +202,7 @@ <method name="integrate_forces"> <return type="void" /> <description> - Calls the built-in force integration code. + Updates the body's linear and angular velocity by applying gravity and damping for the equivalent of one physics tick. </description> </method> <method name="set_constant_force"> diff --git a/doc/classes/PhysicsServer2D.xml b/doc/classes/PhysicsServer2D.xml index 8be92edbad..d40326fa21 100644 --- a/doc/classes/PhysicsServer2D.xml +++ b/doc/classes/PhysicsServer2D.xml @@ -501,7 +501,7 @@ <return type="bool" /> <param index="0" name="body" type="RID" /> <description> - Returns [code]true[/code] if the body uses a callback function to calculate its own physics (see [method body_set_force_integration_callback]). + Returns [code]true[/code] if the body is omitting the standard force integration. See [method body_set_omit_force_integration]. </description> </method> <method name="body_remove_collision_exception"> @@ -592,11 +592,12 @@ <param index="1" name="callable" type="Callable" /> <param index="2" name="userdata" type="Variant" default="null" /> <description> - Sets the function used to calculate physics for the body, if that body allows it (see [method body_set_omit_force_integration]). - The force integration function takes the following two parameters: - 1. a [PhysicsDirectBodyState2D] [code]state[/code]: used to retrieve and modify the body's state, - 2. a [Variant] [param userdata]: optional user data. - [b]Note:[/b] This callback is currently not called in Godot Physics. + Sets the body's custom force integration callback function to [param callable]. Use an empty [Callable] ([code skip-lint]Callable()[/code]) to clear the custom callback. + The function [param callable] will be called every physics tick, before the standard force integration (see [method body_set_omit_force_integration]). It can be used for example to update the body's linear and angular velocity based on contact with other bodies. + If [param userdata] is not [code]null[/code], the function [param callable] must take the following two parameters: + 1. [code]state[/code]: a [PhysicsDirectBodyState2D] used to retrieve and modify the body's state, + 2. [code skip-lint]userdata[/code]: a [Variant]; its value will be the [param userdata] passed into this method. + If [param userdata] is [code]null[/code], then [param callable] must take only the [code]state[/code] parameter. </description> </method> <method name="body_set_max_contacts_reported"> @@ -620,7 +621,8 @@ <param index="0" name="body" type="RID" /> <param index="1" name="enable" type="bool" /> <description> - Sets whether the body uses a callback function to calculate its own physics (see [method body_set_force_integration_callback]). + Sets whether the body omits the standard force integration. If [param enable] is [code]true[/code], the body will not automatically use applied forces, torques, and damping to update the body's linear and angular velocity. In this case, [method body_set_force_integration_callback] can be used to manually update the linear and angular velocity instead. + This method is called when the property [member RigidBody2D.custom_integrator] is set. </description> </method> <method name="body_set_param"> diff --git a/doc/classes/PhysicsServer2DExtension.xml b/doc/classes/PhysicsServer2DExtension.xml index 8d9a171337..815fc742d1 100644 --- a/doc/classes/PhysicsServer2DExtension.xml +++ b/doc/classes/PhysicsServer2DExtension.xml @@ -17,6 +17,7 @@ <param index="2" name="transform" type="Transform2D" /> <param index="3" name="disabled" type="bool" /> <description> + Overridable version of [method PhysicsServer2D.area_add_shape]. </description> </method> <method name="_area_attach_canvas_instance_id" qualifiers="virtual"> @@ -24,6 +25,7 @@ <param index="0" name="area" type="RID" /> <param index="1" name="id" type="int" /> <description> + Overridable version of [method PhysicsServer2D.area_attach_canvas_instance_id]. </description> </method> <method name="_area_attach_object_instance_id" qualifiers="virtual"> @@ -31,41 +33,48 @@ <param index="0" name="area" type="RID" /> <param index="1" name="id" type="int" /> <description> + Overridable version of [method PhysicsServer2D.area_attach_object_instance_id]. </description> </method> <method name="_area_clear_shapes" qualifiers="virtual"> <return type="void" /> <param index="0" name="area" type="RID" /> <description> + Overridable version of [method PhysicsServer2D.area_clear_shapes]. </description> </method> <method name="_area_create" qualifiers="virtual"> <return type="RID" /> <description> + Overridable version of [method PhysicsServer2D.area_create]. </description> </method> <method name="_area_get_canvas_instance_id" qualifiers="virtual const"> <return type="int" /> <param index="0" name="area" type="RID" /> <description> + Overridable version of [method PhysicsServer2D.area_get_canvas_instance_id]. </description> </method> <method name="_area_get_collision_layer" qualifiers="virtual const"> <return type="int" /> <param index="0" name="area" type="RID" /> <description> + Overridable version of [method PhysicsServer2D.area_get_collision_layer]. </description> </method> <method name="_area_get_collision_mask" qualifiers="virtual const"> <return type="int" /> <param index="0" name="area" type="RID" /> <description> + Overridable version of [method PhysicsServer2D.area_get_collision_mask]. </description> </method> <method name="_area_get_object_instance_id" qualifiers="virtual const"> <return type="int" /> <param index="0" name="area" type="RID" /> <description> + Overridable version of [method PhysicsServer2D.area_get_object_instance_id]. </description> </method> <method name="_area_get_param" qualifiers="virtual const"> @@ -73,6 +82,7 @@ <param index="0" name="area" type="RID" /> <param index="1" name="param" type="int" enum="PhysicsServer2D.AreaParameter" /> <description> + Overridable version of [method PhysicsServer2D.area_get_param]. </description> </method> <method name="_area_get_shape" qualifiers="virtual const"> @@ -80,12 +90,14 @@ <param index="0" name="area" type="RID" /> <param index="1" name="shape_idx" type="int" /> <description> + Overridable version of [method PhysicsServer2D.area_get_shape]. </description> </method> <method name="_area_get_shape_count" qualifiers="virtual const"> <return type="int" /> <param index="0" name="area" type="RID" /> <description> + Overridable version of [method PhysicsServer2D.area_get_shape_count]. </description> </method> <method name="_area_get_shape_transform" qualifiers="virtual const"> @@ -93,18 +105,21 @@ <param index="0" name="area" type="RID" /> <param index="1" name="shape_idx" type="int" /> <description> + Overridable version of [method PhysicsServer2D.area_get_shape_transform]. </description> </method> <method name="_area_get_space" qualifiers="virtual const"> <return type="RID" /> <param index="0" name="area" type="RID" /> <description> + Overridable version of [method PhysicsServer2D.area_get_space]. </description> </method> <method name="_area_get_transform" qualifiers="virtual const"> <return type="Transform2D" /> <param index="0" name="area" type="RID" /> <description> + Overridable version of [method PhysicsServer2D.area_get_transform]. </description> </method> <method name="_area_remove_shape" qualifiers="virtual"> @@ -112,6 +127,7 @@ <param index="0" name="area" type="RID" /> <param index="1" name="shape_idx" type="int" /> <description> + Overridable version of [method PhysicsServer2D.area_remove_shape]. </description> </method> <method name="_area_set_area_monitor_callback" qualifiers="virtual"> @@ -119,6 +135,7 @@ <param index="0" name="area" type="RID" /> <param index="1" name="callback" type="Callable" /> <description> + Overridable version of [method PhysicsServer2D.area_set_area_monitor_callback]. </description> </method> <method name="_area_set_collision_layer" qualifiers="virtual"> @@ -126,6 +143,7 @@ <param index="0" name="area" type="RID" /> <param index="1" name="layer" type="int" /> <description> + Overridable version of [method PhysicsServer2D.area_set_collision_layer]. </description> </method> <method name="_area_set_collision_mask" qualifiers="virtual"> @@ -133,6 +151,7 @@ <param index="0" name="area" type="RID" /> <param index="1" name="mask" type="int" /> <description> + Overridable version of [method PhysicsServer2D.area_set_collision_mask]. </description> </method> <method name="_area_set_monitor_callback" qualifiers="virtual"> @@ -140,6 +159,7 @@ <param index="0" name="area" type="RID" /> <param index="1" name="callback" type="Callable" /> <description> + Overridable version of [method PhysicsServer2D.area_set_monitor_callback]. </description> </method> <method name="_area_set_monitorable" qualifiers="virtual"> @@ -147,6 +167,7 @@ <param index="0" name="area" type="RID" /> <param index="1" name="monitorable" type="bool" /> <description> + Overridable version of [method PhysicsServer2D.area_set_monitorable]. </description> </method> <method name="_area_set_param" qualifiers="virtual"> @@ -155,6 +176,7 @@ <param index="1" name="param" type="int" enum="PhysicsServer2D.AreaParameter" /> <param index="2" name="value" type="Variant" /> <description> + Overridable version of [method PhysicsServer2D.area_set_param]. </description> </method> <method name="_area_set_pickable" qualifiers="virtual"> @@ -162,6 +184,8 @@ <param index="0" name="area" type="RID" /> <param index="1" name="pickable" type="bool" /> <description> + If set to [code]true[/code], allows the area with the given [RID] to detect mouse inputs when the mouse cursor is hovering on it. + Overridable version of [PhysicsServer2D]'s internal [code]area_set_pickable[/code] method. Corresponds to [member PhysicsBody2D.input_pickable]. </description> </method> <method name="_area_set_shape" qualifiers="virtual"> @@ -170,6 +194,7 @@ <param index="1" name="shape_idx" type="int" /> <param index="2" name="shape" type="RID" /> <description> + Overridable version of [method PhysicsServer2D.area_set_shape]. </description> </method> <method name="_area_set_shape_disabled" qualifiers="virtual"> @@ -178,6 +203,7 @@ <param index="1" name="shape_idx" type="int" /> <param index="2" name="disabled" type="bool" /> <description> + Overridable version of [method PhysicsServer2D.area_set_shape_disabled]. </description> </method> <method name="_area_set_shape_transform" qualifiers="virtual"> @@ -186,6 +212,7 @@ <param index="1" name="shape_idx" type="int" /> <param index="2" name="transform" type="Transform2D" /> <description> + Overridable version of [method PhysicsServer2D.area_set_shape_transform]. </description> </method> <method name="_area_set_space" qualifiers="virtual"> @@ -193,6 +220,7 @@ <param index="0" name="area" type="RID" /> <param index="1" name="space" type="RID" /> <description> + Overridable version of [method PhysicsServer2D.area_set_space]. </description> </method> <method name="_area_set_transform" qualifiers="virtual"> @@ -200,6 +228,7 @@ <param index="0" name="area" type="RID" /> <param index="1" name="transform" type="Transform2D" /> <description> + Overridable version of [method PhysicsServer2D.area_set_transform]. </description> </method> <method name="_body_add_collision_exception" qualifiers="virtual"> @@ -207,6 +236,7 @@ <param index="0" name="body" type="RID" /> <param index="1" name="excepted_body" type="RID" /> <description> + Overridable version of [method PhysicsServer2D.body_add_collision_exception]. </description> </method> <method name="_body_add_constant_central_force" qualifiers="virtual"> @@ -214,6 +244,7 @@ <param index="0" name="body" type="RID" /> <param index="1" name="force" type="Vector2" /> <description> + Overridable version of [method PhysicsServer2D.body_add_constant_central_force]. </description> </method> <method name="_body_add_constant_force" qualifiers="virtual"> @@ -222,6 +253,7 @@ <param index="1" name="force" type="Vector2" /> <param index="2" name="position" type="Vector2" /> <description> + Overridable version of [method PhysicsServer2D.body_add_constant_force]. </description> </method> <method name="_body_add_constant_torque" qualifiers="virtual"> @@ -229,6 +261,7 @@ <param index="0" name="body" type="RID" /> <param index="1" name="torque" type="float" /> <description> + Overridable version of [method PhysicsServer2D.body_add_constant_torque]. </description> </method> <method name="_body_add_shape" qualifiers="virtual"> @@ -238,6 +271,7 @@ <param index="2" name="transform" type="Transform2D" /> <param index="3" name="disabled" type="bool" /> <description> + Overridable version of [method PhysicsServer2D.body_add_shape]. </description> </method> <method name="_body_apply_central_force" qualifiers="virtual"> @@ -245,6 +279,7 @@ <param index="0" name="body" type="RID" /> <param index="1" name="force" type="Vector2" /> <description> + Overridable version of [method PhysicsServer2D.body_apply_central_force]. </description> </method> <method name="_body_apply_central_impulse" qualifiers="virtual"> @@ -252,6 +287,7 @@ <param index="0" name="body" type="RID" /> <param index="1" name="impulse" type="Vector2" /> <description> + Overridable version of [method PhysicsServer2D.body_apply_central_impulse]. </description> </method> <method name="_body_apply_force" qualifiers="virtual"> @@ -260,6 +296,7 @@ <param index="1" name="force" type="Vector2" /> <param index="2" name="position" type="Vector2" /> <description> + Overridable version of [method PhysicsServer2D.body_apply_force]. </description> </method> <method name="_body_apply_impulse" qualifiers="virtual"> @@ -268,6 +305,7 @@ <param index="1" name="impulse" type="Vector2" /> <param index="2" name="position" type="Vector2" /> <description> + Overridable version of [method PhysicsServer2D.body_apply_impulse]. </description> </method> <method name="_body_apply_torque" qualifiers="virtual"> @@ -275,6 +313,7 @@ <param index="0" name="body" type="RID" /> <param index="1" name="torque" type="float" /> <description> + Overridable version of [method PhysicsServer2D.body_apply_torque]. </description> </method> <method name="_body_apply_torque_impulse" qualifiers="virtual"> @@ -282,6 +321,7 @@ <param index="0" name="body" type="RID" /> <param index="1" name="impulse" type="float" /> <description> + Overridable version of [method PhysicsServer2D.body_apply_torque_impulse]. </description> </method> <method name="_body_attach_canvas_instance_id" qualifiers="virtual"> @@ -289,6 +329,7 @@ <param index="0" name="body" type="RID" /> <param index="1" name="id" type="int" /> <description> + Overridable version of [method PhysicsServer2D.body_attach_canvas_instance_id]. </description> </method> <method name="_body_attach_object_instance_id" qualifiers="virtual"> @@ -296,12 +337,14 @@ <param index="0" name="body" type="RID" /> <param index="1" name="id" type="int" /> <description> + Overridable version of [method PhysicsServer2D.body_attach_object_instance_id]. </description> </method> <method name="_body_clear_shapes" qualifiers="virtual"> <return type="void" /> <param index="0" name="body" type="RID" /> <description> + Overridable version of [method PhysicsServer2D.body_clear_shapes]. </description> </method> <method name="_body_collide_shape" qualifiers="virtual"> @@ -315,89 +358,107 @@ <param index="6" name="result_max" type="int" /> <param index="7" name="result_count" type="int32_t*" /> <description> + Given a [param body], a [param shape], and their respective parameters, this method should return [code]true[/code] if a collision between the two would occur, with additional details passed in [param results]. + Overridable version of [PhysicsServer2D]'s internal [code]shape_collide[/code] method. Corresponds to [method PhysicsDirectSpaceState2D.collide_shape]. </description> </method> <method name="_body_create" qualifiers="virtual"> <return type="RID" /> <description> + Overridable version of [method PhysicsServer2D.body_create]. </description> </method> <method name="_body_get_canvas_instance_id" qualifiers="virtual const"> <return type="int" /> <param index="0" name="body" type="RID" /> <description> + Overridable version of [method PhysicsServer2D.body_get_canvas_instance_id]. </description> </method> <method name="_body_get_collision_exceptions" qualifiers="virtual const"> <return type="RID[]" /> <param index="0" name="body" type="RID" /> <description> + Returns the [RID]s of all bodies added as collision exceptions for the given [param body]. See also [method _body_add_collision_exception] and [method _body_remove_collision_exception]. + Overridable version of [PhysicsServer2D]'s internal [code]body_get_collision_exceptions[/code] method. Corresponds to [method PhysicsBody2D.get_collision_exceptions]. </description> </method> <method name="_body_get_collision_layer" qualifiers="virtual const"> <return type="int" /> <param index="0" name="body" type="RID" /> <description> + Overridable version of [method PhysicsServer2D.body_get_collision_layer]. </description> </method> <method name="_body_get_collision_mask" qualifiers="virtual const"> <return type="int" /> <param index="0" name="body" type="RID" /> <description> + Overridable version of [method PhysicsServer2D.body_get_collision_mask]. </description> </method> <method name="_body_get_collision_priority" qualifiers="virtual const"> <return type="float" /> <param index="0" name="body" type="RID" /> <description> + Overridable version of [method PhysicsServer2D.body_get_collision_priority]. </description> </method> <method name="_body_get_constant_force" qualifiers="virtual const"> <return type="Vector2" /> <param index="0" name="body" type="RID" /> <description> + Overridable version of [method PhysicsServer2D.body_get_constant_force]. </description> </method> <method name="_body_get_constant_torque" qualifiers="virtual const"> <return type="float" /> <param index="0" name="body" type="RID" /> <description> + Overridable version of [method PhysicsServer2D.body_get_constant_torque]. </description> </method> <method name="_body_get_contacts_reported_depth_threshold" qualifiers="virtual const"> <return type="float" /> <param index="0" name="body" type="RID" /> <description> + Overridable version of [PhysicsServer2D]'s internal [code]body_get_contacts_reported_depth_threshold[/code] method. + [b]Note:[/b] This method is currently unused by Godot's default physics implementation. </description> </method> <method name="_body_get_continuous_collision_detection_mode" qualifiers="virtual const"> <return type="int" enum="PhysicsServer2D.CCDMode" /> <param index="0" name="body" type="RID" /> <description> + Overridable version of [method PhysicsServer2D.body_get_continuous_collision_detection_mode]. </description> </method> <method name="_body_get_direct_state" qualifiers="virtual"> <return type="PhysicsDirectBodyState2D" /> <param index="0" name="body" type="RID" /> <description> + Overridable version of [method PhysicsServer2D.body_get_direct_state]. </description> </method> <method name="_body_get_max_contacts_reported" qualifiers="virtual const"> <return type="int" /> <param index="0" name="body" type="RID" /> <description> + Overridable version of [method PhysicsServer2D.body_get_max_contacts_reported]. </description> </method> <method name="_body_get_mode" qualifiers="virtual const"> <return type="int" enum="PhysicsServer2D.BodyMode" /> <param index="0" name="body" type="RID" /> <description> + Overridable version of [method PhysicsServer2D.body_get_mode]. </description> </method> <method name="_body_get_object_instance_id" qualifiers="virtual const"> <return type="int" /> <param index="0" name="body" type="RID" /> <description> + Overridable version of [method PhysicsServer2D.body_get_object_instance_id]. </description> </method> <method name="_body_get_param" qualifiers="virtual const"> @@ -405,6 +466,7 @@ <param index="0" name="body" type="RID" /> <param index="1" name="param" type="int" enum="PhysicsServer2D.BodyParameter" /> <description> + Overridable version of [method PhysicsServer2D.body_get_param]. </description> </method> <method name="_body_get_shape" qualifiers="virtual const"> @@ -412,12 +474,14 @@ <param index="0" name="body" type="RID" /> <param index="1" name="shape_idx" type="int" /> <description> + Overridable version of [method PhysicsServer2D.body_get_shape]. </description> </method> <method name="_body_get_shape_count" qualifiers="virtual const"> <return type="int" /> <param index="0" name="body" type="RID" /> <description> + Overridable version of [method PhysicsServer2D.body_get_shape_count]. </description> </method> <method name="_body_get_shape_transform" qualifiers="virtual const"> @@ -425,12 +489,14 @@ <param index="0" name="body" type="RID" /> <param index="1" name="shape_idx" type="int" /> <description> + Overridable version of [method PhysicsServer2D.body_get_shape_transform]. </description> </method> <method name="_body_get_space" qualifiers="virtual const"> <return type="RID" /> <param index="0" name="body" type="RID" /> <description> + Overridable version of [method PhysicsServer2D.body_get_space]. </description> </method> <method name="_body_get_state" qualifiers="virtual const"> @@ -438,12 +504,14 @@ <param index="0" name="body" type="RID" /> <param index="1" name="state" type="int" enum="PhysicsServer2D.BodyState" /> <description> + Overridable version of [method PhysicsServer2D.body_get_state]. </description> </method> <method name="_body_is_omitting_force_integration" qualifiers="virtual const"> <return type="bool" /> <param index="0" name="body" type="RID" /> <description> + Overridable version of [method PhysicsServer2D.body_is_omitting_force_integration]. </description> </method> <method name="_body_remove_collision_exception" qualifiers="virtual"> @@ -451,6 +519,7 @@ <param index="0" name="body" type="RID" /> <param index="1" name="excepted_body" type="RID" /> <description> + Overridable version of [method PhysicsServer2D.body_remove_collision_exception]. </description> </method> <method name="_body_remove_shape" qualifiers="virtual"> @@ -458,12 +527,14 @@ <param index="0" name="body" type="RID" /> <param index="1" name="shape_idx" type="int" /> <description> + Overridable version of [method PhysicsServer2D.body_remove_shape]. </description> </method> <method name="_body_reset_mass_properties" qualifiers="virtual"> <return type="void" /> <param index="0" name="body" type="RID" /> <description> + Overridable version of [method PhysicsServer2D.body_reset_mass_properties]. </description> </method> <method name="_body_set_axis_velocity" qualifiers="virtual"> @@ -471,6 +542,7 @@ <param index="0" name="body" type="RID" /> <param index="1" name="axis_velocity" type="Vector2" /> <description> + Overridable version of [method PhysicsServer2D.body_set_axis_velocity]. </description> </method> <method name="_body_set_collision_layer" qualifiers="virtual"> @@ -478,6 +550,7 @@ <param index="0" name="body" type="RID" /> <param index="1" name="layer" type="int" /> <description> + Overridable version of [method PhysicsServer2D.body_set_collision_layer]. </description> </method> <method name="_body_set_collision_mask" qualifiers="virtual"> @@ -485,6 +558,7 @@ <param index="0" name="body" type="RID" /> <param index="1" name="mask" type="int" /> <description> + Overridable version of [method PhysicsServer2D.body_set_collision_mask]. </description> </method> <method name="_body_set_collision_priority" qualifiers="virtual"> @@ -492,6 +566,7 @@ <param index="0" name="body" type="RID" /> <param index="1" name="priority" type="float" /> <description> + Overridable version of [method PhysicsServer2D.body_set_collision_priority]. </description> </method> <method name="_body_set_constant_force" qualifiers="virtual"> @@ -499,6 +574,7 @@ <param index="0" name="body" type="RID" /> <param index="1" name="force" type="Vector2" /> <description> + Overridable version of [method PhysicsServer2D.body_set_constant_force]. </description> </method> <method name="_body_set_constant_torque" qualifiers="virtual"> @@ -506,6 +582,7 @@ <param index="0" name="body" type="RID" /> <param index="1" name="torque" type="float" /> <description> + Overridable version of [method PhysicsServer2D.body_set_constant_torque]. </description> </method> <method name="_body_set_contacts_reported_depth_threshold" qualifiers="virtual"> @@ -513,6 +590,8 @@ <param index="0" name="body" type="RID" /> <param index="1" name="threshold" type="float" /> <description> + Overridable version of [PhysicsServer2D]'s internal [code]body_set_contacts_reported_depth_threshold[/code] method. + [b]Note:[/b] This method is currently unused by Godot's default physics implementation. </description> </method> <method name="_body_set_continuous_collision_detection_mode" qualifiers="virtual"> @@ -520,6 +599,7 @@ <param index="0" name="body" type="RID" /> <param index="1" name="mode" type="int" enum="PhysicsServer2D.CCDMode" /> <description> + Overridable version of [method PhysicsServer2D.body_set_continuous_collision_detection_mode]. </description> </method> <method name="_body_set_force_integration_callback" qualifiers="virtual"> @@ -528,6 +608,7 @@ <param index="1" name="callable" type="Callable" /> <param index="2" name="userdata" type="Variant" /> <description> + Overridable version of [method PhysicsServer2D.body_set_force_integration_callback]. </description> </method> <method name="_body_set_max_contacts_reported" qualifiers="virtual"> @@ -535,6 +616,7 @@ <param index="0" name="body" type="RID" /> <param index="1" name="amount" type="int" /> <description> + Overridable version of [method PhysicsServer2D.body_set_max_contacts_reported]. </description> </method> <method name="_body_set_mode" qualifiers="virtual"> @@ -542,6 +624,7 @@ <param index="0" name="body" type="RID" /> <param index="1" name="mode" type="int" enum="PhysicsServer2D.BodyMode" /> <description> + Overridable version of [method PhysicsServer2D.body_set_mode]. </description> </method> <method name="_body_set_omit_force_integration" qualifiers="virtual"> @@ -549,6 +632,7 @@ <param index="0" name="body" type="RID" /> <param index="1" name="enable" type="bool" /> <description> + Overridable version of [method PhysicsServer2D.body_set_omit_force_integration]. </description> </method> <method name="_body_set_param" qualifiers="virtual"> @@ -557,6 +641,7 @@ <param index="1" name="param" type="int" enum="PhysicsServer2D.BodyParameter" /> <param index="2" name="value" type="Variant" /> <description> + Overridable version of [method PhysicsServer2D.body_set_param]. </description> </method> <method name="_body_set_pickable" qualifiers="virtual"> @@ -564,6 +649,8 @@ <param index="0" name="body" type="RID" /> <param index="1" name="pickable" type="bool" /> <description> + If set to [code]true[/code], allows the body with the given [RID] to detect mouse inputs when the mouse cursor is hovering on it. + Overridable version of [PhysicsServer2D]'s internal [code]body_set_pickable[/code] method. Corresponds to [member PhysicsBody2D.input_pickable]. </description> </method> <method name="_body_set_shape" qualifiers="virtual"> @@ -572,6 +659,7 @@ <param index="1" name="shape_idx" type="int" /> <param index="2" name="shape" type="RID" /> <description> + Overridable version of [method PhysicsServer2D.body_set_shape]. </description> </method> <method name="_body_set_shape_as_one_way_collision" qualifiers="virtual"> @@ -581,6 +669,7 @@ <param index="2" name="enable" type="bool" /> <param index="3" name="margin" type="float" /> <description> + Overridable version of [method PhysicsServer2D.body_set_shape_as_one_way_collision]. </description> </method> <method name="_body_set_shape_disabled" qualifiers="virtual"> @@ -589,6 +678,7 @@ <param index="1" name="shape_idx" type="int" /> <param index="2" name="disabled" type="bool" /> <description> + Overridable version of [method PhysicsServer2D.body_set_shape_disabled]. </description> </method> <method name="_body_set_shape_transform" qualifiers="virtual"> @@ -597,6 +687,7 @@ <param index="1" name="shape_idx" type="int" /> <param index="2" name="transform" type="Transform2D" /> <description> + Overridable version of [method PhysicsServer2D.body_set_shape_transform]. </description> </method> <method name="_body_set_space" qualifiers="virtual"> @@ -604,6 +695,7 @@ <param index="0" name="body" type="RID" /> <param index="1" name="space" type="RID" /> <description> + Overridable version of [method PhysicsServer2D.body_set_space]. </description> </method> <method name="_body_set_state" qualifiers="virtual"> @@ -612,6 +704,7 @@ <param index="1" name="state" type="int" enum="PhysicsServer2D.BodyState" /> <param index="2" name="value" type="Variant" /> <description> + Overridable version of [method PhysicsServer2D.body_set_state]. </description> </method> <method name="_body_set_state_sync_callback" qualifiers="virtual"> @@ -619,6 +712,8 @@ <param index="0" name="body" type="RID" /> <param index="1" name="callable" type="Callable" /> <description> + Assigns the [param body] to call the given [param callable] during the synchronization phase of the loop, before [method _step] is called. See also [method _sync]. + Overridable version of [PhysicsServer2D]'s internal [code]body_set_state_sync_callback[/code] method. </description> </method> <method name="_body_test_motion" qualifiers="virtual const"> @@ -631,26 +726,31 @@ <param index="5" name="recovery_as_collision" type="bool" /> <param index="6" name="result" type="PhysicsServer2DExtensionMotionResult*" /> <description> + Overridable version of [method PhysicsServer2D.body_test_motion]. Unlike the exposed implementation, this method does not receive all of the arguments inside a [PhysicsTestMotionParameters2D]. </description> </method> <method name="_capsule_shape_create" qualifiers="virtual"> <return type="RID" /> <description> + Overridable version of [method PhysicsServer2D.capsule_shape_create]. </description> </method> <method name="_circle_shape_create" qualifiers="virtual"> <return type="RID" /> <description> + Overridable version of [method PhysicsServer2D.circle_shape_create]. </description> </method> <method name="_concave_polygon_shape_create" qualifiers="virtual"> <return type="RID" /> <description> + Overridable version of [method PhysicsServer2D.concave_polygon_shape_create]. </description> </method> <method name="_convex_polygon_shape_create" qualifiers="virtual"> <return type="RID" /> <description> + Overridable version of [method PhysicsServer2D.convex_polygon_shape_create]. </description> </method> <method name="_damped_spring_joint_get_param" qualifiers="virtual const"> @@ -658,6 +758,7 @@ <param index="0" name="joint" type="RID" /> <param index="1" name="param" type="int" enum="PhysicsServer2D.DampedSpringParam" /> <description> + Overridable version of [method PhysicsServer2D.damped_spring_joint_get_param]. </description> </method> <method name="_damped_spring_joint_set_param" qualifiers="virtual"> @@ -666,54 +767,69 @@ <param index="1" name="param" type="int" enum="PhysicsServer2D.DampedSpringParam" /> <param index="2" name="value" type="float" /> <description> + Overridable version of [method PhysicsServer2D.damped_spring_joint_set_param]. </description> </method> <method name="_end_sync" qualifiers="virtual"> <return type="void" /> <description> + Called to indicate that the physics server has stopped synchronizing. It is in the loop's iteration/physics phase, and can access physics objects even if running on a separate thread. See also [method _sync]. + Overridable version of [PhysicsServer2D]'s internal [code]end_sync[/code] method. </description> </method> <method name="_finish" qualifiers="virtual"> <return type="void" /> <description> + Called when the main loop finalizes to shut down the physics server. See also [method MainLoop._finalize] and [method _init]. + Overridable version of [PhysicsServer2D]'s internal [code]finish[/code] method. </description> </method> <method name="_flush_queries" qualifiers="virtual"> <return type="void" /> <description> + Called every physics step before [method _step] to process all remaining queries. + Overridable version of [PhysicsServer2D]'s internal [code]flush_queries[/code] method. </description> </method> <method name="_free_rid" qualifiers="virtual"> <return type="void" /> <param index="0" name="rid" type="RID" /> <description> + Overridable version of [method PhysicsServer2D.free_rid]. </description> </method> <method name="_get_process_info" qualifiers="virtual"> <return type="int" /> <param index="0" name="process_info" type="int" enum="PhysicsServer2D.ProcessInfo" /> <description> + Overridable version of [method PhysicsServer2D.get_process_info]. </description> </method> <method name="_init" qualifiers="virtual"> <return type="void" /> <description> + Called when the main loop is initialized and creates a new instance of this physics server. See also [method MainLoop._initialize] and [method _finish]. + Overridable version of [PhysicsServer2D]'s internal [code]init[/code] method. </description> </method> <method name="_is_flushing_queries" qualifiers="virtual const"> <return type="bool" /> <description> + Overridable method that should return [code]true[/code] when the physics server is processing queries. See also [method _flush_queries]. + Overridable version of [PhysicsServer2D]'s internal [code]is_flushing_queries[/code] method. </description> </method> <method name="_joint_clear" qualifiers="virtual"> <return type="void" /> <param index="0" name="joint" type="RID" /> <description> + Overridable version of [method PhysicsServer2D.joint_clear]. </description> </method> <method name="_joint_create" qualifiers="virtual"> <return type="RID" /> <description> + Overridable version of [method PhysicsServer2D.joint_create]. </description> </method> <method name="_joint_disable_collisions_between_bodies" qualifiers="virtual"> @@ -721,6 +837,7 @@ <param index="0" name="joint" type="RID" /> <param index="1" name="disable" type="bool" /> <description> + Overridable version of [method PhysicsServer2D.joint_disable_collisions_between_bodies]. </description> </method> <method name="_joint_get_param" qualifiers="virtual const"> @@ -728,18 +845,21 @@ <param index="0" name="joint" type="RID" /> <param index="1" name="param" type="int" enum="PhysicsServer2D.JointParam" /> <description> + Overridable version of [method PhysicsServer2D.joint_get_param]. </description> </method> <method name="_joint_get_type" qualifiers="virtual const"> <return type="int" enum="PhysicsServer2D.JointType" /> <param index="0" name="joint" type="RID" /> <description> + Overridable version of [method PhysicsServer2D.joint_get_type]. </description> </method> <method name="_joint_is_disabled_collisions_between_bodies" qualifiers="virtual const"> <return type="bool" /> <param index="0" name="joint" type="RID" /> <description> + Overridable version of [method PhysicsServer2D.joint_is_disabled_collisions_between_bodies]. </description> </method> <method name="_joint_make_damped_spring" qualifiers="virtual"> @@ -750,6 +870,7 @@ <param index="3" name="body_a" type="RID" /> <param index="4" name="body_b" type="RID" /> <description> + Overridable version of [method PhysicsServer2D.joint_make_damped_spring]. </description> </method> <method name="_joint_make_groove" qualifiers="virtual"> @@ -761,6 +882,7 @@ <param index="4" name="body_a" type="RID" /> <param index="5" name="body_b" type="RID" /> <description> + Overridable version of [method PhysicsServer2D.joint_make_groove]. </description> </method> <method name="_joint_make_pin" qualifiers="virtual"> @@ -770,6 +892,7 @@ <param index="2" name="body_a" type="RID" /> <param index="3" name="body_b" type="RID" /> <description> + Overridable version of [method PhysicsServer2D.joint_make_pin]. </description> </method> <method name="_joint_set_param" qualifiers="virtual"> @@ -778,6 +901,7 @@ <param index="1" name="param" type="int" enum="PhysicsServer2D.JointParam" /> <param index="2" name="value" type="float" /> <description> + Overridable version of [method PhysicsServer2D.joint_set_param]. </description> </method> <method name="_pin_joint_get_flag" qualifiers="virtual const"> @@ -785,6 +909,7 @@ <param index="0" name="joint" type="RID" /> <param index="1" name="flag" type="int" enum="PhysicsServer2D.PinJointFlag" /> <description> + Overridable version of [method PhysicsServer2D.pin_joint_get_flag]. </description> </method> <method name="_pin_joint_get_param" qualifiers="virtual const"> @@ -792,6 +917,7 @@ <param index="0" name="joint" type="RID" /> <param index="1" name="param" type="int" enum="PhysicsServer2D.PinJointParam" /> <description> + Overridable version of [method PhysicsServer2D.pin_joint_get_param]. </description> </method> <method name="_pin_joint_set_flag" qualifiers="virtual"> @@ -800,6 +926,7 @@ <param index="1" name="flag" type="int" enum="PhysicsServer2D.PinJointFlag" /> <param index="2" name="enabled" type="bool" /> <description> + Overridable version of [method PhysicsServer2D.pin_joint_set_flag]. </description> </method> <method name="_pin_joint_set_param" qualifiers="virtual"> @@ -808,27 +935,32 @@ <param index="1" name="param" type="int" enum="PhysicsServer2D.PinJointParam" /> <param index="2" name="value" type="float" /> <description> + Overridable version of [method PhysicsServer2D.pin_joint_set_param]. </description> </method> <method name="_rectangle_shape_create" qualifiers="virtual"> <return type="RID" /> <description> + Overridable version of [method PhysicsServer2D.rectangle_shape_create]. </description> </method> <method name="_segment_shape_create" qualifiers="virtual"> <return type="RID" /> <description> + Overridable version of [method PhysicsServer2D.segment_shape_create]. </description> </method> <method name="_separation_ray_shape_create" qualifiers="virtual"> <return type="RID" /> <description> + Overridable version of [method PhysicsServer2D.separation_ray_shape_create]. </description> </method> <method name="_set_active" qualifiers="virtual"> <return type="void" /> <param index="0" name="active" type="bool" /> <description> + Overridable version of [method PhysicsServer2D.set_active]. </description> </method> <method name="_shape_collide" qualifiers="virtual"> @@ -843,24 +975,30 @@ <param index="7" name="result_max" type="int" /> <param index="8" name="result_count" type="int32_t*" /> <description> + Given two shapes and their parameters, should return [code]true[/code] if a collision between the two would occur, with additional details passed in [param results]. + Overridable version of [PhysicsServer2D]'s internal [code]shape_collide[/code] method. Corresponds to [method PhysicsDirectSpaceState2D.collide_shape]. </description> </method> <method name="_shape_get_custom_solver_bias" qualifiers="virtual const"> <return type="float" /> <param index="0" name="shape" type="RID" /> <description> + Should return the custom solver bias of the given [param shape], which defines how much bodies are forced to separate on contact when this shape is involved. + Overridable version of [PhysicsServer2D]'s internal [code]shape_get_custom_solver_bias[/code] method. Corresponds to [member Shape2D.custom_solver_bias]. </description> </method> <method name="_shape_get_data" qualifiers="virtual const"> <return type="Variant" /> <param index="0" name="shape" type="RID" /> <description> + Overridable version of [method PhysicsServer2D.shape_get_data]. </description> </method> <method name="_shape_get_type" qualifiers="virtual const"> <return type="int" enum="PhysicsServer2D.ShapeType" /> <param index="0" name="shape" type="RID" /> <description> + Overridable version of [method PhysicsServer2D.shape_get_type]. </description> </method> <method name="_shape_set_custom_solver_bias" qualifiers="virtual"> @@ -868,6 +1006,8 @@ <param index="0" name="shape" type="RID" /> <param index="1" name="bias" type="float" /> <description> + Should set the custom solver bias for the given [param shape]. It defines how much bodies are forced to separate on contact. + Overridable version of [PhysicsServer2D]'s internal [code]shape_get_custom_solver_bias[/code] method. Corresponds to [member Shape2D.custom_solver_bias]. </description> </method> <method name="_shape_set_data" qualifiers="virtual"> @@ -875,29 +1015,36 @@ <param index="0" name="shape" type="RID" /> <param index="1" name="data" type="Variant" /> <description> + Overridable version of [method PhysicsServer2D.shape_set_data]. </description> </method> <method name="_space_create" qualifiers="virtual"> <return type="RID" /> <description> + Overridable version of [method PhysicsServer2D.space_create]. </description> </method> <method name="_space_get_contact_count" qualifiers="virtual const"> <return type="int" /> <param index="0" name="space" type="RID" /> <description> + Should return how many contacts have occurred during the last physics step in the given [param space]. See also [method _space_get_contacts] and [method _space_set_debug_contacts]. + Overridable version of [PhysicsServer2D]'s internal [code]space_get_contact_count[/code] method. </description> </method> <method name="_space_get_contacts" qualifiers="virtual const"> <return type="PackedVector2Array" /> <param index="0" name="space" type="RID" /> <description> + Should return the positions of all contacts that have occurred during the last physics step in the given [param space]. See also [method _space_get_contact_count] and [method _space_set_debug_contacts]. + Overridable version of [PhysicsServer2D]'s internal [code]space_get_contacts[/code] method. </description> </method> <method name="_space_get_direct_state" qualifiers="virtual"> <return type="PhysicsDirectSpaceState2D" /> <param index="0" name="space" type="RID" /> <description> + Overridable version of [method PhysicsServer2D.space_get_direct_state]. </description> </method> <method name="_space_get_param" qualifiers="virtual const"> @@ -905,12 +1052,14 @@ <param index="0" name="space" type="RID" /> <param index="1" name="param" type="int" enum="PhysicsServer2D.SpaceParameter" /> <description> + Overridable version of [method PhysicsServer2D.space_get_param]. </description> </method> <method name="_space_is_active" qualifiers="virtual const"> <return type="bool" /> <param index="0" name="space" type="RID" /> <description> + Overridable version of [method PhysicsServer2D.space_is_active]. </description> </method> <method name="_space_set_active" qualifiers="virtual"> @@ -918,6 +1067,7 @@ <param index="0" name="space" type="RID" /> <param index="1" name="active" type="bool" /> <description> + Overridable version of [method PhysicsServer2D.space_set_active]. </description> </method> <method name="_space_set_debug_contacts" qualifiers="virtual"> @@ -925,6 +1075,8 @@ <param index="0" name="space" type="RID" /> <param index="1" name="max_contacts" type="int" /> <description> + Used internally to allow the given [param space] to store contact points, up to [param max_contacts]. This is automatically set for the main [World2D]'s space when [member SceneTree.debug_collisions_hint] is [code]true[/code], or by checking "Visible Collision Shapes" in the editor. Only works in debug builds. + Overridable version of [PhysicsServer2D]'s internal [code]space_set_debug_contacts[/code] method. </description> </method> <method name="_space_set_param" qualifiers="virtual"> @@ -933,34 +1085,42 @@ <param index="1" name="param" type="int" enum="PhysicsServer2D.SpaceParameter" /> <param index="2" name="value" type="float" /> <description> + Overridable version of [method PhysicsServer2D.space_set_param]. </description> </method> <method name="_step" qualifiers="virtual"> <return type="void" /> <param index="0" name="step" type="float" /> <description> + Called every physics step to process the physics simulation. [param step] is the time elapsed since the last physics step, in seconds. It is usually the same as [method Node.get_physics_process_delta_time]. + Overridable version of [PhysicsServer2D]'s internal [code skip-lint]step[/code] method. </description> </method> <method name="_sync" qualifiers="virtual"> <return type="void" /> <description> + Called to indicate that the physics server is synchronizing and cannot access physics states if running on a separate thread. See also [method _end_sync]. + Overridable version of [PhysicsServer2D]'s internal [code]sync[/code] method. </description> </method> <method name="_world_boundary_shape_create" qualifiers="virtual"> <return type="RID" /> <description> + Overridable version of [method PhysicsServer2D.world_boundary_shape_create]. </description> </method> <method name="body_test_motion_is_excluding_body" qualifiers="const"> <return type="bool" /> <param index="0" name="body" type="RID" /> <description> + Returns [code]true[/code] if the body with the given [RID] is being excluded from [method _body_test_motion]. See also [method Object.get_instance_id]. </description> </method> <method name="body_test_motion_is_excluding_object" qualifiers="const"> <return type="bool" /> <param index="0" name="object" type="int" /> <description> + Returns [code]true[/code] if the object with the given instance ID is being excluded from [method _body_test_motion]. See also [method Object.get_instance_id]. </description> </method> </methods> diff --git a/doc/classes/PhysicsServer3D.xml b/doc/classes/PhysicsServer3D.xml index e40d73862b..7dcb185834 100644 --- a/doc/classes/PhysicsServer3D.xml +++ b/doc/classes/PhysicsServer3D.xml @@ -482,7 +482,7 @@ <return type="bool" /> <param index="0" name="body" type="RID" /> <description> - Returns whether a body uses a callback function to calculate its own physics (see [method body_set_force_integration_callback]). + Returns [code]true[/code] if the body is omitting the standard force integration. See [method body_set_omit_force_integration]. </description> </method> <method name="body_remove_collision_exception"> @@ -582,9 +582,12 @@ <param index="1" name="callable" type="Callable" /> <param index="2" name="userdata" type="Variant" default="null" /> <description> - Sets the function used to calculate physics for an object, if that object allows it (see [method body_set_omit_force_integration]). The force integration function takes 2 arguments: - - [code]state[/code] — [PhysicsDirectBodyState3D] used to retrieve and modify the body's state. - - [code skip-lint]userdata[/code] — optional user data passed to [method body_set_force_integration_callback]. + Sets the body's custom force integration callback function to [param callable]. Use an empty [Callable] ([code skip-lint]Callable()[/code]) to clear the custom callback. + The function [param callable] will be called every physics tick, before the standard force integration (see [method body_set_omit_force_integration]). It can be used for example to update the body's linear and angular velocity based on contact with other bodies. + If [param userdata] is not [code]null[/code], the function [param callable] must take the following two parameters: + 1. [code]state[/code]: a [PhysicsDirectBodyState3D], used to retrieve and modify the body's state, + 2. [code skip-lint]userdata[/code]: a [Variant]; its value will be the [param userdata] passed into this method. + If [param userdata] is [code]null[/code], then [param callable] must take only the [code]state[/code] parameter. </description> </method> <method name="body_set_max_contacts_reported"> @@ -608,7 +611,8 @@ <param index="0" name="body" type="RID" /> <param index="1" name="enable" type="bool" /> <description> - Sets whether a body uses a callback function to calculate its own physics (see [method body_set_force_integration_callback]). + Sets whether the body omits the standard force integration. If [param enable] is [code]true[/code], the body will not automatically use applied forces, torques, and damping to update the body's linear and angular velocity. In this case, [method body_set_force_integration_callback] can be used to manually update the linear and angular velocity instead. + This method is called when the property [member RigidBody3D.custom_integrator] is set. </description> </method> <method name="body_set_param"> diff --git a/doc/classes/Polygon2D.xml b/doc/classes/Polygon2D.xml index 029a69a399..90d3522002 100644 --- a/doc/classes/Polygon2D.xml +++ b/doc/classes/Polygon2D.xml @@ -91,7 +91,6 @@ </member> <member name="polygon" type="PackedVector2Array" setter="set_polygon" getter="get_polygon" default="PackedVector2Array()"> The polygon's list of vertices. The final point will be connected to the first. - [b]Note:[/b] This returns a copy of the [PackedVector2Array] rather than a reference. </member> <member name="polygons" type="Array" setter="set_polygons" getter="get_polygons" default="[]"> The list of polygons, in case more than one is being represented. Every individual polygon is stored as a [PackedInt32Array] where each [int] is an index to a point in [member polygon]. If empty, this property will be ignored, and the resulting single polygon will be composed of all points in [member polygon], using the order they are stored in. diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index 4d3e838bb1..adc2eeccdf 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -10,9 +10,9 @@ [b]Overriding:[/b] Any project setting can be overridden by creating a file named [code]override.cfg[/code] in the project's root directory. This can also be used in exported projects by placing this file in the same directory as the project binary. Overriding will still take the base project settings' [url=$DOCS_URL/tutorials/export/feature_tags.html]feature tags[/url] in account. Therefore, make sure to [i]also[/i] override the setting with the desired feature tags if you want them to override base project settings on all platforms and configurations. </description> <tutorials> - <link title="3D Physics Tests Demo">https://godotengine.org/asset-library/asset/675</link> - <link title="3D Platformer Demo">https://godotengine.org/asset-library/asset/125</link> - <link title="OS Test Demo">https://godotengine.org/asset-library/asset/677</link> + <link title="3D Physics Tests Demo">https://godotengine.org/asset-library/asset/2747</link> + <link title="3D Platformer Demo">https://godotengine.org/asset-library/asset/2748</link> + <link title="Operating System Testing Demo">https://godotengine.org/asset-library/asset/2789</link> </tutorials> <methods> <method name="add_property_info"> @@ -323,6 +323,11 @@ If [code]true[/code], disables printing to standard output. This is equivalent to starting the editor or project with the [code]--quiet[/code] [url=$DOCS_URL/tutorials/editor/command_line_tutorial.html]command line argument[/url]. See also [member application/run/disable_stderr]. Changes to this setting will only be applied upon restarting the application. </member> + <member name="application/run/enable_alt_space_menu" type="bool" setter="" getter="" default="false"> + If [code]true[/code], allows the [kbd]Alt + Space[/kbd] keys to display the window menu. This menu allows the user to perform various window management operations such as moving, resizing, or minimizing the window. + [b]Note:[/b] When the menu is displayed, project execution will pause until the menu is [i]fully[/i] closed due to Windows behavior. Consider this when enabling this setting in a networked multiplayer game. The menu is only considered fully closed when an option is selected, when the user clicks outside, or when [kbd]Escape[/kbd] is pressed after bringing up the window menu [i]and[/i] another key is pressed afterwards. + [b]Note:[/b] This setting is implemented only on Windows. + </member> <member name="application/run/flush_stdout_on_print" type="bool" setter="" getter="" default="false"> If [code]true[/code], flushes the standard output stream every time a line is printed. This affects both terminal logging and file logging. When running a project, this setting must be enabled if you want logs to be collected by service managers such as systemd/journalctl. This setting is disabled by default on release builds, since flushing on every printed line will negatively affect performance if lots of lines are printed in a rapid succession. Also, if this setting is enabled, logged files will still be written successfully if the application crashes or is otherwise killed by the user (without being closed "normally"). @@ -2727,6 +2732,10 @@ <member name="rendering/rendering_device/driver.windows" type="String" setter="" getter=""> Windows override for [member rendering/rendering_device/driver]. </member> + <member name="rendering/rendering_device/pipeline_cache/enable" type="bool" setter="" getter="" default="true"> + Enable the pipeline cache that is saved to disk if the graphics API supports it. + [b]Note:[/b] This property is unable to control the pipeline caching the GPU driver itself does. Only turn this off along with deleting the contents of the driver's cache if you wish to simulate the experience a user will get when starting the game for the first time. + </member> <member name="rendering/rendering_device/pipeline_cache/save_chunk_size_mb" type="float" setter="" getter="" default="3.0"> Determines at which interval pipeline cache is saved to disk. The lower the value, the more often it is saved. </member> diff --git a/doc/classes/QuadMesh.xml b/doc/classes/QuadMesh.xml index c5164e9feb..d840616fdd 100644 --- a/doc/classes/QuadMesh.xml +++ b/doc/classes/QuadMesh.xml @@ -7,8 +7,8 @@ Class representing a square [PrimitiveMesh]. This flat mesh does not have a thickness. By default, this mesh is aligned on the X and Y axes; this rotation is more suited for use with billboarded materials. A [QuadMesh] is equivalent to a [PlaneMesh] except its default [member PlaneMesh.orientation] is [constant PlaneMesh.FACE_Z]. </description> <tutorials> - <link title="GUI in 3D Demo">https://godotengine.org/asset-library/asset/127</link> - <link title="2D in 3D Demo">https://godotengine.org/asset-library/asset/129</link> + <link title="GUI in 3D Viewport Demo">https://godotengine.org/asset-library/asset/2807</link> + <link title="2D in 3D Viewport Demo">https://godotengine.org/asset-library/asset/2803</link> </tutorials> <members> <member name="orientation" type="int" setter="set_orientation" getter="get_orientation" overrides="PlaneMesh" enum="PlaneMesh.Orientation" default="2" /> diff --git a/doc/classes/Quaternion.xml b/doc/classes/Quaternion.xml index 8b59448555..665c6335f2 100644 --- a/doc/classes/Quaternion.xml +++ b/doc/classes/Quaternion.xml @@ -14,7 +14,7 @@ <link title="3Blue1Brown's video on Quaternions">https://www.youtube.com/watch?v=d4EgbgTm0Bg</link> <link title="Online Quaternion Visualization">https://quaternions.online/</link> <link title="Using 3D transforms">$DOCS_URL/tutorials/3d/using_transforms.html#interpolating-with-quaternions</link> - <link title="Third Person Shooter Demo">https://godotengine.org/asset-library/asset/678</link> + <link title="Third Person Shooter (TPS) Demo">https://godotengine.org/asset-library/asset/2710</link> <link title="Advanced Quaternion Visualization">https://iwatake2222.github.io/rotation_master/rotation_master.html</link> </tutorials> <constructors> diff --git a/doc/classes/RayCast2D.xml b/doc/classes/RayCast2D.xml index 31daaab417..16643b0a71 100644 --- a/doc/classes/RayCast2D.xml +++ b/doc/classes/RayCast2D.xml @@ -56,6 +56,21 @@ <return type="int" /> <description> Returns the shape ID of the first object that the ray intersects, or [code]0[/code] if no object is intersecting the ray (i.e. [method is_colliding] returns [code]false[/code]). + To get the intersected shape node, for a [CollisionObject2D] target, use: + [codeblocks] + [gdscript] + var target = get_collider() # A CollisionObject2D. + var shape_id = get_collider_shape() # The shape index in the collider. + var owner_id = target.shape_find_owner(shape_id) # The owner ID in the collider. + var shape = target.shape_owner_get_owner(owner_id) + [/gdscript] + [csharp] + var target = (CollisionObject2D)GetCollider(); // A CollisionObject2D. + var shapeId = GetColliderShape(); // The shape index in the collider. + var ownerId = target.ShapeFindOwner(shapeId); // The owner ID in the collider. + var shape = target.ShapeOwnerGetOwner(ownerId); + [/csharp] + [/codeblocks] </description> </method> <method name="get_collision_mask_value" qualifiers="const"> diff --git a/doc/classes/RayCast3D.xml b/doc/classes/RayCast3D.xml index f9f94e5cfc..18a544d114 100644 --- a/doc/classes/RayCast3D.xml +++ b/doc/classes/RayCast3D.xml @@ -11,7 +11,7 @@ </description> <tutorials> <link title="Ray-casting">$DOCS_URL/tutorials/physics/ray-casting.html</link> - <link title="3D Voxel Demo">https://godotengine.org/asset-library/asset/676</link> + <link title="3D Voxel Demo">https://godotengine.org/asset-library/asset/2755</link> </tutorials> <methods> <method name="add_exception"> @@ -57,6 +57,21 @@ <return type="int" /> <description> Returns the shape ID of the first object that the ray intersects, or [code]0[/code] if no object is intersecting the ray (i.e. [method is_colliding] returns [code]false[/code]). + To get the intersected shape node, for a [CollisionObject3D] target, use: + [codeblocks] + [gdscript] + var target = get_collider() # A CollisionObject3D. + var shape_id = get_collider_shape() # The shape index in the collider. + var owner_id = target.shape_find_owner(shape_id) # The owner ID in the collider. + var shape = target.shape_owner_get_owner(owner_id) + [/gdscript] + [csharp] + var target = (CollisionObject3D)GetCollider(); // A CollisionObject3D. + var shapeId = GetColliderShape(); // The shape index in the collider. + var ownerId = target.ShapeFindOwner(shapeId); // The owner ID in the collider. + var shape = target.ShapeOwnerGetOwner(ownerId); + [/csharp] + [/codeblocks] </description> </method> <method name="get_collision_face_index" qualifiers="const"> diff --git a/doc/classes/RectangleShape2D.xml b/doc/classes/RectangleShape2D.xml index 721d73dc14..a62b997a0a 100644 --- a/doc/classes/RectangleShape2D.xml +++ b/doc/classes/RectangleShape2D.xml @@ -8,8 +8,8 @@ [b]Performance:[/b] [RectangleShape2D] is fast to check collisions against. It is faster than [CapsuleShape2D], but slower than [CircleShape2D]. </description> <tutorials> - <link title="2D Pong Demo">https://godotengine.org/asset-library/asset/121</link> - <link title="2D Kinematic Character Demo">https://godotengine.org/asset-library/asset/113</link> + <link title="2D Pong Demo">https://godotengine.org/asset-library/asset/2728</link> + <link title="2D Kinematic Character Demo">https://godotengine.org/asset-library/asset/2719</link> </tutorials> <members> <member name="size" type="Vector2" setter="set_size" getter="get_size" default="Vector2(20, 20)"> diff --git a/doc/classes/RenderingDevice.xml b/doc/classes/RenderingDevice.xml index 33cd831175..36abde36b4 100644 --- a/doc/classes/RenderingDevice.xml +++ b/doc/classes/RenderingDevice.xml @@ -2129,8 +2129,10 @@ Represents the size of the [enum BlendOperation] enum. </constant> <constant name="DYNAMIC_STATE_LINE_WIDTH" value="1" enum="PipelineDynamicStateFlags" is_bitfield="true"> + Allows dynamically changing the width of rendering lines. </constant> <constant name="DYNAMIC_STATE_DEPTH_BIAS" value="2" enum="PipelineDynamicStateFlags" is_bitfield="true"> + Allows dynamically changing the depth bias. </constant> <constant name="DYNAMIC_STATE_BLEND_CONSTANTS" value="4" enum="PipelineDynamicStateFlags" is_bitfield="true"> </constant> diff --git a/doc/classes/ResourceLoader.xml b/doc/classes/ResourceLoader.xml index 95fbee4a24..885c6f0478 100644 --- a/doc/classes/ResourceLoader.xml +++ b/doc/classes/ResourceLoader.xml @@ -9,7 +9,7 @@ [b]Note:[/b] You have to import the files into the engine first to load them using [method load]. If you want to load [Image]s at run-time, you may use [method Image.load]. If you want to import audio files, you can use the snippet described in [member AudioStreamMP3.data]. </description> <tutorials> - <link title="OS Test Demo">https://godotengine.org/asset-library/asset/677</link> + <link title="Operating System Testing Demo">https://godotengine.org/asset-library/asset/2789</link> </tutorials> <methods> <method name="add_resource_format_loader"> @@ -79,6 +79,7 @@ Returns an empty resource if no [ResourceFormatLoader] could handle the file. GDScript has a simplified [method @GDScript.load] built-in method which can be used in most situations, leaving the use of [ResourceLoader] for more advanced scenarios. [b]Note:[/b] If [member ProjectSettings.editor/export/convert_text_resources_to_binary] is [code]true[/code], [method @GDScript.load] will not be able to read converted files in an exported project. If you rely on run-time loading of files present within the PCK, set [member ProjectSettings.editor/export/convert_text_resources_to_binary] to [code]false[/code]. + [b]Note:[/b] Relative paths will be prefixed with [code]"res://"[/code] before loading, to avoid unexpected results make sure your paths are absolute. </description> </method> <method name="load_threaded_get"> diff --git a/doc/classes/RichTextLabel.xml b/doc/classes/RichTextLabel.xml index b9a6b06fe3..01c4074e6d 100644 --- a/doc/classes/RichTextLabel.xml +++ b/doc/classes/RichTextLabel.xml @@ -12,8 +12,8 @@ </description> <tutorials> <link title="BBCode in RichTextLabel">$DOCS_URL/tutorials/ui/bbcode_in_richtextlabel.html</link> - <link title="GUI Rich Text/BBcode Demo">https://godotengine.org/asset-library/asset/132</link> - <link title="OS Test Demo">https://godotengine.org/asset-library/asset/677</link> + <link title="Rich Text Label with BBCode Demo">https://godotengine.org/asset-library/asset/2774</link> + <link title="Operating System Testing Demo">https://godotengine.org/asset-library/asset/2789</link> </tutorials> <methods> <method name="add_image"> diff --git a/doc/classes/RigidBody2D.xml b/doc/classes/RigidBody2D.xml index 269ead1298..5661d1a276 100644 --- a/doc/classes/RigidBody2D.xml +++ b/doc/classes/RigidBody2D.xml @@ -11,15 +11,15 @@ [b]Note:[/b] Changing the 2D transform or [member linear_velocity] of a [RigidBody2D] very often may lead to some unpredictable behaviors. If you need to directly affect the body, prefer [method _integrate_forces] as it allows you to directly access the physics state. </description> <tutorials> - <link title="2D Physics Platformer Demo">https://godotengine.org/asset-library/asset/119</link> - <link title="Instancing Demo">https://godotengine.org/asset-library/asset/148</link> + <link title="2D Physics Platformer Demo">https://godotengine.org/asset-library/asset/2725</link> + <link title="Instancing Demo">https://godotengine.org/asset-library/asset/2716</link> </tutorials> <methods> <method name="_integrate_forces" qualifiers="virtual"> <return type="void" /> <param index="0" name="state" type="PhysicsDirectBodyState2D" /> <description> - Allows you to read and safely modify the simulation state for the object. Use this instead of [method Node._physics_process] if you need to directly change the body's [code]position[/code] or other physics properties. By default, it works in addition to the usual physics behavior, but [member custom_integrator] allows you to disable the default behavior and write custom force integration for a body. + Called during physics processing, allowing you to read and safely modify the simulation state for the object. By default, it is called before the standard force integration, but the [member custom_integrator] property allows you to disable the standard force integration and do fully custom force integration for a body. </description> </method> <method name="add_constant_central_force"> @@ -159,7 +159,8 @@ Continuous collision detection tries to predict where a moving body will collide instead of moving it and correcting its movement after collision. Continuous collision detection is slower, but more precise and misses fewer collisions with small, fast-moving objects. Raycasting and shapecasting methods are available. See [enum CCDMode] for details. </member> <member name="custom_integrator" type="bool" setter="set_use_custom_integrator" getter="is_using_custom_integrator" default="false"> - If [code]true[/code], internal force integration is disabled for this body. Aside from collision response, the body will only move as determined by the [method _integrate_forces] function. + If [code]true[/code], the standard force integration (like gravity or damping) will be disabled for this body. Other than collision response, the body will only move as determined by the [method _integrate_forces] method, if that virtual method is overridden. + Setting this property will call the method [method PhysicsServer2D.body_set_omit_force_integration] internally. </member> <member name="freeze" type="bool" setter="set_freeze_enabled" getter="is_freeze_enabled" default="false"> If [code]true[/code], the body is frozen. Gravity and forces are not applied anymore. diff --git a/doc/classes/RigidBody3D.xml b/doc/classes/RigidBody3D.xml index c507a7c39a..dae904e2a3 100644 --- a/doc/classes/RigidBody3D.xml +++ b/doc/classes/RigidBody3D.xml @@ -12,15 +12,15 @@ </description> <tutorials> <link title="Physics introduction">$DOCS_URL/tutorials/physics/physics_introduction.html</link> - <link title="3D Truck Town Demo">https://godotengine.org/asset-library/asset/524</link> - <link title="3D Physics Tests Demo">https://godotengine.org/asset-library/asset/675</link> + <link title="3D Truck Town Demo">https://godotengine.org/asset-library/asset/2752</link> + <link title="3D Physics Tests Demo">https://godotengine.org/asset-library/asset/2747</link> </tutorials> <methods> <method name="_integrate_forces" qualifiers="virtual"> <return type="void" /> <param index="0" name="state" type="PhysicsDirectBodyState3D" /> <description> - Called during physics processing, allowing you to read and safely modify the simulation state for the object. By default, it works in addition to the usual physics behavior, but the [member custom_integrator] property allows you to disable the default behavior and do fully custom force integration for a body. + Called during physics processing, allowing you to read and safely modify the simulation state for the object. By default, it is called before the standard force integration, but the [member custom_integrator] property allows you to disable the standard force integration and do fully custom force integration for a body. </description> </method> <method name="add_constant_central_force"> @@ -166,7 +166,8 @@ Continuous collision detection tries to predict where a moving body will collide, instead of moving it and correcting its movement if it collided. Continuous collision detection is more precise, and misses fewer impacts by small, fast-moving objects. Not using continuous collision detection is faster to compute, but can miss small, fast-moving objects. </member> <member name="custom_integrator" type="bool" setter="set_use_custom_integrator" getter="is_using_custom_integrator" default="false"> - If [code]true[/code], internal force integration will be disabled (like gravity or air friction) for this body. Other than collision response, the body will only move as determined by the [method _integrate_forces] function, if defined. + If [code]true[/code], the standard force integration (like gravity or damping) will be disabled for this body. Other than collision response, the body will only move as determined by the [method _integrate_forces] method, if that virtual method is overridden. + Setting this property will call the method [method PhysicsServer3D.body_set_omit_force_integration] internally. </member> <member name="freeze" type="bool" setter="set_freeze_enabled" getter="is_freeze_enabled" default="false"> If [code]true[/code], the body is frozen. Gravity and forces are not applied anymore. diff --git a/doc/classes/ShapeCast2D.xml b/doc/classes/ShapeCast2D.xml index d71c9ce13a..576bd62cc3 100644 --- a/doc/classes/ShapeCast2D.xml +++ b/doc/classes/ShapeCast2D.xml @@ -48,6 +48,7 @@ <return type="float" /> <description> The fraction from the [ShapeCast2D]'s origin to its [member target_position] (between 0 and 1) of how far the shape must move to trigger a collision. + In ideal conditions this would be the same as [method get_closest_collision_safe_fraction], however shape casting is calculated in discrete steps, so the precise point of collision can occur between two calculated positions. </description> </method> <method name="get_collider" qualifiers="const"> diff --git a/doc/classes/ShapeCast3D.xml b/doc/classes/ShapeCast3D.xml index ff057e8c70..2c6efe2ebe 100644 --- a/doc/classes/ShapeCast3D.xml +++ b/doc/classes/ShapeCast3D.xml @@ -48,6 +48,7 @@ <return type="float" /> <description> The fraction from the [ShapeCast3D]'s origin to its [member target_position] (between 0 and 1) of how far the shape must move to trigger a collision. + In ideal conditions this would be the same as [method get_closest_collision_safe_fraction], however shape casting is calculated in discrete steps, so the precise point of collision can occur between two calculated positions. </description> </method> <method name="get_collider" qualifiers="const"> diff --git a/doc/classes/Skeleton3D.xml b/doc/classes/Skeleton3D.xml index c9ad204247..caa3097a6b 100644 --- a/doc/classes/Skeleton3D.xml +++ b/doc/classes/Skeleton3D.xml @@ -9,8 +9,7 @@ Note that "global pose" below refers to the overall transform of the bone with respect to skeleton, so it is not the actual global/world transform of the bone. </description> <tutorials> - <link title="3D Inverse Kinematics Demo">https://godotengine.org/asset-library/asset/523</link> - <link title="Third Person Shooter Demo">https://godotengine.org/asset-library/asset/678</link> + <link title="Third Person Shooter (TPS) Demo">https://godotengine.org/asset-library/asset/2710</link> </tutorials> <methods> <method name="add_bone"> @@ -27,7 +26,7 @@ Clear all the bones in this skeleton. </description> </method> - <method name="clear_bones_global_pose_override"> + <method name="clear_bones_global_pose_override" deprecated=""> <return type="void" /> <description> Removes the global pose override on all bones in the skeleton. @@ -58,6 +57,11 @@ Force updates the bone transform for the bone at [param bone_idx] and all of its children. </description> </method> + <method name="get_animate_physical_bones" qualifiers="const" deprecated=""> + <return type="bool" /> + <description> + </description> + </method> <method name="get_bone_children" qualifiers="const"> <return type="PackedInt32Array" /> <param index="0" name="bone_idx" type="int" /> @@ -76,16 +80,17 @@ <param index="0" name="bone_idx" type="int" /> <description> Returns the overall transform of the specified bone, with respect to the skeleton. Being relative to the skeleton frame, this is not the actual "global" transform of the bone. + [b]Note:[/b] This is the global pose you set to the skeleton in the process, the final global pose can get overridden by modifiers in the deferred process, if you want to access the final global pose, use [signal SkeletonModifier3D.modification_processed]. </description> </method> - <method name="get_bone_global_pose_no_override" qualifiers="const"> + <method name="get_bone_global_pose_no_override" qualifiers="const" deprecated=""> <return type="Transform3D" /> <param index="0" name="bone_idx" type="int" /> <description> Returns the overall transform of the specified bone, with respect to the skeleton, but without any global pose overrides. Being relative to the skeleton frame, this is not the actual "global" transform of the bone. </description> </method> - <method name="get_bone_global_pose_override" qualifiers="const"> + <method name="get_bone_global_pose_override" qualifiers="const" deprecated=""> <return type="Transform3D" /> <param index="0" name="bone_idx" type="int" /> <description> @@ -119,6 +124,7 @@ <param index="0" name="bone_idx" type="int" /> <description> Returns the pose transform of the specified bone. + [b]Note:[/b] This is the pose you set to the skeleton in the process, the final pose can get overridden by modifiers in the deferred process, if you want to access the final pose, use [signal SkeletonModifier3D.modification_processed]. </description> </method> <method name="get_bone_pose_position" qualifiers="const"> @@ -176,7 +182,7 @@ Returns all bones in the skeleton to their rest poses. </description> </method> - <method name="physical_bones_add_collision_exception"> + <method name="physical_bones_add_collision_exception" deprecated=""> <return type="void" /> <param index="0" name="exception" type="RID" /> <description> @@ -184,7 +190,7 @@ Works just like the [RigidBody3D] node. </description> </method> - <method name="physical_bones_remove_collision_exception"> + <method name="physical_bones_remove_collision_exception" deprecated=""> <return type="void" /> <param index="0" name="exception" type="RID" /> <description> @@ -192,7 +198,7 @@ Works just like the [RigidBody3D] node. </description> </method> - <method name="physical_bones_start_simulation"> + <method name="physical_bones_start_simulation" deprecated=""> <return type="void" /> <param index="0" name="bones" type="StringName[]" default="[]" /> <description> @@ -200,7 +206,7 @@ Optionally, a list of bone names can be passed-in, allowing only the passed-in bones to be simulated. </description> </method> - <method name="physical_bones_stop_simulation"> + <method name="physical_bones_stop_simulation" deprecated=""> <return type="void" /> <description> Tells the [PhysicalBone3D] nodes in the Skeleton to stop simulating. @@ -226,6 +232,12 @@ Sets all bone poses to rests. </description> </method> + <method name="set_animate_physical_bones" deprecated=""> + <return type="void" /> + <param index="0" name="enabled" type="bool" /> + <description> + </description> + </method> <method name="set_bone_enabled"> <return type="void" /> <param index="0" name="bone_idx" type="int" /> @@ -234,7 +246,16 @@ Disables the pose for the bone at [param bone_idx] if [code]false[/code], enables the bone pose if [code]true[/code]. </description> </method> - <method name="set_bone_global_pose_override"> + <method name="set_bone_global_pose"> + <return type="void" /> + <param index="0" name="bone_idx" type="int" /> + <param index="1" name="pose" type="Transform3D" /> + <description> + Sets the global pose transform, [param pose], for the bone at [param bone_idx]. + [b]Note:[/b] If other bone poses have been changed, this method executes an update process and will cause performance to deteriorate. If you know that multiple global poses will be applied, consider using [method set_bone_pose] with precalculation. + </description> + </method> + <method name="set_bone_global_pose_override" deprecated=""> <return type="void" /> <param index="0" name="bone_idx" type="int" /> <param index="1" name="pose" type="Transform3D" /> @@ -251,6 +272,7 @@ <param index="0" name="bone_idx" type="int" /> <param index="1" name="name" type="String" /> <description> + Sets the bone name, [param name], for the bone at [param bone_idx]. </description> </method> <method name="set_bone_parent"> @@ -262,6 +284,14 @@ [b]Note:[/b] [param parent_idx] must be less than [param bone_idx]. </description> </method> + <method name="set_bone_pose"> + <return type="void" /> + <param index="0" name="bone_idx" type="int" /> + <param index="1" name="pose" type="Transform3D" /> + <description> + Sets the pose transform, [param pose], for the bone at [param bone_idx]. + </description> + </method> <method name="set_bone_pose_position"> <return type="void" /> <param index="0" name="bone_idx" type="int" /> @@ -303,7 +333,8 @@ </method> </methods> <members> - <member name="animate_physical_bones" type="bool" setter="set_animate_physical_bones" getter="get_animate_physical_bones" default="true"> + <member name="modifier_callback_mode_process" type="int" setter="set_modifier_callback_mode_process" getter="get_modifier_callback_mode_process" enum="Skeleton3D.ModifierCallbackModeProcess" default="1"> + Sets the processing timing for the Modifier. </member> <member name="motion_scale" type="float" setter="set_motion_scale" getter="get_motion_scale" default="1.0"> Multiplies the 3D position track animation. @@ -320,6 +351,10 @@ Emitted when the bone at [param bone_idx] is toggled with [method set_bone_enabled]. Use [method is_bone_enabled] to check the new value. </description> </signal> + <signal name="bone_list_changed"> + <description> + </description> + </signal> <signal name="bone_pose_changed"> <param index="0" name="bone_idx" type="int" /> <description> @@ -342,5 +377,11 @@ Notification received when this skeleton's pose needs to be updated. This notification is received [i]before[/i] the related [signal pose_updated] signal. </constant> + <constant name="MODIFIER_CALLBACK_MODE_PROCESS_PHYSICS" value="0" enum="ModifierCallbackModeProcess"> + Set a flag to process modification during physics frames (see [constant Node.NOTIFICATION_INTERNAL_PHYSICS_PROCESS]). + </constant> + <constant name="MODIFIER_CALLBACK_MODE_PROCESS_IDLE" value="1" enum="ModifierCallbackModeProcess"> + Set a flag to process modification during process frames (see [constant Node.NOTIFICATION_INTERNAL_PROCESS]). + </constant> </constants> </class> diff --git a/doc/classes/SkeletonIK3D.xml b/doc/classes/SkeletonIK3D.xml index d1f96adec2..6de6d9d186 100644 --- a/doc/classes/SkeletonIK3D.xml +++ b/doc/classes/SkeletonIK3D.xml @@ -1,10 +1,10 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="SkeletonIK3D" inherits="Node" deprecated="" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> +<class name="SkeletonIK3D" inherits="SkeletonModifier3D" deprecated="" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> A node used to rotate all bones of a [Skeleton3D] bone chain a way that places the end bone at a desired 3D position. </brief_description> <description> - SkeletonIK3D is used to rotate all bones of a [Skeleton3D] bone chain a way that places the end bone at a desired 3D position. A typical scenario for IK in games is to place a character's feet on the ground or a character's hands on a currently held object. SkeletonIK uses FabrikInverseKinematic internally to solve the bone chain and applies the results to the [Skeleton3D] [code]bones_global_pose_override[/code] property for all affected bones in the chain. If fully applied, this overwrites any bone transform from [Animation]s or bone custom poses set by users. The applied amount can be controlled with the [member interpolation] property. + SkeletonIK3D is used to rotate all bones of a [Skeleton3D] bone chain a way that places the end bone at a desired 3D position. A typical scenario for IK in games is to place a character's feet on the ground or a character's hands on a currently held object. SkeletonIK uses FabrikInverseKinematic internally to solve the bone chain and applies the results to the [Skeleton3D] [code]bones_global_pose_override[/code] property for all affected bones in the chain. If fully applied, this overwrites any bone transform from [Animation]s or bone custom poses set by users. The applied amount can be controlled with the [member SkeletonModifier3D.influence] property. [codeblock] # Apply IK effect automatically on every new frame (not the current) skeleton_ik_node.start() @@ -16,17 +16,16 @@ skeleton_ik_node.stop() # Apply full IK effect - skeleton_ik_node.set_interpolation(1.0) + skeleton_ik_node.set_influence(1.0) # Apply half IK effect - skeleton_ik_node.set_interpolation(0.5) + skeleton_ik_node.set_influence(0.5) # Apply zero IK effect (a value at or below 0.01 also removes bones_global_pose_override on Skeleton) - skeleton_ik_node.set_interpolation(0.0) + skeleton_ik_node.set_influence(0.0) [/codeblock] </description> <tutorials> - <link title="3D Inverse Kinematics Demo">https://godotengine.org/asset-library/asset/523</link> </tutorials> <methods> <method name="get_parent_skeleton" qualifiers="const"> @@ -56,9 +55,6 @@ </method> </methods> <members> - <member name="interpolation" type="float" setter="set_interpolation" getter="get_interpolation" default="1.0"> - Interpolation value for how much the IK results are applied to the current skeleton bone chain. A value of [code]1.0[/code] will overwrite all skeleton bone transforms completely while a value of [code]0.0[/code] will visually disable the SkeletonIK. A value at or below [code]0.01[/code] also calls [method Skeleton3D.clear_bones_global_pose_override]. - </member> <member name="magnet" type="Vector3" setter="set_magnet_position" getter="get_magnet_position" default="Vector3(0, 0, 0)"> Secondary target position (first is [member target] property or [member target_node]) for the IK chain. Use magnet position (pole target) to control the bending of the IK chain. Only works if the bone chain has more than 2 bones. The middle chain bone position will be linearly interpolated with the magnet position. </member> diff --git a/doc/classes/SkeletonModifier3D.xml b/doc/classes/SkeletonModifier3D.xml new file mode 100644 index 0000000000..fab33750ea --- /dev/null +++ b/doc/classes/SkeletonModifier3D.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="SkeletonModifier3D" inherits="Node3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> + <brief_description> + A Node that may modify Skeleton3D's bone. + </brief_description> + <description> + [SkeletonModifier3D] retrieves a target [Skeleton3D] by having a [Skeleton3D] parent. + If there is [AnimationMixer], modification always performs after playback process of the [AnimationMixer]. + </description> + <tutorials> + </tutorials> + <members> + <member name="active" type="bool" setter="set_active" getter="is_active" default="true"> + If [code]true[/code], the [SkeletonModifier3D] will be processing. + </member> + <member name="influence" type="float" setter="set_influence" getter="get_influence" default="1.0"> + Sets the influence of the modification. + [b]Note:[/b] This value is used by [Skeleton3D] to blend, so the [SkeletonModifier3D] should always apply only 100% of the result without interpolation. + </member> + </members> + <signals> + <signal name="modification_processed"> + <description> + Notifies when the modification have been finished. + [b]Note:[/b] If you want to get the modified bone pose by the modifier, you must use [method Skeleton3D.get_bone_pose] or [method Skeleton3D.get_bone_global_pose] at the moment this signal is fired. + </description> + </signal> + </signals> +</class> diff --git a/doc/classes/SkeletonProfile.xml b/doc/classes/SkeletonProfile.xml index 3ed29668e4..b5bb4e3639 100644 --- a/doc/classes/SkeletonProfile.xml +++ b/doc/classes/SkeletonProfile.xml @@ -83,6 +83,14 @@ Returns the texture of the group at [param group_idx] that will be the drawing group background image in the [BoneMap] editor. </description> </method> + <method name="is_required" qualifiers="const"> + <return type="bool" /> + <param index="0" name="bone_idx" type="int" /> + <description> + Returns whether the bone at [param bone_idx] is required for retargeting. + This value is used by the bone map editor. If this method returns [code]true[/code], and no bone is assigned, the handle color will be red on the bone map editor. + </description> + </method> <method name="set_bone_name"> <return type="void" /> <param index="0" name="bone_idx" type="int" /> @@ -141,6 +149,14 @@ Sets the reference pose transform for bone [param bone_idx]. </description> </method> + <method name="set_required"> + <return type="void" /> + <param index="0" name="bone_idx" type="int" /> + <param index="1" name="required" type="bool" /> + <description> + Sets the required status for bone [param bone_idx] to [param required]. + </description> + </method> <method name="set_tail_direction"> <return type="void" /> <param index="0" name="bone_idx" type="int" /> diff --git a/doc/classes/SliderJoint3D.xml b/doc/classes/SliderJoint3D.xml index 49b362041b..8930514492 100644 --- a/doc/classes/SliderJoint3D.xml +++ b/doc/classes/SliderJoint3D.xml @@ -13,6 +13,7 @@ <return type="float" /> <param index="0" name="param" type="int" enum="SliderJoint3D.Param" /> <description> + Returns the value of the given parameter (see [enum Param] constants). </description> </method> <method name="set_param"> @@ -20,6 +21,7 @@ <param index="0" name="param" type="int" enum="SliderJoint3D.Param" /> <param index="1" name="value" type="float" /> <description> + Assigns [param value] to the given parameter (see [enum Param] constants). </description> </method> </methods> @@ -96,70 +98,70 @@ </members> <constants> <constant name="PARAM_LINEAR_LIMIT_UPPER" value="0" enum="Param"> - The maximum difference between the pivot points on their X axis before damping happens. + Constant for accessing [member linear_limit/upper_distance]. The maximum difference between the pivot points on their X axis before damping happens. </constant> <constant name="PARAM_LINEAR_LIMIT_LOWER" value="1" enum="Param"> - The minimum difference between the pivot points on their X axis before damping happens. + Constant for accessing [member linear_limit/lower_distance]. The minimum difference between the pivot points on their X axis before damping happens. </constant> <constant name="PARAM_LINEAR_LIMIT_SOFTNESS" value="2" enum="Param"> - A factor applied to the movement across the slider axis once the limits get surpassed. The lower, the slower the movement. + Constant for accessing [member linear_limit/softness]. A factor applied to the movement across the slider axis once the limits get surpassed. The lower, the slower the movement. </constant> <constant name="PARAM_LINEAR_LIMIT_RESTITUTION" value="3" enum="Param"> - The amount of restitution once the limits are surpassed. The lower, the more velocity-energy gets lost. + Constant for accessing [member linear_limit/restitution]. The amount of restitution once the limits are surpassed. The lower, the more velocity-energy gets lost. </constant> <constant name="PARAM_LINEAR_LIMIT_DAMPING" value="4" enum="Param"> - The amount of damping once the slider limits are surpassed. + Constant for accessing [member linear_limit/damping]. The amount of damping once the slider limits are surpassed. </constant> <constant name="PARAM_LINEAR_MOTION_SOFTNESS" value="5" enum="Param"> - A factor applied to the movement across the slider axis as long as the slider is in the limits. The lower, the slower the movement. + Constant for accessing [member linear_motion/softness]. A factor applied to the movement across the slider axis as long as the slider is in the limits. The lower, the slower the movement. </constant> <constant name="PARAM_LINEAR_MOTION_RESTITUTION" value="6" enum="Param"> - The amount of restitution inside the slider limits. + Constant for accessing [member linear_motion/restitution]. The amount of restitution inside the slider limits. </constant> <constant name="PARAM_LINEAR_MOTION_DAMPING" value="7" enum="Param"> - The amount of damping inside the slider limits. + Constant for accessing [member linear_motion/damping]. The amount of damping inside the slider limits. </constant> <constant name="PARAM_LINEAR_ORTHOGONAL_SOFTNESS" value="8" enum="Param"> - A factor applied to the movement across axes orthogonal to the slider. + Constant for accessing [member linear_ortho/softness]. A factor applied to the movement across axes orthogonal to the slider. </constant> <constant name="PARAM_LINEAR_ORTHOGONAL_RESTITUTION" value="9" enum="Param"> - The amount of restitution when movement is across axes orthogonal to the slider. + Constant for accessing [member linear_motion/restitution]. The amount of restitution when movement is across axes orthogonal to the slider. </constant> <constant name="PARAM_LINEAR_ORTHOGONAL_DAMPING" value="10" enum="Param"> - The amount of damping when movement is across axes orthogonal to the slider. + Constant for accessing [member linear_motion/damping]. The amount of damping when movement is across axes orthogonal to the slider. </constant> <constant name="PARAM_ANGULAR_LIMIT_UPPER" value="11" enum="Param"> - The upper limit of rotation in the slider. + Constant for accessing [member angular_limit/upper_angle]. The upper limit of rotation in the slider. </constant> <constant name="PARAM_ANGULAR_LIMIT_LOWER" value="12" enum="Param"> - The lower limit of rotation in the slider. + Constant for accessing [member angular_limit/lower_angle]. The lower limit of rotation in the slider. </constant> <constant name="PARAM_ANGULAR_LIMIT_SOFTNESS" value="13" enum="Param"> - A factor applied to the all rotation once the limit is surpassed. + Constant for accessing [member angular_limit/softness]. A factor applied to the all rotation once the limit is surpassed. </constant> <constant name="PARAM_ANGULAR_LIMIT_RESTITUTION" value="14" enum="Param"> - The amount of restitution of the rotation when the limit is surpassed. + Constant for accessing [member angular_limit/restitution]. The amount of restitution of the rotation when the limit is surpassed. </constant> <constant name="PARAM_ANGULAR_LIMIT_DAMPING" value="15" enum="Param"> - The amount of damping of the rotation when the limit is surpassed. + Constant for accessing [member angular_limit/damping]. The amount of damping of the rotation when the limit is surpassed. </constant> <constant name="PARAM_ANGULAR_MOTION_SOFTNESS" value="16" enum="Param"> - A factor applied to the all rotation in the limits. + Constant for accessing [member angular_motion/softness]. A factor applied to the all rotation in the limits. </constant> <constant name="PARAM_ANGULAR_MOTION_RESTITUTION" value="17" enum="Param"> - The amount of restitution of the rotation in the limits. + Constant for accessing [member angular_motion/restitution]. The amount of restitution of the rotation in the limits. </constant> <constant name="PARAM_ANGULAR_MOTION_DAMPING" value="18" enum="Param"> - The amount of damping of the rotation in the limits. + Constant for accessing [member angular_motion/damping]. The amount of damping of the rotation in the limits. </constant> <constant name="PARAM_ANGULAR_ORTHOGONAL_SOFTNESS" value="19" enum="Param"> - A factor applied to the all rotation across axes orthogonal to the slider. + Constant for accessing [member angular_ortho/softness]. A factor applied to the all rotation across axes orthogonal to the slider. </constant> <constant name="PARAM_ANGULAR_ORTHOGONAL_RESTITUTION" value="20" enum="Param"> - The amount of restitution of the rotation across axes orthogonal to the slider. + Constant for accessing [member angular_ortho/restitution]. The amount of restitution of the rotation across axes orthogonal to the slider. </constant> <constant name="PARAM_ANGULAR_ORTHOGONAL_DAMPING" value="21" enum="Param"> - The amount of damping of the rotation across axes orthogonal to the slider. + Constant for accessing [member angular_ortho/damping]. The amount of damping of the rotation across axes orthogonal to the slider. </constant> <constant name="PARAM_MAX" value="22" enum="Param"> Represents the size of the [enum Param] enum. diff --git a/doc/classes/SphereShape3D.xml b/doc/classes/SphereShape3D.xml index 313b05dff4..4b14c535dd 100644 --- a/doc/classes/SphereShape3D.xml +++ b/doc/classes/SphereShape3D.xml @@ -8,7 +8,7 @@ [b]Performance:[/b] [SphereShape3D] is fast to check collisions against. It is faster than [BoxShape3D], [CapsuleShape3D], and [CylinderShape3D]. </description> <tutorials> - <link title="3D Physics Tests Demo">https://godotengine.org/asset-library/asset/675</link> + <link title="3D Physics Tests Demo">https://godotengine.org/asset-library/asset/2747</link> </tutorials> <members> <member name="radius" type="float" setter="set_radius" getter="get_radius" default="0.5"> diff --git a/doc/classes/SpotLight3D.xml b/doc/classes/SpotLight3D.xml index ba70edb933..8d2177d3fd 100644 --- a/doc/classes/SpotLight3D.xml +++ b/doc/classes/SpotLight3D.xml @@ -11,7 +11,7 @@ <tutorials> <link title="3D lights and shadows">$DOCS_URL/tutorials/3d/lights_and_shadows.html</link> <link title="Faking global illumination">$DOCS_URL/tutorials/3d/global_illumination/faking_global_illumination.html</link> - <link title="Third Person Shooter Demo">https://godotengine.org/asset-library/asset/678</link> + <link title="Third Person Shooter (TPS) Demo">https://godotengine.org/asset-library/asset/2710</link> </tutorials> <members> <member name="shadow_bias" type="float" setter="set_param" getter="get_param" overrides="Light3D" default="0.03" /> diff --git a/doc/classes/Sprite2D.xml b/doc/classes/Sprite2D.xml index 8dcf286b3a..10ac4b0fcc 100644 --- a/doc/classes/Sprite2D.xml +++ b/doc/classes/Sprite2D.xml @@ -7,7 +7,7 @@ A node that displays a 2D texture. The texture displayed can be a region from a larger atlas texture, or a frame from a sprite sheet animation. </description> <tutorials> - <link title="Instancing Demo">https://godotengine.org/asset-library/asset/148</link> + <link title="Instancing Demo">https://godotengine.org/asset-library/asset/2716</link> </tutorials> <methods> <method name="get_rect" qualifiers="const"> diff --git a/doc/classes/StaticBody3D.xml b/doc/classes/StaticBody3D.xml index 81eda8505b..a2a9a97bc1 100644 --- a/doc/classes/StaticBody3D.xml +++ b/doc/classes/StaticBody3D.xml @@ -9,9 +9,9 @@ [StaticBody3D] is useful for completely static objects like floors and walls, as well as moving surfaces like conveyor belts and circular revolving platforms (by using [member constant_linear_velocity] and [member constant_angular_velocity]). </description> <tutorials> - <link title="3D Physics Tests Demo">https://godotengine.org/asset-library/asset/675</link> - <link title="Third Person Shooter Demo">https://godotengine.org/asset-library/asset/678</link> - <link title="3D Voxel Demo">https://godotengine.org/asset-library/asset/676</link> + <link title="3D Physics Tests Demo">https://godotengine.org/asset-library/asset/2747</link> + <link title="Third Person Shooter (TPS) Demo">https://godotengine.org/asset-library/asset/2710</link> + <link title="3D Voxel Demo">https://godotengine.org/asset-library/asset/2755</link> </tutorials> <members> <member name="constant_angular_velocity" type="Vector3" setter="set_constant_angular_velocity" getter="get_constant_angular_velocity" default="Vector3(0, 0, 0)"> diff --git a/doc/classes/String.xml b/doc/classes/String.xml index 7592342602..a33a1aea41 100644 --- a/doc/classes/String.xml +++ b/doc/classes/String.xml @@ -255,6 +255,13 @@ print("User {id} is {name}.".format([["id", 42], ["name", "Godot"]])) [/codeblock] See also the [url=$DOCS_URL/tutorials/scripting/gdscript/gdscript_format_string.html]GDScript format string[/url] tutorial. + [b]Note:[/b] The replacement of placeholders is not done all at once, instead each placeholder is replaced in the order they are passed, this means that if one of the replacement strings contains a key it will also be replaced. This can be very powerful, but can also cause unexpected results if you are not careful. If you do not need to perform replacement in the replacement strings, make sure your replacements do not contain placeholders to ensure reliable results. + [codeblock] + print("{0} {1}".format(["{1}", "x"])) # Prints "x x". + print("{0} {1}".format(["x", "{0}"])) # Prints "x {0}". + print("{foo} {bar}".format({"foo": "{bar}", "bar": "baz"})) # Prints "baz baz". + print("{foo} {bar}".format({"bar": "baz", "foo": "{bar}"})) # Prints "{bar} baz". + [/codeblock] [b]Note:[/b] In C#, it's recommended to [url=https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/tokens/interpolated]interpolate strings with "$"[/url], instead. </description> </method> diff --git a/doc/classes/SubViewport.xml b/doc/classes/SubViewport.xml index 1a7a4f6e96..605cf949c1 100644 --- a/doc/classes/SubViewport.xml +++ b/doc/classes/SubViewport.xml @@ -10,12 +10,12 @@ <tutorials> <link title="Using Viewports">$DOCS_URL/tutorials/rendering/viewports.html</link> <link title="Viewport and canvas transforms">$DOCS_URL/tutorials/2d/2d_transforms.html</link> - <link title="GUI in 3D Demo">https://godotengine.org/asset-library/asset/127</link> - <link title="3D in 2D Demo">https://godotengine.org/asset-library/asset/128</link> - <link title="2D in 3D Demo">https://godotengine.org/asset-library/asset/129</link> - <link title="Screen Capture Demo">https://godotengine.org/asset-library/asset/130</link> - <link title="Dynamic Split Screen Demo">https://godotengine.org/asset-library/asset/541</link> - <link title="3D Viewport Scaling Demo">https://godotengine.org/asset-library/asset/586</link> + <link title="GUI in 3D Viewport Demo">https://godotengine.org/asset-library/asset/2807</link> + <link title="3D in 2D Viewport Demo">https://godotengine.org/asset-library/asset/2804</link> + <link title="2D in 3D Viewport Demo">https://godotengine.org/asset-library/asset/2803</link> + <link title="Screen Capture Demo">https://godotengine.org/asset-library/asset/2808</link> + <link title="Dynamic Split Screen Demo">https://godotengine.org/asset-library/asset/2806</link> + <link title="3D Resolution Scaling Demo">https://godotengine.org/asset-library/asset/2805</link> </tutorials> <members> <member name="render_target_clear_mode" type="int" setter="set_clear_mode" getter="get_clear_mode" enum="SubViewport.ClearMode" default="0"> diff --git a/doc/classes/SurfaceTool.xml b/doc/classes/SurfaceTool.xml index 094275c349..576587a5df 100644 --- a/doc/classes/SurfaceTool.xml +++ b/doc/classes/SurfaceTool.xml @@ -29,7 +29,7 @@ </description> <tutorials> <link title="Using the SurfaceTool">$DOCS_URL/tutorials/3d/procedural_geometry/surfacetool.html</link> - <link title="3D Voxel Demo">https://godotengine.org/asset-library/asset/676</link> + <link title="3D Voxel Demo">https://godotengine.org/asset-library/asset/2755</link> </tutorials> <methods> <method name="add_index"> diff --git a/doc/classes/TextServer.xml b/doc/classes/TextServer.xml index cd70316aa9..c0cd7f79e7 100644 --- a/doc/classes/TextServer.xml +++ b/doc/classes/TextServer.xml @@ -1736,6 +1736,16 @@ [b]Note:[/b] The result may be longer or shorter than the original. </description> </method> + <method name="string_to_title" qualifiers="const"> + <return type="String" /> + <param index="0" name="string" type="String" /> + <param index="1" name="language" type="String" default="""" /> + <description> + Returns the string converted to title case. + [b]Note:[/b] Casing is locale dependent and context sensitive if server support [constant FEATURE_CONTEXT_SENSITIVE_CASE_CONVERSION] feature (supported by [TextServerAdvanced]). + [b]Note:[/b] The result may be longer or shorter than the original. + </description> + </method> <method name="string_to_upper" qualifiers="const"> <return type="String" /> <param index="0" name="string" type="String" /> diff --git a/doc/classes/TextServerExtension.xml b/doc/classes/TextServerExtension.xml index 9b7fc42ddf..06a0daece6 100644 --- a/doc/classes/TextServerExtension.xml +++ b/doc/classes/TextServerExtension.xml @@ -1913,6 +1913,15 @@ Returns the string converted to lowercase. </description> </method> + <method name="_string_to_title" qualifiers="virtual const"> + <return type="String" /> + <param index="0" name="string" type="String" /> + <param index="1" name="language" type="String" /> + <description> + [b]Optional.[/b] + Returns the string converted to title case. + </description> + </method> <method name="_string_to_upper" qualifiers="virtual const"> <return type="String" /> <param index="0" name="string" type="String" /> diff --git a/doc/classes/TextureButton.xml b/doc/classes/TextureButton.xml index 6c0d56af05..861981f979 100644 --- a/doc/classes/TextureButton.xml +++ b/doc/classes/TextureButton.xml @@ -9,7 +9,7 @@ See also [BaseButton] which contains common properties and methods associated with this node. </description> <tutorials> - <link title="3D Voxel Demo">https://godotengine.org/asset-library/asset/676</link> + <link title="3D Voxel Demo">https://godotengine.org/asset-library/asset/2755</link> </tutorials> <members> <member name="flip_h" type="bool" setter="set_flip_h" getter="is_flipped_h" default="false"> diff --git a/doc/classes/TextureRect.xml b/doc/classes/TextureRect.xml index d18c4a1eee..ab268b6ea5 100644 --- a/doc/classes/TextureRect.xml +++ b/doc/classes/TextureRect.xml @@ -7,7 +7,7 @@ A control that displays a texture, for example an icon inside a GUI. The texture's placement can be controlled with the [member stretch_mode] property. It can scale, tile, or stay centered inside its bounding rectangle. </description> <tutorials> - <link title="3D Voxel Demo">https://godotengine.org/asset-library/asset/676</link> + <link title="3D Voxel Demo">https://godotengine.org/asset-library/asset/2755</link> </tutorials> <members> <member name="expand_mode" type="int" setter="set_expand_mode" getter="get_expand_mode" enum="TextureRect.ExpandMode" default="0" experimental="Using [constant EXPAND_FIT_WIDTH], [constant EXPAND_FIT_WIDTH_PROPORTIONAL], [constant EXPAND_FIT_HEIGHT], or [constant EXPAND_FIT_HEIGHT_PROPORTIONAL] may result in unstable behavior in some [Container] controls. This behavior may be re-evaluated and changed in the future."> diff --git a/doc/classes/Thread.xml b/doc/classes/Thread.xml index dbf63c0852..cf77b93d86 100644 --- a/doc/classes/Thread.xml +++ b/doc/classes/Thread.xml @@ -14,7 +14,7 @@ <tutorials> <link title="Using multiple threads">$DOCS_URL/tutorials/performance/using_multiple_threads.html</link> <link title="Thread-safe APIs">$DOCS_URL/tutorials/performance/thread_safe_apis.html</link> - <link title="3D Voxel Demo">https://godotengine.org/asset-library/asset/676</link> + <link title="3D Voxel Demo">https://godotengine.org/asset-library/asset/2755</link> </tutorials> <methods> <method name="get_id" qualifiers="const"> diff --git a/doc/classes/TileMap.xml b/doc/classes/TileMap.xml index bc8a1d7bf1..687c7194cd 100644 --- a/doc/classes/TileMap.xml +++ b/doc/classes/TileMap.xml @@ -10,12 +10,12 @@ </description> <tutorials> <link title="Using Tilemaps">$DOCS_URL/tutorials/2d/using_tilemaps.html</link> - <link title="2D Platformer Demo">https://godotengine.org/asset-library/asset/120</link> - <link title="2D Isometric Demo">https://godotengine.org/asset-library/asset/112</link> - <link title="2D Hexagonal Demo">https://godotengine.org/asset-library/asset/111</link> - <link title="2D Navigation Astar Demo">https://godotengine.org/asset-library/asset/519</link> - <link title="2D Role Playing Game Demo">https://godotengine.org/asset-library/asset/520</link> - <link title="2D Kinematic Character Demo">https://godotengine.org/asset-library/asset/113</link> + <link title="2D Platformer Demo">https://godotengine.org/asset-library/asset/2727</link> + <link title="2D Isometric Demo">https://godotengine.org/asset-library/asset/2718</link> + <link title="2D Hexagonal Demo">https://godotengine.org/asset-library/asset/2717</link> + <link title="2D Grid-based Navigation with AStarGrid2D Demo">https://godotengine.org/asset-library/asset/2723</link> + <link title="2D Role Playing Game (RPG) Demo">https://godotengine.org/asset-library/asset/2729</link> + <link title="2D Kinematic Character Demo">https://godotengine.org/asset-library/asset/2719</link> </tutorials> <methods> <method name="_tile_data_runtime_update" qualifiers="virtual"> diff --git a/doc/classes/TileSet.xml b/doc/classes/TileSet.xml index 37f4cc5c0b..64cd4fb7b2 100644 --- a/doc/classes/TileSet.xml +++ b/doc/classes/TileSet.xml @@ -13,12 +13,12 @@ </description> <tutorials> <link title="Using Tilemaps">$DOCS_URL/tutorials/2d/using_tilemaps.html</link> - <link title="2D Platformer Demo">https://godotengine.org/asset-library/asset/120</link> - <link title="2D Isometric Demo">https://godotengine.org/asset-library/asset/112</link> - <link title="2D Hexagonal Demo">https://godotengine.org/asset-library/asset/111</link> - <link title="2D Navigation Astar Demo">https://godotengine.org/asset-library/asset/519</link> - <link title="2D Role Playing Game Demo">https://godotengine.org/asset-library/asset/520</link> - <link title="2D Kinematic Character Demo">https://godotengine.org/asset-library/asset/113</link> + <link title="2D Platformer Demo">https://godotengine.org/asset-library/asset/2727</link> + <link title="2D Isometric Demo">https://godotengine.org/asset-library/asset/2718</link> + <link title="2D Hexagonal Demo">https://godotengine.org/asset-library/asset/2717</link> + <link title="2D Grid-based Navigation with AStarGrid2D Demo">https://godotengine.org/asset-library/asset/2723</link> + <link title="2D Role Playing Game (RPG) Demo">https://godotengine.org/asset-library/asset/2729</link> + <link title="2D Kinematic Character Demo">https://godotengine.org/asset-library/asset/2719</link> </tutorials> <methods> <method name="add_custom_data_layer"> diff --git a/doc/classes/Timer.xml b/doc/classes/Timer.xml index 6eee569443..9de1e09273 100644 --- a/doc/classes/Timer.xml +++ b/doc/classes/Timer.xml @@ -15,7 +15,7 @@ [b]Note:[/b] Timers are affected by [member Engine.time_scale]. The higher the time scale, the sooner timers will end. How often a timer processes may depend on the framerate or [member Engine.physics_ticks_per_second]. </description> <tutorials> - <link title="2D Dodge The Creeps Demo">https://godotengine.org/asset-library/asset/515</link> + <link title="2D Dodge The Creeps Demo">https://godotengine.org/asset-library/asset/2712</link> </tutorials> <methods> <method name="is_stopped" qualifiers="const"> diff --git a/doc/classes/Transform2D.xml b/doc/classes/Transform2D.xml index 4247ff81ee..345d0512ff 100644 --- a/doc/classes/Transform2D.xml +++ b/doc/classes/Transform2D.xml @@ -10,8 +10,8 @@ <tutorials> <link title="Math documentation index">$DOCS_URL/tutorials/math/index.html</link> <link title="Matrices and transforms">$DOCS_URL/tutorials/math/matrices_and_transforms.html</link> - <link title="Matrix Transform Demo">https://godotengine.org/asset-library/asset/584</link> - <link title="2.5D Demo">https://godotengine.org/asset-library/asset/583</link> + <link title="Matrix Transform Demo">https://godotengine.org/asset-library/asset/2787</link> + <link title="2.5D Game Demo">https://godotengine.org/asset-library/asset/2783</link> </tutorials> <constructors> <constructor name="Transform2D"> diff --git a/doc/classes/Transform3D.xml b/doc/classes/Transform3D.xml index 1827cdf8f0..2a0bbc46af 100644 --- a/doc/classes/Transform3D.xml +++ b/doc/classes/Transform3D.xml @@ -12,9 +12,9 @@ <link title="Math documentation index">$DOCS_URL/tutorials/math/index.html</link> <link title="Matrices and transforms">$DOCS_URL/tutorials/math/matrices_and_transforms.html</link> <link title="Using 3D transforms">$DOCS_URL/tutorials/3d/using_transforms.html</link> - <link title="Matrix Transform Demo">https://godotengine.org/asset-library/asset/584</link> - <link title="3D Platformer Demo">https://godotengine.org/asset-library/asset/125</link> - <link title="2.5D Demo">https://godotengine.org/asset-library/asset/583</link> + <link title="Matrix Transform Demo">https://godotengine.org/asset-library/asset/2787</link> + <link title="3D Platformer Demo">https://godotengine.org/asset-library/asset/2748</link> + <link title="2.5D Game Demo">https://godotengine.org/asset-library/asset/2783</link> </tutorials> <constructors> <constructor name="Transform3D"> diff --git a/doc/classes/UndoRedo.xml b/doc/classes/UndoRedo.xml index 3d36eafb08..e197f7748c 100644 --- a/doc/classes/UndoRedo.xml +++ b/doc/classes/UndoRedo.xml @@ -112,7 +112,8 @@ <return type="void" /> <param index="0" name="object" type="Object" /> <description> - Register a reference for "do" that will be erased if the "do" history is lost. This is useful mostly for new nodes created for the "do" call. Do not use for resources. + Register a reference to an object that will be erased if the "do" history is deleted. This is useful for objects added by the "do" action and removed by the "undo" action. + When the "do" history is deleted, if the object is a [RefCounted], it will be unreferenced. Otherwise, it will be freed. Do not use for resources. [codeblock] var node = Node2D.new() undo_redo.create_action("Add node") @@ -143,7 +144,8 @@ <return type="void" /> <param index="0" name="object" type="Object" /> <description> - Register a reference for "undo" that will be erased if the "undo" history is lost. This is useful mostly for nodes removed with the "do" call (not the "undo" call!). + Register a reference to an object that will be erased if the "undo" history is deleted. This is useful for objects added by the "undo" action and removed by the "do" action. + When the "undo" history is deleted, if the object is a [RefCounted], it will be unreferenced. Otherwise, it will be freed. Do not use for resources. [codeblock] var node = $Node2D undo_redo.create_action("Remove node") @@ -272,10 +274,10 @@ Makes "do"/"undo" operations stay in separate actions. </constant> <constant name="MERGE_ENDS" value="1" enum="MergeMode"> - Makes so that the action's "undo" operations are from the first action created and the "do" operations are from the last subsequent action with the same name. + Merges this action with the previous one if they have the same name. Keeps only the first action's "undo" operations and the last action's "do" operations. Useful for sequential changes to a single value. </constant> <constant name="MERGE_ALL" value="2" enum="MergeMode"> - Makes subsequent actions with the same name be merged into one. + Merges this action with the previous one if they have the same name. </constant> </constants> </class> diff --git a/doc/classes/VBoxContainer.xml b/doc/classes/VBoxContainer.xml index d3ea94c0eb..62484ecdc0 100644 --- a/doc/classes/VBoxContainer.xml +++ b/doc/classes/VBoxContainer.xml @@ -8,6 +8,6 @@ </description> <tutorials> <link title="Using Containers">$DOCS_URL/tutorials/ui/gui_containers.html</link> - <link title="3D Voxel Demo">https://godotengine.org/asset-library/asset/676</link> + <link title="3D Voxel Demo">https://godotengine.org/asset-library/asset/2755</link> </tutorials> </class> diff --git a/doc/classes/Vector2.xml b/doc/classes/Vector2.xml index f33076a92f..0b39743a72 100644 --- a/doc/classes/Vector2.xml +++ b/doc/classes/Vector2.xml @@ -14,7 +14,7 @@ <link title="Vector math">$DOCS_URL/tutorials/math/vector_math.html</link> <link title="Advanced vector math">$DOCS_URL/tutorials/math/vectors_advanced.html</link> <link title="3Blue1Brown Essence of Linear Algebra">https://www.youtube.com/playlist?list=PLZHQObOWTQDPD3MizzM2xVFitgF8hE_ab</link> - <link title="Matrix Transform Demo">https://godotengine.org/asset-library/asset/584</link> + <link title="Matrix Transform Demo">https://godotengine.org/asset-library/asset/2787</link> <link title="All 2D Demos">https://github.com/godotengine/godot-demo-projects/tree/master/2d</link> </tutorials> <constructors> @@ -132,7 +132,7 @@ <param index="0" name="with" type="Vector2" /> <description> Returns the 2D analog of the cross product for this vector and [param with]. - This is the signed area of the parallelogram formed by the two vectors. If the second vector is clockwise from the first vector, then the cross product is the positive area. If counter-clockwise, the cross product is the negative area. + This is the signed area of the parallelogram formed by the two vectors. If the second vector is clockwise from the first vector, then the cross product is the positive area. If counter-clockwise, the cross product is the negative area. If the two vectors are parallel this returns zero, making it useful for testing if two vectors are parallel. [b]Note:[/b] Cross product is not defined in 2D mathematically. This method embeds the 2D vectors in the XY plane of 3D space and uses their cross product's Z component as the analog. </description> </method> diff --git a/doc/classes/Vector3.xml b/doc/classes/Vector3.xml index 872534fd89..af1383fe22 100644 --- a/doc/classes/Vector3.xml +++ b/doc/classes/Vector3.xml @@ -14,7 +14,7 @@ <link title="Vector math">$DOCS_URL/tutorials/math/vector_math.html</link> <link title="Advanced vector math">$DOCS_URL/tutorials/math/vectors_advanced.html</link> <link title="3Blue1Brown Essence of Linear Algebra">https://www.youtube.com/playlist?list=PLZHQObOWTQDPD3MizzM2xVFitgF8hE_ab</link> - <link title="Matrix Transform Demo">https://godotengine.org/asset-library/asset/584</link> + <link title="Matrix Transform Demo">https://godotengine.org/asset-library/asset/2787</link> <link title="All 3D Demos">https://github.com/godotengine/godot-demo-projects/tree/master/3d</link> </tutorials> <constructors> @@ -108,6 +108,7 @@ <param index="0" name="with" type="Vector3" /> <description> Returns the cross product of this vector and [param with]. + This returns a vector perpendicular to both this and [param with], which would be the normal vector of the plane defined by the two vectors. As there are two such vectors, in opposite directions, this method returns the vector defined by a right-handed coordinate system. If the two vectors are parallel this returns an empty vector, making it useful for testing if two vectors are parallel. </description> </method> <method name="cubic_interpolate" qualifiers="const"> diff --git a/doc/classes/VehicleBody3D.xml b/doc/classes/VehicleBody3D.xml index 359e84c3da..5b79067659 100644 --- a/doc/classes/VehicleBody3D.xml +++ b/doc/classes/VehicleBody3D.xml @@ -9,7 +9,7 @@ [b]Note:[/b] This class has known issues and isn't designed to provide realistic 3D vehicle physics. If you want advanced vehicle physics, you may have to write your own physics integration using [CharacterBody3D] or [RigidBody3D]. </description> <tutorials> - <link title="3D Truck Town Demo">https://godotengine.org/asset-library/asset/524</link> + <link title="3D Truck Town Demo">https://godotengine.org/asset-library/asset/2752</link> </tutorials> <members> <member name="brake" type="float" setter="set_brake" getter="get_brake" default="0.0"> diff --git a/doc/classes/VehicleWheel3D.xml b/doc/classes/VehicleWheel3D.xml index 77cb5ca9f8..92b2297bf4 100644 --- a/doc/classes/VehicleWheel3D.xml +++ b/doc/classes/VehicleWheel3D.xml @@ -8,7 +8,7 @@ [b]Note:[/b] This class has known issues and isn't designed to provide realistic 3D vehicle physics. If you want advanced vehicle physics, you may need to write your own physics integration using another [PhysicsBody3D] class. </description> <tutorials> - <link title="3D Truck Town Demo">https://godotengine.org/asset-library/asset/524</link> + <link title="3D Truck Town Demo">https://godotengine.org/asset-library/asset/2752</link> </tutorials> <methods> <method name="get_contact_body" qualifiers="const"> diff --git a/doc/classes/Viewport.xml b/doc/classes/Viewport.xml index dcc817427b..c272a3e6d7 100644 --- a/doc/classes/Viewport.xml +++ b/doc/classes/Viewport.xml @@ -13,12 +13,12 @@ <tutorials> <link title="Using Viewports">$DOCS_URL/tutorials/rendering/viewports.html</link> <link title="Viewport and canvas transforms">$DOCS_URL/tutorials/2d/2d_transforms.html</link> - <link title="GUI in 3D Demo">https://godotengine.org/asset-library/asset/127</link> - <link title="3D in 2D Demo">https://godotengine.org/asset-library/asset/128</link> - <link title="2D in 3D Demo">https://godotengine.org/asset-library/asset/129</link> - <link title="Screen Capture Demo">https://godotengine.org/asset-library/asset/130</link> - <link title="Dynamic Split Screen Demo">https://godotengine.org/asset-library/asset/541</link> - <link title="3D Viewport Scaling Demo">https://godotengine.org/asset-library/asset/586</link> + <link title="GUI in 3D Viewport Demo">https://godotengine.org/asset-library/asset/2807</link> + <link title="3D in 2D Viewport Demo">https://godotengine.org/asset-library/asset/2804</link> + <link title="2D in 3D Viewport Demo">https://godotengine.org/asset-library/asset/2803</link> + <link title="Screen Capture Demo">https://godotengine.org/asset-library/asset/2808</link> + <link title="Dynamic Split Screen Demo">https://godotengine.org/asset-library/asset/2806</link> + <link title="3D Resolution Scaling Demo">https://godotengine.org/asset-library/asset/2805</link> </tutorials> <methods> <method name="find_world_2d" qualifiers="const"> @@ -347,12 +347,17 @@ Sets the screen-space antialiasing method used. Screen-space antialiasing works by selectively blurring edges in a post-process shader. It differs from MSAA which takes multiple coverage samples while rendering objects. Screen-space AA methods are typically faster than MSAA and will smooth out specular aliasing, but tend to make scenes appear blurry. </member> <member name="sdf_oversize" type="int" setter="set_sdf_oversize" getter="get_sdf_oversize" enum="Viewport.SDFOversize" default="1"> + Controls how much of the original viewport's size should be covered by the 2D signed distance field. This SDF can be sampled in [CanvasItem] shaders and is also used for [GPUParticles2D] collision. Higher values allow portions of occluders located outside the viewport to still be taken into account in the generated signed distance field, at the cost of performance. If you notice particles falling through [LightOccluder2D]s as the occluders leave the viewport, increase this setting. + The percentage is added on each axis and on both sides. For example, with the default [constant SDF_OVERSIZE_120_PERCENT], the signed distance field will cover 20% of the viewport's size outside the viewport on each side (top, right, bottom, left). </member> <member name="sdf_scale" type="int" setter="set_sdf_scale" getter="get_sdf_scale" enum="Viewport.SDFScale" default="1"> + The resolution scale to use for the 2D signed distance field. Higher values lead to a more precise and more stable signed distance field as the camera moves, at the cost of performance. </member> <member name="snap_2d_transforms_to_pixel" type="bool" setter="set_snap_2d_transforms_to_pixel" getter="is_snap_2d_transforms_to_pixel_enabled" default="false"> + If [code]true[/code], [CanvasItem] nodes will internally snap to full pixels. Their position can still be sub-pixel, but the decimals will not have effect. This can lead to a crisper appearance at the cost of less smooth movement, especially when [Camera2D] smoothing is enabled. </member> <member name="snap_2d_vertices_to_pixel" type="bool" setter="set_snap_2d_vertices_to_pixel" getter="is_snap_2d_vertices_to_pixel_enabled" default="false"> + If [code]true[/code], vertices of [CanvasItem] nodes will snap to full pixels. Only affects the final vertex positions, not the transforms. This can lead to a crisper appearance at the cost of less smooth movement, especially when [Camera2D] smoothing is enabled. </member> <member name="texture_mipmap_bias" type="float" setter="set_texture_mipmap_bias" getter="get_texture_mipmap_bias" default="0.0"> Affects the final texture sharpness by reading from a lower or higher mipmap (also called "texture LOD bias"). Negative values make mipmapped textures sharper but grainier when viewed at a distance, while positive values make mipmapped textures blurrier (even when up close). @@ -515,14 +520,16 @@ Objects are displayed without light information. </constant> <constant name="DEBUG_DRAW_LIGHTING" value="2" enum="DebugDraw"> + Objects are displayed without textures and only with lighting information. </constant> <constant name="DEBUG_DRAW_OVERDRAW" value="3" enum="DebugDraw"> Objects are displayed semi-transparent with additive blending so you can see where they are drawing over top of one another. A higher overdraw means you are wasting performance on drawing pixels that are being hidden behind others. </constant> <constant name="DEBUG_DRAW_WIREFRAME" value="4" enum="DebugDraw"> - Objects are displayed in wireframe style. + Objects are displayed as wireframe models. </constant> <constant name="DEBUG_DRAW_NORMAL_BUFFER" value="5" enum="DebugDraw"> + Objects are displayed without lighting information and their textures replaced by normal mapping. </constant> <constant name="DEBUG_DRAW_VOXEL_GI_ALBEDO" value="6" enum="DebugDraw"> Objects are displayed with only the albedo value from [VoxelGI]s. @@ -540,6 +547,7 @@ Draws the shadow atlas that stores shadows from [DirectionalLight3D]s in the upper left quadrant of the [Viewport]. </constant> <constant name="DEBUG_DRAW_SCENE_LUMINANCE" value="11" enum="DebugDraw"> + Draws the scene luminance buffer (if available) in the upper left quadrant of the [Viewport]. </constant> <constant name="DEBUG_DRAW_SSAO" value="12" enum="DebugDraw"> Draws the screen-space ambient occlusion texture instead of the scene so that you can clearly see how it is affecting objects. In order for this display mode to work, you must have [member Environment.ssao_enabled] set in your [WorldEnvironment]. @@ -554,24 +562,36 @@ Draws the decal atlas used by [Decal]s and light projector textures in the upper left quadrant of the [Viewport]. </constant> <constant name="DEBUG_DRAW_SDFGI" value="16" enum="DebugDraw"> + Draws the cascades used to render signed distance field global illumination (SDFGI). + Does nothing if the current environment's [member Environment.sdfgi_enabled] is [code]false[/code] or SDFGI is not supported on the platform. </constant> <constant name="DEBUG_DRAW_SDFGI_PROBES" value="17" enum="DebugDraw"> + Draws the probes used for signed distance field global illumination (SDFGI). + Does nothing if the current environment's [member Environment.sdfgi_enabled] is [code]false[/code] or SDFGI is not supported on the platform. </constant> <constant name="DEBUG_DRAW_GI_BUFFER" value="18" enum="DebugDraw"> + Draws the buffer used for global illumination (GI). </constant> <constant name="DEBUG_DRAW_DISABLE_LOD" value="19" enum="DebugDraw"> + Draws all of the objects at their highest polycount, without low level of detail (LOD). </constant> <constant name="DEBUG_DRAW_CLUSTER_OMNI_LIGHTS" value="20" enum="DebugDraw"> + Draws the cluster used by [OmniLight3D] nodes to optimize light rendering. </constant> <constant name="DEBUG_DRAW_CLUSTER_SPOT_LIGHTS" value="21" enum="DebugDraw"> + Draws the cluster used by [SpotLight3D] nodes to optimize light rendering. </constant> <constant name="DEBUG_DRAW_CLUSTER_DECALS" value="22" enum="DebugDraw"> + Draws the cluster used by [Decal] nodes to optimize decal rendering. </constant> <constant name="DEBUG_DRAW_CLUSTER_REFLECTION_PROBES" value="23" enum="DebugDraw"> + Draws the cluster used by [ReflectionProbe] nodes to optimize decal rendering. </constant> <constant name="DEBUG_DRAW_OCCLUDERS" value="24" enum="DebugDraw"> + Draws the buffer used for occlusion culling. </constant> <constant name="DEBUG_DRAW_MOTION_VECTORS" value="25" enum="DebugDraw"> + Draws vector lines over the viewport to indicate the movement of pixels between frames. </constant> <constant name="DEBUG_DRAW_INTERNAL_BUFFER" value="26" enum="DebugDraw"> Draws the internal resolution buffer of the scene before post-processing is applied. @@ -591,7 +611,7 @@ Use this for non-pixel art textures that may be viewed at a low scale (e.g. due to [Camera2D] zoom or sprite scaling), as mipmaps are important to smooth out pixels that are smaller than on-screen pixels. </constant> <constant name="DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_MAX" value="4" enum="DefaultCanvasItemTextureFilter"> - Max value for [enum DefaultCanvasItemTextureFilter] enum. + Represents the size of the [enum DefaultCanvasItemTextureFilter] enum. </constant> <constant name="DEFAULT_CANVAS_ITEM_TEXTURE_REPEAT_DISABLED" value="0" enum="DefaultCanvasItemTextureRepeat"> Disables textures repeating. Instead, when reading UVs outside the 0-1 range, the value will be clamped to the edge of the texture, resulting in a stretched out look at the borders of the texture. @@ -603,34 +623,43 @@ Flip the texture when repeating so that the edge lines up instead of abruptly changing. </constant> <constant name="DEFAULT_CANVAS_ITEM_TEXTURE_REPEAT_MAX" value="3" enum="DefaultCanvasItemTextureRepeat"> - Max value for [enum DefaultCanvasItemTextureRepeat] enum. + Represents the size of the [enum DefaultCanvasItemTextureRepeat] enum. </constant> <constant name="SDF_OVERSIZE_100_PERCENT" value="0" enum="SDFOversize"> + The signed distance field only covers the viewport's own rectangle. </constant> <constant name="SDF_OVERSIZE_120_PERCENT" value="1" enum="SDFOversize"> + The signed distance field is expanded to cover 20% of the viewport's size around the borders. </constant> <constant name="SDF_OVERSIZE_150_PERCENT" value="2" enum="SDFOversize"> + The signed distance field is expanded to cover 50% of the viewport's size around the borders. </constant> <constant name="SDF_OVERSIZE_200_PERCENT" value="3" enum="SDFOversize"> + The signed distance field is expanded to cover 100% (double) of the viewport's size around the borders. </constant> <constant name="SDF_OVERSIZE_MAX" value="4" enum="SDFOversize"> + Represents the size of the [enum SDFOversize] enum. </constant> <constant name="SDF_SCALE_100_PERCENT" value="0" enum="SDFScale"> + The signed distance field is rendered at full resolution. </constant> <constant name="SDF_SCALE_50_PERCENT" value="1" enum="SDFScale"> + The signed distance field is rendered at half the resolution of this viewport. </constant> <constant name="SDF_SCALE_25_PERCENT" value="2" enum="SDFScale"> + The signed distance field is rendered at a quarter the resolution of this viewport. </constant> <constant name="SDF_SCALE_MAX" value="3" enum="SDFScale"> + Represents the size of the [enum SDFScale] enum. </constant> <constant name="VRS_DISABLED" value="0" enum="VRSMode"> - VRS is disabled. + Variable Rate Shading is disabled. </constant> <constant name="VRS_TEXTURE" value="1" enum="VRSMode"> - VRS uses a texture. Note, for stereoscopic use a texture atlas with a texture for each view. + Variable Rate Shading uses a texture. Note, for stereoscopic use a texture atlas with a texture for each view. </constant> <constant name="VRS_XR" value="2" enum="VRSMode"> - VRS texture is supplied by the primary [XRInterface]. + Variable Rate Shading's texture is supplied by the primary [XRInterface]. </constant> <constant name="VRS_MAX" value="3" enum="VRSMode"> Represents the size of the [enum VRSMode] enum. diff --git a/doc/classes/ViewportTexture.xml b/doc/classes/ViewportTexture.xml index e12933d64b..ba2352ab61 100644 --- a/doc/classes/ViewportTexture.xml +++ b/doc/classes/ViewportTexture.xml @@ -4,20 +4,21 @@ Provides the content of a [Viewport] as a dynamic texture. </brief_description> <description> - Provides the content of a [Viewport] as a dynamic [Texture2D]. This can be used to mix controls, 2D game objects, and 3D game objects in the same scene. - To create a [ViewportTexture] in code, use the [method Viewport.get_texture] method on the target viewport. + A [ViewportTexture] provides the content of a [Viewport] as a dynamic [Texture2D]. This can be used to combine the rendering of [Control], [Node2D] and [Node3D] nodes. For example, you can use this texture to display a 3D scene inside a [TextureRect], or a 2D overlay in a [Sprite3D]. + To get a [ViewportTexture] in code, use the [method Viewport.get_texture] method on the target viewport. [b]Note:[/b] A [ViewportTexture] is always local to its scene (see [member Resource.resource_local_to_scene]). If the scene root is not ready, it may return incorrect data (see [signal Node.ready]). + [b]Note:[/b] Instantiating scenes containing a high-resolution [ViewportTexture] may cause noticeable stutter. </description> <tutorials> - <link title="GUI in 3D Demo">https://godotengine.org/asset-library/asset/127</link> - <link title="3D in 2D Demo">https://godotengine.org/asset-library/asset/128</link> - <link title="2D in 3D Demo">https://godotengine.org/asset-library/asset/129</link> - <link title="3D Viewport Scaling Demo">https://godotengine.org/asset-library/asset/586</link> + <link title="GUI in 3D Viewport Demo">https://godotengine.org/asset-library/asset/2807</link> + <link title="3D in 2D Viewport Demo">https://godotengine.org/asset-library/asset/2804</link> + <link title="2D in 3D Viewport Demo">https://godotengine.org/asset-library/asset/2803</link> + <link title="3D Resolution Scaling Demo">https://godotengine.org/asset-library/asset/2805</link> </tutorials> <members> <member name="viewport_path" type="NodePath" setter="set_viewport_path_in_scene" getter="get_viewport_path_in_scene" default="NodePath("")"> - The path to the [Viewport] node to display. This is relative to the scene root, not to the node that uses the texture. - [b]Note:[/b] In the editor, this path is automatically updated when the target viewport or one of its ancestors is renamed or moved. At runtime, the path may not be able to automatically update due to the inability to determine the scene root. + The path to the [Viewport] node to display. This is relative to the local scene root (see [method Resource.get_local_scene]), [b]not[/b] to the nodes that use this texture. + [b]Note:[/b] In the editor, this path is automatically updated when the target viewport or one of its ancestors is renamed or moved. At runtime, this path may not automatically update if the scene root cannot be found. </member> </members> </class> diff --git a/doc/classes/VisibleOnScreenNotifier2D.xml b/doc/classes/VisibleOnScreenNotifier2D.xml index 9d22bf6cff..cf4a0c0018 100644 --- a/doc/classes/VisibleOnScreenNotifier2D.xml +++ b/doc/classes/VisibleOnScreenNotifier2D.xml @@ -9,7 +9,7 @@ [b]Note:[/b] [VisibleOnScreenNotifier2D] uses the render culling code to determine whether it's visible on screen, so it won't function unless [member CanvasItem.visible] is set to [code]true[/code]. </description> <tutorials> - <link title="2D Dodge The Creeps Demo">https://godotengine.org/asset-library/asset/515</link> + <link title="2D Dodge The Creeps Demo">https://godotengine.org/asset-library/asset/2712</link> </tutorials> <methods> <method name="is_on_screen" qualifiers="const"> diff --git a/doc/classes/VoxelGI.xml b/doc/classes/VoxelGI.xml index 9e4026a750..93679cccf6 100644 --- a/doc/classes/VoxelGI.xml +++ b/doc/classes/VoxelGI.xml @@ -12,7 +12,7 @@ </description> <tutorials> <link title="Using Voxel global illumination">$DOCS_URL/tutorials/3d/global_illumination/using_voxel_gi.html</link> - <link title="Third Person Shooter Demo">https://godotengine.org/asset-library/asset/678</link> + <link title="Third Person Shooter (TPS) Demo">https://godotengine.org/asset-library/asset/2710</link> </tutorials> <methods> <method name="bake"> diff --git a/doc/classes/VoxelGIData.xml b/doc/classes/VoxelGIData.xml index 088854e8f4..43bf04b1f5 100644 --- a/doc/classes/VoxelGIData.xml +++ b/doc/classes/VoxelGIData.xml @@ -8,7 +8,7 @@ [b]Note:[/b] To prevent text-based scene files ([code].tscn[/code]) from growing too much and becoming slow to load and save, always save [VoxelGIData] to an external binary resource file ([code].res[/code]) instead of embedding it within the scene. This can be done by clicking the dropdown arrow next to the [VoxelGIData] resource, choosing [b]Edit[/b], clicking the floppy disk icon at the top of the Inspector then choosing [b]Save As...[/b]. </description> <tutorials> - <link title="Third Person Shooter Demo">https://godotengine.org/asset-library/asset/678</link> + <link title="Third Person Shooter (TPS) Demo">https://godotengine.org/asset-library/asset/2710</link> </tutorials> <methods> <method name="allocate"> diff --git a/doc/classes/WorldEnvironment.xml b/doc/classes/WorldEnvironment.xml index c7ee160174..80dc46ae93 100644 --- a/doc/classes/WorldEnvironment.xml +++ b/doc/classes/WorldEnvironment.xml @@ -10,9 +10,8 @@ </description> <tutorials> <link title="Environment and post-processing">$DOCS_URL/tutorials/3d/environment_and_post_processing.html</link> - <link title="3D Material Testers Demo">https://godotengine.org/asset-library/asset/123</link> - <link title="2D HDR Demo">https://godotengine.org/asset-library/asset/110</link> - <link title="Third Person Shooter Demo">https://godotengine.org/asset-library/asset/678</link> + <link title="3D Material Testers Demo">https://godotengine.org/asset-library/asset/2742</link> + <link title="Third Person Shooter (TPS) Demo">https://godotengine.org/asset-library/asset/2710</link> </tutorials> <members> <member name="camera_attributes" type="CameraAttributes" setter="set_camera_attributes" getter="get_camera_attributes"> diff --git a/doc/classes/XRBodyModifier3D.xml b/doc/classes/XRBodyModifier3D.xml index cb1caf12a9..49a226c106 100644 --- a/doc/classes/XRBodyModifier3D.xml +++ b/doc/classes/XRBodyModifier3D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="XRBodyModifier3D" inherits="Node3D" experimental="" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> +<class name="XRBodyModifier3D" inherits="SkeletonModifier3D" experimental="" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> A node for driving body meshes from [XRBodyTracker] data. </brief_description> @@ -24,9 +24,6 @@ <member name="show_when_tracked" type="bool" setter="set_show_when_tracked" getter="get_show_when_tracked" default="true"> If true then the nodes visibility is determined by whether tracking data is available. </member> - <member name="target" type="NodePath" setter="set_target" getter="get_target" default="NodePath("")"> - A [NodePath] to a [Skeleton3D] to animate. - </member> </members> <constants> <constant name="BODY_UPDATE_UPPER_BODY" value="1" enum="BodyUpdate" is_bitfield="true"> diff --git a/doc/classes/XRHandModifier3D.xml b/doc/classes/XRHandModifier3D.xml index 3192913b87..9ff27bb982 100644 --- a/doc/classes/XRHandModifier3D.xml +++ b/doc/classes/XRHandModifier3D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="XRHandModifier3D" inherits="Node3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> +<class name="XRHandModifier3D" inherits="SkeletonModifier3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> A node for driving hand meshes from [XRHandTracker] data. </brief_description> @@ -18,9 +18,6 @@ <member name="hand_tracker" type="StringName" setter="set_hand_tracker" getter="get_hand_tracker" default="&"/user/left""> The name of the [XRHandTracker] registered with [XRServer] to obtain the hand tracking data from. </member> - <member name="target" type="NodePath" setter="set_target" getter="get_target" default="NodePath("")"> - A [NodePath] to a [Skeleton3D] to animate. - </member> </members> <constants> <constant name="BONE_UPDATE_FULL" value="0" enum="BoneUpdate"> diff --git a/doc/tools/make_rst.py b/doc/tools/make_rst.py index 0d2bc87f55..d7230db6a6 100755 --- a/doc/tools/make_rst.py +++ b/doc/tools/make_rst.py @@ -87,6 +87,7 @@ BASE_STRINGS = [ "This method may be changed or removed in future versions.", "This operator may be changed or removed in future versions.", "This theme property may be changed or removed in future versions.", + "[b]Note:[/b] The returned array is [i]copied[/i] and any changes to it will not update the original property value. See [%s] for more details.", ] strings_l10n: Dict[str, str] = {} @@ -145,6 +146,18 @@ CLASSES_WITH_CSHARP_DIFFERENCES: List[str] = [ "Variant", ] +PACKED_ARRAY_TYPES: List[str] = [ + "PackedByteArray", + "PackedColorArray", + "PackedFloat32Array", + "Packedfloat64Array", + "PackedInt32Array", + "PackedInt64Array", + "PackedStringArray", + "PackedVector2Array", + "PackedVector3Array", +] + class State: def __init__(self) -> None: @@ -1277,6 +1290,9 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir: if property_def.text is not None and property_def.text.strip() != "": f.write(f"{format_text_block(property_def.text.strip(), property_def, state)}\n\n") + if property_def.type_name.type_name in PACKED_ARRAY_TYPES: + tmp = f"[b]Note:[/b] The returned array is [i]copied[/i] and any changes to it will not update the original property value. See [{property_def.type_name.type_name}] for more details." + f.write(f"{format_text_block(tmp, property_def, state)}\n\n") elif property_def.deprecated is None and property_def.experimental is None: f.write(".. container:: contribute\n\n\t") f.write( diff --git a/drivers/gles3/storage/light_storage.cpp b/drivers/gles3/storage/light_storage.cpp index d7d77c6b8f..eebba6b00d 100644 --- a/drivers/gles3/storage/light_storage.cpp +++ b/drivers/gles3/storage/light_storage.cpp @@ -1064,7 +1064,11 @@ bool LightStorage::_shadow_atlas_find_shadow(ShadowAtlas *shadow_atlas, int *p_i for (int j = 0; j < sc; j++) { LightInstance *sli = light_instance_owner.get_or_null(sarr[j].owner); - ERR_CONTINUE(!sli); + if (!sli) { + // Found a released light instance. + found_used_idx = j; + break; + } if (sli->last_scene_pass != RasterizerSceneGLES3::get_singleton()->get_scene_pass()) { // Was just allocated, don't kill it so soon, wait a bit. diff --git a/editor/editor_audio_buses.cpp b/editor/editor_audio_buses.cpp index dd5dc14136..0b42646387 100644 --- a/editor/editor_audio_buses.cpp +++ b/editor/editor_audio_buses.cpp @@ -925,6 +925,7 @@ EditorAudioBus::EditorAudioBus(EditorAudioBuses *p_buses, bool p_is_master) { effects->connect("gui_input", callable_mp(this, &EditorAudioBus::_effects_gui_input)); send = memnew(OptionButton); + send->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED); send->set_clip_text(true); send->connect("item_selected", callable_mp(this, &EditorAudioBus::_send_selected)); vb->add_child(send); @@ -1151,6 +1152,7 @@ void EditorAudioBuses::_duplicate_bus(int p_which) { ur->add_do_method(AudioServer::get_singleton(), "add_bus_effect", add_at_pos, AudioServer::get_singleton()->get_bus_effect(p_which, i)); ur->add_do_method(AudioServer::get_singleton(), "set_bus_effect_enabled", add_at_pos, i, AudioServer::get_singleton()->is_bus_effect_enabled(p_which, i)); } + ur->add_do_method(this, "_update_bus", add_at_pos); ur->add_undo_method(AudioServer::get_singleton(), "remove_bus", add_at_pos); ur->commit_action(); } diff --git a/editor/editor_data.cpp b/editor/editor_data.cpp index 6ab29c1850..72225fd454 100644 --- a/editor/editor_data.cpp +++ b/editor/editor_data.cpp @@ -722,6 +722,7 @@ bool EditorData::check_and_update_scene(int p_idx) { new_scene->set_scene_file_path(edited_scene[p_idx].root->get_scene_file_path()); Node *old_root = edited_scene[p_idx].root; + edited_scene.write[p_idx].root = new_scene; old_root->replace_by(new_scene, false, false); memdelete(old_root); edited_scene.write[p_idx].selection = new_selection; diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp index 516b8f3d74..51e1c27070 100644 --- a/editor/editor_file_system.cpp +++ b/editor/editor_file_system.cpp @@ -1367,6 +1367,10 @@ bool EditorFileSystem::_find_file(const String &p_file, EditorFileSystemDirector String f = ProjectSettings::get_singleton()->localize_path(p_file); + // Note: Only checks if base directory is case sensitive. + Ref<DirAccess> dir = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + bool fs_case_sensitive = dir->is_case_sensitive("res://"); + if (!f.begins_with("res://")) { return false; } @@ -1390,9 +1394,16 @@ bool EditorFileSystem::_find_file(const String &p_file, EditorFileSystemDirector int idx = -1; for (int j = 0; j < fs->get_subdir_count(); j++) { - if (fs->get_subdir(j)->get_name() == path[i]) { - idx = j; - break; + if (fs_case_sensitive) { + if (fs->get_subdir(j)->get_name() == path[i]) { + idx = j; + break; + } + } else { + if (fs->get_subdir(j)->get_name().to_lower() == path[i].to_lower()) { + idx = j; + break; + } } } diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp index 022cf82426..cd498ce089 100644 --- a/editor/editor_help.cpp +++ b/editor/editor_help.cpp @@ -96,6 +96,18 @@ const Vector<String> classes_with_csharp_differences = { }; #endif +const Vector<String> packed_array_types = { + "PackedByteArray", + "PackedColorArray", + "PackedFloat32Array", + "PackedFloat64Array", + "PackedInt32Array", + "PackedInt64Array", + "PackedStringArray", + "PackedVector2Array", + "PackedVector3Array", +}; + // TODO: this is sometimes used directly as doc->something, other times as EditorHelp::get_doc_data(), which is thread-safe. // Might this be a problem? DocTools *EditorHelp::doc = nullptr; @@ -2193,6 +2205,12 @@ void EditorHelp::_update_doc() { } has_prev_text = true; _add_text(descr); + // Add copy note to built-in properties returning Packed*Array. + if (!cd.is_script_doc && packed_array_types.has(prop.type)) { + class_desc->add_newline(); + class_desc->add_newline(); + _add_text(vformat(TTR("[b]Note:[/b] The returned array is [i]copied[/i] and any changes to it will not update the original property value. See [%s] for more details."), prop.type)); + } } else if (!has_prev_text) { class_desc->add_image(get_editor_theme_icon(SNAME("Error"))); class_desc->add_text(" "); @@ -3471,7 +3489,9 @@ EditorHelpHighlighter::HighlightData EditorHelpHighlighter::_get_highlight_data( } text_edits[p_language]->set_text(p_source); - scripts[p_language]->set_source_code(p_source); + if (scripts[p_language].is_valid()) { // See GH-89610. + scripts[p_language]->set_source_code(p_source); + } highlighters[p_language]->_update_cache(); HighlightData result; @@ -3561,16 +3581,18 @@ EditorHelpHighlighter::EditorHelpHighlighter() { #ifdef MODULE_MONO_ENABLED TextEdit *csharp_text_edit = memnew(TextEdit); - Ref<CSharpScript> csharp; - csharp.instantiate(); + // See GH-89610. + //Ref<CSharpScript> csharp; + //csharp.instantiate(); Ref<EditorStandardSyntaxHighlighter> csharp_highlighter; csharp_highlighter.instantiate(); csharp_highlighter->set_text_edit(csharp_text_edit); - csharp_highlighter->_set_edited_resource(csharp); + //csharp_highlighter->_set_edited_resource(csharp); + csharp_highlighter->_set_script_language(CSharpLanguage::get_singleton()); text_edits[LANGUAGE_CSHARP] = csharp_text_edit; - scripts[LANGUAGE_CSHARP] = csharp; + //scripts[LANGUAGE_CSHARP] = csharp; highlighters[LANGUAGE_CSHARP] = csharp_highlighter; #endif } @@ -3578,14 +3600,10 @@ EditorHelpHighlighter::EditorHelpHighlighter() { EditorHelpHighlighter::~EditorHelpHighlighter() { #ifdef MODULE_GDSCRIPT_ENABLED memdelete(text_edits[LANGUAGE_GDSCRIPT]); - scripts[LANGUAGE_GDSCRIPT].unref(); - highlighters[LANGUAGE_GDSCRIPT].unref(); #endif #ifdef MODULE_MONO_ENABLED memdelete(text_edits[LANGUAGE_CSHARP]); - scripts[LANGUAGE_CSHARP].unref(); - highlighters[LANGUAGE_CSHARP].unref(); #endif } diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 6f984f6fab..0e6c814b44 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -3674,7 +3674,9 @@ void EditorNode::set_edited_scene(Node *p_scene) { if (old_edited_scene_root->get_parent() == scene_root) { scene_root->remove_child(old_edited_scene_root); } - old_edited_scene_root->disconnect(SNAME("replacing_by"), callable_mp(this, &EditorNode::set_edited_scene)); + if (old_edited_scene_root->is_connected("replacing_by", callable_mp(this, &EditorNode::set_edited_scene))) { + old_edited_scene_root->disconnect(SNAME("replacing_by"), callable_mp(this, &EditorNode::set_edited_scene)); + } } get_editor_data().set_edited_scene_root(p_scene); @@ -5324,7 +5326,7 @@ bool EditorNode::is_distraction_free_mode_enabled() const { return distraction_free->is_pressed(); } -Variant EditorNode::drag_resource(const Ref<Resource> &p_res, Control *p_from) { +Dictionary EditorNode::drag_resource(const Ref<Resource> &p_res, Control *p_from) { Control *drag_control = memnew(Control); TextureRect *drag_preview = memnew(TextureRect); Label *label = memnew(Label); @@ -5364,7 +5366,7 @@ Variant EditorNode::drag_resource(const Ref<Resource> &p_res, Control *p_from) { return drag_data; } -Variant EditorNode::drag_files_and_dirs(const Vector<String> &p_paths, Control *p_from) { +Dictionary EditorNode::drag_files_and_dirs(const Vector<String> &p_paths, Control *p_from) { bool has_folder = false; bool has_file = false; for (int i = 0; i < p_paths.size(); i++) { diff --git a/editor/editor_node.h b/editor/editor_node.h index 8bcf29811d..96acc8bf46 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -870,8 +870,8 @@ public: bool is_exiting() const { return exiting; } - Variant drag_resource(const Ref<Resource> &p_res, Control *p_from); - Variant drag_files_and_dirs(const Vector<String> &p_paths, Control *p_from); + Dictionary drag_resource(const Ref<Resource> &p_res, Control *p_from); + Dictionary drag_files_and_dirs(const Vector<String> &p_paths, Control *p_from); void add_tool_menu_item(const String &p_name, const Callable &p_callback); void add_tool_submenu_item(const String &p_name, PopupMenu *p_submenu); diff --git a/editor/editor_properties_array_dict.cpp b/editor/editor_properties_array_dict.cpp index a6c7d6b617..c59c221e02 100644 --- a/editor/editor_properties_array_dict.cpp +++ b/editor/editor_properties_array_dict.cpp @@ -328,6 +328,7 @@ void EditorPropertyArray::update_property() { memdelete(container); button_add_item = nullptr; container = nullptr; + slots.clear(); } return; } @@ -483,9 +484,10 @@ bool EditorPropertyArray::_is_drop_valid(const Dictionary &p_drag_data) const { } Dictionary drag_data = p_drag_data; + const String drop_type = drag_data.get("type", ""); - if (drag_data.has("type") && String(drag_data["type"]) == "files") { - Vector<String> files = drag_data["files"]; + if (drop_type == "files") { + PackedStringArray files = drag_data["files"]; for (int i = 0; i < files.size(); i++) { const String &file = files[i]; @@ -504,6 +506,56 @@ bool EditorPropertyArray::_is_drop_valid(const Dictionary &p_drag_data) const { return true; } + if (drop_type == "nodes") { + Array node_paths = drag_data["nodes"]; + + PackedStringArray allowed_subtype_array; + if (allowed_type == "NodePath") { + if (subtype_hint_string == "NodePath") { + return true; + } else { + for (int j = 0; j < subtype_hint_string.get_slice_count(","); j++) { + String ast = subtype_hint_string.get_slice(",", j).strip_edges(); + allowed_subtype_array.append(ast); + } + } + } + + bool is_drop_allowed = true; + + for (int i = 0; i < node_paths.size(); i++) { + const Node *dropped_node = get_node_or_null(node_paths[i]); + ERR_FAIL_NULL_V_MSG(dropped_node, false, "Could not get the dropped node by its path."); + + if (allowed_type != "NodePath") { + if (!ClassDB::is_parent_class(dropped_node->get_class_name(), allowed_type)) { + // Fail if one of the nodes is not of allowed type. + return false; + } + } + + // The array of NodePaths is restricted to specific types using @export_node_path(). + if (allowed_type == "NodePath" && subtype_hint_string != "NodePath") { + if (!allowed_subtype_array.has(dropped_node->get_class_name())) { + // The dropped node type was not found in the allowed subtype array, we must check if it inherits one of them. + for (const String &ast : allowed_subtype_array) { + if (ClassDB::is_parent_class(dropped_node->get_class_name(), ast)) { + is_drop_allowed = true; + break; + } else { + is_drop_allowed = false; + } + } + if (!is_drop_allowed) { + break; + } + } + } + } + + return is_drop_allowed; + } + return false; } @@ -515,18 +567,18 @@ void EditorPropertyArray::drop_data_fw(const Point2 &p_point, const Variant &p_d ERR_FAIL_COND(!_is_drop_valid(p_data)); Dictionary drag_data = p_data; + const String drop_type = drag_data.get("type", ""); + Variant array = object->get_array(); - if (drag_data.has("type") && String(drag_data["type"]) == "files") { - Vector<String> files = drag_data["files"]; - - Variant array = object->get_array(); + // Handle the case where array is not initialized yet. + if (!array.is_array()) { + initialize_array(array); + } else { + array = array.duplicate(); + } - // Handle the case where array is not initialized yet. - if (!array.is_array()) { - initialize_array(array); - } else { - array = array.duplicate(); - } + if (drop_type == "files") { + PackedStringArray files = drag_data["files"]; // Loop the file array and add to existing array. for (int i = 0; i < files.size(); i++) { @@ -540,6 +592,33 @@ void EditorPropertyArray::drop_data_fw(const Point2 &p_point, const Variant &p_d emit_changed(get_edited_property(), array); } + + if (drop_type == "nodes") { + Array node_paths = drag_data["nodes"]; + Node *base_node = get_base_node(); + + for (int i = 0; i < node_paths.size(); i++) { + const NodePath &path = node_paths[i]; + + if (subtype == Variant::OBJECT) { + array.call("push_back", get_node(path)); + } else if (subtype == Variant::NODE_PATH) { + array.call("push_back", base_node->get_path().rel_path_to(path)); + } + } + + emit_changed(get_edited_property(), array); + } +} + +Node *EditorPropertyArray::get_base_node() { + Node *base_node = Object::cast_to<Node>(InspectorDock::get_inspector_singleton()->get_edited_object()); + + if (!base_node) { + base_node = get_tree()->get_edited_scene_root(); + } + + return base_node; } void EditorPropertyArray::_notification(int p_what) { @@ -586,7 +665,7 @@ void EditorPropertyArray::_edit_pressed() { Variant array = get_edited_property_value(); if (!array.is_array() && edit->is_pressed()) { initialize_array(array); - get_edited_object()->set(get_edited_property(), array); + emit_changed(get_edited_property(), array); } get_edited_object()->editor_set_section_unfold(get_edited_property(), edit->is_pressed()); @@ -867,7 +946,7 @@ void EditorPropertyDictionary::setup(PropertyHint p_hint) { void EditorPropertyDictionary::update_property() { Variant updated_val = get_edited_property_value(); - if (updated_val.get_type() == Variant::NIL) { + if (updated_val.get_type() != Variant::DICTIONARY) { edit->set_text(TTR("Dictionary (Nil)")); // This provides symmetry with the array property. edit->set_pressed(false); if (container) { @@ -875,6 +954,7 @@ void EditorPropertyDictionary::update_property() { memdelete(container); button_add_item = nullptr; container = nullptr; + slots.clear(); } return; } @@ -1021,7 +1101,7 @@ void EditorPropertyDictionary::_edit_pressed() { Variant prop_val = get_edited_property_value(); if (prop_val.get_type() == Variant::NIL && edit->is_pressed()) { VariantInternal::initialize(&prop_val, Variant::DICTIONARY); - get_edited_object()->set(get_edited_property(), prop_val); + emit_changed(get_edited_property(), prop_val); } get_edited_object()->editor_set_section_unfold(get_edited_property(), edit->is_pressed()); diff --git a/editor/editor_properties_array_dict.h b/editor/editor_properties_array_dict.h index b1bf45f1b7..dae0fc52a6 100644 --- a/editor/editor_properties_array_dict.h +++ b/editor/editor_properties_array_dict.h @@ -135,6 +135,8 @@ class EditorPropertyArray : public EditorProperty { void _reorder_button_up(); void _create_new_property_slot(); + Node *get_base_node(); + protected: Ref<EditorPropertyArrayObject> object; diff --git a/editor/editor_resource_picker.cpp b/editor/editor_resource_picker.cpp index 1cb71aa933..eee589489d 100644 --- a/editor/editor_resource_picker.cpp +++ b/editor/editor_resource_picker.cpp @@ -615,6 +615,13 @@ void EditorResourcePicker::_get_allowed_types(bool p_with_convert, HashSet<Strin } bool EditorResourcePicker::_is_drop_valid(const Dictionary &p_drag_data) const { + { + const ObjectID source_picker = p_drag_data.get("source_picker", ObjectID()); + if (source_picker == get_instance_id()) { + return false; + } + } + if (base_type.is_empty()) { return true; } @@ -670,7 +677,9 @@ bool EditorResourcePicker::_is_type_valid(const String &p_type_name, const HashS Variant EditorResourcePicker::get_drag_data_fw(const Point2 &p_point, Control *p_from) { if (edited_resource.is_valid()) { - return EditorNode::get_singleton()->drag_resource(edited_resource, p_from); + Dictionary drag_data = EditorNode::get_singleton()->drag_resource(edited_resource, p_from); + drag_data["source_picker"] = get_instance_id(); + return drag_data; } return Variant(); diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp index 4521f4d3ff..233f20a8b3 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -576,9 +576,9 @@ void FileSystemDock::_notification(int p_what) { if ((String(dd["favorite"]) == "all")) { tree->set_drop_mode_flags(Tree::DROP_MODE_INBETWEEN); } - } else if ((String(dd["type"]) == "files") || (String(dd["type"]) == "files_and_dirs") || (String(dd["type"]) == "resource")) { + } else if ((String(dd["type"]) == "files") || (String(dd["type"]) == "files_and_dirs")) { tree->set_drop_mode_flags(Tree::DROP_MODE_ON_ITEM | Tree::DROP_MODE_INBETWEEN); - } else if ((String(dd["type"]) == "nodes")) { + } else if ((String(dd["type"]) == "nodes") || (String(dd["type"]) == "resource")) { holding_branch = true; TreeItem *item = tree->get_next_selected(tree->get_root()); while (item) { @@ -1561,10 +1561,42 @@ void FileSystemDock::_try_duplicate_item(const FileOrFolder &p_item, const Strin } void FileSystemDock::_update_resource_paths_after_move(const HashMap<String, String> &p_renames, const HashMap<String, ResourceUID::ID> &p_uids) const { - // Update the paths in ResourceUID, so that UIDs remain valid. - for (const KeyValue<String, ResourceUID::ID> &pair : p_uids) { - if (p_renames.has(pair.key)) { - ResourceUID::get_singleton()->set_id(pair.value, p_renames[pair.key]); + for (const KeyValue<String, String> &pair : p_renames) { + // Update UID path. + const String &old_path = pair.key; + const String &new_path = pair.value; + + const HashMap<String, ResourceUID::ID>::ConstIterator I = p_uids.find(old_path); + if (I) { + ResourceUID::get_singleton()->set_id(I->value, new_path); + } + + ScriptServer::remove_global_class_by_path(old_path); + + int index = -1; + EditorFileSystemDirectory *efd = EditorFileSystem::get_singleton()->find_file(old_path, &index); + + if (!efd || index < 0) { + // The file was removed. + continue; + } + + // Update paths for global classes. + if (!efd->get_file_script_class_name(index).is_empty()) { + String lang; + for (int i = 0; i < ScriptServer::get_language_count(); i++) { + if (ScriptServer::get_language(i)->handles_global_class_type(efd->get_file_type(index))) { + lang = ScriptServer::get_language(i)->get_name(); + break; + } + } + if (lang.is_empty()) { + continue; // No language found that can handle this global class. + } + + ScriptServer::add_global_class(efd->get_file_script_class_name(index), efd->get_file_script_class_extends(index), lang, new_path); + EditorNode::get_editor_data().script_class_set_icon_path(efd->get_file_script_class_name(index), efd->get_file_script_class_icon_path(index)); + EditorNode::get_editor_data().script_class_set_name(new_path, efd->get_file_script_class_name(index)); } } @@ -1583,10 +1615,13 @@ void FileSystemDock::_update_resource_paths_after_move(const HashMap<String, Str if (p_renames.has(base_path)) { base_path = p_renames[base_path]; + r->set_path(base_path + extra_path); } - - r->set_path(base_path + extra_path); } + + ScriptServer::save_global_classes(); + EditorNode::get_editor_data().script_class_save_icon_paths(); + EditorFileSystem::get_singleton()->emit_signal(SNAME("script_classes_updated")); } void FileSystemDock::_update_dependencies_after_move(const HashMap<String, String> &p_renames, const HashSet<String> &p_file_owners) const { @@ -1710,6 +1745,13 @@ void FileSystemDock::_make_scene_confirm() { } void FileSystemDock::_resource_removed(const Ref<Resource> &p_resource) { + const Ref<Script> &scr = p_resource; + if (scr.is_valid()) { + ScriptServer::remove_global_class_by_path(scr->get_path()); + ScriptServer::save_global_classes(); + EditorNode::get_editor_data().script_class_save_icon_paths(); + EditorFileSystem::get_singleton()->emit_signal(SNAME("script_classes_updated")); + } emit_signal(SNAME("resource_removed"), p_resource); } @@ -2757,7 +2799,7 @@ bool FileSystemDock::can_drop_data_fw(const Point2 &p_point, const Variant &p_da String to_dir; bool favorite; _get_drag_target_folder(to_dir, favorite, p_point, p_from); - return !to_dir.is_empty(); + return !favorite; } if (drag_data.has("type") && (String(drag_data["type"]) == "files" || String(drag_data["type"]) == "files_and_dirs")) { @@ -2872,7 +2914,12 @@ void FileSystemDock::drop_data_fw(const Point2 &p_point, const Variant &p_data, Ref<Resource> res = drag_data["resource"]; String to_dir; bool favorite; + tree->set_drop_mode_flags(Tree::DROP_MODE_ON_ITEM); _get_drag_target_folder(to_dir, favorite, p_point, p_from); + if (to_dir.is_empty()) { + to_dir = get_current_directory(); + } + if (res.is_valid() && !to_dir.is_empty()) { EditorNode::get_singleton()->push_item(res.ptr()); EditorNode::get_singleton()->save_resource_as(res, to_dir); @@ -2918,7 +2965,11 @@ void FileSystemDock::drop_data_fw(const Point2 &p_point, const Variant &p_data, if (drag_data.has("type") && String(drag_data["type"]) == "nodes") { String to_dir; bool favorite; + tree->set_drop_mode_flags(Tree::DROP_MODE_ON_ITEM); _get_drag_target_folder(to_dir, favorite, p_point, p_from); + if (to_dir.is_empty()) { + to_dir = get_current_directory(); + } SceneTreeDock::get_singleton()->save_branch_to_file(to_dir); } } @@ -2931,6 +2982,7 @@ void FileSystemDock::_get_drag_target_folder(String &target, bool &target_favori if (p_from == files) { int pos = files->get_item_at_position(p_point, true); if (pos == -1) { + target = get_current_directory(); return; } @@ -3454,37 +3506,41 @@ void FileSystemDock::_tree_gui_input(Ref<InputEvent> p_event) { void FileSystemDock::_file_list_gui_input(Ref<InputEvent> p_event) { Ref<InputEventMouseMotion> mm = p_event; if (mm.is_valid() && holding_branch) { - const int item_idx = files->get_item_at_position(mm->get_position()); + const int item_idx = files->get_item_at_position(mm->get_position(), true); + files->deselect_all(); + String fpath; if (item_idx != -1) { - files->deselect_all(); - String fpath = files->get_item_metadata(item_idx); + fpath = files->get_item_metadata(item_idx); if (fpath.ends_with("/") || fpath == "res://") { files->select(item_idx); } + } else { + fpath = get_current_directory(); + } - TreeItem *deselect_item = tree->get_next_selected(tree->get_root()); - while (deselect_item) { - deselect_item->deselect(0); - deselect_item = tree->get_next_selected(deselect_item); + TreeItem *deselect_item = tree->get_next_selected(tree->get_root()); + while (deselect_item) { + deselect_item->deselect(0); + deselect_item = tree->get_next_selected(deselect_item); + } + + // Try to select the corresponding tree item. + TreeItem *tree_item = (item_idx != -1) ? tree->get_item_with_text(files->get_item_text(item_idx)) : nullptr; + + if (tree_item) { + tree_item->select(0); + } else { + // Find parent folder. + fpath = fpath.substr(0, fpath.rfind("/") + 1); + if (fpath.size() > String("res://").size()) { + fpath = fpath.left(fpath.size() - 2); // Remove last '/'. + const int slash_idx = fpath.rfind("/"); + fpath = fpath.substr(slash_idx + 1, fpath.size() - slash_idx - 1); } - // Try to select the corresponding tree item. - TreeItem *tree_item = tree->get_item_with_text(files->get_item_text(item_idx)); + tree_item = tree->get_item_with_text(fpath); if (tree_item) { tree_item->select(0); - } else { - // Find parent folder. - fpath = fpath.substr(0, fpath.rfind("/") + 1); - if (fpath.size() > String("res://").size()) { - fpath = fpath.left(fpath.size() - 2); // Remove last '/'. - const int slash_idx = fpath.rfind("/"); - fpath = fpath.substr(slash_idx + 1, fpath.size() - slash_idx - 1); - } - - tree_item = tree->get_item_with_text(fpath); - if (tree_item) { - tree_item->select(0); - } } } } diff --git a/editor/groups_editor.cpp b/editor/groups_editor.cpp index bec13b710d..c4e38b327a 100644 --- a/editor/groups_editor.cpp +++ b/editor/groups_editor.cpp @@ -157,10 +157,14 @@ void GroupsEditor::_update_groups() { _load_scene_groups(scene_root_node); - for (const KeyValue<StringName, bool> &E : scene_groups) { - if (global_groups.has(E.key)) { - scene_groups.erase(E.key); + for (HashMap<StringName, bool>::Iterator E = scene_groups.begin(); E;) { + HashMap<StringName, bool>::Iterator next = E; + ++next; + + if (global_groups.has(E->key)) { + scene_groups.erase(E->key); } + E = next; } updating_groups = false; diff --git a/editor/gui/scene_tree_editor.cpp b/editor/gui/scene_tree_editor.cpp index 0dd75ea033..221061f9f7 100644 --- a/editor/gui/scene_tree_editor.cpp +++ b/editor/gui/scene_tree_editor.cpp @@ -897,6 +897,14 @@ void SceneTreeEditor::_cell_multi_selected(Object *p_object, int p_cell, bool p_ } } +void SceneTreeEditor::_tree_scroll_to_item(ObjectID p_item_id) { + ERR_FAIL_NULL(tree); + TreeItem *item = Object::cast_to<TreeItem>(ObjectDB::get_instance(p_item_id)); + if (item) { + tree->scroll_to_item(item, true); + } +} + void SceneTreeEditor::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { @@ -942,7 +950,8 @@ void SceneTreeEditor::_notification(int p_what) { if (item) { // Must wait until tree is properly sized before scrolling. - callable_mp(tree, &Tree::scroll_to_item).call_deferred(item, true); + ObjectID item_id = item->get_instance_id(); + callable_mp(this, &SceneTreeEditor::_tree_scroll_to_item).call_deferred(item_id); } } } break; diff --git a/editor/gui/scene_tree_editor.h b/editor/gui/scene_tree_editor.h index c1abdcef8b..b4d9644f16 100644 --- a/editor/gui/scene_tree_editor.h +++ b/editor/gui/scene_tree_editor.h @@ -123,6 +123,7 @@ class SceneTreeEditor : public Control { void _set_item_custom_color(TreeItem *p_item, Color p_color); void _update_node_tooltip(Node *p_node, TreeItem *p_item); void _queue_update_node_tooltip(Node *p_node, TreeItem *p_item); + void _tree_scroll_to_item(ObjectID p_item_id); void _selection_changed(); Node *get_scene_node() const; diff --git a/editor/icons/BoneAttachment3D.svg b/editor/icons/BoneAttachment3D.svg index 2cc0adb906..ec515bc423 100644 --- a/editor/icons/BoneAttachment3D.svg +++ b/editor/icons/BoneAttachment3D.svg @@ -1 +1,20 @@ -<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M4.824 8.384a2.466 2.466 0 1 0-1.705 4.496 2.466 2.466 0 1 0 4.496-1.705l3.56-3.56a2.466 2.466 0 1 0 1.705-4.496 2.466 2.466 0 1 0-4.496 1.705z" fill="#fc7f7f"/></svg> +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 15.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<svg version="1.1" id="レイヤー_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" + y="0px" width="16px" height="16px" viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve"> +<path fill="#FC7F7F" d="M9.264,8.125L8.119,9.27c-0.219,0.228-0.212,0.589,0.014,0.81c0.222,0.213,0.574,0.213,0.794,0l0.169-0.168 + v4.912h4.909l-0.167,0.165c-0.219,0.229-0.212,0.591,0.015,0.81c0.222,0.214,0.573,0.214,0.794,0l1.145-1.145 + c0.224-0.224,0.224-0.584,0-0.809l-1.145-1.143c-0.226-0.221-0.588-0.215-0.809,0.014c-0.213,0.221-0.213,0.573,0,0.795l0.167,0.168 + h-2.957l3.195-3.196v0.237c0,0.317,0.256,0.573,0.572,0.573c0.315,0,0.571-0.256,0.571-0.573V9.102c0-0.315-0.256-0.571-0.571-0.571 + h-1.619c-0.316,0-0.572,0.256-0.572,0.571c0,0.316,0.256,0.572,0.572,0.572h0.237l-3.194,3.194V9.911l0.167,0.168 + c0.228,0.218,0.59,0.213,0.81-0.015c0.214-0.223,0.214-0.572,0-0.795l-1.144-1.145C9.848,7.903,9.486,7.903,9.264,8.125L9.264,8.125 + z"/> +<path fill="#FC7F7F" d="M7.615,11.175l0.326-0.326c-0.119-0.06-0.23-0.135-0.328-0.229c-0.524-0.511-0.538-1.349-0.035-1.871 + l1.155-1.155c0.5-0.499,1.367-0.497,1.865-0.003l0.3,0.3l0.276-0.276c0.399,0.266,0.849,0.393,1.296,0.405 + c0.211-0.142,0.453-0.24,0.726-0.24h0.399c0.391-0.186,0.741-0.467,0.998-0.854c0.754-1.134,0.446-2.665-0.688-3.419 + c-0.309-0.205-0.66-0.338-1.026-0.389c-0.188-1.349-1.433-2.291-2.782-2.103c-1.349,0.188-2.29,1.433-2.103,2.782 + c0.051,0.367,0.184,0.717,0.389,1.026l-3.56,3.56C3.69,7.63,2.159,7.938,1.405,9.072c-0.754,1.134-0.446,2.664,0.688,3.419 + c0.308,0.204,0.659,0.338,1.026,0.389c0.188,1.349,1.433,2.29,2.782,2.103c1.349-0.188,2.291-1.433,2.103-2.781 + C7.953,11.834,7.82,11.483,7.615,11.175z"/> +</svg> diff --git a/editor/icons/PhysicalBoneSimulator3D.svg b/editor/icons/PhysicalBoneSimulator3D.svg new file mode 100644 index 0000000000..583c377328 --- /dev/null +++ b/editor/icons/PhysicalBoneSimulator3D.svg @@ -0,0 +1 @@ +<svg enable-background="new 0 0 16 16" height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#fc7f7f"><path d="m15.209 5.934c-.018.574-.181 1.149-.521 1.662-.594.891-1.588 1.424-2.661 1.424-.343 0-.681-.055-1.002-.162l-2.169 2.169c.061.185.104.374.132.565.117.844-.102 1.684-.616 2.363-.515.681-1.263 1.12-2.107 1.236-.113.016-.224.02-.335.023.469.557 1.201.872 1.975.765 1.2-.167 2.036-1.274 1.87-2.473-.045-.326-.163-.639-.346-.913l3.164-3.163c1.008.67 2.369.396 3.039-.612.625-.939.422-2.176-.423-2.884z"/><path d="m13.452 4.173c-.005.599-.173 1.204-.529 1.738-.593.893-1.588 1.426-2.66 1.426-.344 0-.682-.055-1.003-.162l-2.169 2.169c.062.185.105.374.132.565.118.844-.101 1.684-.615 2.363-.514.681-1.262 1.12-2.107 1.237-.09.013-.179.014-.269.019.47.5 1.165.774 1.896.673 1.2-.166 2.037-1.274 1.87-2.472-.045-.327-.164-.64-.346-.913l3.165-3.164c1.008.67 2.368.396 3.039-.612.619-.931.423-2.157-.404-2.867z"/><path d="m8.432.09c-.651-.004-1.27.283-1.689.78l1.397 2.02-1.57.6-3.164 3.163c-1.008-.67-2.369-.396-3.039.612-.67 1.007-.396 2.369.611 3.039.274.183.586.301.912.346.167 1.199 1.274 2.036 2.473 1.87 1.199-.167 2.036-1.274 1.869-2.473-.044-.326-.163-.639-.345-.913l3.164-3.164c1.008.67 2.369.396 3.039-.612.671-1.008.397-2.368-.61-3.039-.274-.182-.587-.3-.913-.345-.152-1.068-1.056-1.867-2.135-1.884zm-8.285 1.573.854 4.291 1.242-.467zm2.457-1.549.854 4.291 1.241-.466zm2.854-.114.632 3.124 1.242-.466z"/></g></svg>
\ No newline at end of file diff --git a/editor/icons/SkeletonModifier3D.svg b/editor/icons/SkeletonModifier3D.svg new file mode 100644 index 0000000000..e3505a5b8b --- /dev/null +++ b/editor/icons/SkeletonModifier3D.svg @@ -0,0 +1 @@ +<svg enable-background="new 0 0 16 16" height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#b56d6d"><path d="m8.445 9.308v-3.001c1.613-.944 2.156-3.016 1.213-4.631-.603-1.033-1.709-1.67-2.905-1.676h-3.385c-1.869.008-3.377 1.532-3.368 3.401.005 1.197.643 2.301 1.676 2.906v3.001c0 .934.758 1.692 1.692 1.692h3.385c.935 0 1.692-.758 1.692-1.692zm-4.23-5.077h1.692v.846h-1.692zm-2.539-.846c0-.468.378-.846.847-.846.468 0 .846.378.846.846 0 .467-.378.846-.846.846-.469 0-.847-.379-.847-.846zm5.923 5.923h-.846v-.847h-.846v.846h-1.692v-.846h-.847v.846h-.846v-3.384h.846v.846h.847v-.846h1.692v.846h.846v-.846h.846zm-.846-5.923c0-.468.378-.846.846-.846s.846.378.846.846c0 .467-.378.846-.846.846s-.846-.379-.846-.846z"/><path d="m15.294 8.617c-.198-.132-.424-.219-.658-.251-.122-.867-.921-1.471-1.787-1.351s-1.472.921-1.353 1.788c.034.235.119.46.251.658l-2.286 2.286c-.729-.484-1.712-.287-2.195.443-.486.727-.287 1.71.441 2.194.198.131.423.217.659.25.121.865.922 1.471 1.787 1.35.866-.12 1.471-.919 1.351-1.786-.034-.235-.118-.46-.25-.658l2.286-2.286c.728.483 1.712.285 2.195-.443.484-.727.287-1.711-.441-2.194z"/></g></svg>
\ No newline at end of file diff --git a/editor/icons/XRBodyModifier3D.svg b/editor/icons/XRBodyModifier3D.svg new file mode 100644 index 0000000000..7e3fcff808 --- /dev/null +++ b/editor/icons/XRBodyModifier3D.svg @@ -0,0 +1 @@ +<svg enable-background="new 0 0 16 16" height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#fc7f7f"><path d="m4 12v1c0 .553.448 1 1 1-.552 0-1 .447-1 1v1h1v-1h1v1h1v-1c0-.553-.448-1-1-1 .552 0 1-.447 1-1v-1h-1v1h-1v-1zm5 0v4h1v-1h1v1h1v-1c-.001-.176-.049-.348-.137-.5.088-.152.136-.324.137-.5v-1c0-.553-.447-1-1-1zm1 1h1v1h-1z"/><path d="m12.091 5.286h-3.384v-.715h.722c.396 0 .714-.319.714-.714v-2.143c0-.394-.319-.714-.714-.714h-2.857c-.395 0-.715.32-.715.714v2.143c0 .395.319.714.715.714h.707v.707l-3.279.008c-.395 0-.714.32-.714.714 0 .395.319.714.714.714h2.04l-.005 3.573c0 .395.32.713.715.713.394 0 .714-.318.714-.713v-1.662h1.073v1.662c0 .395.321.713.716.713.393 0 .713-.318.713-.713v-3.573h2.125c.395 0 .715-.32.715-.714s-.321-.714-.715-.714zm-3.384-3.027h.8v1.598h-.8zm-2.228 1.598v-1.598h.8v1.598z"/></g></svg> diff --git a/editor/icons/XRHandModifier3D.svg b/editor/icons/XRHandModifier3D.svg new file mode 100644 index 0000000000..60578acb4f --- /dev/null +++ b/editor/icons/XRHandModifier3D.svg @@ -0,0 +1 @@ +<svg enable-background="new 0 0 16 16" height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#fc7f7f"><path d="m4 12v1c0 .553.448 1 1 1-.552 0-1 .447-1 1v1h1v-1h1v1h1v-1c0-.553-.448-1-1-1 .552 0 1-.447 1-1v-1h-1v1h-1v-1zm5 0v4h1v-1h1v1h1v-1c-.001-.176-.049-.348-.137-.5.088-.152.136-.324.137-.5v-1c0-.553-.447-1-1-1zm1 1h1v1h-1z"/><path d="m12.375 4.948c-.38-.186-.839-.028-1.025.353l-.916 1.88.688-3.795c.076-.414-.201-.813-.617-.89-.417-.075-.814.2-.891.616l-.575 3.166-.091-.641v-3.869c0-.426-.343-.768-.767-.768s-.766.342-.766.768v3.869 1.146l-.666-3.671c-.076-.416-.475-.693-.891-.617-.417.076-.693.475-.617.89l.745 4.111-1.254-.902c-.37-.344-.949-.322-1.293.047-.344.371-.323.949.048 1.293l3.161 3.066h3.065c.551 0 1.202-1.314 1.202-1.314l1.813-3.712c.185-.383.028-.841-.353-1.026z"/></g></svg> diff --git a/editor/import/resource_importer_csv_translation.cpp b/editor/import/resource_importer_csv_translation.cpp index d56b426c86..d2705ac98a 100644 --- a/editor/import/resource_importer_csv_translation.cpp +++ b/editor/import/resource_importer_csv_translation.cpp @@ -122,11 +122,12 @@ Error ResourceImporterCSVTranslation::import(const String &p_source_file, const if (!key.is_empty()) { ERR_CONTINUE_MSG(line.size() != locales.size() + (int)skipped_locales.size() + 1, vformat("Error importing CSV translation: expected %d locale(s), but the '%s' key has %d locale(s).", locales.size(), key, line.size() - 1)); + int write_index = 0; // Keep track of translations written in case some locales are skipped. for (int i = 1; i < line.size(); i++) { if (skipped_locales.has(i)) { continue; } - translations.write[i - 1]->add_message(key, line[i].c_unescape()); + translations.write[write_index++]->add_message(key, line[i].c_unescape()); } } } while (!f->eof_reached()); diff --git a/editor/plugins/animation_blend_tree_editor_plugin.cpp b/editor/plugins/animation_blend_tree_editor_plugin.cpp index a7c64b79db..cb4963dacf 100644 --- a/editor/plugins/animation_blend_tree_editor_plugin.cpp +++ b/editor/plugins/animation_blend_tree_editor_plugin.cpp @@ -850,7 +850,7 @@ bool AnimationNodeBlendTreeEditor::_update_filters(const Ref<AnimationNode> &ano ti->set_text(0, F->get()); ti->set_selectable(0, false); ti->set_editable(0, false); - ti->set_icon(0, get_editor_theme_icon(SNAME("BoneAttachment3D"))); + ti->set_icon(0, get_editor_theme_icon(SNAME("Bone"))); } else { ti = parenthood[accum]; } @@ -861,7 +861,7 @@ bool AnimationNodeBlendTreeEditor::_update_filters(const Ref<AnimationNode> &ano ti->set_cell_mode(0, TreeItem::CELL_MODE_CHECK); ti->set_text(0, concat); ti->set_checked(0, anode->is_path_filtered(path)); - ti->set_icon(0, get_editor_theme_icon(SNAME("BoneAttachment3D"))); + ti->set_icon(0, get_editor_theme_icon(SNAME("Bone"))); ti->set_metadata(0, path); } else { diff --git a/editor/plugins/bone_map_editor_plugin.cpp b/editor/plugins/bone_map_editor_plugin.cpp index 848989d8e9..e67f3bd596 100644 --- a/editor/plugins/bone_map_editor_plugin.cpp +++ b/editor/plugins/bone_map_editor_plugin.cpp @@ -196,7 +196,7 @@ void BonePicker::create_bones_tree(Skeleton3D *p_skeleton) { items.insert(-1, root); - Ref<Texture> bone_icon = get_editor_theme_icon(SNAME("BoneAttachment3D")); + Ref<Texture> bone_icon = get_editor_theme_icon(SNAME("Bone")); Vector<int> bones_to_process = p_skeleton->get_parentless_bones(); bool is_first = true; @@ -421,7 +421,7 @@ void BoneMapper::recreate_editor() { for (int i = 0; i < len; i++) { if (profile->get_group(i) == profile->get_group_name(current_group_idx)) { - BoneMapperButton *mb = memnew(BoneMapperButton(profile->get_bone_name(i), profile->is_require(i), current_bone_idx == i)); + BoneMapperButton *mb = memnew(BoneMapperButton(profile->get_bone_name(i), profile->is_required(i), current_bone_idx == i)); mb->connect("pressed", callable_mp(this, &BoneMapper::set_current_bone_idx).bind(i), CONNECT_DEFERRED); mb->set_h_grow_direction(GROW_DIRECTION_BOTH); mb->set_v_grow_direction(GROW_DIRECTION_BOTH); diff --git a/editor/plugins/gizmos/physics_bone_3d_gizmo_plugin.cpp b/editor/plugins/gizmos/physics_bone_3d_gizmo_plugin.cpp index b15f9bec3f..94d6be22f4 100644 --- a/editor/plugins/gizmos/physics_bone_3d_gizmo_plugin.cpp +++ b/editor/plugins/gizmos/physics_bone_3d_gizmo_plugin.cpp @@ -61,17 +61,17 @@ void PhysicalBone3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { return; } - Skeleton3D *sk(physical_bone->find_skeleton_parent()); - if (!sk) { + PhysicalBoneSimulator3D *sm(physical_bone->get_simulator()); + if (!sm) { return; } - PhysicalBone3D *pb(sk->get_physical_bone(physical_bone->get_bone_id())); + PhysicalBone3D *pb(sm->get_physical_bone(physical_bone->get_bone_id())); if (!pb) { return; } - PhysicalBone3D *pbp(sk->get_physical_bone_parent(physical_bone->get_bone_id())); + PhysicalBone3D *pbp(sm->get_physical_bone_parent(physical_bone->get_bone_id())); if (!pbp) { return; } diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp index 7e271bb68e..d09e4fd3db 100644 --- a/editor/plugins/node_3d_editor_plugin.cpp +++ b/editor/plugins/node_3d_editor_plugin.cpp @@ -4519,7 +4519,7 @@ bool Node3DEditorViewport::can_drop_data_fw(const Point2 &p_point, const Variant Ref<BaseMaterial3D> base_mat = res; Ref<ShaderMaterial> shader_mat = res; - if (base_mat.is_null() && !shader_mat.is_null()) { + if (base_mat.is_null() && shader_mat.is_null()) { continue; } diff --git a/editor/plugins/root_motion_editor_plugin.cpp b/editor/plugins/root_motion_editor_plugin.cpp index 30fb731fc8..f6a5c58acf 100644 --- a/editor/plugins/root_motion_editor_plugin.cpp +++ b/editor/plugins/root_motion_editor_plugin.cpp @@ -129,7 +129,7 @@ void EditorPropertyRootMotion::_node_assign() { if (skeleton) { HashMap<int, TreeItem *> items; items.insert(-1, ti); - Ref<Texture> bone_icon = get_editor_theme_icon(SNAME("BoneAttachment3D")); + Ref<Texture> bone_icon = get_editor_theme_icon(SNAME("Bone")); Vector<int> bones_to_process = skeleton->get_parentless_bones(); while (bones_to_process.size() > 0) { int current_bone_idx = bones_to_process[0]; diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index eeb84cb79e..6c63d9ff0d 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -137,12 +137,22 @@ void EditorStandardSyntaxHighlighter::_update_cache() { } } - const Ref<Script> scr = _get_edited_resource(); - if (scr.is_valid()) { + const ScriptLanguage *scr_lang = script_language; + StringName instance_base; + + if (scr_lang == nullptr) { + const Ref<Script> scr = _get_edited_resource(); + if (scr.is_valid()) { + scr_lang = scr->get_language(); + instance_base = scr->get_instance_base_type(); + } + } + + if (scr_lang != nullptr) { /* Core types. */ const Color basetype_color = EDITOR_GET("text_editor/theme/highlighting/base_type_color"); List<String> core_types; - scr->get_language()->get_core_type_words(&core_types); + scr_lang->get_core_type_words(&core_types); for (const String &E : core_types) { highlighter->add_keyword_color(E, basetype_color); } @@ -151,9 +161,9 @@ void EditorStandardSyntaxHighlighter::_update_cache() { const Color keyword_color = EDITOR_GET("text_editor/theme/highlighting/keyword_color"); const Color control_flow_keyword_color = EDITOR_GET("text_editor/theme/highlighting/control_flow_keyword_color"); List<String> keywords; - scr->get_language()->get_reserved_words(&keywords); + scr_lang->get_reserved_words(&keywords); for (const String &E : keywords) { - if (scr->get_language()->is_control_flow_keyword(E)) { + if (scr_lang->is_control_flow_keyword(E)) { highlighter->add_keyword_color(E, control_flow_keyword_color); } else { highlighter->add_keyword_color(E, keyword_color); @@ -162,7 +172,6 @@ void EditorStandardSyntaxHighlighter::_update_cache() { /* Member types. */ const Color member_variable_color = EDITOR_GET("text_editor/theme/highlighting/member_variable_color"); - StringName instance_base = scr->get_instance_base_type(); if (instance_base != StringName()) { List<PropertyInfo> plist; ClassDB::get_property_list(instance_base, &plist); @@ -187,7 +196,7 @@ void EditorStandardSyntaxHighlighter::_update_cache() { /* Comments */ const Color comment_color = EDITOR_GET("text_editor/theme/highlighting/comment_color"); List<String> comments; - scr->get_language()->get_comment_delimiters(&comments); + scr_lang->get_comment_delimiters(&comments); for (const String &comment : comments) { String beg = comment.get_slice(" ", 0); String end = comment.get_slice_count(" ") > 1 ? comment.get_slice(" ", 1) : String(); @@ -197,7 +206,7 @@ void EditorStandardSyntaxHighlighter::_update_cache() { /* Doc comments */ const Color doc_comment_color = EDITOR_GET("text_editor/theme/highlighting/doc_comment_color"); List<String> doc_comments; - scr->get_language()->get_doc_comment_delimiters(&doc_comments); + scr_lang->get_doc_comment_delimiters(&doc_comments); for (const String &doc_comment : doc_comments) { String beg = doc_comment.get_slice(" ", 0); String end = doc_comment.get_slice_count(" ") > 1 ? doc_comment.get_slice(" ", 1) : String(); @@ -207,7 +216,7 @@ void EditorStandardSyntaxHighlighter::_update_cache() { /* Strings */ const Color string_color = EDITOR_GET("text_editor/theme/highlighting/string_color"); List<String> strings; - scr->get_language()->get_string_delimiters(&strings); + scr_lang->get_string_delimiters(&strings); for (const String &string : strings) { String beg = string.get_slice(" ", 0); String end = string.get_slice_count(" ") > 1 ? string.get_slice(" ", 1) : String(); diff --git a/editor/plugins/script_editor_plugin.h b/editor/plugins/script_editor_plugin.h index 4fff724866..0252e10b43 100644 --- a/editor/plugins/script_editor_plugin.h +++ b/editor/plugins/script_editor_plugin.h @@ -78,6 +78,7 @@ class EditorStandardSyntaxHighlighter : public EditorSyntaxHighlighter { private: Ref<CodeHighlighter> highlighter; + ScriptLanguage *script_language = nullptr; // See GH-89610. public: virtual void _update_cache() override; @@ -87,6 +88,8 @@ public: virtual Ref<EditorSyntaxHighlighter> _create() const override; + void _set_script_language(ScriptLanguage *p_script_language) { script_language = p_script_language; } + EditorStandardSyntaxHighlighter() { highlighter.instantiate(); } }; diff --git a/editor/plugins/skeleton_3d_editor_plugin.cpp b/editor/plugins/skeleton_3d_editor_plugin.cpp index e0c5f9e596..1bd6b4e5b1 100644 --- a/editor/plugins/skeleton_3d_editor_plugin.cpp +++ b/editor/plugins/skeleton_3d_editor_plugin.cpp @@ -377,6 +377,11 @@ void Skeleton3DEditor::create_physical_skeleton() { bones_infos.resize(bone_count); ur->create_action(TTR("Create physical bones"), UndoRedo::MERGE_ALL); + + PhysicalBoneSimulator3D *simulator = memnew(PhysicalBoneSimulator3D); + ur->add_do_method(skeleton, "add_child", simulator); + ur->add_do_method(simulator, "set_owner", owner); + ur->add_do_method(simulator, "set_name", "PhysicalBoneSimulator3D"); for (int bone_id = 0; bone_count > bone_id; ++bone_id) { const int parent = skeleton->get_bone_parent(bone_id); @@ -395,7 +400,7 @@ void Skeleton3DEditor::create_physical_skeleton() { if (collision_shape) { bones_infos.write[parent].physical_bone = physical_bone; - ur->add_do_method(skeleton, "add_child", physical_bone); + ur->add_do_method(simulator, "add_child", physical_bone); ur->add_do_method(physical_bone, "set_owner", owner); ur->add_do_method(collision_shape, "set_owner", owner); ur->add_do_property(physical_bone, "bone_name", skeleton->get_bone_name(parent)); @@ -409,12 +414,13 @@ void Skeleton3DEditor::create_physical_skeleton() { ur->add_do_method(Node3DEditor::get_singleton(), SceneStringNames::get_singleton()->_request_gizmo, collision_shape); ur->add_do_reference(physical_bone); - ur->add_undo_method(skeleton, "remove_child", physical_bone); + ur->add_undo_method(simulator, "remove_child", physical_bone); } } } } } + ur->add_undo_method(skeleton, "remove_child", simulator); ur->commit_action(); } @@ -670,7 +676,7 @@ void Skeleton3DEditor::update_joint_tree() { items.insert(-1, root); - Ref<Texture> bone_icon = get_editor_theme_icon(SNAME("BoneAttachment3D")); + Ref<Texture> bone_icon = get_editor_theme_icon(SNAME("Bone")); Vector<int> bones_to_process = skeleton->get_parentless_bones(); while (bones_to_process.size() > 0) { diff --git a/main/main.cpp b/main/main.cpp index cf40f764cf..6a132a37ae 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -347,7 +347,6 @@ void initialize_navigation_server() { // Fall back to dummy if no default server has been registered. if (!navigation_server_3d) { - WARN_PRINT_ONCE("No NavigationServer3D implementation has been registered! Falling back to a dummy implementation: navigation features will be unavailable."); navigation_server_3d = memnew(NavigationServer3DDummy); } @@ -358,7 +357,6 @@ void initialize_navigation_server() { // Init 2D Navigation Server navigation_server_2d = NavigationServer2DManager::new_default_server(); if (!navigation_server_2d) { - WARN_PRINT_ONCE("No NavigationServer2D implementation has been registered! Falling back to a dummy implementation: navigation features will be unavailable."); navigation_server_2d = memnew(NavigationServer2DDummy); } diff --git a/methods.py b/methods.py index 7af621befc..fa7db95e92 100644 --- a/methods.py +++ b/methods.py @@ -228,7 +228,32 @@ def get_version_info(module_version_string="", silent=False): return version_info +_cleanup_env = None +_cleanup_bool = False + + def write_file_if_needed(path, string): + """Generates a file only if it doesn't already exist or the content has changed. + + Utilizes a dedicated SCons environment to ensure the files are properly removed + during cleanup; will not attempt to create files during cleanup. + + - `path` - Path to the file in question; used to create cleanup logic. + - `string` - Content to compare against an existing file. + """ + global _cleanup_env + global _cleanup_bool + + if _cleanup_env is None: + from SCons.Environment import Environment + + _cleanup_env = Environment() + _cleanup_bool = _cleanup_env.GetOption("clean") + + _cleanup_env.Clean("#", path) + if _cleanup_bool: + return + try: with open(path, "r", encoding="utf-8", newline="\n") as f: if f.read() == string: @@ -1427,14 +1452,18 @@ def generate_vs_project(env, original_args, project_name="godot"): props_template = props_template.replace("%%OUTPUT%%", output) - props_template = props_template.replace( - "%%DEFINES%%", ";".join([format_key_value(v) for v in list(env["CPPDEFINES"])]) - ) - props_template = props_template.replace("%%INCLUDES%%", ";".join([str(j) for j in env["CPPPATH"]])) - props_template = props_template.replace( - "%%OPTIONS%%", - " ".join(env["CCFLAGS"]) + " " + " ".join([x for x in env["CXXFLAGS"] if not x.startswith("$")]), - ) + proplist = [format_key_value(v) for v in list(env["CPPDEFINES"])] + proplist += [format_key_value(j) for j in env.get("VSHINT_DEFINES", [])] + props_template = props_template.replace("%%DEFINES%%", ";".join(proplist)) + + proplist = [str(j) for j in env["CPPPATH"]] + proplist += [str(j) for j in env.get("VSHINT_INCLUDES", [])] + props_template = props_template.replace("%%INCLUDES%%", ";".join(proplist)) + + proplist = env["CCFLAGS"] + proplist += [x for x in env["CXXFLAGS"] if not x.startswith("$")] + proplist += [str(j) for j in env.get("VSHINT_OPTIONS", [])] + props_template = props_template.replace("%%OPTIONS%%", " ".join(proplist)) # Windows allows us to have spaces in paths, so we need # to double quote off the directory. However, the path ends diff --git a/misc/extension_api_validation/4.2-stable.expected b/misc/extension_api_validation/4.2-stable.expected index 2416f05ee4..bb10b422dc 100644 --- a/misc/extension_api_validation/4.2-stable.expected +++ b/misc/extension_api_validation/4.2-stable.expected @@ -266,3 +266,16 @@ GH-88014 Validate extension JSON: API was removed: classes/VisualShaderNodeComment Removed VisualShaderNodeComment, which is replaced by VisualShaderNodeFrame. + + +GH-87888 +-------- +Validate extension JSON: API was removed: classes/OpenXRHand/methods/get_hand_skeleton +Validate extension JSON: API was removed: classes/OpenXRHand/methods/set_hand_skeleton +Validate extension JSON: API was removed: classes/OpenXRHand/properties/hand_skeleton +Validate extension JSON: API was removed: classes/Skeleton3D/properties/animate_physical_bones +Validate extension JSON: API was removed: classes/SkeletonIK3D/methods/get_interpolation +Validate extension JSON: API was removed: classes/SkeletonIK3D/methods/set_interpolation +Validate extension JSON: API was removed: classes/SkeletonIK3D/properties/interpolation + +These base class is changed to SkeletonModifier3D which is processed by Skeleton3D with the assumption that it is Skeleton3D's child. diff --git a/misc/msvs/props.template b/misc/msvs/props.template index 8facaf7f36..f360871b72 100644 --- a/misc/msvs/props.template +++ b/misc/msvs/props.template @@ -4,13 +4,13 @@ <NMakeBuildCommandLine>%%BUILD%%</NMakeBuildCommandLine> <NMakeReBuildCommandLine>%%REBUILD%%</NMakeReBuildCommandLine> <NMakeCleanCommandLine>%%CLEAN%%</NMakeCleanCommandLine> - <NMakeOutput>%%OUTPUT%%</NMakeOutput> - <NMakePreprocessorDefinitions>%%DEFINES%%</NMakePreprocessorDefinitions> - <NMakeIncludeSearchPath>%%INCLUDES%%</NMakeIncludeSearchPath> + <NMakeOutput Condition="'$(NMakeOutput)' == ''">%%OUTPUT%%</NMakeOutput> + <NMakePreprocessorDefinitions>%%DEFINES%%;$(NMakePreprocessorDefinitions)</NMakePreprocessorDefinitions> + <NMakeIncludeSearchPath>%%INCLUDES%%;$(NMakeIncludeSearchPath)</NMakeIncludeSearchPath> <NMakeForcedIncludes>$(NMakeForcedIncludes)</NMakeForcedIncludes> <NMakeAssemblySearchPath>$(NMakeAssemblySearchPath)</NMakeAssemblySearchPath> <NMakeForcedUsingAssemblies>$(NMakeForcedUsingAssemblies)</NMakeForcedUsingAssemblies> - <AdditionalOptions>%%OPTIONS%%</AdditionalOptions> + <AdditionalOptions>%%OPTIONS%% $(AdditionalOptions)</AdditionalOptions> </PropertyGroup> <PropertyGroup Condition="%%CONDITION%%"> %%PROPERTIES%% diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml index 4869573972..07d917ea04 100644 --- a/modules/gdscript/doc_classes/@GDScript.xml +++ b/modules/gdscript/doc_classes/@GDScript.xml @@ -168,7 +168,7 @@ # Load a scene called "main" located in the root of the project directory and cache it in a variable. var main = load("res://main.tscn") # main will contain a PackedScene resource. [/codeblock] - [b]Important:[/b] The path must be absolute. A relative path will always return [code]null[/code]. + [b]Important:[/b] Relative paths are [i]not[/i] relative to the script calling this method, instead it is prefixed with [code]"res://"[/code]. Loading from relative paths might not work as expected. This function is a simplified version of [method ResourceLoader.load], which can be used for more advanced scenarios. [b]Note:[/b] Files have to be imported into the engine first to load them using this function. If you want to load [Image]s at run-time, you may use [method Image.load]. If you want to import audio files, you can use the snippet described in [member AudioStreamMP3.data]. [b]Note:[/b] If [member ProjectSettings.editor/export/convert_text_resources_to_binary] is [code]true[/code], [method @GDScript.load] will not be able to read converted files in an exported project. If you rely on run-time loading of files present within the PCK, set [member ProjectSettings.editor/export/convert_text_resources_to_binary] to [code]false[/code]. diff --git a/modules/gdscript/editor/gdscript_highlighter.cpp b/modules/gdscript/editor/gdscript_highlighter.cpp index 439555bacb..b4f4e879d0 100644 --- a/modules/gdscript/editor/gdscript_highlighter.cpp +++ b/modules/gdscript/editor/gdscript_highlighter.cpp @@ -65,8 +65,10 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l bool in_function_name = false; // Any call. bool in_function_declaration = false; // Only declaration. - bool in_var_const_declaration = false; bool in_signal_declaration = false; + bool is_after_func_signal_declaration = false; + bool in_var_const_declaration = false; + bool is_after_var_const_declaration = false; bool expect_type = false; int in_declaration_params = 0; // The number of opened `(` after func/signal name. @@ -410,6 +412,8 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l col = class_names[word]; } else if (reserved_keywords.has(word)) { col = reserved_keywords[word]; + // Don't highlight `list` as a type in `for elem: Type in list`. + expect_type = false; } else if (member_keywords.has(word)) { col = member_keywords[word]; } @@ -480,6 +484,13 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l } if (is_a_symbol) { + if (in_function_declaration || in_signal_declaration) { + is_after_func_signal_declaration = true; + } + if (in_var_const_declaration) { + is_after_var_const_declaration = true; + } + if (in_declaration_params > 0) { switch (str[j]) { case '(': @@ -495,7 +506,7 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l in_declaration_param_dicts -= 1; break; } - } else if ((in_function_declaration || in_signal_declaration || prev_text == GDScriptTokenizer::get_token_name(GDScriptTokenizer::Token::FUNC)) && str[j] == '(') { + } else if ((is_after_func_signal_declaration || prev_text == GDScriptTokenizer::get_token_name(GDScriptTokenizer::Token::FUNC)) && str[j] == '(') { in_declaration_params = 1; in_declaration_param_dicts = 0; } @@ -526,19 +537,22 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l expect_type = true; in_type_params = 0; } - if ((in_var_const_declaration || (in_declaration_params == 1 && in_declaration_param_dicts == 0)) && str[j] == ':') { + if ((is_after_var_const_declaration || (in_declaration_params == 1 && in_declaration_param_dicts == 0)) && str[j] == ':') { expect_type = true; in_type_params = 0; } } + in_function_name = false; + in_function_declaration = false; + in_signal_declaration = false; + in_var_const_declaration = false; + in_lambda = false; + in_member_variable = false; + if (!is_whitespace(str[j])) { - in_function_declaration = false; - in_var_const_declaration = false; - in_signal_declaration = false; - in_function_name = false; - in_lambda = false; - in_member_variable = false; + is_after_func_signal_declaration = false; + is_after_var_const_declaration = false; } } diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index 6af6460b31..f67f4913c3 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -3293,6 +3293,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a if (get_function_signature(p_call, is_constructor, base_type, p_call->function_name, return_type, par_types, default_arg_count, method_flags)) { // If the method is implemented in the class hierarchy, the virtual flag will not be set for that MethodInfo and the search stops there. // Virtual check only possible for super() calls because class hierarchy is known. Node/Objects may have scripts attached we don't know of at compile-time. + p_call->is_static = method_flags.has_flag(METHOD_FLAG_STATIC); if (p_call->is_super && method_flags.has_flag(METHOD_FLAG_VIRTUAL)) { push_error(vformat(R"*(Cannot call the parent class' virtual function "%s()" because it hasn't been defined.)*", p_call->function_name), p_call); } @@ -3311,7 +3312,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a base_type.is_meta_type = false; } - if (is_self && static_context && !method_flags.has_flag(METHOD_FLAG_STATIC)) { + if (is_self && static_context && !p_call->is_static) { // Get the parent function above any lambda. GDScriptParser::FunctionNode *parent_function = parser->current_function; while (parent_function && parent_function->source_lambda) { @@ -3323,10 +3324,10 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a } else { push_error(vformat(R"*(Cannot call non-static function "%s()" from a static variable initializer.)*", p_call->function_name), p_call); } - } else if (!is_self && base_type.is_meta_type && !method_flags.has_flag(METHOD_FLAG_STATIC)) { + } else if (!is_self && base_type.is_meta_type && !p_call->is_static) { base_type.is_meta_type = false; // For `to_string()`. push_error(vformat(R"*(Cannot call non-static function "%s()" on the class "%s" directly. Make an instance instead.)*", p_call->function_name, base_type.to_string()), p_call); - } else if (is_self && !method_flags.has_flag(METHOD_FLAG_STATIC)) { + } else if (is_self && !p_call->is_static) { mark_lambda_use_self(); } diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index 13ed66710c..d706c1b101 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -648,7 +648,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code // Not exact arguments, but still can use method bind call. gen->write_call_method_bind(result, self, method, arguments); } - } else if (codegen.is_static || (codegen.function_node && codegen.function_node->is_static) || call->function_name == "new") { + } else if (call->is_static || codegen.is_static || (codegen.function_node && codegen.function_node->is_static) || call->function_name == "new") { GDScriptCodeGenerator::Address self; self.mode = GDScriptCodeGenerator::Address::CLASS; if (is_awaited) { diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h index ea67f1eaff..d047fa8e46 100644 --- a/modules/gdscript/gdscript_parser.h +++ b/modules/gdscript/gdscript_parser.h @@ -498,6 +498,7 @@ public: Vector<ExpressionNode *> arguments; StringName function_name; bool is_super = false; + bool is_static = false; CallNode() { type = CALL; diff --git a/modules/gltf/doc_classes/GLTFPhysicsBody.xml b/modules/gltf/doc_classes/GLTFPhysicsBody.xml index 5cfc22f6b2..cd701e2f2f 100644 --- a/modules/gltf/doc_classes/GLTFPhysicsBody.xml +++ b/modules/gltf/doc_classes/GLTFPhysicsBody.xml @@ -22,7 +22,7 @@ <return type="GLTFPhysicsBody" /> <param index="0" name="body_node" type="CollisionObject3D" /> <description> - Create a new GLTFPhysicsBody instance from the given Godot [CollisionObject3D] node. + Creates a new GLTFPhysicsBody instance from the given Godot [CollisionObject3D] node. </description> </method> <method name="to_dictionary" qualifiers="const"> diff --git a/modules/gltf/doc_classes/GLTFPhysicsShape.xml b/modules/gltf/doc_classes/GLTFPhysicsShape.xml index c397c660d9..a4aaf9415c 100644 --- a/modules/gltf/doc_classes/GLTFPhysicsShape.xml +++ b/modules/gltf/doc_classes/GLTFPhysicsShape.xml @@ -23,7 +23,14 @@ <return type="GLTFPhysicsShape" /> <param index="0" name="shape_node" type="CollisionShape3D" /> <description> - Create a new GLTFPhysicsShape instance from the given Godot [CollisionShape3D] node. + Creates a new GLTFPhysicsShape instance from the given Godot [CollisionShape3D] node. + </description> + </method> + <method name="from_resource" qualifiers="static"> + <return type="GLTFPhysicsShape" /> + <param index="0" name="shape_resource" type="Shape3D" /> + <description> + Creates a new GLTFPhysicsShape instance from the given Godot [Shape3D] resource. </description> </method> <method name="to_dictionary" qualifiers="const"> @@ -39,6 +46,13 @@ Converts this GLTFPhysicsShape instance into a Godot [CollisionShape3D] node. </description> </method> + <method name="to_resource"> + <return type="Shape3D" /> + <param index="0" name="cache_shapes" type="bool" default="false" /> + <description> + Converts this GLTFPhysicsShape instance into a Godot [Shape3D] resource. + </description> + </method> </methods> <members> <member name="height" type="float" setter="set_height" getter="get_height" default="2.0"> diff --git a/modules/gltf/editor/editor_scene_exporter_gltf_settings.cpp b/modules/gltf/editor/editor_scene_exporter_gltf_settings.cpp index b0283b0c55..d11300343a 100644 --- a/modules/gltf/editor/editor_scene_exporter_gltf_settings.cpp +++ b/modules/gltf/editor/editor_scene_exporter_gltf_settings.cpp @@ -84,6 +84,11 @@ void EditorSceneExporterGLTFSettings::_get_property_list(List<PropertyInfo> *p_l } } +void EditorSceneExporterGLTFSettings::_on_extension_property_list_changed() { + generate_property_list(_document); + emit_signal("property_list_changed"); +} + bool EditorSceneExporterGLTFSettings::_set_extension_setting(const String &p_name_str, const Variant &p_value) { PackedStringArray split = String(p_name_str).split("/", true, 1); if (!_config_name_to_extension_map.has(split[0])) { @@ -130,6 +135,10 @@ void EditorSceneExporterGLTFSettings::generate_property_list(Ref<GLTFDocument> p String image_format_hint_string = "None,PNG,JPEG"; // Add properties from all document extensions. for (Ref<GLTFDocumentExtension> &extension : GLTFDocument::get_all_gltf_document_extensions()) { + const Callable on_prop_changed = callable_mp(this, &EditorSceneExporterGLTFSettings::_on_extension_property_list_changed); + if (!extension->is_connected("property_list_changed", on_prop_changed)) { + extension->connect("property_list_changed", on_prop_changed); + } const String config_prefix = get_friendly_config_prefix(extension); _config_name_to_extension_map[config_prefix] = extension; // If the extension allows saving in different image formats, add to the enum. diff --git a/modules/gltf/editor/editor_scene_exporter_gltf_settings.h b/modules/gltf/editor/editor_scene_exporter_gltf_settings.h index 6032932367..e1ce674274 100644 --- a/modules/gltf/editor/editor_scene_exporter_gltf_settings.h +++ b/modules/gltf/editor/editor_scene_exporter_gltf_settings.h @@ -48,6 +48,7 @@ protected: bool _set(const StringName &p_name, const Variant &p_value); bool _get(const StringName &p_name, Variant &r_ret) const; void _get_property_list(List<PropertyInfo> *p_list) const; + void _on_extension_property_list_changed(); bool _set_extension_setting(const String &p_name_str, const Variant &p_value); bool _get_extension_setting(const String &p_name_str, Variant &r_ret) const; diff --git a/modules/gltf/extensions/physics/gltf_document_extension_physics.cpp b/modules/gltf/extensions/physics/gltf_document_extension_physics.cpp index c6e34cc023..352b439332 100644 --- a/modules/gltf/extensions/physics/gltf_document_extension_physics.cpp +++ b/modules/gltf/extensions/physics/gltf_document_extension_physics.cpp @@ -355,6 +355,7 @@ void GLTFDocumentExtensionPhysics::convert_scene_node(Ref<GLTFState> p_state, Re if (cast_to<CollisionShape3D>(p_scene_node)) { CollisionShape3D *godot_shape = Object::cast_to<CollisionShape3D>(p_scene_node); Ref<GLTFPhysicsShape> gltf_shape = GLTFPhysicsShape::from_node(godot_shape); + ERR_FAIL_COND_MSG(gltf_shape.is_null(), "GLTF Physics: Could not convert CollisionShape3D to GLTFPhysicsShape. Does it have a valid Shape3D?"); { Ref<ImporterMesh> importer_mesh = gltf_shape->get_importer_mesh(); if (importer_mesh.is_valid()) { diff --git a/modules/gltf/extensions/physics/gltf_physics_shape.cpp b/modules/gltf/extensions/physics/gltf_physics_shape.cpp index 6c9ed82a69..6897bdbd3a 100644 --- a/modules/gltf/extensions/physics/gltf_physics_shape.cpp +++ b/modules/gltf/extensions/physics/gltf_physics_shape.cpp @@ -46,6 +46,9 @@ void GLTFPhysicsShape::_bind_methods() { ClassDB::bind_static_method("GLTFPhysicsShape", D_METHOD("from_node", "shape_node"), &GLTFPhysicsShape::from_node); ClassDB::bind_method(D_METHOD("to_node", "cache_shapes"), &GLTFPhysicsShape::to_node, DEFVAL(false)); + ClassDB::bind_static_method("GLTFPhysicsShape", D_METHOD("from_resource", "shape_resource"), &GLTFPhysicsShape::from_resource); + ClassDB::bind_method(D_METHOD("to_resource", "cache_shapes"), &GLTFPhysicsShape::to_resource, DEFVAL(false)); + ClassDB::bind_static_method("GLTFPhysicsShape", D_METHOD("from_dictionary", "dictionary"), &GLTFPhysicsShape::from_dictionary); ClassDB::bind_method(D_METHOD("to_dictionary"), &GLTFPhysicsShape::to_dictionary); @@ -159,44 +162,57 @@ Ref<ImporterMesh> _convert_hull_points_to_mesh(const Vector<Vector3> &p_hull_poi Ref<GLTFPhysicsShape> GLTFPhysicsShape::from_node(const CollisionShape3D *p_godot_shape_node) { Ref<GLTFPhysicsShape> gltf_shape; - gltf_shape.instantiate(); ERR_FAIL_NULL_V_MSG(p_godot_shape_node, gltf_shape, "Tried to create a GLTFPhysicsShape from a CollisionShape3D node, but the given node was null."); + Ref<Shape3D> shape_resource = p_godot_shape_node->get_shape(); + ERR_FAIL_COND_V_MSG(shape_resource.is_null(), gltf_shape, "Tried to create a GLTFPhysicsShape from a CollisionShape3D node, but the given node had a null shape."); + gltf_shape = from_resource(shape_resource); + // Check if the shape is part of a trigger. Node *parent = p_godot_shape_node->get_parent(); if (cast_to<const Area3D>(parent)) { gltf_shape->set_is_trigger(true); } - // All the code for working with the shape is below this comment. - Ref<Shape3D> shape_resource = p_godot_shape_node->get_shape(); - ERR_FAIL_COND_V_MSG(shape_resource.is_null(), gltf_shape, "Tried to create a GLTFPhysicsShape from a CollisionShape3D node, but the given node had a null shape."); - gltf_shape->_shape_cache = shape_resource; - if (cast_to<BoxShape3D>(shape_resource.ptr())) { + return gltf_shape; +} + +CollisionShape3D *GLTFPhysicsShape::to_node(bool p_cache_shapes) { + CollisionShape3D *godot_shape_node = memnew(CollisionShape3D); + to_resource(p_cache_shapes); // Sets `_shape_cache`. + godot_shape_node->set_shape(_shape_cache); + return godot_shape_node; +} + +Ref<GLTFPhysicsShape> GLTFPhysicsShape::from_resource(const Ref<Shape3D> &p_shape_resource) { + Ref<GLTFPhysicsShape> gltf_shape; + gltf_shape.instantiate(); + ERR_FAIL_COND_V_MSG(p_shape_resource.is_null(), gltf_shape, "Tried to create a GLTFPhysicsShape from a Shape3D resource, but the given resource was null."); + if (cast_to<BoxShape3D>(p_shape_resource.ptr())) { gltf_shape->shape_type = "box"; - Ref<BoxShape3D> box = shape_resource; + Ref<BoxShape3D> box = p_shape_resource; gltf_shape->set_size(box->get_size()); - } else if (cast_to<const CapsuleShape3D>(shape_resource.ptr())) { + } else if (cast_to<const CapsuleShape3D>(p_shape_resource.ptr())) { gltf_shape->shape_type = "capsule"; - Ref<CapsuleShape3D> capsule = shape_resource; + Ref<CapsuleShape3D> capsule = p_shape_resource; gltf_shape->set_radius(capsule->get_radius()); gltf_shape->set_height(capsule->get_height()); - } else if (cast_to<const CylinderShape3D>(shape_resource.ptr())) { + } else if (cast_to<const CylinderShape3D>(p_shape_resource.ptr())) { gltf_shape->shape_type = "cylinder"; - Ref<CylinderShape3D> cylinder = shape_resource; + Ref<CylinderShape3D> cylinder = p_shape_resource; gltf_shape->set_radius(cylinder->get_radius()); gltf_shape->set_height(cylinder->get_height()); - } else if (cast_to<const SphereShape3D>(shape_resource.ptr())) { + } else if (cast_to<const SphereShape3D>(p_shape_resource.ptr())) { gltf_shape->shape_type = "sphere"; - Ref<SphereShape3D> sphere = shape_resource; + Ref<SphereShape3D> sphere = p_shape_resource; gltf_shape->set_radius(sphere->get_radius()); - } else if (cast_to<const ConvexPolygonShape3D>(shape_resource.ptr())) { + } else if (cast_to<const ConvexPolygonShape3D>(p_shape_resource.ptr())) { gltf_shape->shape_type = "convex"; - Ref<ConvexPolygonShape3D> convex = shape_resource; + Ref<ConvexPolygonShape3D> convex = p_shape_resource; Vector<Vector3> hull_points = convex->get_points(); Ref<ImporterMesh> importer_mesh = _convert_hull_points_to_mesh(hull_points); ERR_FAIL_COND_V_MSG(importer_mesh.is_null(), gltf_shape, "GLTFPhysicsShape: Failed to convert convex hull points to a mesh."); gltf_shape->set_importer_mesh(importer_mesh); - } else if (cast_to<const ConcavePolygonShape3D>(shape_resource.ptr())) { + } else if (cast_to<const ConcavePolygonShape3D>(p_shape_resource.ptr())) { gltf_shape->shape_type = "trimesh"; - Ref<ConcavePolygonShape3D> concave = shape_resource; + Ref<ConcavePolygonShape3D> concave = p_shape_resource; Ref<ImporterMesh> importer_mesh; importer_mesh.instantiate(); Array surface_array; @@ -205,14 +221,14 @@ Ref<GLTFPhysicsShape> GLTFPhysicsShape::from_node(const CollisionShape3D *p_godo importer_mesh->add_surface(Mesh::PRIMITIVE_TRIANGLES, surface_array); gltf_shape->set_importer_mesh(importer_mesh); } else { - ERR_PRINT("Tried to create a GLTFPhysicsShape from a CollisionShape3D node, but the given node's shape '" + String(Variant(shape_resource)) + + ERR_PRINT("Tried to create a GLTFPhysicsShape from a Shape3D, but the given shape '" + String(Variant(p_shape_resource)) + "' had an unsupported shape type. Only BoxShape3D, CapsuleShape3D, CylinderShape3D, SphereShape3D, ConcavePolygonShape3D, and ConvexPolygonShape3D are supported."); } + gltf_shape->_shape_cache = p_shape_resource; return gltf_shape; } -CollisionShape3D *GLTFPhysicsShape::to_node(bool p_cache_shapes) { - CollisionShape3D *godot_shape_node = memnew(CollisionShape3D); +Ref<Shape3D> GLTFPhysicsShape::to_resource(bool p_cache_shapes) { if (!p_cache_shapes || _shape_cache == nullptr) { if (shape_type == "box") { Ref<BoxShape3D> box; @@ -237,19 +253,18 @@ CollisionShape3D *GLTFPhysicsShape::to_node(bool p_cache_shapes) { sphere->set_radius(radius); _shape_cache = sphere; } else if (shape_type == "convex") { - ERR_FAIL_COND_V_MSG(importer_mesh.is_null(), godot_shape_node, "GLTFPhysicsShape: Error converting convex hull shape to a node: The mesh resource is null."); + ERR_FAIL_COND_V_MSG(importer_mesh.is_null(), _shape_cache, "GLTFPhysicsShape: Error converting convex hull shape to a shape resource: The mesh resource is null."); Ref<ConvexPolygonShape3D> convex = importer_mesh->get_mesh()->create_convex_shape(); _shape_cache = convex; } else if (shape_type == "trimesh") { - ERR_FAIL_COND_V_MSG(importer_mesh.is_null(), godot_shape_node, "GLTFPhysicsShape: Error converting concave mesh shape to a node: The mesh resource is null."); + ERR_FAIL_COND_V_MSG(importer_mesh.is_null(), _shape_cache, "GLTFPhysicsShape: Error converting concave mesh shape to a shape resource: The mesh resource is null."); Ref<ConcavePolygonShape3D> concave = importer_mesh->create_trimesh_shape(); _shape_cache = concave; } else { - ERR_PRINT("GLTFPhysicsShape: Error converting to a node: Shape type '" + shape_type + "' is unknown."); + ERR_PRINT("GLTFPhysicsShape: Error converting to a shape resource: Shape type '" + shape_type + "' is unknown."); } } - godot_shape_node->set_shape(_shape_cache); - return godot_shape_node; + return _shape_cache; } Ref<GLTFPhysicsShape> GLTFPhysicsShape::from_dictionary(const Dictionary p_dictionary) { diff --git a/modules/gltf/extensions/physics/gltf_physics_shape.h b/modules/gltf/extensions/physics/gltf_physics_shape.h index ec0a8931f1..a6974473ee 100644 --- a/modules/gltf/extensions/physics/gltf_physics_shape.h +++ b/modules/gltf/extensions/physics/gltf_physics_shape.h @@ -55,7 +55,7 @@ private: bool is_trigger = false; GLTFMeshIndex mesh_index = -1; Ref<ImporterMesh> importer_mesh = nullptr; - // Internal only, for caching Godot shape resources. Used in `to_node`. + // Internal only, for caching Godot shape resources. Used in `to_resource` and `to_node`. Ref<Shape3D> _shape_cache = nullptr; public: @@ -83,6 +83,9 @@ public: static Ref<GLTFPhysicsShape> from_node(const CollisionShape3D *p_shape_node); CollisionShape3D *to_node(bool p_cache_shapes = false); + static Ref<GLTFPhysicsShape> from_resource(const Ref<Shape3D> &p_shape_resource); + Ref<Shape3D> to_resource(bool p_cache_shapes = false); + static Ref<GLTFPhysicsShape> from_dictionary(const Dictionary p_dictionary); Dictionary to_dictionary() const; }; diff --git a/modules/gridmap/doc_classes/GridMap.xml b/modules/gridmap/doc_classes/GridMap.xml index 7aeecef980..ef7276f493 100644 --- a/modules/gridmap/doc_classes/GridMap.xml +++ b/modules/gridmap/doc_classes/GridMap.xml @@ -12,8 +12,8 @@ </description> <tutorials> <link title="Using gridmaps">$DOCS_URL/tutorials/3d/using_gridmaps.html</link> - <link title="3D Platformer Demo">https://godotengine.org/asset-library/asset/125</link> - <link title="3D Kinematic Character Demo">https://godotengine.org/asset-library/asset/126</link> + <link title="3D Platformer Demo">https://godotengine.org/asset-library/asset/2748</link> + <link title="3D Kinematic Character Demo">https://godotengine.org/asset-library/asset/2739</link> </tutorials> <methods> <method name="clear"> diff --git a/modules/mbedtls/SCsub b/modules/mbedtls/SCsub index 4b8f65d8ff..04d26f9942 100644 --- a/modules/mbedtls/SCsub +++ b/modules/mbedtls/SCsub @@ -100,9 +100,9 @@ if env["builtin_mbedtls"]: thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] env_mbed_tls.Prepend(CPPPATH=["#thirdparty/mbedtls/include/"]) - env_mbed_tls.Append( - CPPDEFINES=[("MBEDTLS_CONFIG_FILE", '\\"thirdparty/mbedtls/include/godot_module_mbedtls_config.h\\"')] - ) + config_path = "thirdparty/mbedtls/include/godot_module_mbedtls_config.h" + config_path = f"<{config_path}>" if env_mbed_tls["ninja"] and env_mbed_tls.msvc else f'\\"{config_path}\\"' + env_mbed_tls.Append(CPPDEFINES=[("MBEDTLS_CONFIG_FILE", config_path)]) env_thirdparty = env_mbed_tls.Clone() env_thirdparty.disable_warnings() diff --git a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs index 46020fda89..57611d5f4c 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs @@ -90,6 +90,13 @@ namespace GodotTools.Export $"Resource of type {Internal.CSharpLanguageType} has an invalid file extension: {path}", nameof(path)); + if (!ProjectContainsDotNet()) + { + _maybeLastExportError = $"This project contains C# files but no solution file was found at the following path: {GodotSharpDirs.ProjectSlnPath}\n" + + "A solution file is required for projects with C# files. Please ensure that the solution file exists in the specified location and try again."; + throw new InvalidOperationException($"{path} is a C# file but no solution file exists."); + } + // TODO: What if the source file is not part of the game's C# project? bool includeScriptsContent = (bool)GetOption("dotnet/include_scripts_content"); diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs index bf6cab11c7..d3899c809a 100644 --- a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs +++ b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs @@ -167,8 +167,8 @@ namespace GodotTools public void ShowConfirmCreateSlnDialog() { - _confirmCreateSlnDialog.Title = "C# solution already exists. This will override the existing C# project file, any manual changes will be lost.".TTR(); - _confirmCreateSlnDialog.DialogText = "Create C# solution".TTR(); + _confirmCreateSlnDialog.Title = "Create C# solution".TTR(); + _confirmCreateSlnDialog.DialogText = "C# solution already exists. This will override the existing C# project file, any manual changes will be lost.".TTR(); EditorInterface.Singleton.PopupDialogCentered(_confirmCreateSlnDialog); } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs index 9b5aec7031..fb1d32c0cb 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs @@ -5,6 +5,7 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Runtime.CompilerServices; using Godot.NativeInterop; +using System.Diagnostics; #nullable enable @@ -16,6 +17,8 @@ namespace Godot.Collections /// interfacing with the engine. Otherwise prefer .NET collections /// such as <see cref="System.Array"/> or <see cref="List{T}"/>. /// </summary> + [DebuggerTypeProxy(typeof(ArrayDebugView<Variant>))] + [DebuggerDisplay("Count = {Count}")] #pragma warning disable CA1710 // Identifiers should have correct suffix public sealed class Array : #pragma warning restore CA1710 @@ -1040,6 +1043,8 @@ namespace Godot.Collections /// such as arrays or <see cref="List{T}"/>. /// </summary> /// <typeparam name="T">The type of the array.</typeparam> + [DebuggerTypeProxy(typeof(ArrayDebugView<>))] + [DebuggerDisplay("Count = {Count}")] [SuppressMessage("ReSharper", "RedundantExtendsListEntry")] [SuppressMessage("Naming", "CA1710", MessageId = "Identifiers should have correct suffix")] public sealed class Array<[MustBeVariant] T> : diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/DebugView.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/DebugView.cs new file mode 100644 index 0000000000..6d6eb61f8f --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/DebugView.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; + +namespace Godot.Collections +{ + internal sealed class ArrayDebugView<T> + { + private readonly IList<T> _array; + + public ArrayDebugView(IList<T> array) + { + ArgumentNullException.ThrowIfNull(array); + + _array = array; + } + + [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] + public T[] Items + { + get + { + var items = new T[_array.Count]; + _array.CopyTo(items, 0); + return items; + } + } + } + + internal sealed class DictionaryDebugView<TKey, TValue> + { + private readonly IDictionary<TKey, TValue> _dictionary; + + public DictionaryDebugView(IDictionary<TKey, TValue> dictionary) + { + ArgumentNullException.ThrowIfNull(dictionary); + + _dictionary = dictionary; + } + + [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] + public DictionaryKeyItemDebugView<TKey, TValue>[] Items + { + get + { + var items = new KeyValuePair<TKey, TValue>[_dictionary.Count]; + var views = new DictionaryKeyItemDebugView<TKey, TValue>[_dictionary.Count]; + _dictionary.CopyTo(items, 0); + for (int i = 0; i < items.Length; i++) + { + views[i] = new DictionaryKeyItemDebugView<TKey, TValue>(items[i]); + } + return views; + } + } + } + + [DebuggerDisplay("{Value}", Name = "[{Key}]")] + internal readonly struct DictionaryKeyItemDebugView<TKey, TValue> + { + public DictionaryKeyItemDebugView(KeyValuePair<TKey, TValue> keyValue) + { + Key = keyValue.Key; + Value = keyValue.Value; + } + + [DebuggerBrowsable(DebuggerBrowsableState.Collapsed)] + public TKey Key { get; } + + [DebuggerBrowsable(DebuggerBrowsableState.Collapsed)] + public TValue Value { get; } + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs index ef3c9c79d4..7a88fea5f1 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs @@ -207,7 +207,7 @@ namespace Godot foreach (FieldInfo field in fields) { - Type fieldType = field.GetType(); + Type fieldType = field.FieldType; Variant.Type variantType = GD.TypeToVariantType(fieldType); @@ -216,7 +216,7 @@ namespace Godot static byte[] VarToBytes(in godot_variant var) { - NativeFuncs.godotsharp_var_to_bytes(var, false.ToGodotBool(), out var varBytes); + NativeFuncs.godotsharp_var_to_bytes(var, godot_bool.True, out var varBytes); using (varBytes) return Marshaling.ConvertNativePackedByteArrayToSystemArray(varBytes); } @@ -483,7 +483,7 @@ namespace Godot if (fieldInfo != null) { - var variantValue = GD.BytesToVar(valueBuffer); + var variantValue = GD.BytesToVarWithObjects(valueBuffer); object? managedValue = RuntimeTypeConversionHelper.ConvertToObjectOfType( (godot_variant)variantValue.NativeVar, fieldInfo.FieldType); fieldInfo.SetValue(recreatedTarget, managedValue); @@ -799,7 +799,7 @@ namespace Godot return func(variant); if (typeof(GodotObject).IsAssignableFrom(type)) - return Convert.ChangeType(VariantUtils.ConvertTo<GodotObject>(variant), type, CultureInfo.InvariantCulture); + return VariantUtils.ConvertTo<GodotObject>(variant); if (typeof(GodotObject[]).IsAssignableFrom(type)) { @@ -818,7 +818,7 @@ namespace Godot } using var godotArray = NativeFuncs.godotsharp_variant_as_array(variant); - return Convert.ChangeType(ConvertToSystemArrayOfGodotObject(godotArray, type), type, CultureInfo.InvariantCulture); + return ConvertToSystemArrayOfGodotObject(godotArray, type); } if (type.IsEnum) diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs index d08fad90db..864815866a 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs @@ -4,6 +4,7 @@ using System.Collections; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using Godot.NativeInterop; +using System.Diagnostics; #nullable enable @@ -14,6 +15,8 @@ namespace Godot.Collections /// typed elements allocated in the engine in C++. Useful when /// interfacing with the engine. /// </summary> + [DebuggerTypeProxy(typeof(DictionaryDebugView<Variant, Variant>))] + [DebuggerDisplay("Count = {Count}")] public sealed class Dictionary : IDictionary<Variant, Variant>, IReadOnlyDictionary<Variant, Variant>, @@ -480,6 +483,8 @@ namespace Godot.Collections /// </summary> /// <typeparam name="TKey">The type of the dictionary's keys.</typeparam> /// <typeparam name="TValue">The type of the dictionary's values.</typeparam> + [DebuggerTypeProxy(typeof(DictionaryDebugView<,>))] + [DebuggerDisplay("Count = {Count}")] public class Dictionary<[MustBeVariant] TKey, [MustBeVariant] TValue> : IDictionary<TKey, TValue>, IReadOnlyDictionary<TKey, TValue>, diff --git a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj index d54942e654..d4c11da963 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj +++ b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj @@ -77,6 +77,7 @@ <Compile Include="Core\Color.cs" /> <Compile Include="Core\Colors.cs" /> <Compile Include="Core\DebuggingUtils.cs" /> + <Compile Include="Core\DebugView.cs" /> <Compile Include="Core\DelegateUtils.cs" /> <Compile Include="Core\Dictionary.cs" /> <Compile Include="Core\Dispatcher.cs" /> diff --git a/modules/multiplayer/multiplayer_spawner.cpp b/modules/multiplayer/multiplayer_spawner.cpp index 264a2e9c8e..6c0669b2f3 100644 --- a/modules/multiplayer/multiplayer_spawner.cpp +++ b/modules/multiplayer/multiplayer_spawner.cpp @@ -251,6 +251,7 @@ NodePath MultiplayerSpawner::get_spawn_path() const { void MultiplayerSpawner::set_spawn_path(const NodePath &p_path) { spawn_path = p_path; _update_spawn_node(); + update_configuration_warnings(); } void MultiplayerSpawner::_track(Node *p_node, const Variant &p_argument, int p_scene_id) { diff --git a/modules/multiplayer/multiplayer_synchronizer.cpp b/modules/multiplayer/multiplayer_synchronizer.cpp index 02e3a11964..c2ce500af8 100644 --- a/modules/multiplayer/multiplayer_synchronizer.cpp +++ b/modules/multiplayer/multiplayer_synchronizer.cpp @@ -354,6 +354,7 @@ void MultiplayerSynchronizer::set_root_path(const NodePath &p_path) { _stop(); root_path = p_path; _start(); + update_configuration_warnings(); } NodePath MultiplayerSynchronizer::get_root_path() const { diff --git a/modules/navigation/2d/nav_mesh_generator_2d.cpp b/modules/navigation/2d/nav_mesh_generator_2d.cpp index 069cc0f4a8..0738a102eb 100644 --- a/modules/navigation/2d/nav_mesh_generator_2d.cpp +++ b/modules/navigation/2d/nav_mesh_generator_2d.cpp @@ -575,8 +575,6 @@ void NavMeshGenerator2D::generator_parse_tilemap_node(const Ref<NavigationPolygo return; } - int tilemap_layer = 0; // only main tile map layer is supported - Ref<TileSet> tile_set = tilemap->get_tileset(); if (!tile_set.is_valid()) { return; @@ -589,77 +587,115 @@ void NavMeshGenerator2D::generator_parse_tilemap_node(const Ref<NavigationPolygo return; } - const Transform2D tilemap_xform = p_source_geometry_data->root_node_transform * tilemap->get_global_transform(); - TypedArray<Vector2i> used_cells = tilemap->get_used_cells(tilemap_layer); + HashSet<Vector2i> cells_with_navigation_polygon; + HashSet<Vector2i> cells_with_collision_polygon; - for (int used_cell_index = 0; used_cell_index < used_cells.size(); used_cell_index++) { - const Vector2i &cell = used_cells[used_cell_index]; + const Transform2D tilemap_xform = p_source_geometry_data->root_node_transform * tilemap->get_global_transform(); - const TileData *tile_data = tilemap->get_cell_tile_data(tilemap_layer, cell, false); - if (tile_data == nullptr) { - continue; - } +#ifdef DEBUG_ENABLED + int error_print_counter = 0; + int error_print_max = 10; +#endif // DEBUG_ENABLED - // Transform flags. - const int alternative_id = tilemap->get_cell_alternative_tile(tilemap_layer, cell, false); - bool flip_h = (alternative_id & TileSetAtlasSource::TRANSFORM_FLIP_H); - bool flip_v = (alternative_id & TileSetAtlasSource::TRANSFORM_FLIP_V); - bool transpose = (alternative_id & TileSetAtlasSource::TRANSFORM_TRANSPOSE); + for (int tilemap_layer = 0; tilemap_layer < tilemap->get_layers_count(); tilemap_layer++) { + TypedArray<Vector2i> used_cells = tilemap->get_used_cells(tilemap_layer); - Transform2D tile_transform; - tile_transform.set_origin(tilemap->map_to_local(cell)); + for (int used_cell_index = 0; used_cell_index < used_cells.size(); used_cell_index++) { + const Vector2i &cell = used_cells[used_cell_index]; - const Transform2D tile_transform_offset = tilemap_xform * tile_transform; + const TileData *tile_data = tilemap->get_cell_tile_data(tilemap_layer, cell, false); + if (tile_data == nullptr) { + continue; + } - if (navigation_layers_count > 0) { - Ref<NavigationPolygon> navigation_polygon = tile_data->get_navigation_polygon(tilemap_layer, flip_h, flip_v, transpose); - if (navigation_polygon.is_valid()) { - for (int outline_index = 0; outline_index < navigation_polygon->get_outline_count(); outline_index++) { - const Vector<Vector2> &navigation_polygon_outline = navigation_polygon->get_outline(outline_index); - if (navigation_polygon_outline.size() == 0) { - continue; + // Transform flags. + const int alternative_id = tilemap->get_cell_alternative_tile(tilemap_layer, cell, false); + bool flip_h = (alternative_id & TileSetAtlasSource::TRANSFORM_FLIP_H); + bool flip_v = (alternative_id & TileSetAtlasSource::TRANSFORM_FLIP_V); + bool transpose = (alternative_id & TileSetAtlasSource::TRANSFORM_TRANSPOSE); + + Transform2D tile_transform; + tile_transform.set_origin(tilemap->map_to_local(cell)); + + const Transform2D tile_transform_offset = tilemap_xform * tile_transform; + + if (navigation_layers_count > 0) { + Ref<NavigationPolygon> navigation_polygon = tile_data->get_navigation_polygon(tilemap_layer, flip_h, flip_v, transpose); + if (navigation_polygon.is_valid()) { + if (cells_with_navigation_polygon.has(cell)) { +#ifdef DEBUG_ENABLED + error_print_counter++; + if (error_print_counter <= error_print_max) { + WARN_PRINT(vformat("TileMap navigation mesh baking error. The TileMap cell key Vector2i(%s, %s) has navigation mesh from 2 or more different TileMap layers assigned. This can cause unexpected navigation mesh baking results. The duplicated cell data was ignored.", cell.x, cell.y)); + } +#endif // DEBUG_ENABLED + } else { + cells_with_navigation_polygon.insert(cell); + + for (int outline_index = 0; outline_index < navigation_polygon->get_outline_count(); outline_index++) { + const Vector<Vector2> &navigation_polygon_outline = navigation_polygon->get_outline(outline_index); + if (navigation_polygon_outline.size() == 0) { + continue; + } + + Vector<Vector2> traversable_outline; + traversable_outline.resize(navigation_polygon_outline.size()); + + const Vector2 *navigation_polygon_outline_ptr = navigation_polygon_outline.ptr(); + Vector2 *traversable_outline_ptrw = traversable_outline.ptrw(); + + for (int traversable_outline_index = 0; traversable_outline_index < traversable_outline.size(); traversable_outline_index++) { + traversable_outline_ptrw[traversable_outline_index] = tile_transform_offset.xform(navigation_polygon_outline_ptr[traversable_outline_index]); + } + + p_source_geometry_data->_add_traversable_outline(traversable_outline); + } } + } + } - Vector<Vector2> traversable_outline; - traversable_outline.resize(navigation_polygon_outline.size()); - - const Vector2 *navigation_polygon_outline_ptr = navigation_polygon_outline.ptr(); - Vector2 *traversable_outline_ptrw = traversable_outline.ptrw(); - - for (int traversable_outline_index = 0; traversable_outline_index < traversable_outline.size(); traversable_outline_index++) { - traversable_outline_ptrw[traversable_outline_index] = tile_transform_offset.xform(navigation_polygon_outline_ptr[traversable_outline_index]); + if (physics_layers_count > 0 && (parsed_geometry_type == NavigationPolygon::PARSED_GEOMETRY_STATIC_COLLIDERS || parsed_geometry_type == NavigationPolygon::PARSED_GEOMETRY_BOTH) && (tile_set->get_physics_layer_collision_layer(tilemap_layer) & parsed_collision_mask)) { + if (cells_with_collision_polygon.has(cell)) { +#ifdef DEBUG_ENABLED + error_print_counter++; + if (error_print_counter <= error_print_max) { + WARN_PRINT(vformat("TileMap navigation mesh baking error. The cell key Vector2i(%s, %s) has collision polygons from 2 or more different TileMap layers assigned that all match the parsed collision mask. This can cause unexpected navigation mesh baking results. The duplicated cell data was ignored.", cell.x, cell.y)); } +#endif // DEBUG_ENABLED + } else { + cells_with_collision_polygon.insert(cell); - p_source_geometry_data->_add_traversable_outline(traversable_outline); - } - } - } + for (int collision_polygon_index = 0; collision_polygon_index < tile_data->get_collision_polygons_count(tilemap_layer); collision_polygon_index++) { + PackedVector2Array collision_polygon_points = tile_data->get_collision_polygon_points(tilemap_layer, collision_polygon_index); + if (collision_polygon_points.size() == 0) { + continue; + } - if (physics_layers_count > 0 && (parsed_geometry_type == NavigationPolygon::PARSED_GEOMETRY_STATIC_COLLIDERS || parsed_geometry_type == NavigationPolygon::PARSED_GEOMETRY_BOTH) && (tile_set->get_physics_layer_collision_layer(tilemap_layer) & parsed_collision_mask)) { - for (int collision_polygon_index = 0; collision_polygon_index < tile_data->get_collision_polygons_count(tilemap_layer); collision_polygon_index++) { - PackedVector2Array collision_polygon_points = tile_data->get_collision_polygon_points(tilemap_layer, collision_polygon_index); - if (collision_polygon_points.size() == 0) { - continue; - } + if (flip_h || flip_v || transpose) { + collision_polygon_points = TileData::get_transformed_vertices(collision_polygon_points, flip_h, flip_v, transpose); + } - if (flip_h || flip_v || transpose) { - collision_polygon_points = TileData::get_transformed_vertices(collision_polygon_points, flip_h, flip_v, transpose); - } + Vector<Vector2> obstruction_outline; + obstruction_outline.resize(collision_polygon_points.size()); - Vector<Vector2> obstruction_outline; - obstruction_outline.resize(collision_polygon_points.size()); + const Vector2 *collision_polygon_points_ptr = collision_polygon_points.ptr(); + Vector2 *obstruction_outline_ptrw = obstruction_outline.ptrw(); - const Vector2 *collision_polygon_points_ptr = collision_polygon_points.ptr(); - Vector2 *obstruction_outline_ptrw = obstruction_outline.ptrw(); + for (int obstruction_outline_index = 0; obstruction_outline_index < obstruction_outline.size(); obstruction_outline_index++) { + obstruction_outline_ptrw[obstruction_outline_index] = tile_transform_offset.xform(collision_polygon_points_ptr[obstruction_outline_index]); + } - for (int obstruction_outline_index = 0; obstruction_outline_index < obstruction_outline.size(); obstruction_outline_index++) { - obstruction_outline_ptrw[obstruction_outline_index] = tile_transform_offset.xform(collision_polygon_points_ptr[obstruction_outline_index]); + p_source_geometry_data->_add_obstruction_outline(obstruction_outline); + } } - - p_source_geometry_data->_add_obstruction_outline(obstruction_outline); } } } +#ifdef DEBUG_ENABLED + if (error_print_counter > error_print_max) { + ERR_PRINT(vformat("TileMap navigation mesh baking error. A total of %s cells with navigation or collision polygons from 2 or more different TileMap layers overlap. This can cause unexpected navigation mesh baking results. The duplicated cell data was ignored.", error_print_counter)); + } +#endif // DEBUG_ENABLED } void NavMeshGenerator2D::generator_parse_navigationobstacle_node(const Ref<NavigationPolygon> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry_data, Node *p_node) { diff --git a/modules/openxr/doc_classes/OpenXRCompositionLayer.xml b/modules/openxr/doc_classes/OpenXRCompositionLayer.xml index 66baaf096d..168e0bf077 100644 --- a/modules/openxr/doc_classes/OpenXRCompositionLayer.xml +++ b/modules/openxr/doc_classes/OpenXRCompositionLayer.xml @@ -10,6 +10,15 @@ <tutorials> </tutorials> <methods> + <method name="intersects_ray" qualifiers="const"> + <return type="Vector2" /> + <param index="0" name="origin" type="Vector3" /> + <param index="1" name="direction" type="Vector3" /> + <description> + Returns UV coordinates where the given ray intersects with the composition layer. [param origin] and [param direction] must be in global space. + Returns [code]Vector2(-1.0, -1.0)[/code] if the ray doesn't intersect. + </description> + </method> <method name="is_natively_supported" qualifiers="const"> <return type="bool" /> <description> diff --git a/modules/openxr/doc_classes/OpenXRExtensionWrapperExtension.xml b/modules/openxr/doc_classes/OpenXRExtensionWrapperExtension.xml index 9d6b197ee1..79aa547c52 100644 --- a/modules/openxr/doc_classes/OpenXRExtensionWrapperExtension.xml +++ b/modules/openxr/doc_classes/OpenXRExtensionWrapperExtension.xml @@ -46,6 +46,18 @@ Returns a [PackedStringArray] of positional tracker names that are used within the extension wrapper. </description> </method> + <method name="_get_viewport_composition_layer_extension_properties" qualifiers="virtual"> + <return type="Dictionary[]" /> + <description> + Gets an array of [Dictionary]s that represent properties, just like [method Object._get_property_list], that will be added to [OpenXRCompositionLayer] nodes. + </description> + </method> + <method name="_get_viewport_composition_layer_extension_property_defaults" qualifiers="virtual"> + <return type="Dictionary" /> + <description> + Gets a [Dictionary] containing the default values for the properties returned by [method _get_viewport_composition_layer_extension_properties]. + </description> + </method> <method name="_on_before_instance_created" qualifiers="virtual"> <return type="void" /> <description> @@ -152,6 +164,14 @@ Called when the OpenXR session state is changed to visible. This means OpenXR is now ready to receive frames. </description> </method> + <method name="_on_viewport_composition_layer_destroyed" qualifiers="virtual"> + <return type="void" /> + <param index="0" name="layer" type="const void*" /> + <description> + Called when a composition layer created via [OpenXRCompositionLayer] is destroyed. + [param layer] is a pointer to an [code]XrCompositionLayerBaseHeader[/code] struct. + </description> + </method> <method name="_set_hand_joint_locations_and_get_next_pointer" qualifiers="virtual"> <return type="int" /> <param index="0" name="hand_index" type="int" /> @@ -188,6 +208,17 @@ Adds additional data structures when interogating OpenXR system abilities. </description> </method> + <method name="_set_viewport_composition_layer_and_get_next_pointer" qualifiers="virtual"> + <return type="int" /> + <param index="0" name="layer" type="const void*" /> + <param index="1" name="property_values" type="Dictionary" /> + <param index="2" name="next_pointer" type="void*" /> + <description> + Adds additional data structures to composition layers created by [OpenXRCompositionLayer]. + [param property_values] contains the values of the properties returned by [method _get_viewport_composition_layer_extension_properties]. + [param layer] is a pointer to an [code]XrCompositionLayerBaseHeader[/code] struct. + </description> + </method> <method name="get_openxr_api"> <return type="OpenXRAPIExtension" /> <description> diff --git a/modules/openxr/doc_classes/OpenXRHand.xml b/modules/openxr/doc_classes/OpenXRHand.xml index 23d932ddd7..9cc548dd6f 100644 --- a/modules/openxr/doc_classes/OpenXRHand.xml +++ b/modules/openxr/doc_classes/OpenXRHand.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="OpenXRHand" inherits="Node3D" deprecated="Use [XRHandModifier3D] instead." xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="OpenXRHand" inherits="SkeletonModifier3D" deprecated="Use [XRHandModifier3D] instead." xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> Node supporting hand and finger tracking in OpenXR. </brief_description> @@ -18,14 +18,11 @@ <member name="hand" type="int" setter="set_hand" getter="get_hand" enum="OpenXRHand.Hands" default="0"> Specifies whether this node tracks the left or right hand of the player. </member> - <member name="hand_skeleton" type="NodePath" setter="set_hand_skeleton" getter="get_hand_skeleton" default="NodePath("")"> - Set a [Skeleton3D] node for which the pose positions will be updated. - </member> <member name="motion_range" type="int" setter="set_motion_range" getter="get_motion_range" enum="OpenXRHand.MotionRange" default="0"> Set the motion range (if supported) limiting the hand motion. </member> <member name="skeleton_rig" type="int" setter="set_skeleton_rig" getter="get_skeleton_rig" enum="OpenXRHand.SkeletonRig" default="0"> - Set the type of skeleton rig the [member hand_skeleton] is compliant with. + Set the type of skeleton rig the parent [Skeleton3D] is compliant with. </member> </members> <constants> diff --git a/modules/openxr/extensions/openxr_composition_layer_extension.cpp b/modules/openxr/extensions/openxr_composition_layer_extension.cpp index 7c0c69d747..cb117d7bb7 100644 --- a/modules/openxr/extensions/openxr_composition_layer_extension.cpp +++ b/modules/openxr/extensions/openxr_composition_layer_extension.cpp @@ -86,11 +86,11 @@ int OpenXRCompositionLayerExtension::get_composition_layer_order(int p_index) { return composition_layers[p_index]->get_sort_order(); } -void OpenXRCompositionLayerExtension::register_composition_layer_provider(OpenXRViewportCompositionLayerProvider *p_composition_layer) { +void OpenXRCompositionLayerExtension::register_viewport_composition_layer_provider(OpenXRViewportCompositionLayerProvider *p_composition_layer) { composition_layers.push_back(p_composition_layer); } -void OpenXRCompositionLayerExtension::unregister_composition_layer_provider(OpenXRViewportCompositionLayerProvider *p_composition_layer) { +void OpenXRCompositionLayerExtension::unregister_viewport_composition_layer_provider(OpenXRViewportCompositionLayerProvider *p_composition_layer) { composition_layers.erase(p_composition_layer); } @@ -123,6 +123,10 @@ OpenXRViewportCompositionLayerProvider::OpenXRViewportCompositionLayerProvider(X } OpenXRViewportCompositionLayerProvider::~OpenXRViewportCompositionLayerProvider() { + for (OpenXRExtensionWrapper *extension : OpenXRAPI::get_registered_extension_wrappers()) { + extension->on_viewport_composition_layer_destroyed(composition_layer); + } + // This will reset the viewport and free the swapchain too. set_viewport(RID(), Size2i()); } @@ -159,6 +163,11 @@ void OpenXRViewportCompositionLayerProvider::set_viewport(RID p_viewport, Size2i } } +void OpenXRViewportCompositionLayerProvider::set_extension_property_values(const Dictionary &p_extension_property_values) { + extension_property_values = p_extension_property_values; + extension_property_values_changed = true; +} + void OpenXRViewportCompositionLayerProvider::on_pre_render() { RenderingServer *rs = RenderingServer::get_singleton(); ERR_FAIL_NULL(rs); @@ -233,6 +242,19 @@ XrCompositionLayerBaseHeader *OpenXRViewportCompositionLayerProvider::get_compos } break; } + if (extension_property_values_changed) { + extension_property_values_changed = false; + + void *next_pointer = nullptr; + for (OpenXRExtensionWrapper *extension : OpenXRAPI::get_registered_extension_wrappers()) { + void *np = extension->set_viewport_composition_layer_and_get_next_pointer(composition_layer, extension_property_values, next_pointer); + if (np) { + next_pointer = np; + } + } + composition_layer->next = next_pointer; + } + return composition_layer; } diff --git a/modules/openxr/extensions/openxr_composition_layer_extension.h b/modules/openxr/extensions/openxr_composition_layer_extension.h index 7cc35005f8..4fefc416e6 100644 --- a/modules/openxr/extensions/openxr_composition_layer_extension.h +++ b/modules/openxr/extensions/openxr_composition_layer_extension.h @@ -57,8 +57,8 @@ public: virtual XrCompositionLayerBaseHeader *get_composition_layer(int p_index) override; virtual int get_composition_layer_order(int p_index) override; - void register_composition_layer_provider(OpenXRViewportCompositionLayerProvider *p_composition_layer); - void unregister_composition_layer_provider(OpenXRViewportCompositionLayerProvider *p_composition_layer); + void register_viewport_composition_layer_provider(OpenXRViewportCompositionLayerProvider *p_composition_layer); + void unregister_viewport_composition_layer_provider(OpenXRViewportCompositionLayerProvider *p_composition_layer); bool is_available(XrStructureType p_which); @@ -75,6 +75,8 @@ class OpenXRViewportCompositionLayerProvider { XrCompositionLayerBaseHeader *composition_layer = nullptr; int sort_order = 1; bool alpha_blend = false; + Dictionary extension_property_values; + bool extension_property_values_changed = true; RID viewport; Size2i viewport_size; @@ -102,6 +104,8 @@ public: void set_viewport(RID p_viewport, Size2i p_size); RID get_viewport() const { return viewport; } + void set_extension_property_values(const Dictionary &p_property_values); + void on_pre_render(); XrCompositionLayerBaseHeader *get_composition_layer(); diff --git a/modules/openxr/extensions/openxr_extension_wrapper.h b/modules/openxr/extensions/openxr_extension_wrapper.h index ad326472ab..ce03df0b30 100644 --- a/modules/openxr/extensions/openxr_extension_wrapper.h +++ b/modules/openxr/extensions/openxr_extension_wrapper.h @@ -96,6 +96,11 @@ public: virtual void on_state_loss_pending() {} // `on_state_loss_pending` is called when the OpenXR session state is changed to loss pending. virtual void on_state_exiting() {} // `on_state_exiting` is called when the OpenXR session state is changed to exiting. + virtual void *set_viewport_composition_layer_and_get_next_pointer(const XrCompositionLayerBaseHeader *p_layer, Dictionary p_property_values, void *p_next_pointer) { return p_next_pointer; } // Add additional data structures to composition layers created via OpenXRCompositionLayer. + virtual void on_viewport_composition_layer_destroyed(const XrCompositionLayerBaseHeader *p_layer) {} // `on_viewport_composition_layer_destroyed` is called when a composition layer created via OpenXRCompositionLayer is destroyed. + virtual void get_viewport_composition_layer_extension_properties(List<PropertyInfo> *p_property_list) {} // Get additional property definitions for OpenXRCompositionLayer. + virtual Dictionary get_viewport_composition_layer_extension_property_defaults() { return Dictionary(); } // Get the default values for the additional property definitions for OpenXRCompositionLayer. + // `on_event_polled` is called when there is an OpenXR event to process. // Should return true if the event was handled, false otherwise. virtual bool on_event_polled(const XrEventDataBuffer &event) { diff --git a/modules/openxr/extensions/openxr_extension_wrapper_extension.cpp b/modules/openxr/extensions/openxr_extension_wrapper_extension.cpp index 60a934e3a8..0cb039bec4 100644 --- a/modules/openxr/extensions/openxr_extension_wrapper_extension.cpp +++ b/modules/openxr/extensions/openxr_extension_wrapper_extension.cpp @@ -60,6 +60,10 @@ void OpenXRExtensionWrapperExtension::_bind_methods() { GDVIRTUAL_BIND(_on_state_loss_pending); GDVIRTUAL_BIND(_on_state_exiting); GDVIRTUAL_BIND(_on_event_polled, "event"); + GDVIRTUAL_BIND(_set_viewport_composition_layer_and_get_next_pointer, "layer", "property_values", "next_pointer"); + GDVIRTUAL_BIND(_get_viewport_composition_layer_extension_properties, "layer"); + GDVIRTUAL_BIND(_get_viewport_composition_layer_extension_property_defaults, "layer"); + GDVIRTUAL_BIND(_on_viewport_composition_layer_destroyed, "layer"); ClassDB::bind_method(D_METHOD("get_openxr_api"), &OpenXRExtensionWrapperExtension::get_openxr_api); ClassDB::bind_method(D_METHOD("register_extension_wrapper"), &OpenXRExtensionWrapperExtension::register_extension_wrapper); @@ -240,6 +244,36 @@ bool OpenXRExtensionWrapperExtension::on_event_polled(const XrEventDataBuffer &p return false; } +void *OpenXRExtensionWrapperExtension::set_viewport_composition_layer_and_get_next_pointer(const XrCompositionLayerBaseHeader *p_layer, Dictionary p_property_values, void *p_next_pointer) { + uint64_t pointer = 0; + + if (GDVIRTUAL_CALL(_set_viewport_composition_layer_and_get_next_pointer, GDExtensionConstPtr<void>(p_layer), p_property_values, GDExtensionPtr<void>(p_next_pointer), pointer)) { + return reinterpret_cast<void *>(pointer); + } + + return p_next_pointer; +} + +void OpenXRExtensionWrapperExtension::on_viewport_composition_layer_destroyed(const XrCompositionLayerBaseHeader *p_layer) { + GDVIRTUAL_CALL(_on_viewport_composition_layer_destroyed, GDExtensionConstPtr<void>(p_layer)); +} + +void OpenXRExtensionWrapperExtension::get_viewport_composition_layer_extension_properties(List<PropertyInfo> *p_property_list) { + TypedArray<Dictionary> properties; + + if (GDVIRTUAL_CALL(_get_viewport_composition_layer_extension_properties, properties)) { + for (int i = 0; i < properties.size(); i++) { + p_property_list->push_back(PropertyInfo::from_dict(properties[i])); + } + } +} + +Dictionary OpenXRExtensionWrapperExtension::get_viewport_composition_layer_extension_property_defaults() { + Dictionary property_defaults; + GDVIRTUAL_CALL(_get_viewport_composition_layer_extension_property_defaults, property_defaults); + return property_defaults; +} + Ref<OpenXRAPIExtension> OpenXRExtensionWrapperExtension::get_openxr_api() { return openxr_api; } diff --git a/modules/openxr/extensions/openxr_extension_wrapper_extension.h b/modules/openxr/extensions/openxr_extension_wrapper_extension.h index d3b78bf617..71d2a57ff8 100644 --- a/modules/openxr/extensions/openxr_extension_wrapper_extension.h +++ b/modules/openxr/extensions/openxr_extension_wrapper_extension.h @@ -38,6 +38,7 @@ #include "core/os/os.h" #include "core/os/thread_safe.h" #include "core/variant/native_ptr.h" +#include "core/variant/typed_array.h" class OpenXRExtensionWrapperExtension : public Object, public OpenXRExtensionWrapper, public OpenXRCompositionLayerProvider { GDCLASS(OpenXRExtensionWrapperExtension, Object); @@ -59,6 +60,7 @@ public: virtual void *set_session_create_and_get_next_pointer(void *p_next_pointer) override; virtual void *set_swapchain_create_info_and_get_next_pointer(void *p_next_pointer) override; virtual void *set_hand_joint_locations_and_get_next_pointer(int p_hand_index, void *p_next_pointer) override; + virtual int get_composition_layer_count() override; virtual XrCompositionLayerBaseHeader *get_composition_layer(int p_index) override; virtual int get_composition_layer_order(int p_index) override; @@ -117,6 +119,16 @@ public: GDVIRTUAL1R(bool, _on_event_polled, GDExtensionConstPtr<void>); + virtual void *set_viewport_composition_layer_and_get_next_pointer(const XrCompositionLayerBaseHeader *p_layer, Dictionary p_property_values, void *p_next_pointer) override; + virtual void on_viewport_composition_layer_destroyed(const XrCompositionLayerBaseHeader *p_layer) override; + virtual void get_viewport_composition_layer_extension_properties(List<PropertyInfo> *p_property_list) override; + virtual Dictionary get_viewport_composition_layer_extension_property_defaults() override; + + GDVIRTUAL3R(uint64_t, _set_viewport_composition_layer_and_get_next_pointer, GDExtensionConstPtr<void>, Dictionary, GDExtensionPtr<void>); + GDVIRTUAL1(_on_viewport_composition_layer_destroyed, GDExtensionConstPtr<void>); + GDVIRTUAL0R(TypedArray<Dictionary>, _get_viewport_composition_layer_extension_properties); + GDVIRTUAL0R(Dictionary, _get_viewport_composition_layer_extension_property_defaults); + Ref<OpenXRAPIExtension> get_openxr_api(); void register_extension_wrapper(); diff --git a/modules/openxr/scene/openxr_composition_layer.cpp b/modules/openxr/scene/openxr_composition_layer.cpp index 120914485f..ce883b79b3 100644 --- a/modules/openxr/scene/openxr_composition_layer.cpp +++ b/modules/openxr/scene/openxr_composition_layer.cpp @@ -88,6 +88,8 @@ void OpenXRCompositionLayer::_bind_methods() { ClassDB::bind_method(D_METHOD("is_natively_supported"), &OpenXRCompositionLayer::is_natively_supported); + ClassDB::bind_method(D_METHOD("intersects_ray", "origin", "direction"), &OpenXRCompositionLayer::intersects_ray); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "layer_viewport", PROPERTY_HINT_NODE_TYPE, "SubViewport"), "set_layer_viewport", "get_layer_viewport"); ADD_PROPERTY(PropertyInfo(Variant::INT, "sort_order", PROPERTY_HINT_NONE, ""), "set_sort_order", "get_sort_order"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "alpha_blend", PROPERTY_HINT_NONE, ""), "set_alpha_blend", "get_alpha_blend"); @@ -199,6 +201,10 @@ bool OpenXRCompositionLayer::is_natively_supported() const { return false; } +Vector2 OpenXRCompositionLayer::intersects_ray(const Vector3 &p_origin, const Vector3 &p_direction) const { + return Vector2(-1.0, -1.0); +} + void OpenXRCompositionLayer::_reset_fallback_material() { ERR_FAIL_NULL(fallback); @@ -236,6 +242,14 @@ void OpenXRCompositionLayer::_reset_fallback_material() { void OpenXRCompositionLayer::_notification(int p_what) { switch (p_what) { + case NOTIFICATION_POSTINITIALIZE: { + if (openxr_layer_provider) { + for (OpenXRExtensionWrapper *extension : OpenXRAPI::get_registered_extension_wrappers()) { + extension_property_values.merge(extension->get_viewport_composition_layer_extension_property_defaults()); + } + openxr_layer_provider->set_extension_property_values(extension_property_values); + } + } break; case NOTIFICATION_INTERNAL_PROCESS: { if (fallback) { if (should_update_fallback_mesh) { @@ -260,7 +274,7 @@ void OpenXRCompositionLayer::_notification(int p_what) { } break; case NOTIFICATION_ENTER_TREE: { if (composition_layer_extension) { - composition_layer_extension->register_composition_layer_provider(openxr_layer_provider); + composition_layer_extension->register_viewport_composition_layer_provider(openxr_layer_provider); } if (!fallback && layer_viewport && openxr_api && openxr_api->is_running() && is_visible()) { @@ -269,7 +283,7 @@ void OpenXRCompositionLayer::_notification(int p_what) { } break; case NOTIFICATION_EXIT_TREE: { if (composition_layer_extension) { - composition_layer_extension->unregister_composition_layer_provider(openxr_layer_provider); + composition_layer_extension->unregister_viewport_composition_layer_provider(openxr_layer_provider); } // When a node is removed in the editor, we need to clear the layer viewport, because otherwise @@ -285,6 +299,40 @@ void OpenXRCompositionLayer::_notification(int p_what) { } } +void OpenXRCompositionLayer::_get_property_list(List<PropertyInfo> *p_property_list) const { + List<PropertyInfo> extension_properties; + for (OpenXRExtensionWrapper *extension : OpenXRAPI::get_registered_extension_wrappers()) { + extension->get_viewport_composition_layer_extension_properties(&extension_properties); + } + + for (const PropertyInfo &pinfo : extension_properties) { + StringName prop_name = pinfo.name; + if (!String(prop_name).contains("/")) { + WARN_PRINT_ONCE(vformat("Discarding OpenXRCompositionLayer property name '%s' from extension because it doesn't contain a '/'.")); + continue; + } + p_property_list->push_back(pinfo); + } +} + +bool OpenXRCompositionLayer::_get(const StringName &p_property, Variant &r_value) const { + if (extension_property_values.has(p_property)) { + r_value = extension_property_values[p_property]; + } + + return true; +} + +bool OpenXRCompositionLayer::_set(const StringName &p_property, const Variant &p_value) { + extension_property_values[p_property] = p_value; + + if (openxr_layer_provider) { + openxr_layer_provider->set_extension_property_values(extension_property_values); + } + + return true; +} + PackedStringArray OpenXRCompositionLayer::get_configuration_warnings() const { PackedStringArray warnings = Node3D::get_configuration_warnings(); diff --git a/modules/openxr/scene/openxr_composition_layer.h b/modules/openxr/scene/openxr_composition_layer.h index f683aea647..9f064379d3 100644 --- a/modules/openxr/scene/openxr_composition_layer.h +++ b/modules/openxr/scene/openxr_composition_layer.h @@ -49,6 +49,8 @@ class OpenXRCompositionLayer : public Node3D { MeshInstance3D *fallback = nullptr; bool should_update_fallback_mesh = false; + Dictionary extension_property_values; + void _create_fallback_node(); void _reset_fallback_material(); @@ -60,6 +62,9 @@ protected: static void _bind_methods(); void _notification(int p_what); + void _get_property_list(List<PropertyInfo> *p_property_list) const; + bool _get(const StringName &p_property, Variant &r_value) const; + bool _set(const StringName &p_property, const Variant &p_value); virtual void _on_openxr_session_begun(); virtual void _on_openxr_session_stopping(); @@ -84,6 +89,8 @@ public: virtual PackedStringArray get_configuration_warnings() const override; + virtual Vector2 intersects_ray(const Vector3 &p_origin, const Vector3 &p_direction) const; + OpenXRCompositionLayer(); ~OpenXRCompositionLayer(); }; diff --git a/modules/openxr/scene/openxr_composition_layer_cylinder.cpp b/modules/openxr/scene/openxr_composition_layer_cylinder.cpp index dcc9971ec9..ae5a8da5f3 100644 --- a/modules/openxr/scene/openxr_composition_layer_cylinder.cpp +++ b/modules/openxr/scene/openxr_composition_layer_cylinder.cpp @@ -188,3 +188,48 @@ void OpenXRCompositionLayerCylinder::set_fallback_segments(uint32_t p_fallback_s uint32_t OpenXRCompositionLayerCylinder::get_fallback_segments() const { return fallback_segments; } + +Vector2 OpenXRCompositionLayerCylinder::intersects_ray(const Vector3 &p_origin, const Vector3 &p_direction) const { + Transform3D cylinder_transform = get_global_transform(); + Vector3 cylinder_axis = cylinder_transform.basis.get_column(1); + + Vector3 offset = p_origin - cylinder_transform.origin; + float a = p_direction.dot(p_direction - cylinder_axis * p_direction.dot(cylinder_axis)); + float b = 2.0 * (p_direction.dot(offset - cylinder_axis * offset.dot(cylinder_axis))); + float c = offset.dot(offset - cylinder_axis * offset.dot(cylinder_axis)) - (radius * radius); + + float discriminant = b * b - 4.0 * a * c; + if (discriminant < 0.0) { + return Vector2(-1.0, -1.0); + } + + float t0 = (-b - Math::sqrt(discriminant)) / (2.0 * a); + float t1 = (-b + Math::sqrt(discriminant)) / (2.0 * a); + float t = MAX(t0, t1); + + if (t < 0.0) { + return Vector2(-1.0, -1.0); + } + Vector3 intersection = p_origin + p_direction * t; + + Basis correction = cylinder_transform.basis.inverse(); + correction.rotate(Vector3(0.0, 1.0, 0.0), -Math_PI / 2.0); + Vector3 relative_point = correction.xform(intersection - cylinder_transform.origin); + + Vector2 projected_point = Vector2(relative_point.x, relative_point.z); + float intersection_angle = Math::atan2(projected_point.y, projected_point.x); + if (Math::abs(intersection_angle) > central_angle / 2.0) { + return Vector2(-1.0, -1.0); + } + + float arc_length = radius * central_angle; + float height = aspect_ratio * arc_length; + if (Math::abs(relative_point.y) > height / 2.0) { + return Vector2(-1.0, -1.0); + } + + float u = 0.5 + (intersection_angle / central_angle); + float v = 1.0 - (0.5 + (relative_point.y / height)); + + return Vector2(u, v); +} diff --git a/modules/openxr/scene/openxr_composition_layer_cylinder.h b/modules/openxr/scene/openxr_composition_layer_cylinder.h index dec7dac529..aed0fabd78 100644 --- a/modules/openxr/scene/openxr_composition_layer_cylinder.h +++ b/modules/openxr/scene/openxr_composition_layer_cylinder.h @@ -66,6 +66,8 @@ public: void set_fallback_segments(uint32_t p_fallback_segments); uint32_t get_fallback_segments() const; + virtual Vector2 intersects_ray(const Vector3 &p_origin, const Vector3 &p_direction) const override; + OpenXRCompositionLayerCylinder(); ~OpenXRCompositionLayerCylinder(); }; diff --git a/modules/openxr/scene/openxr_composition_layer_equirect.cpp b/modules/openxr/scene/openxr_composition_layer_equirect.cpp index c911dbf6ae..d67e71443c 100644 --- a/modules/openxr/scene/openxr_composition_layer_equirect.cpp +++ b/modules/openxr/scene/openxr_composition_layer_equirect.cpp @@ -207,3 +207,54 @@ void OpenXRCompositionLayerEquirect::set_fallback_segments(uint32_t p_fallback_s uint32_t OpenXRCompositionLayerEquirect::get_fallback_segments() const { return fallback_segments; } + +Vector2 OpenXRCompositionLayerEquirect::intersects_ray(const Vector3 &p_origin, const Vector3 &p_direction) const { + Transform3D equirect_transform = get_global_transform(); + + Vector3 offset = p_origin - equirect_transform.origin; + float a = p_direction.dot(p_direction); + float b = 2.0 * offset.dot(p_direction); + float c = offset.dot(offset) - (radius * radius); + + float discriminant = b * b - 4.0 * a * c; + if (discriminant < 0.0) { + return Vector2(-1.0, -1.0); + } + + float t0 = (-b - Math::sqrt(discriminant)) / (2.0 * a); + float t1 = (-b + Math::sqrt(discriminant)) / (2.0 * a); + float t = MAX(t0, t1); + + if (t < 0.0) { + return Vector2(-1.0, -1.0); + } + Vector3 intersection = p_origin + p_direction * t; + + Basis correction = equirect_transform.basis.inverse(); + correction.rotate(Vector3(0.0, 1.0, 0.0), -Math_PI / 2.0); + Vector3 relative_point = correction.xform(intersection - equirect_transform.origin); + + float horizontal_intersection_angle = Math::atan2(relative_point.z, relative_point.x); + if (Math::abs(horizontal_intersection_angle) > central_horizontal_angle / 2.0) { + return Vector2(-1.0, -1.0); + } + + float vertical_intersection_angle = Math::acos(relative_point.y / radius) - (Math_PI / 2.0); + if (vertical_intersection_angle < 0) { + if (Math::abs(vertical_intersection_angle) > upper_vertical_angle) { + return Vector2(-1.0, -1.0); + } + } else if (vertical_intersection_angle > lower_vertical_angle) { + return Vector2(-1.0, -1.0); + } + + // Re-center the intersection angle if the vertical angle is uneven between upper and lower. + if (upper_vertical_angle != lower_vertical_angle) { + vertical_intersection_angle -= (-upper_vertical_angle + lower_vertical_angle) / 2.0; + } + + float u = 0.5 + (horizontal_intersection_angle / central_horizontal_angle); + float v = 0.5 + (vertical_intersection_angle / (upper_vertical_angle + lower_vertical_angle)); + + return Vector2(u, v); +} diff --git a/modules/openxr/scene/openxr_composition_layer_equirect.h b/modules/openxr/scene/openxr_composition_layer_equirect.h index b74e131f35..7a002a48dc 100644 --- a/modules/openxr/scene/openxr_composition_layer_equirect.h +++ b/modules/openxr/scene/openxr_composition_layer_equirect.h @@ -70,6 +70,8 @@ public: void set_fallback_segments(uint32_t p_fallback_segments); uint32_t get_fallback_segments() const; + virtual Vector2 intersects_ray(const Vector3 &p_origin, const Vector3 &p_direction) const override; + OpenXRCompositionLayerEquirect(); ~OpenXRCompositionLayerEquirect(); }; diff --git a/modules/openxr/scene/openxr_composition_layer_quad.cpp b/modules/openxr/scene/openxr_composition_layer_quad.cpp index 342df05808..17d57851e4 100644 --- a/modules/openxr/scene/openxr_composition_layer_quad.cpp +++ b/modules/openxr/scene/openxr_composition_layer_quad.cpp @@ -96,3 +96,36 @@ void OpenXRCompositionLayerQuad::set_quad_size(const Size2 &p_size) { Size2 OpenXRCompositionLayerQuad::get_quad_size() const { return quad_size; } + +Vector2 OpenXRCompositionLayerQuad::intersects_ray(const Vector3 &p_origin, const Vector3 &p_direction) const { + Transform3D quad_transform = get_global_transform(); + Vector3 quad_normal = quad_transform.basis.get_column(2); + + float denom = quad_normal.dot(p_direction); + if (Math::abs(denom) > 0.0001) { + Vector3 vector = quad_transform.origin - p_origin; + float t = vector.dot(quad_normal) / denom; + if (t < 0.0) { + return Vector2(-1.0, -1.0); + } + Vector3 intersection = p_origin + p_direction * t; + + Vector3 relative_point = intersection - quad_transform.origin; + Vector2 projected_point = Vector2( + relative_point.dot(quad_transform.basis.get_column(0)), + relative_point.dot(quad_transform.basis.get_column(1))); + if (Math::abs(projected_point.x) > quad_size.x / 2.0) { + return Vector2(-1.0, -1.0); + } + if (Math::abs(projected_point.y) > quad_size.y / 2.0) { + return Vector2(-1.0, -1.0); + } + + float u = 0.5 + (projected_point.x / quad_size.x); + float v = 1.0 - (0.5 + (projected_point.y / quad_size.y)); + + return Vector2(u, v); + } + + return Vector2(-1.0, -1.0); +} diff --git a/modules/openxr/scene/openxr_composition_layer_quad.h b/modules/openxr/scene/openxr_composition_layer_quad.h index e1141586c1..d88b596984 100644 --- a/modules/openxr/scene/openxr_composition_layer_quad.h +++ b/modules/openxr/scene/openxr_composition_layer_quad.h @@ -54,6 +54,8 @@ public: void set_quad_size(const Size2 &p_size); Size2 get_quad_size() const; + virtual Vector2 intersects_ray(const Vector3 &p_origin, const Vector3 &p_direction) const override; + OpenXRCompositionLayerQuad(); ~OpenXRCompositionLayerQuad(); }; diff --git a/modules/openxr/scene/openxr_hand.cpp b/modules/openxr/scene/openxr_hand.cpp index 2a4104f6ee..f20d1f8e19 100644 --- a/modules/openxr/scene/openxr_hand.cpp +++ b/modules/openxr/scene/openxr_hand.cpp @@ -40,9 +40,6 @@ void OpenXRHand::_bind_methods() { ClassDB::bind_method(D_METHOD("set_hand", "hand"), &OpenXRHand::set_hand); ClassDB::bind_method(D_METHOD("get_hand"), &OpenXRHand::get_hand); - ClassDB::bind_method(D_METHOD("set_hand_skeleton", "hand_skeleton"), &OpenXRHand::set_hand_skeleton); - ClassDB::bind_method(D_METHOD("get_hand_skeleton"), &OpenXRHand::get_hand_skeleton); - ClassDB::bind_method(D_METHOD("set_motion_range", "motion_range"), &OpenXRHand::set_motion_range); ClassDB::bind_method(D_METHOD("get_motion_range"), &OpenXRHand::get_motion_range); @@ -54,7 +51,6 @@ void OpenXRHand::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "hand", PROPERTY_HINT_ENUM, "Left,Right"), "set_hand", "get_hand"); ADD_PROPERTY(PropertyInfo(Variant::INT, "motion_range", PROPERTY_HINT_ENUM, "Unobstructed,Conform to controller"), "set_motion_range", "get_motion_range"); - ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "hand_skeleton", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Skeleton3D"), "set_hand_skeleton", "get_hand_skeleton"); ADD_PROPERTY(PropertyInfo(Variant::INT, "skeleton_rig", PROPERTY_HINT_ENUM, "OpenXR,Humanoid"), "set_skeleton_rig", "get_skeleton_rig"); ADD_PROPERTY(PropertyInfo(Variant::INT, "bone_update", PROPERTY_HINT_ENUM, "Full,Rotation Only"), "set_bone_update", "get_bone_update"); @@ -90,12 +86,6 @@ OpenXRHand::Hands OpenXRHand::get_hand() const { return hand; } -void OpenXRHand::set_hand_skeleton(const NodePath &p_hand_skeleton) { - hand_skeleton = p_hand_skeleton; - - // TODO if inside tree call _get_bones() -} - void OpenXRHand::set_motion_range(MotionRange p_motion_range) { ERR_FAIL_INDEX(p_motion_range, MOTION_RANGE_MAX); motion_range = p_motion_range; @@ -107,10 +97,6 @@ OpenXRHand::MotionRange OpenXRHand::get_motion_range() const { return motion_range; } -NodePath OpenXRHand::get_hand_skeleton() const { - return hand_skeleton; -} - void OpenXRHand::_set_motion_range() { if (!hand_tracking_ext) { return; @@ -152,20 +138,6 @@ OpenXRHand::BoneUpdate OpenXRHand::get_bone_update() const { return bone_update; } -Skeleton3D *OpenXRHand::get_skeleton() { - if (!has_node(hand_skeleton)) { - return nullptr; - } - - Node *node = get_node(hand_skeleton); - if (!node) { - return nullptr; - } - - Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(node); - return skeleton; -} - void OpenXRHand::_get_joint_data() { // Table of bone names for different rig types. static const String bone_names[SKELETON_RIG_MAX][XR_HAND_JOINT_COUNT_EXT] = { @@ -290,7 +262,7 @@ void OpenXRHand::_get_joint_data() { } } -void OpenXRHand::_update_skeleton() { +void OpenXRHand::_process_modification() { if (openxr_api == nullptr || !openxr_api->is_initialized()) { return; } else if (hand_tracking_ext == nullptr || !hand_tracking_ext->get_active()) { @@ -395,21 +367,14 @@ void OpenXRHand::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { _get_joint_data(); - - set_process_internal(true); } break; case NOTIFICATION_EXIT_TREE: { - set_process_internal(false); - // reset for (int i = 0; i < XR_HAND_JOINT_COUNT_EXT; i++) { joints[i].bone = -1; joints[i].parent_joint = -1; } } break; - case NOTIFICATION_INTERNAL_PROCESS: { - _update_skeleton(); - } break; default: { } break; } diff --git a/modules/openxr/scene/openxr_hand.h b/modules/openxr/scene/openxr_hand.h index 4c77e7277c..fc0a994f48 100644 --- a/modules/openxr/scene/openxr_hand.h +++ b/modules/openxr/scene/openxr_hand.h @@ -31,16 +31,15 @@ #ifndef OPENXR_HAND_H #define OPENXR_HAND_H -#include "scene/3d/node_3d.h" -#include "scene/3d/skeleton_3d.h" +#include "scene/3d/skeleton_modifier_3d.h" #include <openxr/openxr.h> class OpenXRAPI; class OpenXRHandTrackingExtension; -class OpenXRHand : public Node3D { - GDCLASS(OpenXRHand, Node3D); +class OpenXRHand : public SkeletonModifier3D { + GDCLASS(OpenXRHand, SkeletonModifier3D); public: enum Hands { // Deprecated, need to change this to OpenXRInterface::Hands. @@ -86,13 +85,13 @@ private: void _set_motion_range(); - Skeleton3D *get_skeleton(); void _get_joint_data(); - void _update_skeleton(); protected: static void _bind_methods(); + virtual void _process_modification() override; + public: OpenXRHand(); @@ -102,9 +101,6 @@ public: void set_motion_range(MotionRange p_motion_range); MotionRange get_motion_range() const; - void set_hand_skeleton(const NodePath &p_hand_skeleton); - NodePath get_hand_skeleton() const; - void set_skeleton_rig(SkeletonRig p_skeleton_rig); SkeletonRig get_skeleton_rig() const; diff --git a/modules/openxr/util.h b/modules/openxr/util.h index 7488b5cf8e..4c611d6f4a 100644 --- a/modules/openxr/util.h +++ b/modules/openxr/util.h @@ -34,21 +34,23 @@ #define UNPACK(...) __VA_ARGS__ #define INIT_XR_FUNC_V(openxr_api, name) \ - do { \ + if constexpr (true) { \ XrResult get_instance_proc_addr_result; \ get_instance_proc_addr_result = openxr_api->get_instance_proc_addr(#name, (PFN_xrVoidFunction *)&name##_ptr); \ ERR_FAIL_COND_V(XR_FAILED(get_instance_proc_addr_result), false); \ - } while (0) + } else \ + ((void)0) #define EXT_INIT_XR_FUNC_V(name) INIT_XR_FUNC_V(OpenXRAPI::get_singleton(), name) #define OPENXR_API_INIT_XR_FUNC_V(name) INIT_XR_FUNC_V(this, name) #define INIT_XR_FUNC(openxr_api, name) \ - do { \ + if constexpr (true) { \ XrResult get_instance_proc_addr_result; \ get_instance_proc_addr_result = openxr_api->get_instance_proc_addr(#name, (PFN_xrVoidFunction *)&name##_ptr); \ ERR_FAIL_COND(XR_FAILED(get_instance_proc_addr_result)); \ - } while (0) + } else \ + ((void)0) #define EXT_INIT_XR_FUNC(name) INIT_XR_FUNC(OpenXRAPI::get_singleton(), name) #define OPENXR_API_INIT_XR_FUNC(name) INIT_XR_FUNC(this, name) @@ -59,16 +61,18 @@ #define EXT_TRY_INIT_XR_FUNC(name) TRY_INIT_XR_FUNC(OpenXRAPI::get_singleton(), name) #define OPENXR_TRY_API_INIT_XR_FUNC(name) TRY_INIT_XR_FUNC(this, name) #define GDEXTENSION_INIT_XR_FUNC(name) \ - do { \ + if constexpr (true) { \ name##_ptr = reinterpret_cast<PFN_##name>(get_openxr_api()->get_instance_proc_addr(#name)); \ ERR_FAIL_NULL(name##_ptr); \ - } while (0) + } else \ + ((void)0) #define GDEXTENSION_INIT_XR_FUNC_V(name) \ - do { \ + if constexpr (true) { \ name##_ptr = reinterpret_cast<PFN_##name>(get_openxr_api()->get_instance_proc_addr(#name)); \ ERR_FAIL_NULL_V(name##_ptr, false); \ - } while (0) + } else \ + ((void)0) #define EXT_PROTO_XRRESULT_FUNC1(func_name, arg1_type, arg1) \ PFN_##func_name func_name##_ptr = nullptr; \ diff --git a/modules/text_server_adv/SCsub b/modules/text_server_adv/SCsub index b04ad77bc9..15695476de 100644 --- a/modules/text_server_adv/SCsub +++ b/modules/text_server_adv/SCsub @@ -117,6 +117,7 @@ if env["builtin_harfbuzz"]: "src/hb-subset-cff1.cc", "src/hb-subset-cff2.cc", "src/hb-subset-input.cc", + "src/hb-subset-instancer-iup.cc", "src/hb-subset-instancer-solver.cc", "src/hb-subset-plan.cc", "src/hb-subset-repacker.cc", diff --git a/modules/text_server_adv/gdextension_build/SConstruct b/modules/text_server_adv/gdextension_build/SConstruct index 1f27f5ade9..1d9d36fbbf 100644 --- a/modules/text_server_adv/gdextension_build/SConstruct +++ b/modules/text_server_adv/gdextension_build/SConstruct @@ -357,6 +357,7 @@ thirdparty_harfbuzz_sources = [ "src/hb-subset-cff1.cc", "src/hb-subset-cff2.cc", "src/hb-subset-input.cc", + "src/hb-subset-instancer-iup.cc", "src/hb-subset-instancer-solver.cc", "src/hb-subset-plan.cc", "src/hb-subset-repacker.cc", diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp index f5174d7d46..1ed335fe99 100644 --- a/modules/text_server_adv/text_server_adv.cpp +++ b/modules/text_server_adv/text_server_adv.cpp @@ -958,7 +958,7 @@ void TextServerAdvanced::_generateMTSDF_threaded(void *p_td, uint32_t p_y) { } } -_FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_msdf(FontAdvanced *p_font_data, FontForSizeAdvanced *p_data, int p_pixel_range, int p_rect_margin, FT_Outline *outline, const Vector2 &advance) const { +_FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_msdf(FontAdvanced *p_font_data, FontForSizeAdvanced *p_data, int p_pixel_range, int p_rect_margin, FT_Outline *p_outline, const Vector2 &p_advance) const { msdfgen::Shape shape; shape.contours.clear(); @@ -974,13 +974,13 @@ _FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_msdf( ft_functions.shift = 0; ft_functions.delta = 0; - int error = FT_Outline_Decompose(outline, &ft_functions, &context); + int error = FT_Outline_Decompose(p_outline, &ft_functions, &context); ERR_FAIL_COND_V_MSG(error, FontGlyph(), "FreeType: Outline decomposition error: '" + String(FT_Error_String(error)) + "'."); if (!shape.contours.empty() && shape.contours.back().edges.empty()) { shape.contours.pop_back(); } - if (FT_Outline_Get_Orientation(outline) == 1) { + if (FT_Outline_Get_Orientation(p_outline) == 1) { for (int i = 0; i < (int)shape.contours.size(); ++i) { shape.contours[i].reverse(); } @@ -993,12 +993,19 @@ _FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_msdf( FontGlyph chr; chr.found = true; - chr.advance = advance; + chr.advance = p_advance; if (shape.validate() && shape.contours.size() > 0) { int w = (bounds.r - bounds.l); int h = (bounds.t - bounds.b); + if (w == 0 || h == 0) { + chr.texture_idx = -1; + chr.uv_rect = Rect2(); + chr.rect = Rect2(); + return chr; + } + int mw = w + p_rect_margin * 4; int mh = h + p_rect_margin * 4; @@ -1056,12 +1063,24 @@ _FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_msdf( #endif #ifdef MODULE_FREETYPE_ENABLED -_FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_bitmap(FontForSizeAdvanced *p_data, int p_rect_margin, FT_Bitmap bitmap, int yofs, int xofs, const Vector2 &advance, bool p_bgra) const { - int w = bitmap.width; - int h = bitmap.rows; +_FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_bitmap(FontForSizeAdvanced *p_data, int p_rect_margin, FT_Bitmap p_bitmap, int p_yofs, int p_xofs, const Vector2 &p_advance, bool p_bgra) const { + FontGlyph chr; + chr.advance = p_advance * p_data->scale / p_data->oversampling; + chr.found = true; + + int w = p_bitmap.width; + int h = p_bitmap.rows; + + if (w == 0 || h == 0) { + chr.texture_idx = -1; + chr.uv_rect = Rect2(); + chr.rect = Rect2(); + return chr; + } + int color_size = 2; - switch (bitmap.pixel_mode) { + switch (p_bitmap.pixel_mode) { case FT_PIXEL_MODE_MONO: case FT_PIXEL_MODE_GRAY: { color_size = 2; @@ -1100,54 +1119,54 @@ _FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_bitma for (int j = 0; j < w; j++) { int ofs = ((i + tex_pos.y + p_rect_margin * 2) * tex.texture_w + j + tex_pos.x + p_rect_margin * 2) * color_size; ERR_FAIL_COND_V(ofs >= tex.image->data_size(), FontGlyph()); - switch (bitmap.pixel_mode) { + switch (p_bitmap.pixel_mode) { case FT_PIXEL_MODE_MONO: { - int byte = i * bitmap.pitch + (j >> 3); + int byte = i * p_bitmap.pitch + (j >> 3); int bit = 1 << (7 - (j % 8)); wr[ofs + 0] = 255; // grayscale as 1 - wr[ofs + 1] = (bitmap.buffer[byte] & bit) ? 255 : 0; + wr[ofs + 1] = (p_bitmap.buffer[byte] & bit) ? 255 : 0; } break; case FT_PIXEL_MODE_GRAY: wr[ofs + 0] = 255; // grayscale as 1 - wr[ofs + 1] = bitmap.buffer[i * bitmap.pitch + j]; + wr[ofs + 1] = p_bitmap.buffer[i * p_bitmap.pitch + j]; break; case FT_PIXEL_MODE_BGRA: { - int ofs_color = i * bitmap.pitch + (j << 2); - wr[ofs + 2] = bitmap.buffer[ofs_color + 0]; - wr[ofs + 1] = bitmap.buffer[ofs_color + 1]; - wr[ofs + 0] = bitmap.buffer[ofs_color + 2]; - wr[ofs + 3] = bitmap.buffer[ofs_color + 3]; + int ofs_color = i * p_bitmap.pitch + (j << 2); + wr[ofs + 2] = p_bitmap.buffer[ofs_color + 0]; + wr[ofs + 1] = p_bitmap.buffer[ofs_color + 1]; + wr[ofs + 0] = p_bitmap.buffer[ofs_color + 2]; + wr[ofs + 3] = p_bitmap.buffer[ofs_color + 3]; } break; case FT_PIXEL_MODE_LCD: { - int ofs_color = i * bitmap.pitch + (j * 3); + int ofs_color = i * p_bitmap.pitch + (j * 3); if (p_bgra) { - wr[ofs + 0] = bitmap.buffer[ofs_color + 2]; - wr[ofs + 1] = bitmap.buffer[ofs_color + 1]; - wr[ofs + 2] = bitmap.buffer[ofs_color + 0]; + wr[ofs + 0] = p_bitmap.buffer[ofs_color + 2]; + wr[ofs + 1] = p_bitmap.buffer[ofs_color + 1]; + wr[ofs + 2] = p_bitmap.buffer[ofs_color + 0]; wr[ofs + 3] = 255; } else { - wr[ofs + 0] = bitmap.buffer[ofs_color + 0]; - wr[ofs + 1] = bitmap.buffer[ofs_color + 1]; - wr[ofs + 2] = bitmap.buffer[ofs_color + 2]; + wr[ofs + 0] = p_bitmap.buffer[ofs_color + 0]; + wr[ofs + 1] = p_bitmap.buffer[ofs_color + 1]; + wr[ofs + 2] = p_bitmap.buffer[ofs_color + 2]; wr[ofs + 3] = 255; } } break; case FT_PIXEL_MODE_LCD_V: { - int ofs_color = i * bitmap.pitch * 3 + j; + int ofs_color = i * p_bitmap.pitch * 3 + j; if (p_bgra) { - wr[ofs + 0] = bitmap.buffer[ofs_color + bitmap.pitch * 2]; - wr[ofs + 1] = bitmap.buffer[ofs_color + bitmap.pitch]; - wr[ofs + 2] = bitmap.buffer[ofs_color + 0]; + wr[ofs + 0] = p_bitmap.buffer[ofs_color + p_bitmap.pitch * 2]; + wr[ofs + 1] = p_bitmap.buffer[ofs_color + p_bitmap.pitch]; + wr[ofs + 2] = p_bitmap.buffer[ofs_color + 0]; wr[ofs + 3] = 255; } else { - wr[ofs + 0] = bitmap.buffer[ofs_color + 0]; - wr[ofs + 1] = bitmap.buffer[ofs_color + bitmap.pitch]; - wr[ofs + 2] = bitmap.buffer[ofs_color + bitmap.pitch * 2]; + wr[ofs + 0] = p_bitmap.buffer[ofs_color + 0]; + wr[ofs + 1] = p_bitmap.buffer[ofs_color + p_bitmap.pitch]; + wr[ofs + 2] = p_bitmap.buffer[ofs_color + p_bitmap.pitch * 2]; wr[ofs + 3] = 255; } } break; default: - ERR_FAIL_V_MSG(FontGlyph(), "Font uses unsupported pixel format: " + String::num_int64(bitmap.pixel_mode) + "."); + ERR_FAIL_V_MSG(FontGlyph(), "Font uses unsupported pixel format: " + String::num_int64(p_bitmap.pixel_mode) + "."); break; } } @@ -1156,13 +1175,10 @@ _FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_bitma tex.dirty = true; - FontGlyph chr; - chr.advance = advance * p_data->scale / p_data->oversampling; chr.texture_idx = tex_pos.index; - chr.found = true; chr.uv_rect = Rect2(tex_pos.x + p_rect_margin, tex_pos.y + p_rect_margin, w + p_rect_margin * 2, h + p_rect_margin * 2); - chr.rect.position = Vector2(xofs - p_rect_margin, -yofs - p_rect_margin) * p_data->scale / p_data->oversampling; + chr.rect.position = Vector2(p_xofs - p_rect_margin, -p_yofs - p_rect_margin) * p_data->scale / p_data->oversampling; chr.rect.size = chr.uv_rect.size * p_data->scale / p_data->oversampling; return chr; } @@ -1432,8 +1448,25 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontAdvanced *p_f #endif if (!p_font_data->face_init) { - // Get style flags and name. - if (fd->face->family_name != nullptr) { + // When a font does not provide a `family_name`, FreeType tries to synthesize one based on other names. + // FreeType automatically converts non-ASCII characters to "?" in the synthesized name. + // To avoid that behavior, use the format-specific name directly if available. + hb_face_t *hb_face = hb_font_get_face(fd->hb_handle); + unsigned int num_entries = 0; + const hb_ot_name_entry_t *names = hb_ot_name_list_names(hb_face, &num_entries); + const hb_language_t english = hb_language_from_string("en", -1); + for (unsigned int i = 0; i < num_entries; i++) { + if (names[i].name_id != HB_OT_NAME_ID_FONT_FAMILY) { + continue; + } + if (!p_font_data->font_name.is_empty() && names[i].language != english) { + continue; + } + unsigned int text_size = hb_ot_name_get_utf32(hb_face, names[i].name_id, names[i].language, nullptr, nullptr) + 1; + p_font_data->font_name.resize(text_size); + hb_ot_name_get_utf32(hb_face, names[i].name_id, names[i].language, &text_size, (uint32_t *)p_font_data->font_name.ptrw()); + } + if (p_font_data->font_name.is_empty() && fd->face->family_name != nullptr) { p_font_data->font_name = String::utf8((const char *)fd->face->family_name); } if (fd->face->style_name != nullptr) { @@ -1452,7 +1485,6 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontAdvanced *p_f p_font_data->style_flags.set_flag(FONT_FIXED_WIDTH); } - hb_face_t *hb_face = hb_font_get_face(fd->hb_handle); // Get supported scripts from OpenType font data. p_font_data->supported_scripts.clear(); unsigned int count = hb_ot_layout_table_get_script_tags(hb_face, HB_OT_TAG_GSUB, 0, nullptr, nullptr); @@ -3603,9 +3635,6 @@ void TextServerAdvanced::_font_draw_glyph(const RID &p_font_rid, const RID &p_ca const FontGlyph &gl = fd->cache[size]->glyph_map[index]; if (gl.found) { - if (gl.uv_rect.size.x <= 2 || gl.uv_rect.size.y <= 2) { - return; // Nothing to draw. - } ERR_FAIL_COND(gl.texture_idx < -1 || gl.texture_idx >= fd->cache[size]->textures.size()); if (gl.texture_idx != -1) { @@ -3714,9 +3743,6 @@ void TextServerAdvanced::_font_draw_glyph_outline(const RID &p_font_rid, const R const FontGlyph &gl = fd->cache[size]->glyph_map[index]; if (gl.found) { - if (gl.uv_rect.size.x <= 2 || gl.uv_rect.size.y <= 2) { - return; // Nothing to draw. - } ERR_FAIL_COND(gl.texture_idx < -1 || gl.texture_idx >= fd->cache[size]->textures.size()); if (gl.texture_idx != -1) { @@ -6976,6 +7002,34 @@ String TextServerAdvanced::_string_to_lower(const String &p_string, const String return String::utf16(lower.ptr(), len); } +String TextServerAdvanced::_string_to_title(const String &p_string, const String &p_language) const { +#ifndef ICU_STATIC_DATA + if (!icu_data_loaded) { + return p_string.capitalize(); + } +#endif + + if (p_string.is_empty()) { + return p_string; + } + const String lang = (p_language.is_empty()) ? TranslationServer::get_singleton()->get_tool_locale() : p_language; + + // Convert to UTF-16. + Char16String utf16 = p_string.utf16(); + + Vector<char16_t> upper; + UErrorCode err = U_ZERO_ERROR; + int32_t len = u_strToTitle(nullptr, 0, utf16.get_data(), -1, nullptr, lang.ascii().get_data(), &err); + ERR_FAIL_COND_V_MSG(err != U_BUFFER_OVERFLOW_ERROR, p_string, u_errorName(err)); + upper.resize(len); + err = U_ZERO_ERROR; + u_strToTitle(upper.ptrw(), len, utf16.get_data(), -1, nullptr, lang.ascii().get_data(), &err); + ERR_FAIL_COND_V_MSG(U_FAILURE(err), p_string, u_errorName(err)); + + // Convert back to UTF-32. + return String::utf16(upper.ptr(), len); +} + PackedInt32Array TextServerAdvanced::_string_get_word_breaks(const String &p_string, const String &p_language, int64_t p_chars_per_line) const { const String lang = (p_language.is_empty()) ? TranslationServer::get_singleton()->get_tool_locale() : p_language; // Convert to UTF-16. diff --git a/modules/text_server_adv/text_server_adv.h b/modules/text_server_adv/text_server_adv.h index 154fe670eb..1cd73a6999 100644 --- a/modules/text_server_adv/text_server_adv.h +++ b/modules/text_server_adv/text_server_adv.h @@ -354,10 +354,10 @@ class TextServerAdvanced : public TextServerExtension { _FORCE_INLINE_ FontTexturePosition find_texture_pos_for_glyph(FontForSizeAdvanced *p_data, int p_color_size, Image::Format p_image_format, int p_width, int p_height, bool p_msdf) const; #ifdef MODULE_MSDFGEN_ENABLED - _FORCE_INLINE_ FontGlyph rasterize_msdf(FontAdvanced *p_font_data, FontForSizeAdvanced *p_data, int p_pixel_range, int p_rect_margin, FT_Outline *outline, const Vector2 &advance) const; + _FORCE_INLINE_ FontGlyph rasterize_msdf(FontAdvanced *p_font_data, FontForSizeAdvanced *p_data, int p_pixel_range, int p_rect_margin, FT_Outline *p_outline, const Vector2 &p_advance) const; #endif #ifdef MODULE_FREETYPE_ENABLED - _FORCE_INLINE_ FontGlyph rasterize_bitmap(FontForSizeAdvanced *p_data, int p_rect_margin, FT_Bitmap bitmap, int yofs, int xofs, const Vector2 &advance, bool p_bgra) const; + _FORCE_INLINE_ FontGlyph rasterize_bitmap(FontForSizeAdvanced *p_data, int p_rect_margin, FT_Bitmap p_bitmap, int p_yofs, int p_xofs, const Vector2 &p_advance, bool p_bgra) const; #endif _FORCE_INLINE_ bool _ensure_glyph(FontAdvanced *p_font_data, const Vector2i &p_size, int32_t p_glyph) const; _FORCE_INLINE_ bool _ensure_cache_for_size(FontAdvanced *p_font_data, const Vector2i &p_size) const; @@ -991,6 +991,7 @@ public: MODBIND2RC(String, string_to_upper, const String &, const String &); MODBIND2RC(String, string_to_lower, const String &, const String &); + MODBIND2RC(String, string_to_title, const String &, const String &); MODBIND0(cleanup); diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp index 302bd677fe..c62f308818 100644 --- a/modules/text_server_fb/text_server_fb.cpp +++ b/modules/text_server_fb/text_server_fb.cpp @@ -71,8 +71,10 @@ using namespace godot; #endif #endif -#ifdef MODULE_SVG_ENABLED #ifdef MODULE_FREETYPE_ENABLED +#include FT_SFNT_NAMES_H +#include FT_TRUETYPE_IDS_H +#ifdef MODULE_SVG_ENABLED #include "thorvg_svg_in_ot.h" #endif #endif @@ -392,7 +394,7 @@ void TextServerFallback::_generateMTSDF_threaded(void *p_td, uint32_t p_y) { } } -_FORCE_INLINE_ TextServerFallback::FontGlyph TextServerFallback::rasterize_msdf(FontFallback *p_font_data, FontForSizeFallback *p_data, int p_pixel_range, int p_rect_margin, FT_Outline *outline, const Vector2 &advance) const { +_FORCE_INLINE_ TextServerFallback::FontGlyph TextServerFallback::rasterize_msdf(FontFallback *p_font_data, FontForSizeFallback *p_data, int p_pixel_range, int p_rect_margin, FT_Outline *p_outline, const Vector2 &p_advance) const { msdfgen::Shape shape; shape.contours.clear(); @@ -408,13 +410,13 @@ _FORCE_INLINE_ TextServerFallback::FontGlyph TextServerFallback::rasterize_msdf( ft_functions.shift = 0; ft_functions.delta = 0; - int error = FT_Outline_Decompose(outline, &ft_functions, &context); + int error = FT_Outline_Decompose(p_outline, &ft_functions, &context); ERR_FAIL_COND_V_MSG(error, FontGlyph(), "FreeType: Outline decomposition error: '" + String(FT_Error_String(error)) + "'."); if (!shape.contours.empty() && shape.contours.back().edges.empty()) { shape.contours.pop_back(); } - if (FT_Outline_Get_Orientation(outline) == 1) { + if (FT_Outline_Get_Orientation(p_outline) == 1) { for (int i = 0; i < (int)shape.contours.size(); ++i) { shape.contours[i].reverse(); } @@ -427,12 +429,18 @@ _FORCE_INLINE_ TextServerFallback::FontGlyph TextServerFallback::rasterize_msdf( FontGlyph chr; chr.found = true; - chr.advance = advance; + chr.advance = p_advance; if (shape.validate() && shape.contours.size() > 0) { int w = (bounds.r - bounds.l); int h = (bounds.t - bounds.b); + if (w == 0 || h == 0) { + chr.texture_idx = -1; + chr.uv_rect = Rect2(); + chr.rect = Rect2(); + return chr; + } int mw = w + p_rect_margin * 4; int mh = h + p_rect_margin * 4; @@ -489,12 +497,24 @@ _FORCE_INLINE_ TextServerFallback::FontGlyph TextServerFallback::rasterize_msdf( #endif #ifdef MODULE_FREETYPE_ENABLED -_FORCE_INLINE_ TextServerFallback::FontGlyph TextServerFallback::rasterize_bitmap(FontForSizeFallback *p_data, int p_rect_margin, FT_Bitmap bitmap, int yofs, int xofs, const Vector2 &advance, bool p_bgra) const { - int w = bitmap.width; - int h = bitmap.rows; +_FORCE_INLINE_ TextServerFallback::FontGlyph TextServerFallback::rasterize_bitmap(FontForSizeFallback *p_data, int p_rect_margin, FT_Bitmap p_bitmap, int p_yofs, int p_xofs, const Vector2 &p_advance, bool p_bgra) const { + FontGlyph chr; + chr.advance = p_advance * p_data->scale / p_data->oversampling; + chr.found = true; + + int w = p_bitmap.width; + int h = p_bitmap.rows; + + if (w == 0 || h == 0) { + chr.texture_idx = -1; + chr.uv_rect = Rect2(); + chr.rect = Rect2(); + return chr; + } + int color_size = 2; - switch (bitmap.pixel_mode) { + switch (p_bitmap.pixel_mode) { case FT_PIXEL_MODE_MONO: case FT_PIXEL_MODE_GRAY: { color_size = 2; @@ -533,54 +553,54 @@ _FORCE_INLINE_ TextServerFallback::FontGlyph TextServerFallback::rasterize_bitma for (int j = 0; j < w; j++) { int ofs = ((i + tex_pos.y + p_rect_margin * 2) * tex.texture_w + j + tex_pos.x + p_rect_margin * 2) * color_size; ERR_FAIL_COND_V(ofs >= tex.image->data_size(), FontGlyph()); - switch (bitmap.pixel_mode) { + switch (p_bitmap.pixel_mode) { case FT_PIXEL_MODE_MONO: { - int byte = i * bitmap.pitch + (j >> 3); + int byte = i * p_bitmap.pitch + (j >> 3); int bit = 1 << (7 - (j % 8)); wr[ofs + 0] = 255; // grayscale as 1 - wr[ofs + 1] = (bitmap.buffer[byte] & bit) ? 255 : 0; + wr[ofs + 1] = (p_bitmap.buffer[byte] & bit) ? 255 : 0; } break; case FT_PIXEL_MODE_GRAY: wr[ofs + 0] = 255; // grayscale as 1 - wr[ofs + 1] = bitmap.buffer[i * bitmap.pitch + j]; + wr[ofs + 1] = p_bitmap.buffer[i * p_bitmap.pitch + j]; break; case FT_PIXEL_MODE_BGRA: { - int ofs_color = i * bitmap.pitch + (j << 2); - wr[ofs + 2] = bitmap.buffer[ofs_color + 0]; - wr[ofs + 1] = bitmap.buffer[ofs_color + 1]; - wr[ofs + 0] = bitmap.buffer[ofs_color + 2]; - wr[ofs + 3] = bitmap.buffer[ofs_color + 3]; + int ofs_color = i * p_bitmap.pitch + (j << 2); + wr[ofs + 2] = p_bitmap.buffer[ofs_color + 0]; + wr[ofs + 1] = p_bitmap.buffer[ofs_color + 1]; + wr[ofs + 0] = p_bitmap.buffer[ofs_color + 2]; + wr[ofs + 3] = p_bitmap.buffer[ofs_color + 3]; } break; case FT_PIXEL_MODE_LCD: { - int ofs_color = i * bitmap.pitch + (j * 3); + int ofs_color = i * p_bitmap.pitch + (j * 3); if (p_bgra) { - wr[ofs + 0] = bitmap.buffer[ofs_color + 2]; - wr[ofs + 1] = bitmap.buffer[ofs_color + 1]; - wr[ofs + 2] = bitmap.buffer[ofs_color + 0]; + wr[ofs + 0] = p_bitmap.buffer[ofs_color + 2]; + wr[ofs + 1] = p_bitmap.buffer[ofs_color + 1]; + wr[ofs + 2] = p_bitmap.buffer[ofs_color + 0]; wr[ofs + 3] = 255; } else { - wr[ofs + 0] = bitmap.buffer[ofs_color + 0]; - wr[ofs + 1] = bitmap.buffer[ofs_color + 1]; - wr[ofs + 2] = bitmap.buffer[ofs_color + 2]; + wr[ofs + 0] = p_bitmap.buffer[ofs_color + 0]; + wr[ofs + 1] = p_bitmap.buffer[ofs_color + 1]; + wr[ofs + 2] = p_bitmap.buffer[ofs_color + 2]; wr[ofs + 3] = 255; } } break; case FT_PIXEL_MODE_LCD_V: { - int ofs_color = i * bitmap.pitch * 3 + j; + int ofs_color = i * p_bitmap.pitch * 3 + j; if (p_bgra) { - wr[ofs + 0] = bitmap.buffer[ofs_color + bitmap.pitch * 2]; - wr[ofs + 1] = bitmap.buffer[ofs_color + bitmap.pitch]; - wr[ofs + 2] = bitmap.buffer[ofs_color + 0]; + wr[ofs + 0] = p_bitmap.buffer[ofs_color + p_bitmap.pitch * 2]; + wr[ofs + 1] = p_bitmap.buffer[ofs_color + p_bitmap.pitch]; + wr[ofs + 2] = p_bitmap.buffer[ofs_color + 0]; wr[ofs + 3] = 255; } else { - wr[ofs + 0] = bitmap.buffer[ofs_color + 0]; - wr[ofs + 1] = bitmap.buffer[ofs_color + bitmap.pitch]; - wr[ofs + 2] = bitmap.buffer[ofs_color + bitmap.pitch * 2]; + wr[ofs + 0] = p_bitmap.buffer[ofs_color + 0]; + wr[ofs + 1] = p_bitmap.buffer[ofs_color + p_bitmap.pitch]; + wr[ofs + 2] = p_bitmap.buffer[ofs_color + p_bitmap.pitch * 2]; wr[ofs + 3] = 255; } } break; default: - ERR_FAIL_V_MSG(FontGlyph(), "Font uses unsupported pixel format: " + String::num_int64(bitmap.pixel_mode) + "."); + ERR_FAIL_V_MSG(FontGlyph(), "Font uses unsupported pixel format: " + String::num_int64(p_bitmap.pixel_mode) + "."); break; } } @@ -589,13 +609,10 @@ _FORCE_INLINE_ TextServerFallback::FontGlyph TextServerFallback::rasterize_bitma tex.dirty = true; - FontGlyph chr; - chr.advance = advance * p_data->scale / p_data->oversampling; chr.texture_idx = tex_pos.index; - chr.found = true; chr.uv_rect = Rect2(tex_pos.x + p_rect_margin, tex_pos.y + p_rect_margin, w + p_rect_margin * 2, h + p_rect_margin * 2); - chr.rect.position = Vector2(xofs - p_rect_margin, -yofs - p_rect_margin) * p_data->scale / p_data->oversampling; + chr.rect.position = Vector2(p_xofs - p_rect_margin, -p_yofs - p_rect_margin) * p_data->scale / p_data->oversampling; chr.rect.size = chr.uv_rect.size * p_data->scale / p_data->oversampling; return chr; } @@ -857,8 +874,37 @@ _FORCE_INLINE_ bool TextServerFallback::_ensure_cache_for_size(FontFallback *p_f fd->underline_thickness = (FT_MulFix(fd->face->underline_thickness, fd->face->size->metrics.y_scale) / 64.0) / fd->oversampling * fd->scale; if (!p_font_data->face_init) { - // Get style flags and name. - if (fd->face->family_name != nullptr) { + // When a font does not provide a `family_name`, FreeType tries to synthesize one based on other names. + // FreeType automatically converts non-ASCII characters to "?" in the synthesized name. + // To avoid that behavior, use the format-specific name directly if available. + if (FT_IS_SFNT(fd->face)) { + int name_count = FT_Get_Sfnt_Name_Count(fd->face); + for (int i = 0; i < name_count; i++) { + FT_SfntName sfnt_name; + if (FT_Get_Sfnt_Name(fd->face, i, &sfnt_name) != 0) { + continue; + } + if (sfnt_name.name_id != TT_NAME_ID_FONT_FAMILY && sfnt_name.name_id != TT_NAME_ID_TYPOGRAPHIC_FAMILY) { + continue; + } + if (!p_font_data->font_name.is_empty() && sfnt_name.language_id != TT_MS_LANGID_ENGLISH_UNITED_STATES) { + continue; + } + + switch (sfnt_name.platform_id) { + case TT_PLATFORM_APPLE_UNICODE: { + p_font_data->font_name.parse_utf16((const char16_t *)sfnt_name.string, sfnt_name.string_len / 2, false); + } break; + + case TT_PLATFORM_MICROSOFT: { + if (sfnt_name.encoding_id == TT_MS_ID_UNICODE_CS || sfnt_name.encoding_id == TT_MS_ID_UCS_4) { + p_font_data->font_name.parse_utf16((const char16_t *)sfnt_name.string, sfnt_name.string_len / 2, false); + } + } break; + } + } + } + if (p_font_data->font_name.is_empty() && fd->face->family_name != nullptr) { p_font_data->font_name = String::utf8((const char *)fd->face->family_name); } if (fd->face->style_name != nullptr) { @@ -2536,9 +2582,6 @@ void TextServerFallback::_font_draw_glyph(const RID &p_font_rid, const RID &p_ca const FontGlyph &gl = fd->cache[size]->glyph_map[index]; if (gl.found) { - if (gl.uv_rect.size.x <= 2 || gl.uv_rect.size.y <= 2) { - return; // Nothing to draw. - } ERR_FAIL_COND(gl.texture_idx < -1 || gl.texture_idx >= fd->cache[size]->textures.size()); if (gl.texture_idx != -1) { @@ -2647,9 +2690,6 @@ void TextServerFallback::_font_draw_glyph_outline(const RID &p_font_rid, const R const FontGlyph &gl = fd->cache[size]->glyph_map[index]; if (gl.found) { - if (gl.uv_rect.size.x <= 2 || gl.uv_rect.size.y <= 2) { - return; // Nothing to draw. - } ERR_FAIL_COND(gl.texture_idx < -1 || gl.texture_idx >= fd->cache[size]->textures.size()); if (gl.texture_idx != -1) { @@ -4439,6 +4479,10 @@ String TextServerFallback::_string_to_lower(const String &p_string, const String return p_string.to_lower(); } +String TextServerFallback::_string_to_title(const String &p_string, const String &p_language) const { + return p_string.capitalize(); +} + PackedInt32Array TextServerFallback::_string_get_word_breaks(const String &p_string, const String &p_language, int64_t p_chars_per_line) const { PackedInt32Array ret; diff --git a/modules/text_server_fb/text_server_fb.h b/modules/text_server_fb/text_server_fb.h index 0db1f7318f..31370c7da7 100644 --- a/modules/text_server_fb/text_server_fb.h +++ b/modules/text_server_fb/text_server_fb.h @@ -303,10 +303,10 @@ class TextServerFallback : public TextServerExtension { _FORCE_INLINE_ FontTexturePosition find_texture_pos_for_glyph(FontForSizeFallback *p_data, int p_color_size, Image::Format p_image_format, int p_width, int p_height, bool p_msdf) const; #ifdef MODULE_MSDFGEN_ENABLED - _FORCE_INLINE_ FontGlyph rasterize_msdf(FontFallback *p_font_data, FontForSizeFallback *p_data, int p_pixel_range, int p_rect_margin, FT_Outline *outline, const Vector2 &advance) const; + _FORCE_INLINE_ FontGlyph rasterize_msdf(FontFallback *p_font_data, FontForSizeFallback *p_data, int p_pixel_range, int p_rect_margin, FT_Outline *p_outline, const Vector2 &p_advance) const; #endif #ifdef MODULE_FREETYPE_ENABLED - _FORCE_INLINE_ FontGlyph rasterize_bitmap(FontForSizeFallback *p_data, int p_rect_margin, FT_Bitmap bitmap, int yofs, int xofs, const Vector2 &advance, bool p_bgra) const; + _FORCE_INLINE_ FontGlyph rasterize_bitmap(FontForSizeFallback *p_data, int p_rect_margin, FT_Bitmap p_bitmap, int p_yofs, int p_xofs, const Vector2 &p_advance, bool p_bgra) const; #endif _FORCE_INLINE_ bool _ensure_glyph(FontFallback *p_font_data, const Vector2i &p_size, int32_t p_glyph) const; _FORCE_INLINE_ bool _ensure_cache_for_size(FontFallback *p_font_data, const Vector2i &p_size) const; @@ -848,6 +848,7 @@ public: MODBIND2RC(String, string_to_upper, const String &, const String &); MODBIND2RC(String, string_to_lower, const String &, const String &); + MODBIND2RC(String, string_to_title, const String &, const String &); MODBIND0(cleanup); diff --git a/modules/upnp/doc_classes/UPNPDevice.xml b/modules/upnp/doc_classes/UPNPDevice.xml index a70ae1b9cc..82ac179611 100644 --- a/modules/upnp/doc_classes/UPNPDevice.xml +++ b/modules/upnp/doc_classes/UPNPDevice.xml @@ -71,7 +71,7 @@ <constant name="IGD_STATUS_HTTP_EMPTY" value="2" enum="IGDStatus"> Empty HTTP response. </constant> - <constant name="IGD_STATUS_NO_URLS" value="3" enum="IGDStatus"> + <constant name="IGD_STATUS_NO_URLS" value="3" enum="IGDStatus" deprecated="This value is no longer used."> Returned response contained no URLs. </constant> <constant name="IGD_STATUS_NO_IGD" value="4" enum="IGDStatus"> @@ -86,7 +86,7 @@ <constant name="IGD_STATUS_INVALID_CONTROL" value="7" enum="IGDStatus"> Invalid control. </constant> - <constant name="IGD_STATUS_MALLOC_ERROR" value="8" enum="IGDStatus"> + <constant name="IGD_STATUS_MALLOC_ERROR" value="8" enum="IGDStatus" deprecated="This value is no longer used."> Memory allocation error. </constant> <constant name="IGD_STATUS_UNKNOWN_ERROR" value="9" enum="IGDStatus"> diff --git a/modules/upnp/upnp.cpp b/modules/upnp/upnp.cpp index 2812f37eb2..95453c1ecd 100644 --- a/modules/upnp/upnp.cpp +++ b/modules/upnp/upnp.cpp @@ -121,33 +121,20 @@ void UPNP::parse_igd(Ref<UPNPDevice> dev, UPNPDev *devlist) { return; } - struct UPNPUrls *urls = (UPNPUrls *)malloc(sizeof(struct UPNPUrls)); - - if (!urls) { - dev->set_igd_status(UPNPDevice::IGD_STATUS_MALLOC_ERROR); - return; - } - + struct UPNPUrls urls = {}; struct IGDdatas data; - memset(urls, 0, sizeof(struct UPNPUrls)); - parserootdesc(xml, size, &data); free(xml); xml = nullptr; - GetUPNPUrls(urls, &data, dev->get_description_url().utf8().get_data(), 0); - - if (!urls) { - dev->set_igd_status(UPNPDevice::IGD_STATUS_NO_URLS); - return; - } + GetUPNPUrls(&urls, &data, dev->get_description_url().utf8().get_data(), 0); char addr[16]; - int i = UPNP_GetValidIGD(devlist, urls, &data, (char *)&addr, 16); + int i = UPNP_GetValidIGD(devlist, &urls, &data, (char *)&addr, 16); if (i != 1) { - FreeUPNPUrls(urls); + FreeUPNPUrls(&urls); switch (i) { case 0: @@ -165,18 +152,18 @@ void UPNP::parse_igd(Ref<UPNPDevice> dev, UPNPDev *devlist) { } } - if (urls->controlURL[0] == '\0') { - FreeUPNPUrls(urls); + if (urls.controlURL[0] == '\0') { + FreeUPNPUrls(&urls); dev->set_igd_status(UPNPDevice::IGD_STATUS_INVALID_CONTROL); return; } - dev->set_igd_control_url(urls->controlURL); + dev->set_igd_control_url(urls.controlURL); dev->set_igd_service_type(data.first.servicetype); dev->set_igd_our_addr(addr); dev->set_igd_status(UPNPDevice::IGD_STATUS_OK); - FreeUPNPUrls(urls); + FreeUPNPUrls(&urls); } int UPNP::upnp_result(int in) { diff --git a/platform/android/doc_classes/EditorExportPlatformAndroid.xml b/platform/android/doc_classes/EditorExportPlatformAndroid.xml index 7fce5359ae..b49475b0f2 100644 --- a/platform/android/doc_classes/EditorExportPlatformAndroid.xml +++ b/platform/android/doc_classes/EditorExportPlatformAndroid.xml @@ -595,6 +595,7 @@ Application version visible to the user. Falls back to [member ProjectSettings.application/config/version] if left empty. </member> <member name="xr_features/xr_mode" type="int" setter="" getter=""> + The extended reality (XR) mode for this application. </member> </members> </class> diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp index 64ef1397ba..19e215bfaf 100644 --- a/platform/android/export/export_plugin.cpp +++ b/platform/android/export/export_plugin.cpp @@ -1920,7 +1920,15 @@ bool EditorExportPlatformAndroid::get_export_option_visibility(const EditorExpor bool advanced_options_enabled = p_preset->are_advanced_options_enabled(); if (p_option == "graphics/opengl_debug" || p_option == "command_line/extra_args" || - p_option == "permissions/custom_permissions") { + p_option == "permissions/custom_permissions" || + p_option == "gradle_build/compress_native_libraries" || + p_option == "package/retain_data_on_uninstall" || + p_option == "package/exclude_from_recents" || + p_option == "package/show_in_app_library" || + p_option == "package/show_as_launcher_app" || + p_option == "apk_expansion/enable" || + p_option == "apk_expansion/SALT" || + p_option == "apk_expansion/public_key") { return advanced_options_enabled; } if (p_option == "gradle_build/gradle_build_directory" || p_option == "gradle_build/android_source_template") { diff --git a/platform/ios/export/export_plugin.cpp b/platform/ios/export/export_plugin.cpp index 82b4f6f904..dff4f844c7 100644 --- a/platform/ios/export/export_plugin.cpp +++ b/platform/ios/export/export_plugin.cpp @@ -1316,7 +1316,7 @@ Error EditorExportPlatformIOS::_export_additional_assets(const Ref<EditorExportP if (asset.begins_with("res://")) { Error err = _copy_asset(p_preset, p_out_dir, asset, nullptr, p_is_framework, p_should_embed, r_exported_assets); ERR_FAIL_COND_V(err != OK, err); - } else if (ProjectSettings::get_singleton()->localize_path(asset).begins_with("res://")) { + } else if (asset.is_absolute_path() && ProjectSettings::get_singleton()->localize_path(asset).begins_with("res://")) { Error err = _copy_asset(p_preset, p_out_dir, ProjectSettings::get_singleton()->localize_path(asset), nullptr, p_is_framework, p_should_embed, r_exported_assets); ERR_FAIL_COND_V(err != OK, err); } else { diff --git a/platform/linuxbsd/os_linuxbsd.cpp b/platform/linuxbsd/os_linuxbsd.cpp index f29275c910..68b4cd7f5a 100644 --- a/platform/linuxbsd/os_linuxbsd.cpp +++ b/platform/linuxbsd/os_linuxbsd.cpp @@ -62,6 +62,10 @@ #include <mntent.h> #endif +#if defined(__FreeBSD__) +#include <sys/sysctl.h> +#endif + void OS_LinuxBSD::alert(const String &p_alert, const String &p_title) { const char *message_programs[] = { "zenity", "kdialog", "Xdialog", "xmessage" }; @@ -145,17 +149,36 @@ void OS_LinuxBSD::initialize_joypads() { String OS_LinuxBSD::get_unique_id() const { static String machine_id; if (machine_id.is_empty()) { +#if defined(__FreeBSD__) + const int mib[2] = { CTL_KERN, KERN_HOSTUUID }; + char buf[4096]; + memset(buf, 0, sizeof(buf)); + size_t len = sizeof(buf) - 1; + if (sysctl(mib, 2, buf, &len, 0x0, 0) != -1) { + machine_id = String::utf8(buf).replace("-", ""); + } +#else Ref<FileAccess> f = FileAccess::open("/etc/machine-id", FileAccess::READ); if (f.is_valid()) { while (machine_id.is_empty() && !f->eof_reached()) { machine_id = f->get_line().strip_edges(); } } +#endif } return machine_id; } String OS_LinuxBSD::get_processor_name() const { +#if defined(__FreeBSD__) + const int mib[2] = { CTL_HW, HW_MODEL }; + char buf[4096]; + memset(buf, 0, sizeof(buf)); + size_t len = sizeof(buf) - 1; + if (sysctl(mib, 2, buf, &len, 0x0, 0) != -1) { + return String::utf8(buf); + } +#else Ref<FileAccess> f = FileAccess::open("/proc/cpuinfo", FileAccess::READ); ERR_FAIL_COND_V_MSG(f.is_null(), "", String("Couldn't open `/proc/cpuinfo` to get the CPU model name. Returning an empty string.")); @@ -165,8 +188,9 @@ String OS_LinuxBSD::get_processor_name() const { return line.split(":")[1].strip_edges(); } } +#endif - ERR_FAIL_V_MSG("", String("Couldn't get the CPU model name from `/proc/cpuinfo`. Returning an empty string.")); + ERR_FAIL_V_MSG("", String("Couldn't get the CPU model. Returning an empty string.")); } bool OS_LinuxBSD::is_sandboxed() const { diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp index e540b7617f..2d98feb81b 100644 --- a/platform/windows/display_server_windows.cpp +++ b/platform/windows/display_server_windows.cpp @@ -1895,7 +1895,7 @@ Size2i DisplayServerWindows::window_get_size_with_decorations(WindowID p_window) return Size2(); } -void DisplayServerWindows::_get_window_style(bool p_main_window, bool p_fullscreen, bool p_multiwindow_fs, bool p_borderless, bool p_resizable, bool p_maximized, bool p_no_activate_focus, DWORD &r_style, DWORD &r_style_ex) { +void DisplayServerWindows::_get_window_style(bool p_main_window, bool p_fullscreen, bool p_multiwindow_fs, bool p_borderless, bool p_resizable, bool p_maximized, bool p_maximized_fs, bool p_no_activate_focus, DWORD &r_style, DWORD &r_style_ex) { // Windows docs for window styles: // https://docs.microsoft.com/en-us/windows/win32/winmsg/window-styles // https://docs.microsoft.com/en-us/windows/win32/winmsg/extended-window-styles @@ -1909,7 +1909,17 @@ void DisplayServerWindows::_get_window_style(bool p_main_window, bool p_fullscre if (p_fullscreen || p_borderless) { r_style |= WS_POPUP; // p_borderless was WS_EX_TOOLWINDOW in the past. - if ((p_fullscreen && p_multiwindow_fs) || p_maximized) { + if (p_maximized) { + r_style |= WS_MAXIMIZE; + } + if (!p_fullscreen) { + r_style |= WS_SYSMENU | WS_MINIMIZEBOX; + + if (p_resizable) { + r_style |= WS_MAXIMIZEBOX; + } + } + if ((p_fullscreen && p_multiwindow_fs) || p_maximized_fs) { r_style |= WS_BORDER; // Allows child windows to be displayed on top of full screen. } } else { @@ -1945,7 +1955,7 @@ void DisplayServerWindows::_update_window_style(WindowID p_window, bool p_repain DWORD style = 0; DWORD style_ex = 0; - _get_window_style(p_window == MAIN_WINDOW_ID, wd.fullscreen, wd.multiwindow_fs, wd.borderless, wd.resizable, wd.maximized, wd.no_focus || wd.is_popup, style, style_ex); + _get_window_style(p_window == MAIN_WINDOW_ID, wd.fullscreen, wd.multiwindow_fs, wd.borderless, wd.resizable, wd.maximized, wd.maximized_fs, wd.no_focus || wd.is_popup, style, style_ex); SetWindowLongPtr(wd.hWnd, GWL_STYLE, style); SetWindowLongPtr(wd.hWnd, GWL_EXSTYLE, style_ex); @@ -1988,6 +1998,7 @@ void DisplayServerWindows::window_set_mode(WindowMode p_mode, WindowID p_window) wd.pre_fs_valid = true; } + ShowWindow(wd.hWnd, SW_RESTORE); MoveWindow(wd.hWnd, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, TRUE); if (restore_mouse_trails > 1) { @@ -2023,7 +2034,7 @@ void DisplayServerWindows::window_set_mode(WindowMode p_mode, WindowID p_window) } if ((p_mode == WINDOW_MODE_FULLSCREEN || p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN) && !wd.fullscreen) { - if (wd.minimized) { + if (wd.minimized || wd.maximized) { ShowWindow(wd.hWnd, SW_RESTORE); } wd.was_maximized = wd.maximized; @@ -3709,63 +3720,18 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA return MA_NOACTIVATE; // Do not activate, but process mouse messages. } } break; - case WM_SETFOCUS: { - windows[window_id].window_has_focus = true; - last_focused_window = window_id; - - // Restore mouse mode. - _set_mouse_mode_impl(mouse_mode); - - if (!app_focused) { - if (OS::get_singleton()->get_main_loop()) { - OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_FOCUS_IN); - } - app_focused = true; - } - } break; - case WM_KILLFOCUS: { - windows[window_id].window_has_focus = false; - last_focused_window = window_id; - - // Release capture unconditionally because it can be set due to dragging, in addition to captured mode. - ReleaseCapture(); - - // Release every touch to avoid sticky points. - for (const KeyValue<int, Vector2> &E : touch_state) { - _touch_event(window_id, false, E.value.x, E.value.y, E.key); - } - touch_state.clear(); - - bool self_steal = false; - HWND new_hwnd = (HWND)wParam; - if (IsWindow(new_hwnd)) { - self_steal = true; + case WM_ACTIVATEAPP: { + bool new_app_focused = (bool)wParam; + if (new_app_focused == app_focused) { + break; } - - if (!self_steal) { - if (OS::get_singleton()->get_main_loop()) { - OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_FOCUS_OUT); - } - app_focused = false; + app_focused = new_app_focused; + if (OS::get_singleton()->get_main_loop()) { + OS::get_singleton()->get_main_loop()->notification(app_focused ? MainLoop::NOTIFICATION_APPLICATION_FOCUS_IN : MainLoop::NOTIFICATION_APPLICATION_FOCUS_OUT); } } break; - case WM_ACTIVATE: { // Watch for window activate message. - if (!windows[window_id].window_focused) { - _process_activate_event(window_id, wParam, lParam); - } else { - windows[window_id].saved_wparam = wParam; - windows[window_id].saved_lparam = lParam; - - // Run a timer to prevent event catching warning if the focused window is closing. - windows[window_id].focus_timer_id = SetTimer(windows[window_id].hWnd, 2, USER_TIMER_MINIMUM, (TIMERPROC) nullptr); - } - if (wParam != WA_INACTIVE) { - track_mouse_leave_event(hWnd); - - if (!IsIconic(hWnd)) { - SetFocus(hWnd); - } - } + case WM_ACTIVATE: { + _process_activate_event(window_id, wParam, lParam); return 0; // Return to the message loop. } break; case WM_GETMINMAXINFO: { @@ -3782,6 +3748,15 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA min_max_info->ptMaxTrackSize.x = windows[window_id].max_size.x + decor.x; min_max_info->ptMaxTrackSize.y = windows[window_id].max_size.y + decor.y; } + if (windows[window_id].borderless) { + Rect2i screen_rect = screen_get_usable_rect(window_get_current_screen(window_id)); + + // Set the size of (borderless) maximized mode to exclude taskbar (or any other panel) if present. + min_max_info->ptMaxPosition.x = screen_rect.position.x; + min_max_info->ptMaxPosition.y = screen_rect.position.y; + min_max_info->ptMaxSize.x = screen_rect.size.x; + min_max_info->ptMaxSize.y = screen_rect.size.y; + } return 0; } } break; @@ -3833,9 +3808,15 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA case SC_MONITORPOWER: // Monitor trying to enter powersave? return 0; // Prevent from happening. case SC_KEYMENU: - if ((lParam >> 16) <= 0) { + Engine *engine = Engine::get_singleton(); + if (((lParam >> 16) <= 0) && !engine->is_project_manager_hint() && !engine->is_editor_hint() && !GLOBAL_GET("application/run/enable_alt_space_menu")) { + return 0; + } + if (!alt_mem || !(GetAsyncKeyState(VK_SPACE) & (1 << 15))) { return 0; } + SendMessage(windows[window_id].hWnd, WM_SYSKEYUP, VK_SPACE, 0); + SendMessage(windows[window_id].hWnd, WM_SYSKEYUP, VK_MENU, 0); } } break; case WM_INDICATOR_CALLBACK_MESSAGE: { @@ -3864,9 +3845,6 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA } break; case WM_CLOSE: // Did we receive a close message? { - if (windows[window_id].focus_timer_id != 0U) { - KillTimer(windows[window_id].hWnd, windows[window_id].focus_timer_id); - } _send_window_event(windows[window_id], WINDOW_EVENT_CLOSE_REQUEST); return 0; // Jump back. @@ -4569,10 +4547,23 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA window.minimized = true; } else if (IsZoomed(hWnd)) { window.maximized = true; + + // If maximized_window_size == screen_size add 1px border to prevent switching to exclusive_fs. + if (!window.maximized_fs && window.borderless && window_rect.position == screen_position && window_rect.size == screen_size) { + // Window (borderless) was just maximized and the covers the entire screen. + window.maximized_fs = true; + _update_window_style(window_id, false); + } } else if (window_rect.position == screen_position && window_rect.size == screen_size) { window.fullscreen = true; } + if (window.maximized_fs && !window.maximized) { + // Window (maximized and covering fullscreen) was just non-maximized. + window.maximized_fs = false; + _update_window_style(window_id, false); + } + if (!window.minimized) { window.width = window_client_rect.size.width; window.height = window_client_rect.size.height; @@ -4625,10 +4616,6 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA if (!Main::is_iterating()) { Main::iteration(); } - } else if (wParam == windows[window_id].focus_timer_id) { - _process_activate_event(window_id, windows[window_id].saved_wparam, windows[window_id].saved_lparam); - KillTimer(windows[window_id].hWnd, wParam); - windows[window_id].focus_timer_id = 0U; } } break; case WM_SYSKEYUP: @@ -4834,20 +4821,25 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { void DisplayServerWindows::_process_activate_event(WindowID p_window_id, WPARAM wParam, LPARAM lParam) { if (LOWORD(wParam) == WA_ACTIVE || LOWORD(wParam) == WA_CLICKACTIVE) { - _send_window_event(windows[p_window_id], WINDOW_EVENT_FOCUS_IN); - windows[p_window_id].window_focused = true; + last_focused_window = p_window_id; alt_mem = false; control_mem = false; shift_mem = false; gr_mem = false; - - // Restore mouse mode. _set_mouse_mode_impl(mouse_mode); + if (!IsIconic(windows[p_window_id].hWnd)) { + SetFocus(windows[p_window_id].hWnd); + } + windows[p_window_id].window_focused = true; + _send_window_event(windows[p_window_id], WINDOW_EVENT_FOCUS_IN); } else { // WM_INACTIVE. Input::get_singleton()->release_pressed_events(); - _send_window_event(windows[p_window_id], WINDOW_EVENT_FOCUS_OUT); - windows[p_window_id].window_focused = false; + track_mouse_leave_event(windows[p_window_id].hWnd); + // Release capture unconditionally because it can be set due to dragging, in addition to captured mode. + ReleaseCapture(); alt_mem = false; + windows[p_window_id].window_focused = false; + _send_window_event(windows[p_window_id], WINDOW_EVENT_FOCUS_OUT); } if ((tablet_get_current_driver() == "wintab") && wintab_available && windows[p_window_id].wtctx) { @@ -5051,7 +5043,7 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode, DWORD dwExStyle; DWORD dwStyle; - _get_window_style(window_id_counter == MAIN_WINDOW_ID, (p_mode == WINDOW_MODE_FULLSCREEN || p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN), p_mode != WINDOW_MODE_EXCLUSIVE_FULLSCREEN, p_flags & WINDOW_FLAG_BORDERLESS_BIT, !(p_flags & WINDOW_FLAG_RESIZE_DISABLED_BIT), p_mode == WINDOW_MODE_MAXIMIZED, (p_flags & WINDOW_FLAG_NO_FOCUS_BIT) | (p_flags & WINDOW_FLAG_POPUP), dwStyle, dwExStyle); + _get_window_style(window_id_counter == MAIN_WINDOW_ID, (p_mode == WINDOW_MODE_FULLSCREEN || p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN), p_mode != WINDOW_MODE_EXCLUSIVE_FULLSCREEN, p_flags & WINDOW_FLAG_BORDERLESS_BIT, !(p_flags & WINDOW_FLAG_RESIZE_DISABLED_BIT), p_mode == WINDOW_MODE_MAXIMIZED, false, (p_flags & WINDOW_FLAG_NO_FOCUS_BIT) | (p_flags & WINDOW_FLAG_POPUP), dwStyle, dwExStyle); RECT WindowRect; @@ -5284,6 +5276,12 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode, wd.height = p_rect.size.height; } + // Set size of maximized borderless window (by default it covers the entire screen). + if (p_mode == WINDOW_MODE_MAXIMIZED && (p_flags & WINDOW_FLAG_BORDERLESS_BIT)) { + Rect2i srect = screen_get_usable_rect(rq_screen); + SetWindowPos(wd.hWnd, HWND_TOP, srect.position.x, srect.position.y, srect.size.width, srect.size.height, SWP_NOZORDER | SWP_NOACTIVATE); + } + window_id_counter++; } diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h index 1191f22968..2f1309176d 100644 --- a/platform/windows/display_server_windows.h +++ b/platform/windows/display_server_windows.h @@ -382,6 +382,7 @@ class DisplayServerWindows : public DisplayServer { bool pre_fs_valid = false; RECT pre_fs_rect; bool maximized = false; + bool maximized_fs = false; bool minimized = false; bool fullscreen = false; bool multiwindow_fs = false; @@ -402,7 +403,6 @@ class DisplayServerWindows : public DisplayServer { // Timers. uint32_t move_timer_id = 0U; - uint32_t focus_timer_id = 0U; HANDLE wtctx; LOGCONTEXTW wtlc; @@ -472,7 +472,7 @@ class DisplayServerWindows : public DisplayServer { HashMap<IndicatorID, IndicatorData> indicators; void _send_window_event(const WindowData &wd, WindowEvent p_event); - void _get_window_style(bool p_main_window, bool p_fullscreen, bool p_multiwindow_fs, bool p_borderless, bool p_resizable, bool p_maximized, bool p_no_activate_focus, DWORD &r_style, DWORD &r_style_ex); + void _get_window_style(bool p_main_window, bool p_fullscreen, bool p_multiwindow_fs, bool p_borderless, bool p_resizable, bool p_maximized, bool p_maximized_fs, bool p_no_activate_focus, DWORD &r_style, DWORD &r_style_ex); MouseMode mouse_mode; int restore_mouse_trails = 0; diff --git a/platform/windows/doc_classes/EditorExportPlatformWindows.xml b/platform/windows/doc_classes/EditorExportPlatformWindows.xml index 1239a2b32f..06b272c10e 100644 --- a/platform/windows/doc_classes/EditorExportPlatformWindows.xml +++ b/platform/windows/doc_classes/EditorExportPlatformWindows.xml @@ -4,6 +4,7 @@ Exporter for Windows. </brief_description> <description> + The Windows exporter customizes how a Windows build is handled. In the editor's "Export" window, it is created when adding a new "Windows" preset. </description> <tutorials> <link title="Exporting for Windows">$DOCS_URL/tutorials/export/exporting_for_windows.html</link> diff --git a/scene/2d/camera_2d.cpp b/scene/2d/camera_2d.cpp index e9bab274c6..4b866fc6de 100644 --- a/scene/2d/camera_2d.cpp +++ b/scene/2d/camera_2d.cpp @@ -627,7 +627,7 @@ void Camera2D::align() { } void Camera2D::set_position_smoothing_speed(real_t p_speed) { - position_smoothing_speed = p_speed; + position_smoothing_speed = MAX(0, p_speed); _update_process_internal_for_smoothing(); } @@ -636,7 +636,7 @@ real_t Camera2D::get_position_smoothing_speed() const { } void Camera2D::set_rotation_smoothing_speed(real_t p_speed) { - rotation_smoothing_speed = p_speed; + rotation_smoothing_speed = MAX(0, p_speed); _update_process_internal_for_smoothing(); } diff --git a/scene/2d/marker_2d.cpp b/scene/2d/marker_2d.cpp index 39026591b0..b1b9705bf8 100644 --- a/scene/2d/marker_2d.cpp +++ b/scene/2d/marker_2d.cpp @@ -48,7 +48,7 @@ void Marker2D::_draw_cross() { // Use a darkened axis color for the negative axis. // This makes it possible to see in which direction the Marker3D node is rotated // (which can be important depending on how it's used). - // Axis colors are taken from `axis_x_color` and `axis_y_color` (defined in `editor/editor_themes.cpp`). + // Axis colors are taken from `axis_x_color` and `axis_y_color` (defined in `editor/themes/editor_theme_manager.cpp`). const Color color_x = Color(0.96, 0.20, 0.32); const Color color_y = Color(0.53, 0.84, 0.01); PackedColorArray colors = { diff --git a/scene/2d/physics/joints/joint_2d.cpp b/scene/2d/physics/joints/joint_2d.cpp index dd1697a29c..1afac7c150 100644 --- a/scene/2d/physics/joints/joint_2d.cpp +++ b/scene/2d/physics/joints/joint_2d.cpp @@ -136,7 +136,7 @@ void Joint2D::set_node_a(const NodePath &p_node_a) { if (Engine::get_singleton()->is_editor_hint()) { // When in editor, the setter may be called as a result of node rename. // It happens before the node actually changes its name, which triggers false warning. - callable_mp(this, &Joint2D::_update_joint).call_deferred(); + callable_mp(this, &Joint2D::_update_joint).call_deferred(false); } else { _update_joint(); } @@ -157,7 +157,7 @@ void Joint2D::set_node_b(const NodePath &p_node_b) { b = p_node_b; if (Engine::get_singleton()->is_editor_hint()) { - callable_mp(this, &Joint2D::_update_joint).call_deferred(); + callable_mp(this, &Joint2D::_update_joint).call_deferred(false); } else { _update_joint(); } diff --git a/scene/3d/bone_attachment_3d.cpp b/scene/3d/bone_attachment_3d.cpp index 76e89f24d8..221200284d 100644 --- a/scene/3d/bone_attachment_3d.cpp +++ b/scene/3d/bone_attachment_3d.cpp @@ -187,7 +187,7 @@ void BoneAttachment3D::_transform_changed() { return; } - if (override_pose) { + if (override_pose && !overriding) { Skeleton3D *sk = _get_skeleton3d(); ERR_FAIL_NULL_MSG(sk, "Cannot override pose: Skeleton not found!"); @@ -198,8 +198,11 @@ void BoneAttachment3D::_transform_changed() { our_trans = sk->get_global_transform().affine_inverse() * get_global_transform(); } - sk->set_bone_global_pose_override(bone_idx, our_trans, 1.0, true); + overriding = true; + sk->set_bone_global_pose(bone_idx, our_trans); + sk->force_update_all_dirty_bones(); } + overriding = false; } void BoneAttachment3D::set_bone_name(const String &p_name) { @@ -246,14 +249,6 @@ void BoneAttachment3D::set_override_pose(bool p_override) { override_pose = p_override; set_notify_transform(override_pose); set_process_internal(override_pose); - - if (!override_pose) { - Skeleton3D *sk = _get_skeleton3d(); - if (sk) { - sk->set_bone_global_pose_override(bone_idx, Transform3D(), 0.0, false); - } - _transform_changed(); - } notify_property_list_changed(); } @@ -314,6 +309,10 @@ void BoneAttachment3D::_notification(int p_what) { } void BoneAttachment3D::on_bone_pose_update(int p_bone_index) { + if (updating) { + return; + } + updating = true; if (bone_idx == p_bone_index) { Skeleton3D *sk = _get_skeleton3d(); if (sk) { @@ -331,6 +330,7 @@ void BoneAttachment3D::on_bone_pose_update(int p_bone_index) { } } } + updating = false; } #ifdef TOOLS_ENABLED void BoneAttachment3D::notify_skeleton_bones_renamed(Node *p_base_scene, Skeleton3D *p_skeleton, Dictionary p_rename_map) { diff --git a/scene/3d/bone_attachment_3d.h b/scene/3d/bone_attachment_3d.h index 1bf44c2756..ec0f7344d7 100644 --- a/scene/3d/bone_attachment_3d.h +++ b/scene/3d/bone_attachment_3d.h @@ -45,6 +45,7 @@ class BoneAttachment3D : public Node3D { bool override_pose = false; bool _override_dirty = false; + bool overriding = false; bool use_external_skeleton = false; NodePath external_skeleton_node; @@ -53,6 +54,7 @@ class BoneAttachment3D : public Node3D { void _check_bind(); void _check_unbind(); + bool updating = false; void _transform_changed(); void _update_external_skeleton_cache(); Skeleton3D *_get_skeleton3d(); diff --git a/scene/3d/decal.cpp b/scene/3d/decal.cpp index caabb4225f..8415fb38cb 100644 --- a/scene/3d/decal.cpp +++ b/scene/3d/decal.cpp @@ -231,7 +231,7 @@ void Decal::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "texture_emission", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture", TEXTURE_EMISSION); ADD_GROUP("Parameters", ""); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_energy", PROPERTY_HINT_RANGE, "0,128,0.01"), "set_emission_energy", "get_emission_energy"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_energy", PROPERTY_HINT_RANGE, "0,16,0.01,or_greater"), "set_emission_energy", "get_emission_energy"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "modulate"), "set_modulate", "get_modulate"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "albedo_mix", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_albedo_mix", "get_albedo_mix"); // A Normal Fade of 1.0 causes the decal to be invisible even if fully perpendicular to a surface. diff --git a/scene/3d/physical_bone_simulator_3d.cpp b/scene/3d/physical_bone_simulator_3d.cpp new file mode 100644 index 0000000000..aba052165c --- /dev/null +++ b/scene/3d/physical_bone_simulator_3d.cpp @@ -0,0 +1,396 @@ +/**************************************************************************/ +/* physical_bone_simulator_3d.cpp */ +/**************************************************************************/ +/* 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. */ +/**************************************************************************/ + +#include "physical_bone_simulator_3d.h" + +void PhysicalBoneSimulator3D::_skeleton_changed(Skeleton3D *p_old, Skeleton3D *p_new) { + if (p_old) { + if (p_old->is_connected(SNAME("bone_list_changed"), callable_mp(this, &PhysicalBoneSimulator3D::_bone_list_changed))) { + p_old->disconnect(SNAME("bone_list_changed"), callable_mp(this, &PhysicalBoneSimulator3D::_bone_list_changed)); + } + if (p_old->is_connected(SNAME("pose_updated"), callable_mp(this, &PhysicalBoneSimulator3D::_pose_updated))) { + p_old->disconnect(SNAME("pose_updated"), callable_mp(this, &PhysicalBoneSimulator3D::_pose_updated)); + } + } + if (p_new) { + if (!p_new->is_connected(SNAME("bone_list_changed"), callable_mp(this, &PhysicalBoneSimulator3D::_bone_list_changed))) { + p_new->connect(SNAME("bone_list_changed"), callable_mp(this, &PhysicalBoneSimulator3D::_bone_list_changed)); + } + if (!p_new->is_connected(SNAME("pose_updated"), callable_mp(this, &PhysicalBoneSimulator3D::_pose_updated))) { + p_new->connect(SNAME("pose_updated"), callable_mp(this, &PhysicalBoneSimulator3D::_pose_updated)); + } + } + _bone_list_changed(); +} + +void PhysicalBoneSimulator3D::_bone_list_changed() { + bones.clear(); + Skeleton3D *skeleton = get_skeleton(); + if (!skeleton) { + return; + } + for (int i = 0; i < skeleton->get_bone_count(); i++) { + SimulatedBone sb; + sb.parent = skeleton->get_bone_parent(i); + sb.child_bones = skeleton->get_bone_children(i); + bones.push_back(sb); + } + _rebuild_physical_bones_cache(); + _pose_updated(); +} + +void PhysicalBoneSimulator3D::_pose_updated() { + Skeleton3D *skeleton = get_skeleton(); + if (!skeleton || simulating) { + return; + } + ERR_FAIL_COND(skeleton->get_bone_count() != bones.size()); + for (int i = 0; i < skeleton->get_bone_count(); i++) { + bones.write[i].global_pose = skeleton->get_bone_global_pose(i); + } +} + +void PhysicalBoneSimulator3D::_set_active(bool p_active) { + if (!Engine::get_singleton()->is_editor_hint()) { + _reset_physical_bones_state(); + } +} + +void PhysicalBoneSimulator3D::_reset_physical_bones_state() { + for (int i = 0; i < bones.size(); i += 1) { + if (bones[i].physical_bone) { + bones[i].physical_bone->reset_physics_simulation_state(); + } + } +} + +bool PhysicalBoneSimulator3D::is_simulating_physics() const { + return simulating; +} + +int PhysicalBoneSimulator3D::find_bone(const String &p_name) const { + Skeleton3D *skeleton = get_skeleton(); + if (!skeleton) { + return -1; + } + return skeleton->find_bone(p_name); +} + +String PhysicalBoneSimulator3D::get_bone_name(int p_bone) const { + Skeleton3D *skeleton = get_skeleton(); + if (!skeleton) { + return String(); + } + return skeleton->get_bone_name(p_bone); +} + +int PhysicalBoneSimulator3D::get_bone_count() const { + return bones.size(); +} + +bool PhysicalBoneSimulator3D::is_bone_parent_of(int p_bone, int p_parent_bone_id) const { + Skeleton3D *skeleton = get_skeleton(); + if (!skeleton) { + return false; + } + return skeleton->is_bone_parent_of(p_bone, p_parent_bone_id); +} + +void PhysicalBoneSimulator3D::bind_physical_bone_to_bone(int p_bone, PhysicalBone3D *p_physical_bone) { + const int bone_size = bones.size(); + ERR_FAIL_INDEX(p_bone, bone_size); + ERR_FAIL_COND(bones[p_bone].physical_bone); + ERR_FAIL_NULL(p_physical_bone); + bones.write[p_bone].physical_bone = p_physical_bone; + + _rebuild_physical_bones_cache(); +} + +void PhysicalBoneSimulator3D::unbind_physical_bone_from_bone(int p_bone) { + const int bone_size = bones.size(); + ERR_FAIL_INDEX(p_bone, bone_size); + bones.write[p_bone].physical_bone = nullptr; + + _rebuild_physical_bones_cache(); +} + +PhysicalBone3D *PhysicalBoneSimulator3D::get_physical_bone(int p_bone) { + const int bone_size = bones.size(); + ERR_FAIL_INDEX_V(p_bone, bone_size, nullptr); + + return bones[p_bone].physical_bone; +} + +PhysicalBone3D *PhysicalBoneSimulator3D::get_physical_bone_parent(int p_bone) { + const int bone_size = bones.size(); + ERR_FAIL_INDEX_V(p_bone, bone_size, nullptr); + + if (bones[p_bone].cache_parent_physical_bone) { + return bones[p_bone].cache_parent_physical_bone; + } + + return _get_physical_bone_parent(p_bone); +} + +PhysicalBone3D *PhysicalBoneSimulator3D::_get_physical_bone_parent(int p_bone) { + const int bone_size = bones.size(); + ERR_FAIL_INDEX_V(p_bone, bone_size, nullptr); + + const int parent_bone = bones[p_bone].parent; + if (parent_bone < 0) { + return nullptr; + } + + PhysicalBone3D *pb = bones[parent_bone].physical_bone; + if (pb) { + return pb; + } else { + return get_physical_bone_parent(parent_bone); + } +} + +void PhysicalBoneSimulator3D::_rebuild_physical_bones_cache() { + const int b_size = bones.size(); + for (int i = 0; i < b_size; ++i) { + PhysicalBone3D *parent_pb = _get_physical_bone_parent(i); + if (parent_pb != bones[i].cache_parent_physical_bone) { + bones.write[i].cache_parent_physical_bone = parent_pb; + if (bones[i].physical_bone) { + bones[i].physical_bone->_on_bone_parent_changed(); + } + } + } +} + +#ifndef DISABLE_DEPRECATED +void _pb_stop_simulation_compat(Node *p_node) { + PhysicalBoneSimulator3D *ps = Object::cast_to<PhysicalBoneSimulator3D>(p_node); + if (ps) { + return; // Prevent conflict. + } + for (int i = p_node->get_child_count() - 1; i >= 0; --i) { + _pb_stop_simulation_compat(p_node->get_child(i)); + } + PhysicalBone3D *pb = Object::cast_to<PhysicalBone3D>(p_node); + if (pb) { + pb->set_simulate_physics(false); + } +} +#endif // _DISABLE_DEPRECATED + +void _pb_stop_simulation(Node *p_node) { + for (int i = p_node->get_child_count() - 1; i >= 0; --i) { + PhysicalBone3D *pb = Object::cast_to<PhysicalBone3D>(p_node->get_child(i)); + if (!pb) { + continue; + } + _pb_stop_simulation(pb); + } + PhysicalBone3D *pb = Object::cast_to<PhysicalBone3D>(p_node); + if (pb) { + pb->set_simulate_physics(false); + } +} + +void PhysicalBoneSimulator3D::physical_bones_stop_simulation() { + simulating = false; + _reset_physical_bones_state(); +#ifndef DISABLE_DEPRECATED + if (is_compat) { + Skeleton3D *sk = get_skeleton(); + if (sk) { + _pb_stop_simulation_compat(sk); + } + } else { + _pb_stop_simulation(this); + } +#else + _pb_stop_simulation(this); +#endif // _DISABLE_DEPRECATED +} + +#ifndef DISABLE_DEPRECATED +void _pb_start_simulation_compat(const PhysicalBoneSimulator3D *p_simulator, Node *p_node, const Vector<int> &p_sim_bones) { + PhysicalBoneSimulator3D *ps = Object::cast_to<PhysicalBoneSimulator3D>(p_node); + if (ps) { + return; // Prevent conflict. + } + for (int i = p_node->get_child_count() - 1; i >= 0; --i) { + _pb_start_simulation_compat(p_simulator, p_node->get_child(i), p_sim_bones); + } + PhysicalBone3D *pb = Object::cast_to<PhysicalBone3D>(p_node); + if (pb) { + if (p_sim_bones.is_empty()) { // If no bones are specified, activate ragdoll on full body. + pb->set_simulate_physics(true); + } else { + for (int i = p_sim_bones.size() - 1; i >= 0; --i) { + if (p_sim_bones[i] == pb->get_bone_id() || p_simulator->is_bone_parent_of(pb->get_bone_id(), p_sim_bones[i])) { + pb->set_simulate_physics(true); + break; + } + } + } + } +} +#endif // _DISABLE_DEPRECATED + +void _pb_start_simulation(const PhysicalBoneSimulator3D *p_simulator, Node *p_node, const Vector<int> &p_sim_bones) { + for (int i = p_node->get_child_count() - 1; i >= 0; --i) { + PhysicalBone3D *pb = Object::cast_to<PhysicalBone3D>(p_node->get_child(i)); + if (!pb) { + continue; + } + _pb_start_simulation(p_simulator, pb, p_sim_bones); + } + PhysicalBone3D *pb = Object::cast_to<PhysicalBone3D>(p_node); + if (pb) { + if (p_sim_bones.is_empty()) { // If no bones are specified, activate ragdoll on full body. + pb->set_simulate_physics(true); + } else { + for (int i = p_sim_bones.size() - 1; i >= 0; --i) { + if (p_sim_bones[i] == pb->get_bone_id() || p_simulator->is_bone_parent_of(pb->get_bone_id(), p_sim_bones[i])) { + pb->set_simulate_physics(true); + break; + } + } + } + } +} + +void PhysicalBoneSimulator3D::physical_bones_start_simulation_on(const TypedArray<StringName> &p_bones) { + simulating = true; + _reset_physical_bones_state(); + + _pose_updated(); + + Vector<int> sim_bones; + if (p_bones.size() > 0) { + sim_bones.resize(p_bones.size()); + int c = 0; + for (int i = sim_bones.size() - 1; i >= 0; --i) { + int bone_id = find_bone(p_bones[i]); + if (bone_id != -1) { + sim_bones.write[c++] = bone_id; + } + } + sim_bones.resize(c); + } + +#ifndef DISABLE_DEPRECATED + if (is_compat) { + Skeleton3D *sk = get_skeleton(); + if (sk) { + _pb_start_simulation_compat(this, sk, sim_bones); + } + } else { + _pb_start_simulation(this, this, sim_bones); + } +#else + _pb_start_simulation(this, this, sim_bones); +#endif // _DISABLE_DEPRECATED +} + +void _physical_bones_add_remove_collision_exception(bool p_add, Node *p_node, RID p_exception) { + for (int i = p_node->get_child_count() - 1; i >= 0; --i) { + _physical_bones_add_remove_collision_exception(p_add, p_node->get_child(i), p_exception); + } + + CollisionObject3D *co = Object::cast_to<CollisionObject3D>(p_node); + if (co) { + if (p_add) { + PhysicsServer3D::get_singleton()->body_add_collision_exception(co->get_rid(), p_exception); + } else { + PhysicsServer3D::get_singleton()->body_remove_collision_exception(co->get_rid(), p_exception); + } + } +} + +void PhysicalBoneSimulator3D::physical_bones_add_collision_exception(RID p_exception) { + _physical_bones_add_remove_collision_exception(true, this, p_exception); +} + +void PhysicalBoneSimulator3D::physical_bones_remove_collision_exception(RID p_exception) { + _physical_bones_add_remove_collision_exception(false, this, p_exception); +} + +Transform3D PhysicalBoneSimulator3D::get_bone_global_pose(int p_bone) const { + const int bone_size = bones.size(); + ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D()); + return bones[p_bone].global_pose; +} + +void PhysicalBoneSimulator3D::set_bone_global_pose(int p_bone, const Transform3D &p_pose) { + const int bone_size = bones.size(); + ERR_FAIL_INDEX(p_bone, bone_size); + bones.write[p_bone].global_pose = p_pose; +} + +void PhysicalBoneSimulator3D::_process_modification() { + Skeleton3D *skeleton = get_skeleton(); + if (!skeleton) { + return; + } + if (!enabled) { + for (int i = 0; i < bones.size(); i++) { + if (bones[i].physical_bone) { + if (bones[i].physical_bone->is_simulating_physics() == false) { + bones[i].physical_bone->reset_to_rest_position(); + } + } + } + } else { + ERR_FAIL_COND(skeleton->get_bone_count() != bones.size()); + LocalVector<Transform3D> local_poses; + for (int i = 0; i < skeleton->get_bone_count(); i++) { + Transform3D pt; + if (skeleton->get_bone_parent(i) >= 0) { + pt = get_bone_global_pose(skeleton->get_bone_parent(i)); + } + local_poses.push_back(pt.affine_inverse() * bones[i].global_pose); + } + for (int i = 0; i < skeleton->get_bone_count(); i++) { + skeleton->set_bone_pose_position(i, local_poses[i].origin); + skeleton->set_bone_pose_rotation(i, local_poses[i].basis.get_rotation_quaternion()); + skeleton->set_bone_pose_scale(i, local_poses[i].basis.get_scale()); + } + } +} + +void PhysicalBoneSimulator3D::_bind_methods() { + ClassDB::bind_method(D_METHOD("is_simulating_physics"), &PhysicalBoneSimulator3D::is_simulating_physics); + + ClassDB::bind_method(D_METHOD("physical_bones_stop_simulation"), &PhysicalBoneSimulator3D::physical_bones_stop_simulation); + ClassDB::bind_method(D_METHOD("physical_bones_start_simulation", "bones"), &PhysicalBoneSimulator3D::physical_bones_start_simulation_on, DEFVAL(Array())); + ClassDB::bind_method(D_METHOD("physical_bones_add_collision_exception", "exception"), &PhysicalBoneSimulator3D::physical_bones_add_collision_exception); + ClassDB::bind_method(D_METHOD("physical_bones_remove_collision_exception", "exception"), &PhysicalBoneSimulator3D::physical_bones_remove_collision_exception); +} + +PhysicalBoneSimulator3D::PhysicalBoneSimulator3D() { +} diff --git a/scene/3d/physical_bone_simulator_3d.h b/scene/3d/physical_bone_simulator_3d.h new file mode 100644 index 0000000000..ee900e0e77 --- /dev/null +++ b/scene/3d/physical_bone_simulator_3d.h @@ -0,0 +1,110 @@ +/**************************************************************************/ +/* physical_bone_simulator_3d.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 PHYSICAL_BONE_SIMULATOR_3D_H +#define PHYSICAL_BONE_SIMULATOR_3D_H + +#include "scene/3d/skeleton_modifier_3d.h" + +#include "scene/3d/physics/physical_bone_3d.h" + +class PhysicalBone3D; + +class PhysicalBoneSimulator3D : public SkeletonModifier3D { + GDCLASS(PhysicalBoneSimulator3D, SkeletonModifier3D); + + bool simulating = false; + bool enabled = true; + + struct SimulatedBone { + int parent; + Vector<int> child_bones; + + Transform3D global_pose; + + PhysicalBone3D *physical_bone = nullptr; + PhysicalBone3D *cache_parent_physical_bone = nullptr; + + SimulatedBone() { + parent = -1; + global_pose = Transform3D(); + physical_bone = nullptr; + cache_parent_physical_bone = nullptr; + } + }; + + Vector<SimulatedBone> bones; + + /// This is a slow API, so it's cached + PhysicalBone3D *_get_physical_bone_parent(int p_bone); + void _rebuild_physical_bones_cache(); + void _reset_physical_bones_state(); + +protected: + static void _bind_methods(); + + virtual void _set_active(bool p_active) override; + + void _bone_list_changed(); + void _pose_updated(); + + virtual void _process_modification() override; + + virtual void _skeleton_changed(Skeleton3D *p_old, Skeleton3D *p_new) override; + +public: +#ifndef DISABLE_DEPRECATED + bool is_compat = false; +#endif // _DISABLE_DEPRECATED + bool is_simulating_physics() const; + + int find_bone(const String &p_name) const; + String get_bone_name(int p_bone) const; + int get_bone_count() const; + bool is_bone_parent_of(int p_bone_id, int p_parent_bone_id) const; + + Transform3D get_bone_global_pose(int p_bone) const; + void set_bone_global_pose(int p_bone, const Transform3D &p_pose); + + void bind_physical_bone_to_bone(int p_bone, PhysicalBone3D *p_physical_bone); + void unbind_physical_bone_from_bone(int p_bone); + + PhysicalBone3D *get_physical_bone(int p_bone); + PhysicalBone3D *get_physical_bone_parent(int p_bone); + + void physical_bones_stop_simulation(); + void physical_bones_start_simulation_on(const TypedArray<StringName> &p_bones); + void physical_bones_add_collision_exception(RID p_exception); + void physical_bones_remove_collision_exception(RID p_exception); + + PhysicalBoneSimulator3D(); +}; + +#endif // PHYSICAL_BONE_SIMULATOR_3D_H diff --git a/scene/3d/physics/physical_bone_3d.cpp b/scene/3d/physics/physical_bone_3d.cpp index 10c1fceb94..c6be2a9da8 100644 --- a/scene/3d/physics/physical_bone_3d.cpp +++ b/scene/3d/physics/physical_bone_3d.cpp @@ -29,6 +29,9 @@ /**************************************************************************/ #include "physical_bone_3d.h" +#ifndef DISABLE_DEPRECATED +#include "scene/3d/skeleton_3d.h" +#endif //_DISABLE_DEPRECATED bool PhysicalBone3D::JointData::_set(const StringName &p_name, const Variant &p_value, RID j) { return false; @@ -89,15 +92,14 @@ void PhysicalBone3D::reset_physics_simulation_state() { } void PhysicalBone3D::reset_to_rest_position() { - if (parent_skeleton) { - Transform3D new_transform = parent_skeleton->get_global_transform(); + PhysicalBoneSimulator3D *simulator = get_simulator(); + Skeleton3D *skeleton = get_skeleton(); + if (simulator && skeleton) { if (bone_id == -1) { - new_transform *= body_offset; + set_global_transform((skeleton->get_global_transform() * body_offset).orthonormalized()); } else { - new_transform *= parent_skeleton->get_bone_global_pose(bone_id) * body_offset; + set_global_transform((skeleton->get_global_transform() * simulator->get_bone_global_pose(bone_id) * body_offset).orthonormalized()); } - new_transform.orthonormalize(); - set_global_transform(new_transform); } } @@ -734,15 +736,14 @@ bool PhysicalBone3D::_get(const StringName &p_name, Variant &r_ret) const { } void PhysicalBone3D::_get_property_list(List<PropertyInfo> *p_list) const { - Skeleton3D *parent = find_skeleton_parent(get_parent()); - - if (parent) { + Skeleton3D *skeleton = get_skeleton(); + if (skeleton) { String names; - for (int i = 0; i < parent->get_bone_count(); i++) { + for (int i = 0; i < skeleton->get_bone_count(); i++) { if (i > 0) { names += ","; } - names += parent->get_bone_name(i); + names += skeleton->get_bone_name(i); } p_list->push_back(PropertyInfo(Variant::STRING_NAME, PNAME("bone_name"), PROPERTY_HINT_ENUM, names)); @@ -758,7 +759,8 @@ void PhysicalBone3D::_get_property_list(List<PropertyInfo> *p_list) const { void PhysicalBone3D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: - parent_skeleton = find_skeleton_parent(get_parent()); + case NOTIFICATION_PARENTED: + _update_simulator_path(); update_bone_id(); reset_to_rest_position(); reset_physics_simulation_state(); @@ -768,13 +770,13 @@ void PhysicalBone3D::_notification(int p_what) { break; case NOTIFICATION_EXIT_TREE: { - if (parent_skeleton) { + PhysicalBoneSimulator3D *simulator = get_simulator(); + if (simulator) { if (-1 != bone_id) { - parent_skeleton->unbind_physical_bone_from_bone(bone_id); + simulator->unbind_physical_bone_from_bone(bone_id); bone_id = -1; } } - parent_skeleton = nullptr; PhysicsServer3D::get_singleton()->joint_clear(joint); } break; @@ -818,10 +820,12 @@ void PhysicalBone3D::_body_state_changed(PhysicsDirectBodyState3D *p_state) { Transform3D global_transform(p_state->get_transform()); - // Update skeleton - if (parent_skeleton) { + // Update simulator + PhysicalBoneSimulator3D *simulator = get_simulator(); + Skeleton3D *skeleton = get_skeleton(); + if (simulator && skeleton) { if (-1 != bone_id) { - parent_skeleton->set_bone_global_pose_override(bone_id, parent_skeleton->get_global_transform().affine_inverse() * (global_transform * body_offset_inverse), 1.0, true); + simulator->set_bone_global_pose(bone_id, skeleton->get_global_transform().affine_inverse() * (global_transform * body_offset_inverse)); } } } @@ -916,14 +920,6 @@ void PhysicalBone3D::_bind_methods() { BIND_ENUM_CONSTANT(JOINT_TYPE_6DOF); } -Skeleton3D *PhysicalBone3D::find_skeleton_parent(Node *p_parent) { - if (!p_parent) { - return nullptr; - } - Skeleton3D *s = Object::cast_to<Skeleton3D>(p_parent); - return s ? s : find_skeleton_parent(p_parent->get_parent()); -} - void PhysicalBone3D::_update_joint_offset() { _fix_joint_offset(); @@ -938,18 +934,20 @@ void PhysicalBone3D::_update_joint_offset() { void PhysicalBone3D::_fix_joint_offset() { // Clamp joint origin to bone origin - if (parent_skeleton) { + PhysicalBoneSimulator3D *simulator = get_simulator(); + if (simulator) { joint_offset.origin = body_offset.affine_inverse().origin; } } void PhysicalBone3D::_reload_joint() { - if (!parent_skeleton) { + PhysicalBoneSimulator3D *simulator = get_simulator(); + if (!simulator || !simulator->get_skeleton()) { PhysicsServer3D::get_singleton()->joint_clear(joint); return; } - PhysicalBone3D *body_a = parent_skeleton->get_physical_bone_parent(bone_id); + PhysicalBone3D *body_a = simulator->get_physical_bone_parent(bone_id); if (!body_a) { PhysicsServer3D::get_singleton()->joint_clear(joint); return; @@ -1041,6 +1039,36 @@ void PhysicalBone3D::_on_bone_parent_changed() { _reload_joint(); } +void PhysicalBone3D::_update_simulator_path() { + simulator_id = ObjectID(); + PhysicalBoneSimulator3D *sim = cast_to<PhysicalBoneSimulator3D>(get_parent()); + if (sim) { + simulator_id = sim->get_instance_id(); + return; + } +#ifndef DISABLE_DEPRECATED + Skeleton3D *sk = cast_to<Skeleton3D>(get_parent()); + if (sk) { + PhysicalBoneSimulator3D *ssim = cast_to<PhysicalBoneSimulator3D>(sk->get_simulator()); + if (ssim) { + simulator_id = ssim->get_instance_id(); + } + } +#endif // _DISABLE_DEPRECATED +} + +PhysicalBoneSimulator3D *PhysicalBone3D::get_simulator() const { + return Object::cast_to<PhysicalBoneSimulator3D>(ObjectDB::get_instance(simulator_id)); +} + +Skeleton3D *PhysicalBone3D::get_skeleton() const { + PhysicalBoneSimulator3D *simulator = get_simulator(); + if (simulator) { + return simulator->get_skeleton(); + } + return nullptr; +} + #ifdef TOOLS_ENABLED void PhysicalBone3D::_set_gizmo_move_joint(bool p_move_joint) { gizmo_move_joint = p_move_joint; @@ -1059,10 +1087,6 @@ const PhysicalBone3D::JointData *PhysicalBone3D::get_joint_data() const { return joint_data; } -Skeleton3D *PhysicalBone3D::find_skeleton_parent() { - return find_skeleton_parent(this); -} - void PhysicalBone3D::set_joint_type(JointType p_joint_type) { if (p_joint_type == get_joint_type()) { return; @@ -1269,21 +1293,22 @@ PhysicalBone3D::~PhysicalBone3D() { } void PhysicalBone3D::update_bone_id() { - if (!parent_skeleton) { + PhysicalBoneSimulator3D *simulator = get_simulator(); + if (!simulator) { return; } - const int new_bone_id = parent_skeleton->find_bone(bone_name); + const int new_bone_id = simulator->find_bone(bone_name); if (new_bone_id != bone_id) { if (-1 != bone_id) { // Assert the unbind from old node - parent_skeleton->unbind_physical_bone_from_bone(bone_id); + simulator->unbind_physical_bone_from_bone(bone_id); } bone_id = new_bone_id; - parent_skeleton->bind_physical_bone_to_bone(bone_id, this); + simulator->bind_physical_bone_to_bone(bone_id, this); _fix_joint_offset(); reset_physics_simulation_state(); @@ -1292,10 +1317,12 @@ void PhysicalBone3D::update_bone_id() { void PhysicalBone3D::update_offset() { #ifdef TOOLS_ENABLED - if (parent_skeleton) { - Transform3D bone_transform(parent_skeleton->get_global_transform()); + PhysicalBoneSimulator3D *simulator = get_simulator(); + Skeleton3D *skeleton = get_skeleton(); + if (simulator && skeleton) { + Transform3D bone_transform(skeleton->get_global_transform()); if (-1 != bone_id) { - bone_transform *= parent_skeleton->get_bone_global_pose(bone_id); + bone_transform *= simulator->get_bone_global_pose(bone_id); } if (gizmo_move_joint) { @@ -1309,7 +1336,7 @@ void PhysicalBone3D::update_offset() { } void PhysicalBone3D::_start_physics_simulation() { - if (_internal_simulate_physics || !parent_skeleton) { + if (_internal_simulate_physics || !simulator_id.is_valid()) { return; } reset_to_rest_position(); @@ -1323,23 +1350,22 @@ void PhysicalBone3D::_start_physics_simulation() { } void PhysicalBone3D::_stop_physics_simulation() { - if (!parent_skeleton) { - return; - } - if (parent_skeleton->get_animate_physical_bones()) { - set_body_mode(PhysicsServer3D::BODY_MODE_KINEMATIC); - PhysicsServer3D::get_singleton()->body_set_collision_layer(get_rid(), get_collision_layer()); - PhysicsServer3D::get_singleton()->body_set_collision_mask(get_rid(), get_collision_mask()); - PhysicsServer3D::get_singleton()->body_set_collision_priority(get_rid(), get_collision_priority()); - } else { - set_body_mode(PhysicsServer3D::BODY_MODE_STATIC); - PhysicsServer3D::get_singleton()->body_set_collision_layer(get_rid(), 0); - PhysicsServer3D::get_singleton()->body_set_collision_mask(get_rid(), 0); - PhysicsServer3D::get_singleton()->body_set_collision_priority(get_rid(), 1.0); + PhysicalBoneSimulator3D *simulator = get_simulator(); + if (simulator) { + if (simulator->is_simulating_physics()) { + set_body_mode(PhysicsServer3D::BODY_MODE_KINEMATIC); + PhysicsServer3D::get_singleton()->body_set_collision_layer(get_rid(), get_collision_layer()); + PhysicsServer3D::get_singleton()->body_set_collision_mask(get_rid(), get_collision_mask()); + PhysicsServer3D::get_singleton()->body_set_collision_priority(get_rid(), get_collision_priority()); + } else { + set_body_mode(PhysicsServer3D::BODY_MODE_STATIC); + PhysicsServer3D::get_singleton()->body_set_collision_layer(get_rid(), 0); + PhysicsServer3D::get_singleton()->body_set_collision_mask(get_rid(), 0); + PhysicsServer3D::get_singleton()->body_set_collision_priority(get_rid(), 1.0); + } } if (_internal_simulate_physics) { PhysicsServer3D::get_singleton()->body_set_state_sync_callback(get_rid(), Callable()); - parent_skeleton->set_bone_global_pose_override(bone_id, Transform3D(), 0.0, false); set_as_top_level(false); _internal_simulate_physics = false; } diff --git a/scene/3d/physics/physical_bone_3d.h b/scene/3d/physics/physical_bone_3d.h index 953400da2f..4765e41572 100644 --- a/scene/3d/physics/physical_bone_3d.h +++ b/scene/3d/physics/physical_bone_3d.h @@ -31,8 +31,10 @@ #ifndef PHYSICAL_BONE_3D_H #define PHYSICAL_BONE_3D_H +#include "scene/3d/physical_bone_simulator_3d.h" #include "scene/3d/physics/physics_body_3d.h" -#include "scene/3d/skeleton_3d.h" + +class PhysicalBoneSimulator3D; class PhysicalBone3D : public PhysicsBody3D { GDCLASS(PhysicalBone3D, PhysicsBody3D); @@ -169,7 +171,7 @@ private: Transform3D joint_offset; RID joint; - Skeleton3D *parent_skeleton = nullptr; + ObjectID simulator_id; Transform3D body_offset; Transform3D body_offset_inverse; bool simulate_physics = false; @@ -206,15 +208,19 @@ protected: private: void _sync_body_state(PhysicsDirectBodyState3D *p_state); - static Skeleton3D *find_skeleton_parent(Node *p_parent); void _update_joint_offset(); void _fix_joint_offset(); void _reload_joint(); + void _update_simulator_path(); + public: void _on_bone_parent_changed(); + PhysicalBoneSimulator3D *get_simulator() const; + Skeleton3D *get_skeleton() const; + void set_linear_velocity(const Vector3 &p_velocity); Vector3 get_linear_velocity() const override; @@ -231,7 +237,6 @@ public: #endif const JointData *get_joint_data() const; - Skeleton3D *find_skeleton_parent(); int get_bone_id() const { return bone_id; diff --git a/scene/3d/skeleton_3d.cpp b/scene/3d/skeleton_3d.cpp index f0ffb7b2d5..95e94de0e3 100644 --- a/scene/3d/skeleton_3d.cpp +++ b/scene/3d/skeleton_3d.cpp @@ -32,10 +32,12 @@ #include "skeleton_3d.compat.inc" #include "core/variant/type_info.h" -#include "scene/3d/physics/physical_bone_3d.h" -#include "scene/3d/physics/physics_body_3d.h" +#include "scene/3d/skeleton_modifier_3d.h" #include "scene/resources/surface_tool.h" #include "scene/scene_string_names.h" +#ifndef DISABLE_DEPRECATED +#include "scene/3d/physical_bone_simulator_3d.h" +#endif // _DISABLE_DEPRECATED void SkinReference::_skin_changed() { if (skeleton_node) { @@ -70,6 +72,12 @@ SkinReference::~SkinReference() { bool Skeleton3D::_set(const StringName &p_path, const Variant &p_value) { String path = p_path; +#ifndef DISABLE_DEPRECATED + if (path.begins_with("animate_physical_bones")) { + set_animate_physical_bones(p_value); + } +#endif + if (!path.begins_with("bones/")) { return false; } @@ -134,6 +142,12 @@ bool Skeleton3D::_set(const StringName &p_path, const Variant &p_value) { bool Skeleton3D::_get(const StringName &p_path, Variant &r_ret) const { String path = p_path; +#ifndef DISABLE_DEPRECATED + if (path.begins_with("animate_physical_bones")) { + r_ret = get_animate_physical_bones(); + } +#endif + if (!path.begins_with("bones/")) { return false; } @@ -251,26 +265,70 @@ void Skeleton3D::_update_process_order() { } process_order_dirty = false; + + emit_signal("bone_list_changed"); } +#ifndef DISABLE_DEPRECATED +void Skeleton3D::setup_simulator() { + PhysicalBoneSimulator3D *sim = memnew(PhysicalBoneSimulator3D); + simulator = sim; + sim->is_compat = true; + sim->set_active(false); // Don't run unneeded process. + add_child(sim); +} + +void Skeleton3D::remove_simulator() { + remove_child(simulator); + memdelete(simulator); +} +#endif // _DISABLE_DEPRECATED + void Skeleton3D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { - if (dirty) { - notification(NOTIFICATION_UPDATE_SKELETON); - } + _process_changed(); + _make_modifiers_dirty(); + force_update_all_dirty_bones(); +#ifndef DISABLE_DEPRECATED + setup_simulator(); +#endif // _DISABLE_DEPRECATED + } break; +#ifndef DISABLE_DEPRECATED + case NOTIFICATION_EXIT_TREE: { + remove_simulator(); } break; +#endif // _DISABLE_DEPRECATED case NOTIFICATION_UPDATE_SKELETON: { - RenderingServer *rs = RenderingServer::get_singleton(); - Bone *bonesptr = bones.ptrw(); + // Update bone transforms to apply unprocessed poses. + force_update_all_dirty_bones(); + + updating = true; + Bone *bonesptr = bones.ptrw(); int len = bones.size(); - dirty = false; - // Update bone transforms. - force_update_all_bone_transforms(); + // Process modifiers. + _find_modifiers(); + LocalVector<Transform3D> current_bone_poses; + LocalVector<Vector3> current_pose_positions; + LocalVector<Quaternion> current_pose_rotations; + LocalVector<Vector3> current_pose_scales; + LocalVector<Transform3D> current_bone_global_poses; + if (!modifiers.is_empty()) { + // Store unmodified bone poses. + for (int i = 0; i < len; i++) { + current_bone_poses.push_back(bones[i].pose_cache); + current_pose_positions.push_back(bones[i].pose_position); + current_pose_rotations.push_back(bones[i].pose_rotation); + current_pose_scales.push_back(bones[i].pose_scale); + current_bone_global_poses.push_back(bones[i].global_pose); + } + _process_modifiers(); + } // Update skins. + RenderingServer *rs = RenderingServer::get_singleton(); for (SkinReference *E : skin_bindings) { const Skin *skin = E->skin.operator->(); RID skeleton = E->skeleton; @@ -322,74 +380,78 @@ void Skeleton3D::_notification(int p_what) { for (uint32_t i = 0; i < bind_count; i++) { uint32_t bone_index = E->skin_bone_indices_ptrs[i]; ERR_CONTINUE(bone_index >= (uint32_t)len); - rs->skeleton_bone_set_transform(skeleton, i, bonesptr[bone_index].pose_global * skin->get_bind_pose(i)); + rs->skeleton_bone_set_transform(skeleton, i, bonesptr[bone_index].global_pose * skin->get_bind_pose(i)); } } - emit_signal(SceneStringNames::get_singleton()->pose_updated); - } break; -#ifndef _3D_DISABLED - case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { - // This is active only if the skeleton animates the physical bones - // and the state of the bone is not active. - if (animate_physical_bones) { - for (int i = 0; i < bones.size(); i += 1) { - if (bones[i].physical_bone) { - if (bones[i].physical_bone->is_simulating_physics() == false) { - bones[i].physical_bone->reset_to_rest_position(); - } - } + if (!modifiers.is_empty()) { + // Restore unmodified bone poses. + for (int i = 0; i < len; i++) { + bonesptr[i].pose_cache = current_bone_poses[i]; + bonesptr[i].pose_position = current_pose_positions[i]; + bonesptr[i].pose_rotation = current_pose_rotations[i]; + bonesptr[i].pose_scale = current_pose_scales[i]; + bonesptr[i].global_pose = current_bone_global_poses[i]; } } + + updating = false; + is_update_needed = false; } break; - case NOTIFICATION_READY: { - if (Engine::get_singleton()->is_editor_hint()) { - set_physics_process_internal(true); + case NOTIFICATION_INTERNAL_PROCESS: + case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { + _find_modifiers(); + if (!modifiers.is_empty()) { + _update_deferred(); } } break; -#endif // _3D_DISABLED } } -void Skeleton3D::clear_bones_global_pose_override() { - for (int i = 0; i < bones.size(); i += 1) { - bones.write[i].global_pose_override_amount = 0; - bones.write[i].global_pose_override_reset = true; +void Skeleton3D::set_modifier_callback_mode_process(Skeleton3D::ModifierCallbackModeProcess p_mode) { + if (modifier_callback_mode_process == p_mode) { + return; } - _make_dirty(); + modifier_callback_mode_process = p_mode; + _process_changed(); } -void Skeleton3D::set_bone_global_pose_override(int p_bone, const Transform3D &p_pose, real_t p_amount, bool p_persistent) { - const int bone_size = bones.size(); - ERR_FAIL_INDEX(p_bone, bone_size); - bones.write[p_bone].global_pose_override_amount = p_amount; - bones.write[p_bone].global_pose_override = p_pose; - bones.write[p_bone].global_pose_override_reset = !p_persistent; - _make_dirty(); +Skeleton3D::ModifierCallbackModeProcess Skeleton3D::get_modifier_callback_mode_process() const { + return modifier_callback_mode_process; } -Transform3D Skeleton3D::get_bone_global_pose_override(int p_bone) const { - const int bone_size = bones.size(); - ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D()); - return bones[p_bone].global_pose_override; +void Skeleton3D::_process_changed() { + if (modifier_callback_mode_process == MODIFIER_CALLBACK_MODE_PROCESS_IDLE) { + set_process_internal(true); + set_physics_process_internal(false); + } else if (modifier_callback_mode_process == MODIFIER_CALLBACK_MODE_PROCESS_PHYSICS) { + set_process_internal(false); + set_physics_process_internal(true); + } +} + +void Skeleton3D::_make_modifiers_dirty() { + modifiers_dirty = true; + _update_deferred(); } Transform3D Skeleton3D::get_bone_global_pose(int p_bone) const { const int bone_size = bones.size(); ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D()); - if (dirty) { - const_cast<Skeleton3D *>(this)->notification(NOTIFICATION_UPDATE_SKELETON); - } - return bones[p_bone].pose_global; + const_cast<Skeleton3D *>(this)->force_update_all_dirty_bones(); + return bones[p_bone].global_pose; } -Transform3D Skeleton3D::get_bone_global_pose_no_override(int p_bone) const { +void Skeleton3D::set_bone_global_pose(int p_bone, const Transform3D &p_pose) { const int bone_size = bones.size(); - ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D()); - if (dirty) { - const_cast<Skeleton3D *>(this)->notification(NOTIFICATION_UPDATE_SKELETON); + ERR_FAIL_INDEX(p_bone, bone_size); + + Transform3D pt; + if (bones[p_bone].parent >= 0) { + pt = get_bone_global_pose(bones[p_bone].parent); } - return bones[p_bone].pose_global_no_override; + Transform3D t = pt.affine_inverse() * p_pose; + set_bone_pose(p_bone, t); } void Skeleton3D::set_motion_scale(float p_motion_scale) { @@ -548,7 +610,7 @@ Transform3D Skeleton3D::get_bone_global_rest(int p_bone) const { const int bone_size = bones.size(); ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D()); if (rest_dirty) { - const_cast<Skeleton3D *>(this)->notification(NOTIFICATION_UPDATE_SKELETON); + const_cast<Skeleton3D *>(this)->force_update_all_bone_transforms(); } return bones[p_bone].global_rest; } @@ -588,6 +650,19 @@ void Skeleton3D::clear_bones() { // Posing api +void Skeleton3D::set_bone_pose(int p_bone, const Transform3D &p_pose) { + const int bone_size = bones.size(); + ERR_FAIL_INDEX(p_bone, bone_size); + + bones.write[p_bone].pose_position = p_pose.origin; + bones.write[p_bone].pose_rotation = p_pose.basis.get_rotation_quaternion(); + bones.write[p_bone].pose_scale = p_pose.basis.get_scale(); + bones.write[p_bone].pose_cache_dirty = true; + if (is_inside_tree()) { + _make_dirty(); + } +} + void Skeleton3D::set_bone_pose_position(int p_bone, const Vector3 &p_position) { const int bone_size = bones.size(); ERR_FAIL_INDEX(p_bone, bone_size); @@ -654,7 +729,7 @@ void Skeleton3D::reset_bone_poses() { Transform3D Skeleton3D::get_bone_pose(int p_bone) const { const int bone_size = bones.size(); ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D()); - ((Skeleton3D *)this)->bones.write[p_bone].update_pose_cache(); + const_cast<Skeleton3D *>(this)->bones.write[p_bone].update_pose_cache(); return bones[p_bone].pose_cache; } @@ -662,11 +737,15 @@ void Skeleton3D::_make_dirty() { if (dirty) { return; } + dirty = true; + _update_deferred(); +} - if (is_inside_tree()) { - notify_deferred_thread_group(NOTIFICATION_UPDATE_SKELETON); +void Skeleton3D::_update_deferred() { + if (!is_update_needed && !updating && is_inside_tree()) { + is_update_needed = true; + notify_deferred_thread_group(NOTIFICATION_UPDATE_SKELETON); // It must never be called more than once in a single frame. } - dirty = true; } void Skeleton3D::localize_rests() { @@ -687,173 +766,6 @@ void Skeleton3D::localize_rests() { } } -void Skeleton3D::set_animate_physical_bones(bool p_enabled) { - animate_physical_bones = p_enabled; - - if (Engine::get_singleton()->is_editor_hint() == false) { - bool sim = false; - for (int i = 0; i < bones.size(); i += 1) { - if (bones[i].physical_bone) { - bones[i].physical_bone->reset_physics_simulation_state(); - if (bones[i].physical_bone->is_simulating_physics()) { - sim = true; - } - } - } - set_physics_process_internal(sim == false && p_enabled); - } -} - -bool Skeleton3D::get_animate_physical_bones() const { - return animate_physical_bones; -} - -void Skeleton3D::bind_physical_bone_to_bone(int p_bone, PhysicalBone3D *p_physical_bone) { - const int bone_size = bones.size(); - ERR_FAIL_INDEX(p_bone, bone_size); - ERR_FAIL_COND(bones[p_bone].physical_bone); - ERR_FAIL_NULL(p_physical_bone); - bones.write[p_bone].physical_bone = p_physical_bone; - - _rebuild_physical_bones_cache(); -} - -void Skeleton3D::unbind_physical_bone_from_bone(int p_bone) { - const int bone_size = bones.size(); - ERR_FAIL_INDEX(p_bone, bone_size); - bones.write[p_bone].physical_bone = nullptr; - - _rebuild_physical_bones_cache(); -} - -PhysicalBone3D *Skeleton3D::get_physical_bone(int p_bone) { - const int bone_size = bones.size(); - ERR_FAIL_INDEX_V(p_bone, bone_size, nullptr); - - return bones[p_bone].physical_bone; -} - -PhysicalBone3D *Skeleton3D::get_physical_bone_parent(int p_bone) { - const int bone_size = bones.size(); - ERR_FAIL_INDEX_V(p_bone, bone_size, nullptr); - - if (bones[p_bone].cache_parent_physical_bone) { - return bones[p_bone].cache_parent_physical_bone; - } - - return _get_physical_bone_parent(p_bone); -} - -PhysicalBone3D *Skeleton3D::_get_physical_bone_parent(int p_bone) { - const int bone_size = bones.size(); - ERR_FAIL_INDEX_V(p_bone, bone_size, nullptr); - - const int parent_bone = bones[p_bone].parent; - if (0 > parent_bone) { - return nullptr; - } - - PhysicalBone3D *pb = bones[parent_bone].physical_bone; - if (pb) { - return pb; - } else { - return get_physical_bone_parent(parent_bone); - } -} - -void Skeleton3D::_rebuild_physical_bones_cache() { - const int b_size = bones.size(); - for (int i = 0; i < b_size; ++i) { - PhysicalBone3D *parent_pb = _get_physical_bone_parent(i); - if (parent_pb != bones[i].cache_parent_physical_bone) { - bones.write[i].cache_parent_physical_bone = parent_pb; - if (bones[i].physical_bone) { - bones[i].physical_bone->_on_bone_parent_changed(); - } - } - } -} - -void _pb_stop_simulation(Node *p_node) { - for (int i = p_node->get_child_count() - 1; 0 <= i; --i) { - _pb_stop_simulation(p_node->get_child(i)); - } - - PhysicalBone3D *pb = Object::cast_to<PhysicalBone3D>(p_node); - if (pb) { - pb->set_simulate_physics(false); - } -} - -void Skeleton3D::physical_bones_stop_simulation() { - _pb_stop_simulation(this); - if (Engine::get_singleton()->is_editor_hint() == false && animate_physical_bones) { - set_physics_process_internal(true); - } -} - -void _pb_start_simulation(const Skeleton3D *p_skeleton, Node *p_node, const Vector<int> &p_sim_bones) { - for (int i = p_node->get_child_count() - 1; 0 <= i; --i) { - _pb_start_simulation(p_skeleton, p_node->get_child(i), p_sim_bones); - } - - PhysicalBone3D *pb = Object::cast_to<PhysicalBone3D>(p_node); - if (pb) { - if (p_sim_bones.is_empty()) { // If no bones is specified, activate ragdoll on full body. - pb->set_simulate_physics(true); - } else { - for (int i = p_sim_bones.size() - 1; 0 <= i; --i) { - if (p_sim_bones[i] == pb->get_bone_id() || p_skeleton->is_bone_parent_of(pb->get_bone_id(), p_sim_bones[i])) { - pb->set_simulate_physics(true); - break; - } - } - } - } -} - -void Skeleton3D::physical_bones_start_simulation_on(const TypedArray<StringName> &p_bones) { - set_physics_process_internal(false); - - Vector<int> sim_bones; - if (p_bones.size() > 0) { - sim_bones.resize(p_bones.size()); - int c = 0; - for (int i = sim_bones.size() - 1; 0 <= i; --i) { - int bone_id = find_bone(p_bones[i]); - if (bone_id != -1) { - sim_bones.write[c++] = bone_id; - } - } - sim_bones.resize(c); - } - - _pb_start_simulation(this, this, sim_bones); -} - -void _physical_bones_add_remove_collision_exception(bool p_add, Node *p_node, RID p_exception) { - for (int i = p_node->get_child_count() - 1; 0 <= i; --i) { - _physical_bones_add_remove_collision_exception(p_add, p_node->get_child(i), p_exception); - } - - CollisionObject3D *co = Object::cast_to<CollisionObject3D>(p_node); - if (co) { - if (p_add) { - PhysicsServer3D::get_singleton()->body_add_collision_exception(co->get_rid(), p_exception); - } else { - PhysicsServer3D::get_singleton()->body_remove_collision_exception(co->get_rid(), p_exception); - } - } -} - -void Skeleton3D::physical_bones_add_collision_exception(RID p_exception) { - _physical_bones_add_remove_collision_exception(true, this, p_exception); -} - -void Skeleton3D::physical_bones_remove_collision_exception(RID p_exception) { - _physical_bones_add_remove_collision_exception(false, this, p_exception); -} - void Skeleton3D::_skin_changed() { _make_dirty(); } @@ -927,18 +839,23 @@ Ref<SkinReference> Skeleton3D::register_skin(const Ref<Skin> &p_skin) { } void Skeleton3D::force_update_all_dirty_bones() { - if (dirty) { - const_cast<Skeleton3D *>(this)->notification(NOTIFICATION_UPDATE_SKELETON); + if (!dirty) { + return; } + force_update_all_bone_transforms(); } void Skeleton3D::force_update_all_bone_transforms() { _update_process_order(); - for (int i = 0; i < parentless_bones.size(); i++) { force_update_bone_children_transforms(parentless_bones[i]); } rest_dirty = false; + dirty = false; + if (updating) { + return; + } + emit_signal(SceneStringNames::get_singleton()->pose_updated); } void Skeleton3D::force_update_bone_children_transforms(int p_bone_idx) { @@ -961,32 +878,43 @@ void Skeleton3D::force_update_bone_children_transforms(int p_bone_idx) { Transform3D pose = b.pose_cache; if (b.parent >= 0) { - b.pose_global = bonesptr[b.parent].pose_global * pose; - b.pose_global_no_override = bonesptr[b.parent].pose_global_no_override * pose; + b.global_pose = bonesptr[b.parent].global_pose * pose; } else { - b.pose_global = pose; - b.pose_global_no_override = pose; + b.global_pose = pose; } } else { if (b.parent >= 0) { - b.pose_global = bonesptr[b.parent].pose_global * b.rest; - b.pose_global_no_override = bonesptr[b.parent].pose_global_no_override * b.rest; + b.global_pose = bonesptr[b.parent].global_pose * b.rest; } else { - b.pose_global = b.rest; - b.pose_global_no_override = b.rest; + b.global_pose = b.rest; } } if (rest_dirty) { b.global_rest = b.parent >= 0 ? bonesptr[b.parent].global_rest * b.rest : b.rest; } +#ifndef DISABLE_DEPRECATED + if (bone_enabled) { + Transform3D pose = b.pose_cache; + if (b.parent >= 0) { + b.pose_global_no_override = bonesptr[b.parent].pose_global_no_override * pose; + } else { + b.pose_global_no_override = pose; + } + } else { + if (b.parent >= 0) { + b.pose_global_no_override = bonesptr[b.parent].pose_global_no_override * b.rest; + } else { + b.pose_global_no_override = b.rest; + } + } if (b.global_pose_override_amount >= CMP_EPSILON) { - b.pose_global = b.pose_global.interpolate_with(b.global_pose_override, b.global_pose_override_amount); + b.global_pose = b.global_pose.interpolate_with(b.global_pose_override, b.global_pose_override_amount); } - if (b.global_pose_override_reset) { b.global_pose_override_amount = 0.0; } +#endif // _DISABLE_DEPRECATED // Add the bone's children to the list of bones to be processed. int child_bone_size = b.child_bones.size(); @@ -998,6 +926,72 @@ void Skeleton3D::force_update_bone_children_transforms(int p_bone_idx) { } } +void Skeleton3D::_find_modifiers() { + if (!modifiers_dirty) { + return; + } + modifiers.clear(); + for (int i = 0; i < get_child_count(); i++) { + SkeletonModifier3D *c = Object::cast_to<SkeletonModifier3D>(get_child(i)); + if (c) { + modifiers.push_back(c->get_instance_id()); + } + } + modifiers_dirty = false; +} + +void Skeleton3D::_process_modifiers() { + for (const ObjectID &oid : modifiers) { + Object *t_obj = ObjectDB::get_instance(oid); + if (!t_obj) { + continue; + } + SkeletonModifier3D *mod = cast_to<SkeletonModifier3D>(t_obj); + if (!mod) { + continue; + } + real_t influence = mod->get_influence(); + if (influence < 1.0) { + LocalVector<Transform3D> old_poses; + for (int i = 0; i < get_bone_count(); i++) { + old_poses.push_back(get_bone_pose(i)); + } + mod->process_modification(); + LocalVector<Transform3D> new_poses; + for (int i = 0; i < get_bone_count(); i++) { + new_poses.push_back(get_bone_pose(i)); + } + for (int i = 0; i < get_bone_count(); i++) { + if (old_poses[i] == new_poses[i]) { + continue; // Avoid unneeded calculation. + } + set_bone_pose(i, old_poses[i].interpolate_with(new_poses[i], influence)); + } + } else { + mod->process_modification(); + } + force_update_all_dirty_bones(); + } +} + +void Skeleton3D::add_child_notify(Node *p_child) { + if (Object::cast_to<SkeletonModifier3D>(p_child)) { + _make_modifiers_dirty(); + } +} + +void Skeleton3D::move_child_notify(Node *p_child) { + if (Object::cast_to<SkeletonModifier3D>(p_child)) { + _make_modifiers_dirty(); + } +} + +void Skeleton3D::remove_child_notify(Node *p_child) { + if (Object::cast_to<SkeletonModifier3D>(p_child)) { + _make_modifiers_dirty(); + } +} + void Skeleton3D::_bind_methods() { ClassDB::bind_method(D_METHOD("add_bone", "name"), &Skeleton3D::add_bone); ClassDB::bind_method(D_METHOD("find_bone", "name"), &Skeleton3D::find_bone); @@ -1028,6 +1022,7 @@ void Skeleton3D::_bind_methods() { ClassDB::bind_method(D_METHOD("clear_bones"), &Skeleton3D::clear_bones); ClassDB::bind_method(D_METHOD("get_bone_pose", "bone_idx"), &Skeleton3D::get_bone_pose); + ClassDB::bind_method(D_METHOD("set_bone_pose", "bone_idx", "pose"), &Skeleton3D::set_bone_pose); ClassDB::bind_method(D_METHOD("set_bone_pose_position", "bone_idx", "position"), &Skeleton3D::set_bone_pose_position); ClassDB::bind_method(D_METHOD("set_bone_pose_rotation", "bone_idx", "rotation"), &Skeleton3D::set_bone_pose_rotation); ClassDB::bind_method(D_METHOD("set_bone_pose_scale", "bone_idx", "scale"), &Skeleton3D::set_bone_pose_scale); @@ -1042,11 +1037,8 @@ void Skeleton3D::_bind_methods() { ClassDB::bind_method(D_METHOD("is_bone_enabled", "bone_idx"), &Skeleton3D::is_bone_enabled); ClassDB::bind_method(D_METHOD("set_bone_enabled", "bone_idx", "enabled"), &Skeleton3D::set_bone_enabled, DEFVAL(true)); - ClassDB::bind_method(D_METHOD("clear_bones_global_pose_override"), &Skeleton3D::clear_bones_global_pose_override); - ClassDB::bind_method(D_METHOD("set_bone_global_pose_override", "bone_idx", "pose", "amount", "persistent"), &Skeleton3D::set_bone_global_pose_override, DEFVAL(false)); - ClassDB::bind_method(D_METHOD("get_bone_global_pose_override", "bone_idx"), &Skeleton3D::get_bone_global_pose_override); ClassDB::bind_method(D_METHOD("get_bone_global_pose", "bone_idx"), &Skeleton3D::get_bone_global_pose); - ClassDB::bind_method(D_METHOD("get_bone_global_pose_no_override", "bone_idx"), &Skeleton3D::get_bone_global_pose_no_override); + ClassDB::bind_method(D_METHOD("set_bone_global_pose", "bone_idx", "pose"), &Skeleton3D::set_bone_global_pose); ClassDB::bind_method(D_METHOD("force_update_all_bone_transforms"), &Skeleton3D::force_update_all_bone_transforms); ClassDB::bind_method(D_METHOD("force_update_bone_child_transform", "bone_idx"), &Skeleton3D::force_update_bone_children_transforms); @@ -1057,27 +1049,125 @@ void Skeleton3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_show_rest_only", "enabled"), &Skeleton3D::set_show_rest_only); ClassDB::bind_method(D_METHOD("is_show_rest_only"), &Skeleton3D::is_show_rest_only); - ClassDB::bind_method(D_METHOD("set_animate_physical_bones", "enabled"), &Skeleton3D::set_animate_physical_bones); - ClassDB::bind_method(D_METHOD("get_animate_physical_bones"), &Skeleton3D::get_animate_physical_bones); - - ClassDB::bind_method(D_METHOD("physical_bones_stop_simulation"), &Skeleton3D::physical_bones_stop_simulation); - ClassDB::bind_method(D_METHOD("physical_bones_start_simulation", "bones"), &Skeleton3D::physical_bones_start_simulation_on, DEFVAL(Array())); - ClassDB::bind_method(D_METHOD("physical_bones_add_collision_exception", "exception"), &Skeleton3D::physical_bones_add_collision_exception); - ClassDB::bind_method(D_METHOD("physical_bones_remove_collision_exception", "exception"), &Skeleton3D::physical_bones_remove_collision_exception); + ClassDB::bind_method(D_METHOD("set_modifier_callback_mode_process", "mode"), &Skeleton3D::set_modifier_callback_mode_process); + ClassDB::bind_method(D_METHOD("get_modifier_callback_mode_process"), &Skeleton3D::get_modifier_callback_mode_process); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "motion_scale", PROPERTY_HINT_RANGE, "0.001,10,0.001,or_greater"), "set_motion_scale", "get_motion_scale"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_rest_only"), "set_show_rest_only", "is_show_rest_only"); -#ifndef _3D_DISABLED - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "animate_physical_bones"), "set_animate_physical_bones", "get_animate_physical_bones"); -#endif // _3D_DISABLED + + ADD_GROUP("Modifier", "modifier_"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "modifier_callback_mode_process", PROPERTY_HINT_ENUM, "Physics,Idle"), "set_modifier_callback_mode_process", "get_modifier_callback_mode_process"); ADD_SIGNAL(MethodInfo("pose_updated")); ADD_SIGNAL(MethodInfo("bone_pose_changed", PropertyInfo(Variant::INT, "bone_idx"))); ADD_SIGNAL(MethodInfo("bone_enabled_changed", PropertyInfo(Variant::INT, "bone_idx"))); + ADD_SIGNAL(MethodInfo("bone_list_changed")); ADD_SIGNAL(MethodInfo("show_rest_only_changed")); BIND_CONSTANT(NOTIFICATION_UPDATE_SKELETON); + BIND_ENUM_CONSTANT(MODIFIER_CALLBACK_MODE_PROCESS_PHYSICS); + BIND_ENUM_CONSTANT(MODIFIER_CALLBACK_MODE_PROCESS_IDLE); + +#ifndef DISABLE_DEPRECATED + ClassDB::bind_method(D_METHOD("clear_bones_global_pose_override"), &Skeleton3D::clear_bones_global_pose_override); + ClassDB::bind_method(D_METHOD("set_bone_global_pose_override", "bone_idx", "pose", "amount", "persistent"), &Skeleton3D::set_bone_global_pose_override, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("get_bone_global_pose_override", "bone_idx"), &Skeleton3D::get_bone_global_pose_override); + ClassDB::bind_method(D_METHOD("get_bone_global_pose_no_override", "bone_idx"), &Skeleton3D::get_bone_global_pose_no_override); + + ClassDB::bind_method(D_METHOD("set_animate_physical_bones", "enabled"), &Skeleton3D::set_animate_physical_bones); + ClassDB::bind_method(D_METHOD("get_animate_physical_bones"), &Skeleton3D::get_animate_physical_bones); + ClassDB::bind_method(D_METHOD("physical_bones_stop_simulation"), &Skeleton3D::physical_bones_stop_simulation); + ClassDB::bind_method(D_METHOD("physical_bones_start_simulation", "bones"), &Skeleton3D::physical_bones_start_simulation_on, DEFVAL(Array())); + ClassDB::bind_method(D_METHOD("physical_bones_add_collision_exception", "exception"), &Skeleton3D::physical_bones_add_collision_exception); + ClassDB::bind_method(D_METHOD("physical_bones_remove_collision_exception", "exception"), &Skeleton3D::physical_bones_remove_collision_exception); +#endif // _DISABLE_DEPRECATED +} + +#ifndef DISABLE_DEPRECATED +void Skeleton3D::clear_bones_global_pose_override() { + for (int i = 0; i < bones.size(); i += 1) { + bones.write[i].global_pose_override_amount = 0; + bones.write[i].global_pose_override_reset = true; + } + _make_dirty(); +} + +void Skeleton3D::set_bone_global_pose_override(int p_bone, const Transform3D &p_pose, real_t p_amount, bool p_persistent) { + const int bone_size = bones.size(); + ERR_FAIL_INDEX(p_bone, bone_size); + bones.write[p_bone].global_pose_override_amount = p_amount; + bones.write[p_bone].global_pose_override = p_pose; + bones.write[p_bone].global_pose_override_reset = !p_persistent; + _make_dirty(); +} + +Transform3D Skeleton3D::get_bone_global_pose_override(int p_bone) const { + const int bone_size = bones.size(); + ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D()); + return bones[p_bone].global_pose_override; +} + +Transform3D Skeleton3D::get_bone_global_pose_no_override(int p_bone) const { + const int bone_size = bones.size(); + ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D()); + const_cast<Skeleton3D *>(this)->force_update_all_dirty_bones(); + return bones[p_bone].pose_global_no_override; +} + +Node *Skeleton3D::get_simulator() { + return simulator; +} + +void Skeleton3D::set_animate_physical_bones(bool p_enabled) { + PhysicalBoneSimulator3D *sim = cast_to<PhysicalBoneSimulator3D>(simulator); + if (!sim) { + return; + } + sim->set_active(p_enabled); +} + +bool Skeleton3D::get_animate_physical_bones() const { + PhysicalBoneSimulator3D *sim = cast_to<PhysicalBoneSimulator3D>(simulator); + if (!sim) { + return false; + } + return sim->is_active(); +} + +void Skeleton3D::physical_bones_stop_simulation() { + PhysicalBoneSimulator3D *sim = cast_to<PhysicalBoneSimulator3D>(simulator); + if (!sim) { + return; + } + sim->physical_bones_stop_simulation(); + sim->set_active(false); +} + +void Skeleton3D::physical_bones_start_simulation_on(const TypedArray<StringName> &p_bones) { + PhysicalBoneSimulator3D *sim = cast_to<PhysicalBoneSimulator3D>(simulator); + if (!sim) { + return; + } + sim->set_active(true); + sim->physical_bones_start_simulation_on(p_bones); +} + +void Skeleton3D::physical_bones_add_collision_exception(RID p_exception) { + PhysicalBoneSimulator3D *sim = cast_to<PhysicalBoneSimulator3D>(simulator); + if (!sim) { + return; + } + sim->physical_bones_add_collision_exception(p_exception); +} + +void Skeleton3D::physical_bones_remove_collision_exception(RID p_exception) { + PhysicalBoneSimulator3D *sim = cast_to<PhysicalBoneSimulator3D>(simulator); + if (!sim) { + return; + } + sim->physical_bones_remove_collision_exception(p_exception); } +#endif // _DISABLE_DEPRECATED Skeleton3D::Skeleton3D() { } diff --git a/scene/3d/skeleton_3d.h b/scene/3d/skeleton_3d.h index 979e2e52b7..5b6f60dbd4 100644 --- a/scene/3d/skeleton_3d.h +++ b/scene/3d/skeleton_3d.h @@ -36,8 +36,8 @@ typedef int BoneId; -class PhysicalBone3D; class Skeleton3D; +class SkeletonModifier3D; class SkinReference : public RefCounted { GDCLASS(SkinReference, RefCounted) @@ -66,61 +66,71 @@ public: class Skeleton3D : public Node3D { GDCLASS(Skeleton3D, Node3D); +#ifndef DISABLE_DEPRECATED + Node *simulator = nullptr; + void setup_simulator(); + void remove_simulator(); +#endif // _DISABLE_DEPRECATED + +public: + enum ModifierCallbackModeProcess { + MODIFIER_CALLBACK_MODE_PROCESS_PHYSICS, + MODIFIER_CALLBACK_MODE_PROCESS_IDLE, + }; + private: friend class SkinReference; + void _update_deferred(); + bool is_update_needed = false; // Is updating reserved? + bool updating = false; // Is updating now? + struct Bone { String name; - bool enabled; int parent; + Vector<int> child_bones; Transform3D rest; Transform3D global_rest; - _FORCE_INLINE_ void update_pose_cache() { + bool enabled; + Transform3D pose_cache; + bool pose_cache_dirty = true; + Vector3 pose_position; + Quaternion pose_rotation; + Vector3 pose_scale = Vector3(1, 1, 1); + Transform3D global_pose; + + void update_pose_cache() { if (pose_cache_dirty) { pose_cache.basis.set_quaternion_scale(pose_rotation, pose_scale); pose_cache.origin = pose_position; pose_cache_dirty = false; } } - bool pose_cache_dirty = true; - Transform3D pose_cache; - Vector3 pose_position; - Quaternion pose_rotation; - Vector3 pose_scale = Vector3(1, 1, 1); - Transform3D pose_global; +#ifndef DISABLE_DEPRECATED Transform3D pose_global_no_override; - real_t global_pose_override_amount = 0.0; bool global_pose_override_reset = false; Transform3D global_pose_override; - - PhysicalBone3D *physical_bone = nullptr; - PhysicalBone3D *cache_parent_physical_bone = nullptr; - - Vector<int> child_bones; +#endif // _DISABLE_DEPRECATED Bone() { parent = -1; + child_bones = Vector<int>(); enabled = true; +#ifndef DISABLE_DEPRECATED global_pose_override_amount = 0; global_pose_override_reset = false; -#ifndef _3D_DISABLED - physical_bone = nullptr; - cache_parent_physical_bone = nullptr; -#endif // _3D_DISABLED - child_bones = Vector<int>(); +#endif // _DISABLE_DEPRECATED } }; HashSet<SkinReference *> skin_bindings; - void _skin_changed(); - bool animate_physical_bones = true; Vector<Bone> bones; bool process_order_dirty = false; @@ -138,6 +148,15 @@ private: void _update_process_order(); + // To process modifiers. + ModifierCallbackModeProcess modifier_callback_mode_process = MODIFIER_CALLBACK_MODE_PROCESS_IDLE; + LocalVector<ObjectID> modifiers; + bool modifiers_dirty = false; + void _find_modifiers(); + void _process_modifiers(); + void _process_changed(); + void _make_modifiers_dirty(); + #ifndef DISABLE_DEPRECATED void _add_bone_bind_compat_88791(const String &p_name); @@ -152,12 +171,16 @@ protected: void _notification(int p_what); static void _bind_methods(); + virtual void add_child_notify(Node *p_child) override; + virtual void move_child_notify(Node *p_child) override; + virtual void remove_child_notify(Node *p_child) override; + public: enum { NOTIFICATION_UPDATE_SKELETON = 50 }; - // skeleton creation api + // Skeleton creation API uint64_t get_version() const; int add_bone(const String &p_name); int find_bone(const String &p_name) const; @@ -179,8 +202,6 @@ public: void set_bone_rest(int p_bone, const Transform3D &p_rest); Transform3D get_bone_rest(int p_bone) const; Transform3D get_bone_global_rest(int p_bone) const; - Transform3D get_bone_global_pose(int p_bone) const; - Transform3D get_bone_global_pose_no_override(int p_bone) const; void set_bone_enabled(int p_bone, bool p_enabled); bool is_bone_enabled(int p_bone) const; @@ -192,26 +213,23 @@ public: void set_motion_scale(float p_motion_scale); float get_motion_scale() const; - // posing api - - void set_bone_pose_position(int p_bone, const Vector3 &p_position); - void set_bone_pose_rotation(int p_bone, const Quaternion &p_rotation); - void set_bone_pose_scale(int p_bone, const Vector3 &p_scale); - + // Posing API Transform3D get_bone_pose(int p_bone) const; - Vector3 get_bone_pose_position(int p_bone) const; Quaternion get_bone_pose_rotation(int p_bone) const; Vector3 get_bone_pose_scale(int p_bone) const; + void set_bone_pose(int p_bone, const Transform3D &p_pose); + void set_bone_pose_position(int p_bone, const Vector3 &p_position); + void set_bone_pose_rotation(int p_bone, const Quaternion &p_rotation); + void set_bone_pose_scale(int p_bone, const Vector3 &p_scale); + + Transform3D get_bone_global_pose(int p_bone) const; + void set_bone_global_pose(int p_bone, const Transform3D &p_pose); void reset_bone_pose(int p_bone); void reset_bone_poses(); - void clear_bones_global_pose_override(); - Transform3D get_bone_global_pose_override(int p_bone) const; - void set_bone_global_pose_override(int p_bone, const Transform3D &p_pose, real_t p_amount, bool p_persistent = false); - - void localize_rests(); // used for loaders and tools + void localize_rests(); // Used for loaders and tools. Ref<Skin> create_skin_from_rest_transforms(); @@ -221,31 +239,29 @@ public: void force_update_all_bone_transforms(); void force_update_bone_children_transforms(int bone_idx); - // Physical bone API + void set_modifier_callback_mode_process(ModifierCallbackModeProcess p_mode); + ModifierCallbackModeProcess get_modifier_callback_mode_process() const; + +#ifndef DISABLE_DEPRECATED + Transform3D get_bone_global_pose_no_override(int p_bone) const; + void clear_bones_global_pose_override(); + Transform3D get_bone_global_pose_override(int p_bone) const; + void set_bone_global_pose_override(int p_bone, const Transform3D &p_pose, real_t p_amount, bool p_persistent = false); + Node *get_simulator(); void set_animate_physical_bones(bool p_enabled); bool get_animate_physical_bones() const; - - void bind_physical_bone_to_bone(int p_bone, PhysicalBone3D *p_physical_bone); - void unbind_physical_bone_from_bone(int p_bone); - - PhysicalBone3D *get_physical_bone(int p_bone); - PhysicalBone3D *get_physical_bone_parent(int p_bone); - -private: - /// This is a slow API, so it's cached - PhysicalBone3D *_get_physical_bone_parent(int p_bone); - void _rebuild_physical_bones_cache(); - -public: void physical_bones_stop_simulation(); void physical_bones_start_simulation_on(const TypedArray<StringName> &p_bones); void physical_bones_add_collision_exception(RID p_exception); void physical_bones_remove_collision_exception(RID p_exception); +#endif // _DISABLE_DEPRECATED public: Skeleton3D(); ~Skeleton3D(); }; +VARIANT_ENUM_CAST(Skeleton3D::ModifierCallbackModeProcess); + #endif // SKELETON_3D_H diff --git a/scene/3d/skeleton_ik_3d.cpp b/scene/3d/skeleton_ik_3d.cpp index 286268b4a2..9581ae58d8 100644 --- a/scene/3d/skeleton_ik_3d.cpp +++ b/scene/3d/skeleton_ik_3d.cpp @@ -30,8 +30,6 @@ #include "skeleton_ik_3d.h" -#ifndef _3D_DISABLED - FabrikInverseKinematic::ChainItem *FabrikInverseKinematic::ChainItem::find_child(const BoneId p_bone_id) { for (int i = children.size() - 1; 0 <= i; --i) { if (p_bone_id == children[i].bone) { @@ -236,46 +234,21 @@ void FabrikInverseKinematic::set_goal(Task *p_task, const Transform3D &p_goal) { p_task->goal_global_transform = p_goal; } -void FabrikInverseKinematic::make_goal(Task *p_task, const Transform3D &p_inverse_transf, real_t blending_delta) { - if (blending_delta >= 0.99f) { - // Update the end_effector (local transform) without blending - p_task->end_effectors.write[0].goal_transform = p_inverse_transf * p_task->goal_global_transform; - } else { - // End effector in local transform - const Transform3D end_effector_pose(p_task->skeleton->get_bone_global_pose_no_override(p_task->end_effectors[0].tip_bone)); - - // Update the end_effector (local transform) by blending with current pose - p_task->end_effectors.write[0].goal_transform = end_effector_pose.interpolate_with(p_inverse_transf * p_task->goal_global_transform, blending_delta); - } +void FabrikInverseKinematic::make_goal(Task *p_task, const Transform3D &p_inverse_transf) { + // Update the end_effector (local transform) by blending with current pose + p_task->end_effectors.write[0].goal_transform = p_inverse_transf * p_task->goal_global_transform; } -void FabrikInverseKinematic::solve(Task *p_task, real_t blending_delta, bool override_tip_basis, bool p_use_magnet, const Vector3 &p_magnet_position) { - if (blending_delta <= 0.01f) { - // Before skipping, make sure we undo the global pose overrides - ChainItem *ci(&p_task->chain.chain_root); - while (ci) { - p_task->skeleton->set_bone_global_pose_override(ci->bone, ci->initial_transform, 0.0, false); - - if (!ci->children.is_empty()) { - ci = &ci->children.write[0]; - } else { - ci = nullptr; - } - } - - return; // Skip solving - } - +void FabrikInverseKinematic::solve(Task *p_task, bool override_tip_basis, bool p_use_magnet, const Vector3 &p_magnet_position) { // Update the initial root transform so its synced with any animation changes _update_chain(p_task->skeleton, &p_task->chain.chain_root); - p_task->skeleton->set_bone_global_pose_override(p_task->chain.chain_root.bone, Transform3D(), 0.0, false); Vector3 origin_pos = p_task->skeleton->get_bone_global_pose(p_task->chain.chain_root.bone).origin; - make_goal(p_task, p_task->skeleton->get_global_transform().affine_inverse(), blending_delta); + make_goal(p_task, p_task->skeleton->get_global_transform().affine_inverse()); if (p_use_magnet && p_task->chain.middle_chain_item) { - p_task->chain.magnet_position = p_task->chain.middle_chain_item->initial_transform.origin.lerp(p_magnet_position, blending_delta); + p_task->chain.magnet_position = p_magnet_position; solve_simple(p_task, true, origin_pos); } solve_simple(p_task, false, origin_pos); @@ -303,8 +276,7 @@ void FabrikInverseKinematic::solve(Task *p_task, real_t blending_delta, bool ove // IK should not affect scale, so undo any scaling new_bone_pose.basis.orthonormalize(); new_bone_pose.basis.scale(p_task->skeleton->get_bone_global_pose(ci->bone).basis.get_scale()); - - p_task->skeleton->set_bone_global_pose_override(ci->bone, new_bone_pose, 1.0, true); + p_task->skeleton->set_bone_global_pose(ci->bone, Transform3D(new_bone_pose.basis, p_task->skeleton->get_bone_global_pose(ci->bone).origin)); if (!ci->children.is_empty()) { ci = &ci->children.write[0]; @@ -319,7 +291,7 @@ void FabrikInverseKinematic::_update_chain(const Skeleton3D *p_sk, ChainItem *p_ return; } - p_chain_item->initial_transform = p_sk->get_bone_global_pose_no_override(p_chain_item->bone); + p_chain_item->initial_transform = p_sk->get_bone_global_pose(p_chain_item->bone); p_chain_item->current_pos = p_chain_item->initial_transform.origin; ChainItem *items = p_chain_item->children.ptrw(); @@ -329,8 +301,10 @@ void FabrikInverseKinematic::_update_chain(const Skeleton3D *p_sk, ChainItem *p_ } void SkeletonIK3D::_validate_property(PropertyInfo &p_property) const { + SkeletonModifier3D::_validate_property(p_property); + if (p_property.name == "root_bone" || p_property.name == "tip_bone") { - Skeleton3D *skeleton = get_parent_skeleton(); + Skeleton3D *skeleton = get_skeleton(); if (skeleton) { String names("--,"); for (int i = 0; i < skeleton->get_bone_count(); i++) { @@ -356,9 +330,6 @@ void SkeletonIK3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_tip_bone", "tip_bone"), &SkeletonIK3D::set_tip_bone); ClassDB::bind_method(D_METHOD("get_tip_bone"), &SkeletonIK3D::get_tip_bone); - ClassDB::bind_method(D_METHOD("set_interpolation", "interpolation"), &SkeletonIK3D::set_interpolation); - ClassDB::bind_method(D_METHOD("get_interpolation"), &SkeletonIK3D::get_interpolation); - ClassDB::bind_method(D_METHOD("set_target_transform", "target"), &SkeletonIK3D::set_target_transform); ClassDB::bind_method(D_METHOD("get_target_transform"), &SkeletonIK3D::get_target_transform); @@ -388,7 +359,6 @@ void SkeletonIK3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "root_bone"), "set_root_bone", "get_root_bone"); ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "tip_bone"), "set_tip_bone", "get_tip_bone"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "interpolation", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_interpolation", "get_interpolation"); ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM3D, "target", PROPERTY_HINT_NONE, "suffix:m"), "set_target_transform", "get_target_transform"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "override_tip_basis"), "set_override_tip_basis", "is_override_tip_basis"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_magnet"), "set_use_magnet", "is_using_magnet"); @@ -398,21 +368,21 @@ void SkeletonIK3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "max_iterations"), "set_max_iterations", "get_max_iterations"); } +void SkeletonIK3D::_process_modification() { + if (!internal_active) { + return; + } + if (target_node_override_ref) { + reload_goal(); + } + _solve_chain(); +} + void SkeletonIK3D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { - skeleton_ref = Object::cast_to<Skeleton3D>(get_parent()); - set_process_priority(1); reload_chain(); } break; - - case NOTIFICATION_INTERNAL_PROCESS: { - if (target_node_override_ref) { - reload_goal(); - } - _solve_chain(); - } break; - case NOTIFICATION_EXIT_TREE: { stop(); } break; @@ -445,14 +415,6 @@ StringName SkeletonIK3D::get_tip_bone() const { return tip_bone; } -void SkeletonIK3D::set_interpolation(real_t p_interpolation) { - interpolation = p_interpolation; -} - -real_t SkeletonIK3D::get_interpolation() const { - return interpolation; -} - void SkeletonIK3D::set_target_transform(const Transform3D &p_target) { target = p_target; reload_goal(); @@ -505,33 +467,25 @@ void SkeletonIK3D::set_max_iterations(int p_iterations) { } Skeleton3D *SkeletonIK3D::get_parent_skeleton() const { - return cast_to<Skeleton3D>(skeleton_ref.get_validated_object()); + return get_skeleton(); } bool SkeletonIK3D::is_running() { - return is_processing_internal(); + return internal_active; } void SkeletonIK3D::start(bool p_one_time) { if (p_one_time) { - set_process_internal(false); - - if (target_node_override_ref) { - reload_goal(); - } - - _solve_chain(); + internal_active = true; + SkeletonModifier3D::process_modification(); + internal_active = false; } else { - set_process_internal(true); + internal_active = true; } } void SkeletonIK3D::stop() { - set_process_internal(false); - Skeleton3D *skeleton = get_parent_skeleton(); - if (skeleton) { - skeleton->clear_bones_global_pose_override(); - } + internal_active = false; } Transform3D SkeletonIK3D::_get_target_transform() { @@ -551,7 +505,7 @@ void SkeletonIK3D::reload_chain() { FabrikInverseKinematic::free_task(task); task = nullptr; - Skeleton3D *skeleton = get_parent_skeleton(); + Skeleton3D *skeleton = get_skeleton(); if (!skeleton) { return; } @@ -575,7 +529,5 @@ void SkeletonIK3D::_solve_chain() { if (!task) { return; } - FabrikInverseKinematic::solve(task, interpolation, override_tip_basis, use_magnet, magnet_position); + FabrikInverseKinematic::solve(task, override_tip_basis, use_magnet, magnet_position); } - -#endif // _3D_DISABLED diff --git a/scene/3d/skeleton_ik_3d.h b/scene/3d/skeleton_ik_3d.h index 0a03e96905..eff018f2cc 100644 --- a/scene/3d/skeleton_ik_3d.h +++ b/scene/3d/skeleton_ik_3d.h @@ -31,9 +31,7 @@ #ifndef SKELETON_IK_3D_H #define SKELETON_IK_3D_H -#ifndef _3D_DISABLED - -#include "scene/3d/skeleton_3d.h" +#include "scene/3d/skeleton_modifier_3d.h" class FabrikInverseKinematic { struct EndEffector { @@ -111,18 +109,19 @@ public: static void free_task(Task *p_task); // The goal of chain should be always in local space static void set_goal(Task *p_task, const Transform3D &p_goal); - static void make_goal(Task *p_task, const Transform3D &p_inverse_transf, real_t blending_delta); - static void solve(Task *p_task, real_t blending_delta, bool override_tip_basis, bool p_use_magnet, const Vector3 &p_magnet_position); + static void make_goal(Task *p_task, const Transform3D &p_inverse_transf); + static void solve(Task *p_task, bool override_tip_basis, bool p_use_magnet, const Vector3 &p_magnet_position); static void _update_chain(const Skeleton3D *p_skeleton, ChainItem *p_chain_item); }; -class SkeletonIK3D : public Node { - GDCLASS(SkeletonIK3D, Node); +class SkeletonIK3D : public SkeletonModifier3D { + GDCLASS(SkeletonIK3D, SkeletonModifier3D); + + bool internal_active = false; StringName root_bone; StringName tip_bone; - real_t interpolation = 1.0; Transform3D target; NodePath target_node_path_override; bool override_tip_basis = true; @@ -132,7 +131,6 @@ class SkeletonIK3D : public Node { real_t min_distance = 0.01; int max_iterations = 10; - Variant skeleton_ref = Variant(); Variant target_node_override_ref = Variant(); FabrikInverseKinematic::Task *task = nullptr; @@ -142,6 +140,8 @@ protected: static void _bind_methods(); virtual void _notification(int p_what); + virtual void _process_modification() override; + public: SkeletonIK3D(); virtual ~SkeletonIK3D(); @@ -152,9 +152,6 @@ public: void set_tip_bone(const StringName &p_tip_bone); StringName get_tip_bone() const; - void set_interpolation(real_t p_interpolation); - real_t get_interpolation() const; - void set_target_transform(const Transform3D &p_target); const Transform3D &get_target_transform() const; @@ -190,6 +187,4 @@ private: void _solve_chain(); }; -#endif // _3D_DISABLED - #endif // SKELETON_IK_3D_H diff --git a/scene/3d/skeleton_modifier_3d.cpp b/scene/3d/skeleton_modifier_3d.cpp new file mode 100644 index 0000000000..96e3e33841 --- /dev/null +++ b/scene/3d/skeleton_modifier_3d.cpp @@ -0,0 +1,139 @@ +/**************************************************************************/ +/* skeleton_modifier_3d.cpp */ +/**************************************************************************/ +/* 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. */ +/**************************************************************************/ + +#include "skeleton_modifier_3d.h" + +void SkeletonModifier3D::_validate_property(PropertyInfo &p_property) const { + // +} + +PackedStringArray SkeletonModifier3D::get_configuration_warnings() const { + PackedStringArray warnings = Node3D::get_configuration_warnings(); + if (skeleton_id.is_null()) { + warnings.push_back(RTR("Skeleton3D node not set! SkeletonModifier3D must be child of Skeleton3D or set a path to an external skeleton.")); + } + return warnings; +} + +/* Skeleton3D */ + +Skeleton3D *SkeletonModifier3D::get_skeleton() const { + return Object::cast_to<Skeleton3D>(ObjectDB::get_instance(skeleton_id)); +} + +void SkeletonModifier3D::_update_skeleton_path() { + skeleton_id = ObjectID(); + + // Make sure parent is a Skeleton3D. + Skeleton3D *sk = Object::cast_to<Skeleton3D>(get_parent()); + if (sk) { + skeleton_id = sk->get_instance_id(); + } +} + +void SkeletonModifier3D::_update_skeleton() { + if (!is_inside_tree()) { + return; + } + Skeleton3D *old_sk = get_skeleton(); + _update_skeleton_path(); + Skeleton3D *new_sk = get_skeleton(); + if (old_sk != new_sk) { + _skeleton_changed(old_sk, new_sk); + } + update_configuration_warnings(); +} + +void SkeletonModifier3D::_skeleton_changed(Skeleton3D *p_old, Skeleton3D *p_new) { + // +} + +/* Process */ + +void SkeletonModifier3D::set_active(bool p_active) { + if (active == p_active) { + return; + } + active = p_active; + _set_active(active); +} + +bool SkeletonModifier3D::is_active() const { + return active; +} + +void SkeletonModifier3D::_set_active(bool p_active) { + // +} + +void SkeletonModifier3D::set_influence(real_t p_influence) { + influence = p_influence; +} + +real_t SkeletonModifier3D::get_influence() const { + return influence; +} + +void SkeletonModifier3D::process_modification() { + if (!active) { + return; + } + _process_modification(); + emit_signal(SNAME("modification_processed")); +} + +void SkeletonModifier3D::_process_modification() { + // +} + +void SkeletonModifier3D::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: + case NOTIFICATION_PARENTED: { + _update_skeleton(); + } break; + } +} + +void SkeletonModifier3D::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_active", "active"), &SkeletonModifier3D::set_active); + ClassDB::bind_method(D_METHOD("is_active"), &SkeletonModifier3D::is_active); + + ClassDB::bind_method(D_METHOD("set_influence", "influence"), &SkeletonModifier3D::set_influence); + ClassDB::bind_method(D_METHOD("get_influence"), &SkeletonModifier3D::get_influence); + + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "active"), "set_active", "is_active"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "influence", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_influence", "get_influence"); + + ADD_SIGNAL(MethodInfo("modification_processed")); +} + +SkeletonModifier3D::SkeletonModifier3D() { +} diff --git a/scene/3d/skeleton_modifier_3d.h b/scene/3d/skeleton_modifier_3d.h new file mode 100644 index 0000000000..25c09f3b93 --- /dev/null +++ b/scene/3d/skeleton_modifier_3d.h @@ -0,0 +1,81 @@ +/**************************************************************************/ +/* skeleton_modifier_3d.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 SKELETON_MODIFIER_3D_H +#define SKELETON_MODIFIER_3D_H + +#include "scene/3d/node_3d.h" + +#include "scene/3d/skeleton_3d.h" +#include "scene/animation/animation_mixer.h" + +class SkeletonModifier3D : public Node3D { + GDCLASS(SkeletonModifier3D, Node3D); + + void rebind(); + +protected: + bool active = true; + real_t influence = 1.0; + + // Cache them for the performance reason since finding node with NodePath is slow. + ObjectID skeleton_id; + + void _update_skeleton(); + void _update_skeleton_path(); + + virtual void _skeleton_changed(Skeleton3D *p_old, Skeleton3D *p_new); + + void _validate_property(PropertyInfo &p_property) const; + void _notification(int p_what); + static void _bind_methods(); + + virtual void _set_active(bool p_active); + + virtual void _process_modification(); + +public: + virtual PackedStringArray get_configuration_warnings() const override; + virtual bool has_process() const { return false; } // Return true if modifier needs to modify bone pose without external animation such as physics, jiggle and etc. + + void set_active(bool p_active); + bool is_active() const; + + void set_influence(real_t p_influence); + real_t get_influence() const; + + Skeleton3D *get_skeleton() const; + + void process_modification(); + + SkeletonModifier3D(); +}; + +#endif // SKELETON_MODIFIER_3D_H diff --git a/scene/3d/xr_body_modifier_3d.cpp b/scene/3d/xr_body_modifier_3d.cpp index ac648d66d0..0099784a05 100644 --- a/scene/3d/xr_body_modifier_3d.cpp +++ b/scene/3d/xr_body_modifier_3d.cpp @@ -38,9 +38,6 @@ void XRBodyModifier3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_body_tracker", "tracker_name"), &XRBodyModifier3D::set_body_tracker); ClassDB::bind_method(D_METHOD("get_body_tracker"), &XRBodyModifier3D::get_body_tracker); - ClassDB::bind_method(D_METHOD("set_target", "target"), &XRBodyModifier3D::set_target); - ClassDB::bind_method(D_METHOD("get_target"), &XRBodyModifier3D::get_target); - ClassDB::bind_method(D_METHOD("set_body_update", "body_update"), &XRBodyModifier3D::set_body_update); ClassDB::bind_method(D_METHOD("get_body_update"), &XRBodyModifier3D::get_body_update); @@ -51,7 +48,6 @@ void XRBodyModifier3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_show_when_tracked"), &XRBodyModifier3D::get_show_when_tracked); ADD_PROPERTY(PropertyInfo(Variant::STRING, "body_tracker", PROPERTY_HINT_ENUM_SUGGESTION, "/user/body"), "set_body_tracker", "get_body_tracker"); - ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "target", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Skeleton3D"), "set_target", "get_target"); ADD_PROPERTY(PropertyInfo(Variant::INT, "body_update", PROPERTY_HINT_FLAGS, "Upper Body,Lower Body,Hands"), "set_body_update", "get_body_update"); ADD_PROPERTY(PropertyInfo(Variant::INT, "bone_update", PROPERTY_HINT_ENUM, "Full,Rotation Only"), "set_bone_update", "get_bone_update"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_when_tracked"), "set_show_when_tracked", "get_show_when_tracked"); @@ -73,18 +69,6 @@ StringName XRBodyModifier3D::get_body_tracker() const { return tracker_name; } -void XRBodyModifier3D::set_target(const NodePath &p_target) { - target = p_target; - - if (is_inside_tree()) { - _get_joint_data(); - } -} - -NodePath XRBodyModifier3D::get_target() const { - return target; -} - void XRBodyModifier3D::set_body_update(BitField<BodyUpdate> p_body_update) { body_update = p_body_update; } @@ -110,20 +94,6 @@ bool XRBodyModifier3D::get_show_when_tracked() const { return show_when_tracked; } -Skeleton3D *XRBodyModifier3D::get_skeleton() { - if (!has_node(target)) { - return nullptr; - } - - Node *node = get_node(target); - if (!node) { - return nullptr; - } - - Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(node); - return skeleton; -} - void XRBodyModifier3D::_get_joint_data() { // Table of Godot Humanoid bone names. static const String bone_names[XRBodyTracker::JOINT_MAX] = { @@ -281,7 +251,7 @@ void XRBodyModifier3D::_get_joint_data() { } } -void XRBodyModifier3D::_update_skeleton() { +void XRBodyModifier3D::_process_modification() { Skeleton3D *skeleton = get_skeleton(); if (!skeleton) { return; @@ -379,6 +349,10 @@ void XRBodyModifier3D::_tracker_changed(const StringName &p_tracker_name, const } } +void XRBodyModifier3D::_skeleton_changed(Skeleton3D *p_old, Skeleton3D *p_new) { + _get_joint_data(); +} + void XRBodyModifier3D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { @@ -388,10 +362,7 @@ void XRBodyModifier3D::_notification(int p_what) { xr_server->connect("body_tracker_updated", callable_mp(this, &XRBodyModifier3D::_tracker_changed)); xr_server->connect("body_tracker_removed", callable_mp(this, &XRBodyModifier3D::_tracker_changed).bind(Ref<XRBodyTracker>())); } - _get_joint_data(); - - set_process_internal(true); } break; case NOTIFICATION_EXIT_TREE: { XRServer *xr_server = XRServer::get_singleton(); @@ -400,17 +371,11 @@ void XRBodyModifier3D::_notification(int p_what) { xr_server->disconnect("body_tracker_updated", callable_mp(this, &XRBodyModifier3D::_tracker_changed)); xr_server->disconnect("body_tracker_removed", callable_mp(this, &XRBodyModifier3D::_tracker_changed).bind(Ref<XRBodyTracker>())); } - - set_process_internal(false); - for (int i = 0; i < XRBodyTracker::JOINT_MAX; i++) { joints[i].bone = -1; joints[i].parent_joint = -1; } } break; - case NOTIFICATION_INTERNAL_PROCESS: { - _update_skeleton(); - } break; default: { } break; } diff --git a/scene/3d/xr_body_modifier_3d.h b/scene/3d/xr_body_modifier_3d.h index 89ac69b6b0..03b1c07d53 100644 --- a/scene/3d/xr_body_modifier_3d.h +++ b/scene/3d/xr_body_modifier_3d.h @@ -31,7 +31,7 @@ #ifndef XR_BODY_MODIFIER_3D_H #define XR_BODY_MODIFIER_3D_H -#include "scene/3d/node_3d.h" +#include "scene/3d/skeleton_modifier_3d.h" #include "servers/xr/xr_body_tracker.h" class Skeleton3D; @@ -41,8 +41,8 @@ class Skeleton3D; data from an XRBodyTracker instance. */ -class XRBodyModifier3D : public Node3D { - GDCLASS(XRBodyModifier3D, Node3D); +class XRBodyModifier3D : public SkeletonModifier3D { + GDCLASS(XRBodyModifier3D, SkeletonModifier3D); public: enum BodyUpdate { @@ -60,9 +60,6 @@ public: void set_body_tracker(const StringName &p_tracker_name); StringName get_body_tracker() const; - void set_target(const NodePath &p_target); - NodePath get_target() const; - void set_body_update(BitField<BodyUpdate> p_body_update); BitField<BodyUpdate> get_body_update() const; @@ -77,6 +74,9 @@ public: protected: static void _bind_methods(); + virtual void _skeleton_changed(Skeleton3D *p_old, Skeleton3D *p_new) override; + virtual void _process_modification() override; + private: struct JointData { int bone = -1; @@ -84,15 +84,12 @@ private: }; StringName tracker_name = "/user/body"; - NodePath target; BitField<BodyUpdate> body_update = BODY_UPDATE_UPPER_BODY | BODY_UPDATE_LOWER_BODY | BODY_UPDATE_HANDS; BoneUpdate bone_update = BONE_UPDATE_FULL; bool show_when_tracked = true; JointData joints[XRBodyTracker::JOINT_MAX]; - Skeleton3D *get_skeleton(); void _get_joint_data(); - void _update_skeleton(); void _tracker_changed(const StringName &p_tracker_name, const Ref<XRBodyTracker> &p_tracker); }; diff --git a/scene/3d/xr_hand_modifier_3d.cpp b/scene/3d/xr_hand_modifier_3d.cpp index 1e1449b54b..7fecb53008 100644 --- a/scene/3d/xr_hand_modifier_3d.cpp +++ b/scene/3d/xr_hand_modifier_3d.cpp @@ -30,7 +30,6 @@ #include "xr_hand_modifier_3d.h" -#include "scene/3d/skeleton_3d.h" #include "servers/xr/xr_pose.h" #include "servers/xr_server.h" @@ -38,14 +37,10 @@ void XRHandModifier3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_hand_tracker", "tracker_name"), &XRHandModifier3D::set_hand_tracker); ClassDB::bind_method(D_METHOD("get_hand_tracker"), &XRHandModifier3D::get_hand_tracker); - ClassDB::bind_method(D_METHOD("set_target", "target"), &XRHandModifier3D::set_target); - ClassDB::bind_method(D_METHOD("get_target"), &XRHandModifier3D::get_target); - ClassDB::bind_method(D_METHOD("set_bone_update", "bone_update"), &XRHandModifier3D::set_bone_update); ClassDB::bind_method(D_METHOD("get_bone_update"), &XRHandModifier3D::get_bone_update); ADD_PROPERTY(PropertyInfo(Variant::STRING, "hand_tracker", PROPERTY_HINT_ENUM_SUGGESTION, "/user/left,/user/right"), "set_hand_tracker", "get_hand_tracker"); - ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "target", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Skeleton3D"), "set_target", "get_target"); ADD_PROPERTY(PropertyInfo(Variant::INT, "bone_update", PROPERTY_HINT_ENUM, "Full,Rotation Only"), "set_bone_update", "get_bone_update"); BIND_ENUM_CONSTANT(BONE_UPDATE_FULL); @@ -61,18 +56,6 @@ StringName XRHandModifier3D::get_hand_tracker() const { return tracker_name; } -void XRHandModifier3D::set_target(const NodePath &p_target) { - target = p_target; - - if (is_inside_tree()) { - _get_joint_data(); - } -} - -NodePath XRHandModifier3D::get_target() const { - return target; -} - void XRHandModifier3D::set_bone_update(BoneUpdate p_bone_update) { ERR_FAIL_INDEX(p_bone_update, BONE_UPDATE_MAX); bone_update = p_bone_update; @@ -82,21 +65,11 @@ XRHandModifier3D::BoneUpdate XRHandModifier3D::get_bone_update() const { return bone_update; } -Skeleton3D *XRHandModifier3D::get_skeleton() { - if (!has_node(target)) { - return nullptr; - } - - Node *node = get_node(target); - if (!node) { - return nullptr; +void XRHandModifier3D::_get_joint_data() { + if (!is_inside_tree()) { + return; } - Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(node); - return skeleton; -} - -void XRHandModifier3D::_get_joint_data() { // Table of bone names for different rig types. static const String bone_names[XRHandTracker::HAND_JOINT_MAX] = { "Palm", @@ -197,7 +170,7 @@ void XRHandModifier3D::_get_joint_data() { } } -void XRHandModifier3D::_update_skeleton() { +void XRHandModifier3D::_process_modification() { Skeleton3D *skeleton = get_skeleton(); if (!skeleton) { return; @@ -276,6 +249,10 @@ void XRHandModifier3D::_tracker_changed(StringName p_tracker_name, const Ref<XRH } } +void XRHandModifier3D::_skeleton_changed(Skeleton3D *p_old, Skeleton3D *p_new) { + _get_joint_data(); +} + void XRHandModifier3D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { @@ -287,8 +264,6 @@ void XRHandModifier3D::_notification(int p_what) { } _get_joint_data(); - - set_process_internal(true); } break; case NOTIFICATION_EXIT_TREE: { XRServer *xr_server = XRServer::get_singleton(); @@ -298,16 +273,11 @@ void XRHandModifier3D::_notification(int p_what) { xr_server->disconnect("hand_tracker_removed", callable_mp(this, &XRHandModifier3D::_tracker_changed).bind(Ref<XRHandTracker>())); } - set_process_internal(false); - for (int i = 0; i < XRHandTracker::HAND_JOINT_MAX; i++) { joints[i].bone = -1; joints[i].parent_joint = -1; } } break; - case NOTIFICATION_INTERNAL_PROCESS: { - _update_skeleton(); - } break; default: { } break; } diff --git a/scene/3d/xr_hand_modifier_3d.h b/scene/3d/xr_hand_modifier_3d.h index 2bc30d42d4..9f7ce45c9d 100644 --- a/scene/3d/xr_hand_modifier_3d.h +++ b/scene/3d/xr_hand_modifier_3d.h @@ -31,18 +31,16 @@ #ifndef XR_HAND_MODIFIER_3D_H #define XR_HAND_MODIFIER_3D_H -#include "scene/3d/node_3d.h" +#include "scene/3d/skeleton_modifier_3d.h" #include "servers/xr/xr_hand_tracker.h" -class Skeleton3D; - /** The XRHandModifier3D node drives a hand skeleton using hand tracking data from an XRHandTracking instance. */ -class XRHandModifier3D : public Node3D { - GDCLASS(XRHandModifier3D, Node3D); +class XRHandModifier3D : public SkeletonModifier3D { + GDCLASS(XRHandModifier3D, SkeletonModifier3D); public: enum BoneUpdate { @@ -54,9 +52,6 @@ public: void set_hand_tracker(const StringName &p_tracker_name); StringName get_hand_tracker() const; - void set_target(const NodePath &p_target); - NodePath get_target() const; - void set_bone_update(BoneUpdate p_bone_update); BoneUpdate get_bone_update() const; @@ -65,6 +60,9 @@ public: protected: static void _bind_methods(); + virtual void _skeleton_changed(Skeleton3D *p_old, Skeleton3D *p_new) override; + virtual void _process_modification() override; + private: struct JointData { int bone = -1; @@ -72,13 +70,10 @@ private: }; StringName tracker_name = "/user/left"; - NodePath target; BoneUpdate bone_update = BONE_UPDATE_FULL; JointData joints[XRHandTracker::HAND_JOINT_MAX]; - Skeleton3D *get_skeleton(); void _get_joint_data(); - void _update_skeleton(); void _tracker_changed(StringName p_tracker_name, const Ref<XRHandTracker> &p_tracker); }; diff --git a/scene/animation/animation_mixer.cpp b/scene/animation/animation_mixer.cpp index 22f919ad36..01e1de8f23 100644 --- a/scene/animation/animation_mixer.cpp +++ b/scene/animation/animation_mixer.cpp @@ -42,6 +42,7 @@ #include "scene/3d/mesh_instance_3d.h" #include "scene/3d/node_3d.h" #include "scene/3d/skeleton_3d.h" +#include "scene/3d/skeleton_modifier_3d.h" #endif // _3D_DISABLED #ifdef TOOLS_ENABLED @@ -484,10 +485,6 @@ void AnimationMixer::set_callback_mode_process(AnimationCallbackModeProcess p_mo if (was_active) { set_active(true); } - -#ifdef TOOLS_ENABLED - emit_signal(SNAME("mixer_updated")); -#endif // TOOLS_ENABLED } AnimationMixer::AnimationCallbackModeProcess AnimationMixer::get_callback_mode_process() const { @@ -496,9 +493,7 @@ AnimationMixer::AnimationCallbackModeProcess AnimationMixer::get_callback_mode_p void AnimationMixer::set_callback_mode_method(AnimationCallbackModeMethod p_mode) { callback_mode_method = p_mode; -#ifdef TOOLS_ENABLED emit_signal(SNAME("mixer_updated")); -#endif // TOOLS_ENABLED } AnimationMixer::AnimationCallbackModeMethod AnimationMixer::get_callback_mode_method() const { @@ -507,9 +502,7 @@ AnimationMixer::AnimationCallbackModeMethod AnimationMixer::get_callback_mode_me void AnimationMixer::set_callback_mode_discrete(AnimationCallbackModeDiscrete p_mode) { callback_mode_discrete = p_mode; -#ifdef TOOLS_ENABLED emit_signal(SNAME("mixer_updated")); -#endif // TOOLS_ENABLED } AnimationMixer::AnimationCallbackModeDiscrete AnimationMixer::get_callback_mode_discrete() const { @@ -696,14 +689,19 @@ 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; + // Can't interpolate them, need to convert. track_value->is_variant_interpolatable = Animation::is_variant_interpolatable(track_value->init_value); // If there is a Reset Animation, it takes precedence by overwriting. if (has_reset_anim) { int rt = reset_anim->find_track(path, track_src_type); - if (rt >= 0 && reset_anim->track_get_key_count(rt) > 0) { - track_value->init_value = track_src_type == Animation::TYPE_VALUE ? reset_anim->track_get_key_value(rt, 0) : (reset_anim->track_get_key_value(rt, 0).operator Array())[0]; + if (rt >= 0) { + 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 = track_src_type == Animation::TYPE_VALUE ? reset_anim->track_get_key_value(rt, 0) : (reset_anim->track_get_key_value(rt, 0).operator Array())[0]; + } } } @@ -930,6 +928,7 @@ void AnimationMixer::_process_animation(double p_delta, bool p_update_only) { _blend_process(p_delta, p_update_only); _blend_apply(); _blend_post_process(); + emit_signal(SNAME("mixer_applied")); }; clear_animation_instances(); } @@ -1002,6 +1001,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_discrete = false; } break; case Animation::TYPE_AUDIO: { @@ -1421,6 +1421,7 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) { bool is_discrete = is_value && a->value_track_get_update_mode(i) == Animation::UPDATE_DISCRETE; bool force_continuous = callback_mode_discrete == ANIMATION_CALLBACK_MODE_DISCRETE_FORCE_CONTINUOUS; if (t->is_variant_interpolatable && (!is_discrete || force_continuous)) { + t->use_continuous = true; Variant value = is_value ? a->value_track_interpolate(i, time, is_discrete && force_continuous ? backward : false) : Variant(a->bezier_track_interpolate(i, time)); value = post_process_key_value(a, i, value, t->object_id); if (value == Variant()) { @@ -1733,7 +1734,7 @@ void AnimationMixer::_blend_apply() { case Animation::TYPE_VALUE: { TrackCacheValue *t = static_cast<TrackCacheValue *>(track); - if (!t->is_variant_interpolatable || (callback_mode_discrete == ANIMATION_CALLBACK_MODE_DISCRETE_DOMINANT && t->use_discrete)) { + if (!t->is_variant_interpolatable || !t->use_continuous || (callback_mode_discrete == ANIMATION_CALLBACK_MODE_DISCRETE_DOMINANT && t->use_discrete)) { break; // Don't overwrite the value set by UPDATE_DISCRETE. } @@ -1975,6 +1976,7 @@ void AnimationMixer::_build_backup_track_cache() { if (t_obj) { t->value = Animation::cast_to_blendwise(t_obj->get_indexed(t->subpath)); } + t->use_continuous = true; t->use_discrete = false; if (t->init_value.is_array()) { t->element_size = MAX(t->element_size.operator int(), (t->value.operator Array()).size()); @@ -2228,19 +2230,16 @@ void AnimationMixer::_bind_methods() { ClassDB::bind_method(D_METHOD("advance", "delta"), &AnimationMixer::advance); GDVIRTUAL_BIND(_post_process_key_value, "animation", "track", "value", "object_id", "object_sub_idx"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "active"), "set_active", "is_active"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "deterministic"), "set_deterministic", "is_deterministic"); + /* ---- Capture feature ---- */ + ClassDB::bind_method(D_METHOD("capture", "name", "duration", "trans_type", "ease_type"), &AnimationMixer::capture, DEFVAL(Tween::TRANS_LINEAR), DEFVAL(Tween::EASE_IN)); /* ---- Reset on save ---- */ ClassDB::bind_method(D_METHOD("set_reset_on_save_enabled", "enabled"), &AnimationMixer::set_reset_on_save_enabled); ClassDB::bind_method(D_METHOD("is_reset_on_save_enabled"), &AnimationMixer::is_reset_on_save_enabled); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "reset_on_save", PROPERTY_HINT_NONE, ""), "set_reset_on_save_enabled", "is_reset_on_save_enabled"); - - /* ---- Capture feature ---- */ - ClassDB::bind_method(D_METHOD("capture", "name", "duration", "trans_type", "ease_type"), &AnimationMixer::capture, DEFVAL(Tween::TRANS_LINEAR), DEFVAL(Tween::EASE_IN)); - - ADD_SIGNAL(MethodInfo("mixer_updated")); // For updating dummy player. + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "active"), "set_active", "is_active"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "deterministic"), "set_deterministic", "is_deterministic"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "reset_on_save", PROPERTY_HINT_NONE, ""), "set_reset_on_save_enabled", "is_reset_on_save_enabled"); ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "root_node"), "set_root_node", "get_root_node"); ADD_GROUP("Root Motion", "root_motion_"); @@ -2270,6 +2269,8 @@ void AnimationMixer::_bind_methods() { ADD_SIGNAL(MethodInfo(SNAME("animation_finished"), PropertyInfo(Variant::STRING_NAME, "anim_name"))); ADD_SIGNAL(MethodInfo(SNAME("animation_started"), PropertyInfo(Variant::STRING_NAME, "anim_name"))); ADD_SIGNAL(MethodInfo(SNAME("caches_cleared"))); + ADD_SIGNAL(MethodInfo(SNAME("mixer_applied"))); + ADD_SIGNAL(MethodInfo(SNAME("mixer_updated"))); // For updating dummy player. ClassDB::bind_method(D_METHOD("_reset"), &AnimationMixer::reset); ClassDB::bind_method(D_METHOD("_restore", "backup"), &AnimationMixer::restore); diff --git a/scene/animation/animation_mixer.h b/scene/animation/animation_mixer.h index 682246f9ab..b7898cffc9 100644 --- a/scene/animation/animation_mixer.h +++ b/scene/animation/animation_mixer.h @@ -222,9 +222,14 @@ protected: Variant init_value; Variant value; Vector<StringName> subpath; + + // TODO: There are many boolean, can be packed into one integer. + bool init_use_continuous = false; + bool use_continuous = false; bool use_discrete = false; bool is_using_angle = false; bool is_variant_interpolatable = true; + Variant element_size; TrackCacheValue(const TrackCacheValue &p_other) : @@ -232,6 +237,8 @@ protected: init_value(p_other.init_value), value(p_other.value), subpath(p_other.subpath), + init_use_continuous(p_other.init_use_continuous), + use_continuous(p_other.use_continuous), use_discrete(p_other.use_discrete), is_using_angle(p_other.is_using_angle), is_variant_interpolatable(p_other.is_variant_interpolatable), @@ -341,6 +348,8 @@ protected: /* ---- Blending processor ---- */ virtual void _process_animation(double p_delta, bool p_update_only = false); + + // For post process with retrieved key value during blending. virtual Variant _post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant p_value, ObjectID p_object_id, int p_object_sub_idx = -1); Variant post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant p_value, ObjectID p_object_id, int p_object_sub_idx = -1); GDVIRTUAL5RC(Variant, _post_process_key_value, Ref<Animation>, int, Variant, ObjectID, int); @@ -377,7 +386,6 @@ protected: #ifndef DISABLE_DEPRECATED virtual Variant _post_process_key_value_bind_compat_86687(const Ref<Animation> &p_anim, int p_track, Variant p_value, Object *p_object, int p_object_idx = -1); - static void _bind_compatibility_methods(); #endif // DISABLE_DEPRECATED @@ -436,7 +444,7 @@ public: void make_animation_instance(const StringName &p_name, const PlaybackInfo p_playback_info); void clear_animation_instances(); virtual void advance(double p_time); - virtual void clear_caches(); ///< must be called by hand if an animation was modified after added + virtual void clear_caches(); // Must be called by hand if an animation was modified after added. /* ---- Capture feature ---- */ void capture(const StringName &p_name, double p_duration, Tween::TransitionType p_trans_type = Tween::TRANS_LINEAR, Tween::EaseType p_ease_type = Tween::EASE_IN); diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp index 2df1e81e25..4880a0f6ed 100644 --- a/scene/animation/animation_tree.cpp +++ b/scene/animation/animation_tree.cpp @@ -815,10 +815,7 @@ void AnimationTree::set_animation_player(const NodePath &p_path) { remove_animation_library(animation_libraries[0].name); } } -#ifdef TOOLS_ENABLED emit_signal(SNAME("animation_player_changed")); // Needs to unpin AnimationPlayerEditor. - emit_signal(SNAME("mixer_updated")); -#endif // TOOLS_ENABLED _setup_animation_player(); notify_property_list_changed(); } @@ -964,9 +961,7 @@ void AnimationTree::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "advance_expression_base_node", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node"), "set_advance_expression_base_node", "get_advance_expression_base_node"); ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "anim_player", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "AnimationPlayer"), "set_animation_player", "get_animation_player"); -#ifdef TOOLS_ENABLED ADD_SIGNAL(MethodInfo(SNAME("animation_player_changed"))); -#endif // TOOLS_ENABLED } AnimationTree::AnimationTree() { diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index 62bb14459d..f1a8c2f6ae 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -241,10 +241,6 @@ PackedStringArray Control::get_configuration_warnings() const { warnings.push_back(RTR("The Hint Tooltip won't be displayed as the control's Mouse Filter is set to \"Ignore\". To solve this, set the Mouse Filter to \"Stop\" or \"Pass\".")); } - if (get_z_index() != 0) { - warnings.push_back(RTR("Changing the Z index of a control only affects the drawing order, not the input event handling order.")); - } - return warnings; } diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp index 3816b337b8..12b2364ddf 100644 --- a/scene/gui/file_dialog.cpp +++ b/scene/gui/file_dialog.cpp @@ -170,6 +170,20 @@ void FileDialog::_validate_property(PropertyInfo &p_property) const { void FileDialog::_notification(int p_what) { switch (p_what) { + case NOTIFICATION_READY: { +#ifdef TOOLS_ENABLED + if (is_part_of_edited_scene()) { + return; + } +#endif + + // Replace the built-in dialog with the native one if it started visible. + if (is_visible() && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_NATIVE_DIALOG_FILE) && (use_native_dialog || OS::get_singleton()->is_sandboxed())) { + ConfirmationDialog::set_visible(false); + _native_popup(); + } + } break; + case NOTIFICATION_VISIBILITY_CHANGED: { if (!is_visible()) { set_process_shortcut_input(false); @@ -1392,6 +1406,18 @@ void FileDialog::set_default_show_hidden_files(bool p_show) { void FileDialog::set_use_native_dialog(bool p_native) { use_native_dialog = p_native; + +#ifdef TOOLS_ENABLED + if (is_part_of_edited_scene()) { + return; + } +#endif + + // Replace the built-in dialog with the native one if it's currently visible. + if (is_visible() && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_NATIVE_DIALOG_FILE) && (use_native_dialog || OS::get_singleton()->is_sandboxed())) { + ConfirmationDialog::set_visible(false); + _native_popup(); + } } bool FileDialog::get_use_native_dialog() const { diff --git a/scene/main/multiplayer_api.cpp b/scene/main/multiplayer_api.cpp index 35e4302bb7..bd097ec2d0 100644 --- a/scene/main/multiplayer_api.cpp +++ b/scene/main/multiplayer_api.cpp @@ -266,10 +266,6 @@ Error MultiplayerAPI::decode_and_decompress_variants(Vector<Variant> &r_variants return OK; } - Vector<Variant> args; - Vector<const Variant *> argp; - args.resize(argc); - for (int i = 0; i < argc; i++) { ERR_FAIL_COND_V_MSG(r_len >= p_len, ERR_INVALID_DATA, "Invalid packet received. Size too small."); diff --git a/scene/main/node.cpp b/scene/main/node.cpp index 11c200064e..beb2583b61 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -3780,7 +3780,7 @@ void Node::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "process_thread_messages", PROPERTY_HINT_FLAGS, "Process,Physics Process"), "set_process_thread_messages", "get_process_thread_messages"); ADD_GROUP("Physics Interpolation", "physics_interpolation_"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "physics_interpolation_mode", PROPERTY_HINT_ENUM, "Inherit,Off,On"), "set_physics_interpolation_mode", "get_physics_interpolation_mode"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "physics_interpolation_mode", PROPERTY_HINT_ENUM, "Inherit,On,Off"), "set_physics_interpolation_mode", "get_physics_interpolation_mode"); ADD_GROUP("Auto Translate", "auto_translate_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "auto_translate_mode", PROPERTY_HINT_ENUM, "Inherit,Always,Disabled"), "set_auto_translate_mode", "get_auto_translate_mode"); @@ -3833,7 +3833,7 @@ Node::Node() { data.unhandled_input = false; data.unhandled_key_input = false; - data.physics_interpolated = false; + data.physics_interpolated = true; data.parent_owned = false; data.in_constructor = true; diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index f645bb5e73..752cfe2288 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -253,6 +253,7 @@ #include "scene/3d/node_3d.h" #include "scene/3d/occluder_instance_3d.h" #include "scene/3d/path_3d.h" +#include "scene/3d/physical_bone_simulator_3d.h" #include "scene/3d/physics/animatable_body_3d.h" #include "scene/3d/physics/area_3d.h" #include "scene/3d/physics/character_body_3d.h" @@ -277,6 +278,7 @@ #include "scene/3d/remote_transform_3d.h" #include "scene/3d/skeleton_3d.h" #include "scene/3d/skeleton_ik_3d.h" +#include "scene/3d/skeleton_modifier_3d.h" #include "scene/3d/soft_body_3d.h" #include "scene/3d/sprite_3d.h" #include "scene/3d/visible_on_screen_notifier_3d.h" @@ -586,6 +588,7 @@ void register_scene_types() { GDREGISTER_CLASS(CPUParticles3D); GDREGISTER_CLASS(Marker3D); GDREGISTER_CLASS(RootMotionView); + GDREGISTER_ABSTRACT_CLASS(SkeletonModifier3D); OS::get_singleton()->yield(); // may take time to init @@ -598,6 +601,7 @@ void register_scene_types() { GDREGISTER_CLASS(CharacterBody3D); GDREGISTER_CLASS(SpringArm3D); + GDREGISTER_CLASS(PhysicalBoneSimulator3D); GDREGISTER_CLASS(PhysicalBone3D); GDREGISTER_CLASS(SoftBody3D); diff --git a/scene/resources/skeleton_profile.cpp b/scene/resources/skeleton_profile.cpp index 24ed480289..2c1d3d4a4c 100644 --- a/scene/resources/skeleton_profile.cpp +++ b/scene/resources/skeleton_profile.cpp @@ -68,7 +68,7 @@ bool SkeletonProfile::_set(const StringName &p_path, const Variant &p_value) { } else if (what == "group") { set_group(which, p_value); } else if (what == "require") { - set_require(which, p_value); + set_required(which, p_value); } else { return false; } @@ -113,7 +113,7 @@ bool SkeletonProfile::_get(const StringName &p_path, Variant &r_ret) const { } else if (what == "group") { r_ret = get_group(which); } else if (what == "require") { - r_ret = is_require(which); + r_ret = is_required(which); } else { return false; } @@ -299,7 +299,7 @@ SkeletonProfile::TailDirection SkeletonProfile::get_tail_direction(int p_bone_id return bones[p_bone_idx].tail_direction; } -void SkeletonProfile::set_tail_direction(int p_bone_idx, const TailDirection p_tail_direction) { +void SkeletonProfile::set_tail_direction(int p_bone_idx, TailDirection p_tail_direction) { if (is_read_only) { return; } @@ -328,7 +328,7 @@ Transform3D SkeletonProfile::get_reference_pose(int p_bone_idx) const { return bones[p_bone_idx].reference_pose; } -void SkeletonProfile::set_reference_pose(int p_bone_idx, const Transform3D p_reference_pose) { +void SkeletonProfile::set_reference_pose(int p_bone_idx, const Transform3D &p_reference_pose) { if (is_read_only) { return; } @@ -342,7 +342,7 @@ Vector2 SkeletonProfile::get_handle_offset(int p_bone_idx) const { return bones[p_bone_idx].handle_offset; } -void SkeletonProfile::set_handle_offset(int p_bone_idx, const Vector2 p_handle_offset) { +void SkeletonProfile::set_handle_offset(int p_bone_idx, const Vector2 &p_handle_offset) { if (is_read_only) { return; } @@ -365,17 +365,17 @@ void SkeletonProfile::set_group(int p_bone_idx, const StringName &p_group) { emit_signal("profile_updated"); } -bool SkeletonProfile::is_require(int p_bone_idx) const { +bool SkeletonProfile::is_required(int p_bone_idx) const { ERR_FAIL_INDEX_V(p_bone_idx, bones.size(), false); - return bones[p_bone_idx].require; + return bones[p_bone_idx].required; } -void SkeletonProfile::set_require(int p_bone_idx, const bool p_require) { +void SkeletonProfile::set_required(int p_bone_idx, bool p_required) { if (is_read_only) { return; } ERR_FAIL_INDEX(p_bone_idx, bones.size()); - bones.write[p_bone_idx].require = p_require; + bones.write[p_bone_idx].required = p_required; emit_signal("profile_updated"); } @@ -432,6 +432,9 @@ void SkeletonProfile::_bind_methods() { ClassDB::bind_method(D_METHOD("get_group", "bone_idx"), &SkeletonProfile::get_group); ClassDB::bind_method(D_METHOD("set_group", "bone_idx", "group"), &SkeletonProfile::set_group); + ClassDB::bind_method(D_METHOD("is_required", "bone_idx"), &SkeletonProfile::is_required); + ClassDB::bind_method(D_METHOD("set_required", "bone_idx", "required"), &SkeletonProfile::set_required); + ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "root_bone", PROPERTY_HINT_ENUM_SUGGESTION, ""), "set_root_bone", "get_root_bone"); ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "scale_base_bone", PROPERTY_HINT_ENUM_SUGGESTION, ""), "set_scale_base_bone", "get_scale_base_bone"); @@ -478,14 +481,14 @@ SkeletonProfileHumanoid::SkeletonProfileHumanoid() { bones.write[1].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.75, 0); bones.write[1].handle_offset = Vector2(0.5, 0.5); bones.write[1].group = "Body"; - bones.write[1].require = true; + bones.write[1].required = true; bones.write[2].bone_name = "Spine"; bones.write[2].bone_parent = "Hips"; bones.write[2].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.1, 0); bones.write[2].handle_offset = Vector2(0.5, 0.43); bones.write[2].group = "Body"; - bones.write[2].require = true; + bones.write[2].required = true; bones.write[3].bone_name = "Chest"; bones.write[3].bone_parent = "Spine"; @@ -506,7 +509,7 @@ SkeletonProfileHumanoid::SkeletonProfileHumanoid() { bones.write[5].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.1, 0); bones.write[5].handle_offset = Vector2(0.5, 0.23); bones.write[5].group = "Body"; - bones.write[5].require = false; + bones.write[5].required = false; bones.write[6].bone_name = "Head"; bones.write[6].bone_parent = "Neck"; @@ -514,7 +517,7 @@ SkeletonProfileHumanoid::SkeletonProfileHumanoid() { bones.write[6].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.1, 0); bones.write[6].handle_offset = Vector2(0.5, 0.18); bones.write[6].group = "Body"; - bones.write[6].require = true; + bones.write[6].required = true; bones.write[7].bone_name = "LeftEye"; bones.write[7].bone_parent = "Head"; @@ -539,21 +542,21 @@ SkeletonProfileHumanoid::SkeletonProfileHumanoid() { bones.write[10].reference_pose = Transform3D(0, 1, 0, 0, 0, 1, 1, 0, 0, 0.05, 0.1, 0); bones.write[10].handle_offset = Vector2(0.55, 0.235); bones.write[10].group = "Body"; - bones.write[10].require = true; + bones.write[10].required = true; bones.write[11].bone_name = "LeftUpperArm"; bones.write[11].bone_parent = "LeftShoulder"; bones.write[11].reference_pose = Transform3D(-1, 0, 0, 0, 1, 0, 0, 0, -1, 0, 0.05, 0); bones.write[11].handle_offset = Vector2(0.6, 0.24); bones.write[11].group = "Body"; - bones.write[11].require = true; + bones.write[11].required = true; bones.write[12].bone_name = "LeftLowerArm"; bones.write[12].bone_parent = "LeftUpperArm"; bones.write[12].reference_pose = Transform3D(0, 0, -1, 0, 1, 0, 1, 0, 0, 0, 0.25, 0); bones.write[12].handle_offset = Vector2(0.7, 0.24); bones.write[12].group = "Body"; - bones.write[12].require = true; + bones.write[12].required = true; bones.write[13].bone_name = "LeftHand"; bones.write[13].bone_parent = "LeftLowerArm"; @@ -562,7 +565,7 @@ SkeletonProfileHumanoid::SkeletonProfileHumanoid() { bones.write[13].reference_pose = Transform3D(0, 0, 1, 0, 1, 0, -1, 0, 0, 0, 0.25, 0); bones.write[13].handle_offset = Vector2(0.82, 0.235); bones.write[13].group = "Body"; - bones.write[13].require = true; + bones.write[13].required = true; bones.write[14].bone_name = "LeftThumbMetacarpal"; bones.write[14].bone_parent = "LeftHand"; @@ -659,21 +662,21 @@ SkeletonProfileHumanoid::SkeletonProfileHumanoid() { bones.write[29].reference_pose = Transform3D(0, -1, 0, 0, 0, 1, -1, 0, 0, -0.05, 0.1, 0); bones.write[29].handle_offset = Vector2(0.45, 0.235); bones.write[29].group = "Body"; - bones.write[29].require = true; + bones.write[29].required = true; bones.write[30].bone_name = "RightUpperArm"; bones.write[30].bone_parent = "RightShoulder"; bones.write[30].reference_pose = Transform3D(-1, 0, 0, 0, 1, 0, 0, 0, -1, 0, 0.05, 0); bones.write[30].handle_offset = Vector2(0.4, 0.24); bones.write[30].group = "Body"; - bones.write[30].require = true; + bones.write[30].required = true; bones.write[31].bone_name = "RightLowerArm"; bones.write[31].bone_parent = "RightUpperArm"; bones.write[31].reference_pose = Transform3D(0, 0, 1, 0, 1, 0, -1, 0, 0, 0, 0.25, 0); bones.write[31].handle_offset = Vector2(0.3, 0.24); bones.write[31].group = "Body"; - bones.write[31].require = true; + bones.write[31].required = true; bones.write[32].bone_name = "RightHand"; bones.write[32].bone_parent = "RightLowerArm"; @@ -682,7 +685,7 @@ SkeletonProfileHumanoid::SkeletonProfileHumanoid() { bones.write[32].reference_pose = Transform3D(0, 0, -1, 0, 1, 0, 1, 0, 0, 0, 0.25, 0); bones.write[32].handle_offset = Vector2(0.18, 0.235); bones.write[32].group = "Body"; - bones.write[32].require = true; + bones.write[32].required = true; bones.write[33].bone_name = "RightThumbMetacarpal"; bones.write[33].bone_parent = "RightHand"; @@ -779,21 +782,21 @@ SkeletonProfileHumanoid::SkeletonProfileHumanoid() { bones.write[48].reference_pose = Transform3D(-1, 0, 0, 0, -1, 0, 0, 0, 1, 0.1, 0, 0); bones.write[48].handle_offset = Vector2(0.549, 0.49); bones.write[48].group = "Body"; - bones.write[48].require = true; + bones.write[48].required = true; bones.write[49].bone_name = "LeftLowerLeg"; bones.write[49].bone_parent = "LeftUpperLeg"; bones.write[49].reference_pose = Transform3D(-1, 0, 0, 0, 1, 0, 0, 0, -1, 0, 0.375, 0); bones.write[49].handle_offset = Vector2(0.548, 0.683); bones.write[49].group = "Body"; - bones.write[49].require = true; + bones.write[49].required = true; bones.write[50].bone_name = "LeftFoot"; bones.write[50].bone_parent = "LeftLowerLeg"; bones.write[50].reference_pose = Transform3D(-1, 0, 0, 0, 0, -1, 0, -1, 0, 0, 0.375, 0); bones.write[50].handle_offset = Vector2(0.545, 0.9); bones.write[50].group = "Body"; - bones.write[50].require = true; + bones.write[50].required = true; bones.write[51].bone_name = "LeftToes"; bones.write[51].bone_parent = "LeftFoot"; @@ -806,21 +809,21 @@ SkeletonProfileHumanoid::SkeletonProfileHumanoid() { bones.write[52].reference_pose = Transform3D(-1, 0, 0, 0, -1, 0, 0, 0, 1, -0.1, 0, 0); bones.write[52].handle_offset = Vector2(0.451, 0.49); bones.write[52].group = "Body"; - bones.write[52].require = true; + bones.write[52].required = true; bones.write[53].bone_name = "RightLowerLeg"; bones.write[53].bone_parent = "RightUpperLeg"; bones.write[53].reference_pose = Transform3D(-1, 0, 0, 0, 1, 0, 0, 0, -1, 0, 0.375, 0); bones.write[53].handle_offset = Vector2(0.452, 0.683); bones.write[53].group = "Body"; - bones.write[53].require = true; + bones.write[53].required = true; bones.write[54].bone_name = "RightFoot"; bones.write[54].bone_parent = "RightLowerLeg"; bones.write[54].reference_pose = Transform3D(-1, 0, 0, 0, 0, -1, 0, -1, 0, 0, 0.375, 0); bones.write[54].handle_offset = Vector2(0.455, 0.9); bones.write[54].group = "Body"; - bones.write[54].require = true; + bones.write[54].required = true; bones.write[55].bone_name = "RightToes"; bones.write[55].bone_parent = "RightFoot"; diff --git a/scene/resources/skeleton_profile.h b/scene/resources/skeleton_profile.h index 143f495c61..b5a3bce940 100644 --- a/scene/resources/skeleton_profile.h +++ b/scene/resources/skeleton_profile.h @@ -61,7 +61,7 @@ protected: Transform3D reference_pose; Vector2 handle_offset; StringName group; - bool require = false; + bool required = false; }; StringName root_bone; @@ -104,22 +104,22 @@ public: void set_bone_parent(int p_bone_idx, const StringName &p_bone_parent); TailDirection get_tail_direction(int p_bone_idx) const; - void set_tail_direction(int p_bone_idx, const TailDirection p_tail_direction); + void set_tail_direction(int p_bone_idx, TailDirection p_tail_direction); StringName get_bone_tail(int p_bone_idx) const; void set_bone_tail(int p_bone_idx, const StringName &p_bone_tail); Transform3D get_reference_pose(int p_bone_idx) const; - void set_reference_pose(int p_bone_idx, const Transform3D p_reference_pose); + void set_reference_pose(int p_bone_idx, const Transform3D &p_reference_pose); Vector2 get_handle_offset(int p_bone_idx) const; - void set_handle_offset(int p_bone_idx, const Vector2 p_handle_offset); + void set_handle_offset(int p_bone_idx, const Vector2 &p_handle_offset); StringName get_group(int p_bone_idx) const; void set_group(int p_bone_idx, const StringName &p_group); - bool is_require(int p_bone_idx) const; - void set_require(int p_bone_idx, const bool p_require); + bool is_required(int p_bone_idx) const; + void set_required(int p_bone_idx, bool p_required); bool has_bone(const StringName &p_bone_name); diff --git a/scene/theme/default_theme.cpp b/scene/theme/default_theme.cpp index 7ab1d399fb..d1d8854b18 100644 --- a/scene/theme/default_theme.cpp +++ b/scene/theme/default_theme.cpp @@ -639,7 +639,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const // Dialogs // AcceptDialog is currently the base dialog, so this defines styles for all extending nodes. - theme->set_stylebox("panel", "AcceptDialog", make_flat_stylebox(style_popup_color, Math::round(8 * scale), Math::round(8 * scale), Math::round(8 * scale), Math::round(8 * scale))); + theme->set_stylebox("panel", "AcceptDialog", make_flat_stylebox(style_popup_color, Math::round(8 * scale), Math::round(8 * scale), Math::round(8 * scale), Math::round(8 * scale), 0)); theme->set_constant("buttons_separation", "AcceptDialog", Math::round(10 * scale)); // File Dialog 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 b54f511d05..48c9cda253 100644 --- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp +++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp @@ -777,7 +777,7 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color if (rb->get_scaling_3d_mode() != RS::VIEWPORT_SCALING_3D_MODE_OFF) { // can't do blit subpass because we're scaling using_subpass_post_process = false; - } else if (p_render_data->environment.is_valid() && (environment_get_glow_enabled(p_render_data->environment) || RSG::camera_attributes->camera_attributes_uses_auto_exposure(p_render_data->camera_attributes) || RSG::camera_attributes->camera_attributes_uses_dof(p_render_data->camera_attributes))) { + } else if (p_render_data->environment.is_valid() && (environment_get_glow_enabled(p_render_data->environment) || RSG::camera_attributes->camera_attributes_uses_auto_exposure(p_render_data->camera_attributes) || RSG::camera_attributes->camera_attributes_uses_dof(p_render_data->camera_attributes) || environment_get_background(p_render_data->environment) == RS::ENV_BG_CANVAS)) { // can't do blit subpass because we're using post processes using_subpass_post_process = false; } diff --git a/servers/rendering/rendering_device.cpp b/servers/rendering/rendering_device.cpp index 7dfff0b76f..00291d2ac4 100644 --- a/servers/rendering/rendering_device.cpp +++ b/servers/rendering/rendering_device.cpp @@ -5089,7 +5089,8 @@ Error RenderingDevice::initialize(RenderingContextDriver *p_context, DisplayServ draw_list = nullptr; compute_list = nullptr; - if (main_instance) { + bool project_pipeline_cache_enable = GLOBAL_GET("rendering/rendering_device/pipeline_cache/enable"); + if (main_instance && project_pipeline_cache_enable) { // Only the instance that is not a local device and is also the singleton is allowed to manage a pipeline cache. pipeline_cache_file_path = vformat("user://vulkan/pipelines.%s.%s", OS::get_singleton()->get_current_rendering_method(), @@ -5223,8 +5224,12 @@ uint64_t RenderingDevice::get_driver_resource(DriverResource p_resource, RID p_r case DRIVER_RESOURCE_LOGICAL_DEVICE: case DRIVER_RESOURCE_PHYSICAL_DEVICE: case DRIVER_RESOURCE_TOPMOST_OBJECT: + break; case DRIVER_RESOURCE_COMMAND_QUEUE: + driver_id = main_queue.id; + break; case DRIVER_RESOURCE_QUEUE_FAMILY: + driver_id = main_queue_family.id; break; case DRIVER_RESOURCE_TEXTURE: case DRIVER_RESOURCE_TEXTURE_VIEW: @@ -5232,19 +5237,19 @@ uint64_t RenderingDevice::get_driver_resource(DriverResource p_resource, RID p_r Texture *tex = texture_owner.get_or_null(p_rid); ERR_FAIL_NULL_V(tex, 0); - driver_id = tex->driver_id; + driver_id = tex->driver_id.id; } break; case DRIVER_RESOURCE_SAMPLER: { RDD::SamplerID *sampler_driver_id = sampler_owner.get_or_null(p_rid); ERR_FAIL_NULL_V(sampler_driver_id, 0); - driver_id = *sampler_driver_id; + driver_id = (*sampler_driver_id).id; } break; case DRIVER_RESOURCE_UNIFORM_SET: { UniformSet *uniform_set = uniform_set_owner.get_or_null(p_rid); ERR_FAIL_NULL_V(uniform_set, 0); - driver_id = uniform_set->driver_id; + driver_id = uniform_set->driver_id.id; } break; case DRIVER_RESOURCE_BUFFER: { Buffer *buffer = nullptr; @@ -5261,19 +5266,19 @@ uint64_t RenderingDevice::get_driver_resource(DriverResource p_resource, RID p_r } ERR_FAIL_NULL_V(buffer, 0); - driver_id = buffer->driver_id; + driver_id = buffer->driver_id.id; } break; case DRIVER_RESOURCE_COMPUTE_PIPELINE: { ComputePipeline *compute_pipeline = compute_pipeline_owner.get_or_null(p_rid); ERR_FAIL_NULL_V(compute_pipeline, 0); - driver_id = compute_pipeline->driver_id; + driver_id = compute_pipeline->driver_id.id; } break; case DRIVER_RESOURCE_RENDER_PIPELINE: { RenderPipeline *render_pipeline = render_pipeline_owner.get_or_null(p_rid); ERR_FAIL_NULL_V(render_pipeline, 0); - driver_id = render_pipeline->driver_id; + driver_id = render_pipeline->driver_id.id; } break; default: { ERR_FAIL_V(0); diff --git a/servers/rendering/rendering_device_driver.h b/servers/rendering/rendering_device_driver.h index 09a0412941..e9464ba321 100644 --- a/servers/rendering/rendering_device_driver.h +++ b/servers/rendering/rendering_device_driver.h @@ -128,7 +128,7 @@ public: #define DEFINE_ID(m_name) \ struct m_name##ID : public ID { \ - _ALWAYS_INLINE_ operator bool() const { return id != 0; } \ + _ALWAYS_INLINE_ explicit operator bool() const { return id != 0; } \ _ALWAYS_INLINE_ m_name##ID &operator=(m_name##ID p_other) { \ id = p_other.id; \ return *this; \ diff --git a/servers/rendering/rendering_device_graph.cpp b/servers/rendering/rendering_device_graph.cpp index 4b85d1c2bf..adac7ee3eb 100644 --- a/servers/rendering/rendering_device_graph.cpp +++ b/servers/rendering/rendering_device_graph.cpp @@ -261,7 +261,7 @@ void RenderingDeviceGraph::_add_command_to_graph(ResourceTracker **p_resource_tr } if (resource_tracker->parent->usage == RESOURCE_USAGE_NONE) { - if (resource_tracker->parent->texture_driver_id != 0) { + if (resource_tracker->parent->texture_driver_id.id != 0) { // If the resource is a texture, we transition it entirely to the layout determined by the first slice that uses it. _add_texture_barrier_to_command(resource_tracker->parent->texture_driver_id, RDD::BarrierAccessBits(0), new_usage_access, RDG::RESOURCE_USAGE_NONE, new_resource_usage, resource_tracker->parent->texture_subresources, command_normalization_barriers, r_command->normalization_barrier_index, r_command->normalization_barrier_count); } @@ -324,7 +324,7 @@ void RenderingDeviceGraph::_add_command_to_graph(ResourceTracker **p_resource_tr ERR_FAIL_MSG("Texture slices that overlap can't be used in the same command."); } else { // Delete the slice from the dirty list and revert it to the usage of the parent. - if (current_tracker->texture_driver_id != 0) { + if (current_tracker->texture_driver_id.id != 0) { _add_texture_barrier_to_command(current_tracker->texture_driver_id, current_tracker->usage_access, new_usage_access, current_tracker->usage, resource_tracker->parent->usage, current_tracker->texture_subresources, command_normalization_barriers, r_command->normalization_barrier_index, r_command->normalization_barrier_count); // Merge the area of the slice with the current tracking area of the command and indicate it's a write usage as well. @@ -383,7 +383,7 @@ void RenderingDeviceGraph::_add_command_to_graph(ResourceTracker **p_resource_tr while (current_tracker != nullptr) { current_tracker->reset_if_outdated(tracking_frame); - if (current_tracker->texture_driver_id != 0) { + if (current_tracker->texture_driver_id.id != 0) { // Transition all slices to the layout of the parent resource. _add_texture_barrier_to_command(current_tracker->texture_driver_id, current_tracker->usage_access, new_usage_access, current_tracker->usage, resource_tracker->usage, current_tracker->texture_subresources, command_normalization_barriers, r_command->normalization_barrier_index, r_command->normalization_barrier_count); } diff --git a/servers/rendering/rendering_light_culler.cpp b/servers/rendering/rendering_light_culler.cpp index fdf0d73654..0889898f0b 100644 --- a/servers/rendering/rendering_light_culler.cpp +++ b/servers/rendering/rendering_light_culler.cpp @@ -427,15 +427,19 @@ bool RenderingLightCuller::_add_light_camera_planes(LightCullPlanes &r_cull_plan uint8_t *entry = &data.LUT_entries[lookup][0]; int n_edges = data.LUT_entry_sizes[lookup] - 1; + const Vector3 &pt2 = p_light_source.pos; + for (int e = 0; e < n_edges; e++) { int i0 = entry[e]; int i1 = entry[e + 1]; const Vector3 &pt0 = data.frustum_points[i0]; const Vector3 &pt1 = data.frustum_points[i1]; - // Create plane from 3 points. - Plane p(pt0, pt1, p_light_source.pos); - r_cull_planes.add_cull_plane(p); + if (!_is_colinear_tri(pt0, pt1, pt2)) { + // Create plane from 3 points. + Plane p(pt0, pt1, pt2); + r_cull_planes.add_cull_plane(p); + } } // Last to 0 edge. @@ -446,9 +450,11 @@ bool RenderingLightCuller::_add_light_camera_planes(LightCullPlanes &r_cull_plan const Vector3 &pt0 = data.frustum_points[i0]; const Vector3 &pt1 = data.frustum_points[i1]; - // Create plane from 3 points. - Plane p(pt0, pt1, p_light_source.pos); - r_cull_planes.add_cull_plane(p); + if (!_is_colinear_tri(pt0, pt1, pt2)) { + // Create plane from 3 points. + Plane p(pt0, pt1, pt2); + r_cull_planes.add_cull_plane(p); + } } #ifdef LIGHT_CULLER_DEBUG_LOGGING diff --git a/servers/rendering/rendering_light_culler.h b/servers/rendering/rendering_light_culler.h index 602543850a..0bf975430b 100644 --- a/servers/rendering/rendering_light_culler.h +++ b/servers/rendering/rendering_light_culler.h @@ -163,6 +163,39 @@ private: bool _prepare_light(const RendererSceneCull::Instance &p_instance, int32_t p_directional_light_id = -1); + // Avoid adding extra culling planes derived from near colinear triangles. + // The normals derived from these will be inaccurate, and can lead to false + // culling of objects that should be within the light volume. + bool _is_colinear_tri(const Vector3 &p_a, const Vector3 &p_b, const Vector3 &p_c) const { + // Lengths of sides a, b and c. + float la = (p_b - p_a).length(); + float lb = (p_c - p_b).length(); + float lc = (p_c - p_a).length(); + + // Get longest side into lc. + if (lb < la) { + SWAP(la, lb); + } + if (lc < lb) { + SWAP(lb, lc); + } + + // Prevent divide by zero. + if (lc > 0.00001f) { + // If the summed length of the smaller two + // sides is close to the length of the longest side, + // the points are colinear, and the triangle is near degenerate. + float ld = ((la + lb) - lc) / lc; + + // ld will be close to zero for colinear tris. + return ld < 0.00001f; + } + + // Don't create planes from tiny triangles, + // they won't be accurate. + return true; + } + // Internal version uses LightSource. bool _add_light_camera_planes(LightCullPlanes &r_cull_planes, const LightSource &p_light_source); diff --git a/servers/text/text_server_extension.cpp b/servers/text/text_server_extension.cpp index eb9f1c1484..53e1d119fd 100644 --- a/servers/text/text_server_extension.cpp +++ b/servers/text/text_server_extension.cpp @@ -341,6 +341,7 @@ void TextServerExtension::_bind_methods() { GDVIRTUAL_BIND(_string_to_upper, "string", "language"); GDVIRTUAL_BIND(_string_to_lower, "string", "language"); + GDVIRTUAL_BIND(_string_to_title, "string", "language"); GDVIRTUAL_BIND(_parse_structured_text, "parser_type", "args", "text"); @@ -1507,6 +1508,14 @@ String TextServerExtension::string_to_upper(const String &p_string, const String return p_string; } +String TextServerExtension::string_to_title(const String &p_string, const String &p_language) const { + String ret; + if (GDVIRTUAL_CALL(_string_to_title, p_string, p_language, ret)) { + return ret; + } + return p_string; +} + String TextServerExtension::string_to_lower(const String &p_string, const String &p_language) const { String ret; if (GDVIRTUAL_CALL(_string_to_lower, p_string, p_language, ret)) { diff --git a/servers/text/text_server_extension.h b/servers/text/text_server_extension.h index 84d68de4fa..53d30abee4 100644 --- a/servers/text/text_server_extension.h +++ b/servers/text/text_server_extension.h @@ -566,8 +566,10 @@ public: virtual String string_to_upper(const String &p_string, const String &p_language = "") const override; virtual String string_to_lower(const String &p_string, const String &p_language = "") const override; + virtual String string_to_title(const String &p_string, const String &p_language = "") const override; GDVIRTUAL2RC(String, _string_to_upper, const String &, const String &); GDVIRTUAL2RC(String, _string_to_lower, const String &, const String &); + GDVIRTUAL2RC(String, _string_to_title, const String &, const String &); TypedArray<Vector3i> parse_structured_text(StructuredTextParser p_parser_type, const Array &p_args, const String &p_text) const; GDVIRTUAL3RC(TypedArray<Vector3i>, _parse_structured_text, StructuredTextParser, const Array &, const String &); diff --git a/servers/text_server.cpp b/servers/text_server.cpp index 078ee27753..fac9e32d01 100644 --- a/servers/text_server.cpp +++ b/servers/text_server.cpp @@ -491,6 +491,7 @@ void TextServer::_bind_methods() { ClassDB::bind_method(D_METHOD("string_to_upper", "string", "language"), &TextServer::string_to_upper, DEFVAL("")); ClassDB::bind_method(D_METHOD("string_to_lower", "string", "language"), &TextServer::string_to_lower, DEFVAL("")); + ClassDB::bind_method(D_METHOD("string_to_title", "string", "language"), &TextServer::string_to_title, DEFVAL("")); ClassDB::bind_method(D_METHOD("parse_structured_text", "parser_type", "args", "text"), &TextServer::parse_structured_text); diff --git a/servers/text_server.h b/servers/text_server.h index 4a16ae64e8..396d7ca8e5 100644 --- a/servers/text_server.h +++ b/servers/text_server.h @@ -546,6 +546,7 @@ public: // Other string operations. virtual String string_to_upper(const String &p_string, const String &p_language = "") const = 0; virtual String string_to_lower(const String &p_string, const String &p_language = "") const = 0; + virtual String string_to_title(const String &p_string, const String &p_language = "") const = 0; TypedArray<Vector3i> parse_structured_text(StructuredTextParser p_parser_type, const Array &p_args, const String &p_text) const; diff --git a/tests/core/math/test_vector2.h b/tests/core/math/test_vector2.h index f23fffe5eb..fc3fd6a87d 100644 --- a/tests/core/math/test_vector2.h +++ b/tests/core/math/test_vector2.h @@ -354,6 +354,7 @@ TEST_CASE("[Vector2] Plane methods") { const Vector2 vector_y = Vector2(0, 1); const Vector2 vector_normal = Vector2(0.95879811270838721622267, 0.2840883296913739899919); const Vector2 vector_non_normal = Vector2(5.4, 1.6); + const real_t p_d = 99.1; CHECK_MESSAGE( vector.bounce(vector_y) == Vector2(1.2, -3.4), "Vector2 bounce on a plane with normal of the Y axis should."); @@ -373,6 +374,9 @@ TEST_CASE("[Vector2] Plane methods") { vector.project(vector_normal).is_equal_approx(Vector2(2.0292559899117276166, 0.60126103404791929382)), "Vector2 projected on a normal should return expected value."); CHECK_MESSAGE( + vector_normal.plane_project(p_d, vector).is_equal_approx(Vector2(94.187635516479631, 30.951892004882851)), + "Vector2 plane_project should return expected value."); + CHECK_MESSAGE( vector.slide(vector_y) == Vector2(1.2, 0), "Vector2 slide on a plane with normal of the Y axis should set the Y to zero."); CHECK_MESSAGE( diff --git a/thirdparty/README.md b/thirdparty/README.md index 62312697d2..b5b3036d9b 100644 --- a/thirdparty/README.md +++ b/thirdparty/README.md @@ -87,7 +87,7 @@ Files extracted from upstream source: ## certs - Upstream: Mozilla, via https://github.com/bagder/ca-bundle -- Version: git (bef37a977ccb45fb4c1b213b79dd6ba438077561, 2023) +- Version: git (c5a419971b1bec220368c619aaafd0b818aa119f, 2024) - License: MPL 2.0 @@ -194,7 +194,7 @@ commits. ## enet - Upstream: https://github.com/lsalzman/enet -- Version: git (ea4607a90dbfbcf4da2669ea998585253d8e70b1, 2023) +- Version: git (c44b7d0f7ff21edb702745e4c019d0537928c373, 2024) - License: MIT Files extracted from upstream source: @@ -318,7 +318,7 @@ Files extracted from upstream source: ## glad - Upstream: https://github.com/Dav1dde/glad -- Version: 2.0.4 (d08b1aa01f8fe57498f04d47b5fa8c48725be877, 2023) +- Version: 2.0.6 (658f48e72aee3c6582e80b05ac0f8787a64fe6bb, 2024) - License: CC0 1.0 and Apache 2.0 Files extracted from upstream source: @@ -334,11 +334,11 @@ Files generated from [upstream web instance](https://gen.glad.sh/): - `glx.c` - `glad/glx.h` -See the permalinks in `glad/gl.h` and `glad/glx.h` to regenrate the files with -a new version of the web instance. +See the permalinks in `glad/egl.h`, `glad/gl.h`, and `glad/glx.h` to regenrate +the files with a new version of the web instance. -Some changes have been made in order to allow loading OpenGL and OpenGLES APIs at the same time. -See the patches in the `patches` directory. +Some changes have been made in order to allow loading OpenGL and OpenGLES APIs +at the same time. See the patches in the `patches` directory. ## glslang @@ -377,16 +377,15 @@ Files extracted from upstream source: ## harfbuzz - Upstream: https://github.com/harfbuzz/harfbuzz -- Version: 8.3.0 (894a1f72ee93a1fd8dc1d9218cb3fd8f048be29a, 2023) +- Version: 8.4.0 (63973005bc07aba599b47fdd4cf788647b601ccd, 2024) - License: MIT Files extracted from upstream source: - `AUTHORS`, `COPYING`, `THANKS` - From the `src` folder, recursively: - - All the `.c`, `.cc`, `.h`, `.hh` files - - Except `main.cc`, `harfbuzz*.cc`, `failing-alloc.c`, `test*.cc`, `hb-wasm*.*`, - and the `wasm` folder + - All the `.cc`, `.h`, `.hh` files + - Except `main.cc`, `harfbuzz*.cc`, `failing-alloc.c`, `test*.cc`, `hb-wasm*.*` ## icu4c @@ -521,7 +520,7 @@ in the MSVC debugger. ## mbedtls - Upstream: https://github.com/Mbed-TLS/mbedtls -- Version: 2.28.7 (555f84735aecdbd76a566cf087ec8425dfb0c8ab, 2024) +- Version: 2.28.8 (5a764e5555c64337ed17444410269ff21cb617b1, 2024) - License: Apache 2.0 File extracted from upstream release tarball: @@ -838,11 +837,13 @@ folder. Files extracted from upstream source: +- `LICENSE.txt` - All `.cpp`, `.h` and `.inl` files -Important: Some files have Godot-made changes. -They are marked with `// -- GODOT start --` and `// -- GODOT end --` -comments and a patch is provided in the squish/ folder. +Some downstream changes have been made and are identified by +`// -- GODOT begin --` and `// -- GODOT end --` comments. +They can be reapplied using the patches included in the `patches` +folder. ## tinyexr diff --git a/thirdparty/certs/ca-certificates.crt b/thirdparty/certs/ca-certificates.crt index df7a026bed..f437587091 100644 --- a/thirdparty/certs/ca-certificates.crt +++ b/thirdparty/certs/ca-certificates.crt @@ -1,7 +1,7 @@ ## ## Bundle of CA Root Certificates ## -## Certificate data from Mozilla as of: Wed Dec 13 07:16:08 2023 GMT +## Certificate data from Mozilla as of: Mon Mar 11 15:15:21 2024 GMT ## ## This is a bundle of X.509 certificates of public Certificate Authorities ## (CA). These were automatically extracted from Mozilla's root certificates @@ -14,7 +14,7 @@ ## Just configure this file as the SSLCACertificateFile. ## ## Conversion done with mk-ca-bundle.pl version 1.29. -## SHA256: 1970dd65858925d68498d2356aea6d03f764422523c5887deca8ce3ba9e1f845 +## SHA256: 4d96bd539f4719e9ace493757afbe4a23ee8579de1c97fbebc50bba3c12e8c1e ## @@ -3532,3 +3532,50 @@ dVwPaFsdZcJfMw8eD/A7hvWwTruc9+olBdytoptLFwG+Qt81IR2tq670v64fG9PiO/yzcnMcmyiQ iRM9HcEARwmWmjgb3bHPDcK0RPOWlc4yOo80nOAXx17Org3bhzjlP1v9mxnhMUF6cKojawHhRUzN lM47ni3niAIi9G7oyOzWPPO5std3eqx7 -----END CERTIFICATE----- + +Telekom Security TLS ECC Root 2020 +================================== +-----BEGIN CERTIFICATE----- +MIICQjCCAcmgAwIBAgIQNjqWjMlcsljN0AFdxeVXADAKBggqhkjOPQQDAzBjMQswCQYDVQQGEwJE +RTEnMCUGA1UECgweRGV1dHNjaGUgVGVsZWtvbSBTZWN1cml0eSBHbWJIMSswKQYDVQQDDCJUZWxl +a29tIFNlY3VyaXR5IFRMUyBFQ0MgUm9vdCAyMDIwMB4XDTIwMDgyNTA3NDgyMFoXDTQ1MDgyNTIz +NTk1OVowYzELMAkGA1UEBhMCREUxJzAlBgNVBAoMHkRldXRzY2hlIFRlbGVrb20gU2VjdXJpdHkg +R21iSDErMCkGA1UEAwwiVGVsZWtvbSBTZWN1cml0eSBUTFMgRUNDIFJvb3QgMjAyMDB2MBAGByqG +SM49AgEGBSuBBAAiA2IABM6//leov9Wq9xCazbzREaK9Z0LMkOsVGJDZos0MKiXrPk/OtdKPD/M1 +2kOLAoC+b1EkHQ9rK8qfwm9QMuU3ILYg/4gND21Ju9sGpIeQkpT0CdDPf8iAC8GXs7s1J8nCG6NC +MEAwHQYDVR0OBBYEFONyzG6VmUex5rNhTNHLq+O6zd6fMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0P +AQH/BAQDAgEGMAoGCCqGSM49BAMDA2cAMGQCMHVSi7ekEE+uShCLsoRbQuHmKjYC2qBuGT8lv9pZ +Mo7k+5Dck2TOrbRBR2Diz6fLHgIwN0GMZt9Ba9aDAEH9L1r3ULRn0SyocddDypwnJJGDSA3PzfdU +ga/sf+Rn27iQ7t0l +-----END CERTIFICATE----- + +Telekom Security TLS RSA Root 2023 +================================== +-----BEGIN CERTIFICATE----- +MIIFszCCA5ugAwIBAgIQIZxULej27HF3+k7ow3BXlzANBgkqhkiG9w0BAQwFADBjMQswCQYDVQQG +EwJERTEnMCUGA1UECgweRGV1dHNjaGUgVGVsZWtvbSBTZWN1cml0eSBHbWJIMSswKQYDVQQDDCJU +ZWxla29tIFNlY3VyaXR5IFRMUyBSU0EgUm9vdCAyMDIzMB4XDTIzMDMyODEyMTY0NVoXDTQ4MDMy +NzIzNTk1OVowYzELMAkGA1UEBhMCREUxJzAlBgNVBAoMHkRldXRzY2hlIFRlbGVrb20gU2VjdXJp +dHkgR21iSDErMCkGA1UEAwwiVGVsZWtvbSBTZWN1cml0eSBUTFMgUlNBIFJvb3QgMjAyMzCCAiIw +DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAO01oYGA88tKaVvC+1GDrib94W7zgRJ9cUD/h3VC +KSHtgVIs3xLBGYSJwb3FKNXVS2xE1kzbB5ZKVXrKNoIENqil/Cf2SfHVcp6R+SPWcHu79ZvB7JPP +GeplfohwoHP89v+1VmLhc2o0mD6CuKyVU/QBoCcHcqMAU6DksquDOFczJZSfvkgdmOGjup5czQRx +UX11eKvzWarE4GC+j4NSuHUaQTXtvPM6Y+mpFEXX5lLRbtLevOP1Czvm4MS9Q2QTps70mDdsipWo +l8hHD/BeEIvnHRz+sTugBTNoBUGCwQMrAcjnj02r6LX2zWtEtefdi+zqJbQAIldNsLGyMcEWzv/9 +FIS3R/qy8XDe24tsNlikfLMR0cN3f1+2JeANxdKz+bi4d9s3cXFH42AYTyS2dTd4uaNir73Jco4v +zLuu2+QVUhkHM/tqty1LkCiCc/4YizWN26cEar7qwU02OxY2kTLvtkCJkUPg8qKrBC7m8kwOFjQg +rIfBLX7JZkcXFBGk8/ehJImr2BrIoVyxo/eMbcgByU/J7MT8rFEz0ciD0cmfHdRHNCk+y7AO+oML +KFjlKdw/fKifybYKu6boRhYPluV75Gp6SG12mAWl3G0eQh5C2hrgUve1g8Aae3g1LDj1H/1Joy7S +WWO/gLCMk3PLNaaZlSJhZQNg+y+TS/qanIA7AgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBBjAdBgNV +HQ4EFgQUtqeXgj10hZv3PJ+TmpV5dVKMbUcwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBS2 +p5eCPXSFm/c8n5OalXl1UoxtRzANBgkqhkiG9w0BAQwFAAOCAgEAqMxhpr51nhVQpGv7qHBFfLp+ +sVr8WyP6Cnf4mHGCDG3gXkaqk/QeoMPhk9tLrbKmXauw1GLLXrtm9S3ul0A8Yute1hTWjOKWi0Fp +kzXmuZlrYrShF2Y0pmtjxrlO8iLpWA1WQdH6DErwM807u20hOq6OcrXDSvvpfeWxm4bu4uB9tPcy +/SKE8YXJN3nptT+/XOR0so8RYgDdGGah2XsjX/GO1WfoVNpbOms2b/mBsTNHM3dA+VKq3dSDz4V4 +mZqTuXNnQkYRIer+CqkbGmVps4+uFrb2S1ayLfmlyOw7YqPta9BO1UAJpB+Y1zqlklkg5LB9zVtz +aL1txKITDmcZuI1CfmwMmm6gJC3VRRvcxAIU/oVbZZfKTpBQCHpCNfnqwmbU+AGuHrS+w6jv/naa +oqYfRvaE7fzbzsQCzndILIyy7MMAo+wsVRjBfhnu4S/yrYObnqsZ38aKL4x35bcF7DvB7L6Gs4a8 +wPfc5+pbrrLMtTWGS9DiP7bY+A4A7l3j941Y/8+LN+ljX273CXE2whJdV/LItM3z7gLfEdxquVeE +HVlNjM7IDiPCtyaaEBRx/pOyiriA8A4QntOoUAw3gi/q4Iqd4Sw5/7W0cwDk90imc6y/st53BIe0 +o82bNSQ3+pCTE4FCxpgmdTdmQRCsu/WU48IxK63nI1bMNSWSs1A= +-----END CERTIFICATE----- diff --git a/thirdparty/enet/patches/godot_socket.patch b/thirdparty/enet/patches/godot_socket.patch index 364b3536be..d0fb97fb92 100644 --- a/thirdparty/enet/patches/godot_socket.patch +++ b/thirdparty/enet/patches/godot_socket.patch @@ -1,5 +1,5 @@ diff --git a/thirdparty/enet/enet/enet.h b/thirdparty/enet/enet/enet.h -index fc45cbd0c9..77f8004b80 100644 +index 4a207041b3..5232f8a869 100644 --- a/thirdparty/enet/enet/enet.h +++ b/thirdparty/enet/enet/enet.h @@ -10,13 +10,19 @@ extern "C" @@ -22,7 +22,7 @@ index fc45cbd0c9..77f8004b80 100644 #include "enet/types.h" #include "enet/protocol.h" -@@ -86,11 +92,15 @@ typedef enum _ENetSocketShutdown +@@ -87,11 +93,15 @@ typedef enum _ENetSocketShutdown * but not for enet_host_create. Once a server responds to a broadcast, the * address is updated from ENET_HOST_BROADCAST to the server's actual IP address. */ @@ -38,7 +38,7 @@ index fc45cbd0c9..77f8004b80 100644 /** * Packet flag bit constants. -@@ -604,6 +614,10 @@ ENET_API size_t enet_range_coder_decompress (void *, const enet_uint8 *, size_t, +@@ -608,6 +618,10 @@ ENET_API size_t enet_range_coder_decompress (void *, const enet_uint8 *, size_t, extern size_t enet_protocol_command_size (enet_uint8); @@ -50,7 +50,7 @@ index fc45cbd0c9..77f8004b80 100644 } #endif diff --git a/thirdparty/enet/host.c b/thirdparty/enet/host.c -index 3b2180f7fd..21ab27e247 100644 +index fff946a392..adb3533cf1 100644 --- a/thirdparty/enet/host.c +++ b/thirdparty/enet/host.c @@ -87,7 +87,7 @@ enet_host_create (const ENetAddress * address, size_t peerCount, size_t channelL @@ -63,10 +63,10 @@ index 3b2180f7fd..21ab27e247 100644 host -> receivedData = NULL; host -> receivedDataLength = 0; diff --git a/thirdparty/enet/protocol.c b/thirdparty/enet/protocol.c -index 9d654f1d96..d7fe80f117 100644 +index 843a719af3..5f18700599 100644 --- a/thirdparty/enet/protocol.c +++ b/thirdparty/enet/protocol.c -@@ -309,7 +309,7 @@ enet_protocol_handle_connect (ENetHost * host, ENetProtocolHeader * header, ENet +@@ -318,7 +318,7 @@ enet_protocol_handle_connect (ENetHost * host, ENetProtocolHeader * header, ENet } else if (currentPeer -> state != ENET_PEER_STATE_CONNECTING && @@ -75,7 +75,7 @@ index 9d654f1d96..d7fe80f117 100644 { if (currentPeer -> address.port == host -> receivedAddress.port && currentPeer -> connectID == command -> connect.connectID) -@@ -1031,9 +1031,8 @@ enet_protocol_handle_incoming_commands (ENetHost * host, ENetEvent * event) +@@ -1043,9 +1043,8 @@ enet_protocol_handle_incoming_commands (ENetHost * host, ENetEvent * event) if (peer -> state == ENET_PEER_STATE_DISCONNECTED || peer -> state == ENET_PEER_STATE_ZOMBIE || @@ -87,7 +87,7 @@ index 9d654f1d96..d7fe80f117 100644 (peer -> outgoingPeerID < ENET_PROTOCOL_MAXIMUM_PEER_ID && sessionID != peer -> incomingSessionID)) return 0; -@@ -1075,7 +1074,7 @@ enet_protocol_handle_incoming_commands (ENetHost * host, ENetEvent * event) +@@ -1090,7 +1089,7 @@ enet_protocol_handle_incoming_commands (ENetHost * host, ENetEvent * event) if (peer != NULL) { diff --git a/thirdparty/enet/protocol.c b/thirdparty/enet/protocol.c index af307af7e5..5f18700599 100644 --- a/thirdparty/enet/protocol.c +++ b/thirdparty/enet/protocol.c @@ -1071,11 +1071,14 @@ enet_protocol_handle_incoming_commands (ENetHost * host, ENetEvent * event) if (host -> checksum != NULL) { - enet_uint32 * checksum = (enet_uint32 *) & host -> receivedData [headerSize - sizeof (enet_uint32)], - desiredChecksum = * checksum; + enet_uint32 * checksum = (enet_uint32 *) & host -> receivedData [headerSize - sizeof (enet_uint32)]; + enet_uint32 desiredChecksum, newChecksum; ENetBuffer buffer; + /* Checksum may be an unaligned pointer, use memcpy to avoid undefined behaviour. */ + memcpy (& desiredChecksum, checksum, sizeof (enet_uint32)); - * checksum = peer != NULL ? peer -> connectID : 0; + newChecksum = peer != NULL ? peer -> connectID : 0; + memcpy (checksum, & newChecksum, sizeof (enet_uint32)); buffer.data = host -> receivedData; buffer.dataLength = host -> receivedDataLength; @@ -1703,9 +1706,12 @@ enet_protocol_send_outgoing_commands (ENetHost * host, ENetEvent * event, int ch if (host -> checksum != NULL) { enet_uint32 * checksum = (enet_uint32 *) & headerData [host -> buffers -> dataLength]; - * checksum = currentPeer -> outgoingPeerID < ENET_PROTOCOL_MAXIMUM_PEER_ID ? currentPeer -> connectID : 0; + enet_uint32 newChecksum = currentPeer -> outgoingPeerID < ENET_PROTOCOL_MAXIMUM_PEER_ID ? currentPeer -> connectID : 0; + /* Checksum may be unaligned, use memcpy to avoid undefined behaviour. */ + memcpy(checksum, & newChecksum, sizeof (enet_uint32)); host -> buffers -> dataLength += sizeof (enet_uint32); - * checksum = host -> checksum (host -> buffers, host -> bufferCount); + newChecksum = host -> checksum (host -> buffers, host -> bufferCount); + memcpy(checksum, & newChecksum, sizeof (enet_uint32)); } if (shouldCompress > 0) diff --git a/thirdparty/glad/EGL/eglplatform.h b/thirdparty/glad/EGL/eglplatform.h index 99362a23de..6786afd90b 100644 --- a/thirdparty/glad/EGL/eglplatform.h +++ b/thirdparty/glad/EGL/eglplatform.h @@ -64,6 +64,12 @@ typedef HDC EGLNativeDisplayType; typedef HBITMAP EGLNativePixmapType; typedef HWND EGLNativeWindowType; +#elif defined(__QNX__) + +typedef khronos_uintptr_t EGLNativeDisplayType; +typedef struct _screen_pixmap* EGLNativePixmapType; /* screen_pixmap_t */ +typedef struct _screen_window* EGLNativeWindowType; /* screen_window_t */ + #elif defined(__EMSCRIPTEN__) typedef int EGLNativeDisplayType; diff --git a/thirdparty/glad/gl.c b/thirdparty/glad/gl.c index ee0cc188fc..38ecb514bd 100644 --- a/thirdparty/glad/gl.c +++ b/thirdparty/glad/gl.c @@ -453,6 +453,7 @@ PFNGLMULTITEXCOORDP3UIPROC glad_glMultiTexCoordP3ui = NULL; PFNGLMULTITEXCOORDP3UIVPROC glad_glMultiTexCoordP3uiv = NULL; PFNGLMULTITEXCOORDP4UIPROC glad_glMultiTexCoordP4ui = NULL; PFNGLMULTITEXCOORDP4UIVPROC glad_glMultiTexCoordP4uiv = NULL; +PFNGLNAMEDFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC glad_glNamedFramebufferTextureMultiviewOVR = NULL; PFNGLNEWLISTPROC glad_glNewList = NULL; PFNGLNORMAL3BPROC glad_glNormal3b = NULL; PFNGLNORMAL3BVPROC glad_glNormal3bv = NULL; @@ -2108,40 +2109,29 @@ static void glad_gl_load_GL_EXT_framebuffer_object( GLADuserptrloadfunc load, vo static void glad_gl_load_GL_OVR_multiview( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_OVR_multiview) return; glad_glFramebufferTextureMultiviewOVR = (PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC) load(userptr, "glFramebufferTextureMultiviewOVR"); + glad_glNamedFramebufferTextureMultiviewOVR = (PFNGLNAMEDFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC) load(userptr, "glNamedFramebufferTextureMultiviewOVR"); } -#if defined(GL_ES_VERSION_3_0) || defined(GL_VERSION_3_0) -#define GLAD_GL_IS_SOME_NEW_VERSION 1 -#else -#define GLAD_GL_IS_SOME_NEW_VERSION 0 -#endif - -static int glad_gl_get_extensions( int version, const char **out_exts, unsigned int *out_num_exts_i, char ***out_exts_i) { -#if GLAD_GL_IS_SOME_NEW_VERSION - if(GLAD_VERSION_MAJOR(version) < 3) { -#else - GLAD_UNUSED(version); - GLAD_UNUSED(out_num_exts_i); - GLAD_UNUSED(out_exts_i); -#endif - if (glad_glGetString == NULL) { - return 0; +static void glad_gl_free_extensions(char **exts_i) { + if (exts_i != NULL) { + unsigned int index; + for(index = 0; exts_i[index]; index++) { + free((void *) (exts_i[index])); } - *out_exts = (const char *)glad_glGetString(GL_EXTENSIONS); -#if GLAD_GL_IS_SOME_NEW_VERSION - } else { + free((void *)exts_i); + exts_i = NULL; + } +} +static int glad_gl_get_extensions( const char **out_exts, char ***out_exts_i) { +#if defined(GL_ES_VERSION_3_0) || defined(GL_VERSION_3_0) + if (glad_glGetStringi != NULL && glad_glGetIntegerv != NULL) { unsigned int index = 0; unsigned int num_exts_i = 0; char **exts_i = NULL; - if (glad_glGetStringi == NULL || glad_glGetIntegerv == NULL) { - return 0; - } glad_glGetIntegerv(GL_NUM_EXTENSIONS, (int*) &num_exts_i); - if (num_exts_i > 0) { - exts_i = (char **) malloc(num_exts_i * (sizeof *exts_i)); - } + exts_i = (char **) malloc((num_exts_i + 1) * (sizeof *exts_i)); if (exts_i == NULL) { return 0; } @@ -2150,31 +2140,40 @@ static int glad_gl_get_extensions( int version, const char **out_exts, unsigned size_t len = strlen(gl_str_tmp) + 1; char *local_str = (char*) malloc(len * sizeof(char)); - if(local_str != NULL) { - memcpy(local_str, gl_str_tmp, len * sizeof(char)); + if(local_str == NULL) { + exts_i[index] = NULL; + glad_gl_free_extensions(exts_i); + return 0; } + memcpy(local_str, gl_str_tmp, len * sizeof(char)); exts_i[index] = local_str; } + exts_i[index] = NULL; - *out_num_exts_i = num_exts_i; *out_exts_i = exts_i; + + return 1; } +#else + GLAD_UNUSED(out_exts_i); #endif + if (glad_glGetString == NULL) { + return 0; + } + *out_exts = (const char *)glad_glGetString(GL_EXTENSIONS); return 1; } -static void glad_gl_free_extensions(char **exts_i, unsigned int num_exts_i) { - if (exts_i != NULL) { +static int glad_gl_has_extension(const char *exts, char **exts_i, const char *ext) { + if(exts_i) { unsigned int index; - for(index = 0; index < num_exts_i; index++) { - free((void *) (exts_i[index])); + for(index = 0; exts_i[index]; index++) { + const char *e = exts_i[index]; + if(strcmp(e, ext) == 0) { + return 1; + } } - free((void *)exts_i); - exts_i = NULL; - } -} -static int glad_gl_has_extension(int version, const char *exts, unsigned int num_exts_i, char **exts_i, const char *ext) { - if(GLAD_VERSION_MAJOR(version) < 3 || !GLAD_GL_IS_SOME_NEW_VERSION) { + } else { const char *extensions; const char *loc; const char *terminator; @@ -2194,14 +2193,6 @@ static int glad_gl_has_extension(int version, const char *exts, unsigned int num } extensions = terminator; } - } else { - unsigned int index; - for(index = 0; index < num_exts_i; index++) { - const char *e = exts_i[index]; - if(strcmp(e, ext) == 0) { - return 1; - } - } } return 0; } @@ -2210,22 +2201,21 @@ static GLADapiproc glad_gl_get_proc_from_userptr(void *userptr, const char* name return (GLAD_GNUC_EXTENSION (GLADapiproc (*)(const char *name)) userptr)(name); } -static int glad_gl_find_extensions_gl( int version) { +static int glad_gl_find_extensions_gl(void) { const char *exts = NULL; - unsigned int num_exts_i = 0; char **exts_i = NULL; - if (!glad_gl_get_extensions(version, &exts, &num_exts_i, &exts_i)) return 0; + if (!glad_gl_get_extensions(&exts, &exts_i)) return 0; - GLAD_GL_ARB_debug_output = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_debug_output"); - GLAD_GL_ARB_framebuffer_object = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_framebuffer_object"); - GLAD_GL_ARB_get_program_binary = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_get_program_binary"); - GLAD_GL_EXT_framebuffer_blit = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_framebuffer_blit"); - GLAD_GL_EXT_framebuffer_multisample = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_framebuffer_multisample"); - GLAD_GL_EXT_framebuffer_object = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_framebuffer_object"); - GLAD_GL_OVR_multiview = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OVR_multiview"); - GLAD_GL_OVR_multiview2 = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OVR_multiview2"); + GLAD_GL_ARB_debug_output = glad_gl_has_extension(exts, exts_i, "GL_ARB_debug_output"); + GLAD_GL_ARB_framebuffer_object = glad_gl_has_extension(exts, exts_i, "GL_ARB_framebuffer_object"); + GLAD_GL_ARB_get_program_binary = glad_gl_has_extension(exts, exts_i, "GL_ARB_get_program_binary"); + GLAD_GL_EXT_framebuffer_blit = glad_gl_has_extension(exts, exts_i, "GL_EXT_framebuffer_blit"); + GLAD_GL_EXT_framebuffer_multisample = glad_gl_has_extension(exts, exts_i, "GL_EXT_framebuffer_multisample"); + GLAD_GL_EXT_framebuffer_object = glad_gl_has_extension(exts, exts_i, "GL_EXT_framebuffer_object"); + GLAD_GL_OVR_multiview = glad_gl_has_extension(exts, exts_i, "GL_OVR_multiview"); + GLAD_GL_OVR_multiview2 = glad_gl_has_extension(exts, exts_i, "GL_OVR_multiview2"); - glad_gl_free_extensions(exts_i, num_exts_i); + glad_gl_free_extensions(exts_i); return 1; } @@ -2275,7 +2265,6 @@ int gladLoadGLUserPtr( GLADuserptrloadfunc load, void *userptr) { glad_glGetString = (PFNGLGETSTRINGPROC) load(userptr, "glGetString"); if(glad_glGetString == NULL) return 0; - if(glad_glGetString(GL_VERSION) == NULL) return 0; version = glad_gl_find_core_gl(); glad_gl_load_GL_VERSION_1_0(load, userptr); @@ -2291,7 +2280,7 @@ int gladLoadGLUserPtr( GLADuserptrloadfunc load, void *userptr) { glad_gl_load_GL_VERSION_3_2(load, userptr); glad_gl_load_GL_VERSION_3_3(load, userptr); - if (!glad_gl_find_extensions_gl(version)) return 0; + if (!glad_gl_find_extensions_gl()) return 0; glad_gl_load_GL_ARB_debug_output(load, userptr); glad_gl_load_GL_ARB_framebuffer_object(load, userptr); glad_gl_load_GL_ARB_get_program_binary(load, userptr); @@ -2310,16 +2299,15 @@ int gladLoadGL( GLADloadfunc load) { return gladLoadGLUserPtr( glad_gl_get_proc_from_userptr, GLAD_GNUC_EXTENSION (void*) load); } -static int glad_gl_find_extensions_gles2( int version) { +static int glad_gl_find_extensions_gles2(void) { const char *exts = NULL; - unsigned int num_exts_i = 0; char **exts_i = NULL; - if (!glad_gl_get_extensions(version, &exts, &num_exts_i, &exts_i)) return 0; + if (!glad_gl_get_extensions(&exts, &exts_i)) return 0; - GLAD_GL_OVR_multiview = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OVR_multiview"); - GLAD_GL_OVR_multiview2 = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OVR_multiview2"); + GLAD_GL_OVR_multiview = glad_gl_has_extension(exts, exts_i, "GL_OVR_multiview"); + GLAD_GL_OVR_multiview2 = glad_gl_has_extension(exts, exts_i, "GL_OVR_multiview2"); - glad_gl_free_extensions(exts_i, num_exts_i); + glad_gl_free_extensions(exts_i); return 1; } @@ -2361,7 +2349,6 @@ int gladLoadGLES2UserPtr( GLADuserptrloadfunc load, void *userptr) { glad_glGetString = (PFNGLGETSTRINGPROC) load(userptr, "glGetString"); if(glad_glGetString == NULL) return 0; - if(glad_glGetString(GL_VERSION) == NULL) return 0; version = glad_gl_find_core_gles2(); glad_gl_load_GL_ES_VERSION_2_0(load, userptr); @@ -2369,7 +2356,7 @@ int gladLoadGLES2UserPtr( GLADuserptrloadfunc load, void *userptr) { glad_gl_load_GL_ES_VERSION_3_1(load, userptr); glad_gl_load_GL_ES_VERSION_3_2(load, userptr); - if (!glad_gl_find_extensions_gles2(version)) return 0; + if (!glad_gl_find_extensions_gles2()) return 0; glad_gl_load_GL_OVR_multiview(load, userptr); @@ -2627,10 +2614,9 @@ static GLADapiproc glad_dlsym_handle(void* handle, const char *name) { typedef __eglMustCastToProperFunctionPointerType (GLAD_API_PTR *PFNEGLGETPROCADDRESSPROC)(const char *name); #endif extern __eglMustCastToProperFunctionPointerType emscripten_GetProcAddress(const char *name); -#elif EGL_STATIC - typedef void (*__eglMustCastToProperFunctionPointerType)(void); +#elif defined(GLAD_GLES2_USE_SYSTEM_EGL) + #include <EGL/egl.h> typedef __eglMustCastToProperFunctionPointerType (GLAD_API_PTR *PFNEGLGETPROCADDRESSPROC)(const char *name); - extern __eglMustCastToProperFunctionPointerType GLAD_API_PTR eglGetProcAddress(const char *name); #else #include <glad/egl.h> #endif @@ -2658,7 +2644,7 @@ static GLADapiproc glad_gles2_get_proc(void *vuserptr, const char* name) { return result; } -static void* _glad_GL_loader_handle = NULL; +static void* _glad_GLES2_loader_handle = NULL; static void* glad_gles2_dlopen_handle(void) { #if GLAD_PLATFORM_EMSCRIPTEN @@ -2674,11 +2660,11 @@ static void* glad_gles2_dlopen_handle(void) { GLAD_UNUSED(glad_get_dlopen_handle); return NULL; #else - if (_glad_GL_loader_handle == NULL) { - _glad_GL_loader_handle = glad_get_dlopen_handle(NAMES, sizeof(NAMES) / sizeof(NAMES[0])); + if (_glad_GLES2_loader_handle == NULL) { + _glad_GLES2_loader_handle = glad_get_dlopen_handle(NAMES, sizeof(NAMES) / sizeof(NAMES[0])); } - return _glad_GL_loader_handle; + return _glad_GLES2_loader_handle; #endif } @@ -2708,11 +2694,12 @@ int gladLoaderLoadGLES2(void) { userptr.get_proc_address_ptr = emscripten_GetProcAddress; version = gladLoadGLES2UserPtr(glad_gles2_get_proc, &userptr); #else +#ifndef GLAD_GLES2_USE_SYSTEM_EGL if (eglGetProcAddress == NULL) { return 0; } - - did_load = _glad_GL_loader_handle == NULL; +#endif + did_load = _glad_GLES2_loader_handle == NULL; handle = glad_gles2_dlopen_handle(); if (handle != NULL) { userptr = glad_gles2_build_userptr(handle); @@ -2731,9 +2718,9 @@ int gladLoaderLoadGLES2(void) { void gladLoaderUnloadGLES2(void) { - if (_glad_GL_loader_handle != NULL) { - glad_close_dlopen_handle(_glad_GL_loader_handle); - _glad_GL_loader_handle = NULL; + if (_glad_GLES2_loader_handle != NULL) { + glad_close_dlopen_handle(_glad_GLES2_loader_handle); + _glad_GLES2_loader_handle = NULL; } } diff --git a/thirdparty/glad/glad/egl.h b/thirdparty/glad/glad/egl.h index 1bf35c1404..053c5853a7 100644 --- a/thirdparty/glad/glad/egl.h +++ b/thirdparty/glad/glad/egl.h @@ -1,5 +1,5 @@ /** - * Loader generated by glad 2.0.3 on Fri Feb 3 07:06:48 2023 + * Loader generated by glad 2.0.6 on Fri Apr 5 08:17:09 2024 * * SPDX-License-Identifier: (WTFPL OR CC0-1.0) AND Apache-2.0 * @@ -141,7 +141,7 @@ extern "C" { #define GLAD_VERSION_MAJOR(version) (version / 10000) #define GLAD_VERSION_MINOR(version) (version % 10000) -#define GLAD_GENERATOR_VERSION "2.0.3" +#define GLAD_GENERATOR_VERSION "2.0.6" typedef void (*GLADapiproc)(void); diff --git a/thirdparty/glad/glad/gl.h b/thirdparty/glad/glad/gl.h index 307ea4dbb8..1301d10b65 100644 --- a/thirdparty/glad/glad/gl.h +++ b/thirdparty/glad/glad/gl.h @@ -1,5 +1,5 @@ /** - * Loader generated by glad 2.0.4 on Mon May 22 13:18:29 2023 + * Loader generated by glad 2.0.6 on Fri Apr 5 08:14:44 2024 * * SPDX-License-Identifier: (WTFPL OR CC0-1.0) AND Apache-2.0 * @@ -178,7 +178,7 @@ extern "C" { #define GLAD_VERSION_MAJOR(version) (version / 10000) #define GLAD_VERSION_MINOR(version) (version % 10000) -#define GLAD_GENERATOR_VERSION "2.0.4" +#define GLAD_GENERATOR_VERSION "2.0.6" typedef void (*GLADapiproc)(void); @@ -2394,6 +2394,7 @@ typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORDP3UIPROC)(GLenum texture, GLenum t typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORDP3UIVPROC)(GLenum texture, GLenum type, const GLuint * coords); typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORDP4UIPROC)(GLenum texture, GLenum type, GLuint coords); typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORDP4UIVPROC)(GLenum texture, GLenum type, const GLuint * coords); +typedef void (GLAD_API_PTR *PFNGLNAMEDFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC)(GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLint baseViewIndex, GLsizei numViews); typedef void (GLAD_API_PTR *PFNGLNEWLISTPROC)(GLuint list, GLenum mode); typedef void (GLAD_API_PTR *PFNGLNORMAL3BPROC)(GLbyte nx, GLbyte ny, GLbyte nz); typedef void (GLAD_API_PTR *PFNGLNORMAL3BVPROC)(const GLbyte * v); @@ -3654,6 +3655,8 @@ GLAD_API_CALL PFNGLMULTITEXCOORDP4UIPROC glad_glMultiTexCoordP4ui; #define glMultiTexCoordP4ui glad_glMultiTexCoordP4ui GLAD_API_CALL PFNGLMULTITEXCOORDP4UIVPROC glad_glMultiTexCoordP4uiv; #define glMultiTexCoordP4uiv glad_glMultiTexCoordP4uiv +GLAD_API_CALL PFNGLNAMEDFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC glad_glNamedFramebufferTextureMultiviewOVR; +#define glNamedFramebufferTextureMultiviewOVR glad_glNamedFramebufferTextureMultiviewOVR GLAD_API_CALL PFNGLNEWLISTPROC glad_glNewList; #define glNewList glad_glNewList GLAD_API_CALL PFNGLNORMAL3BPROC glad_glNormal3b; diff --git a/thirdparty/glad/glad/glx.h b/thirdparty/glad/glad/glx.h index cf7663a3af..a2fa0dadee 100644 --- a/thirdparty/glad/glad/glx.h +++ b/thirdparty/glad/glad/glx.h @@ -1,5 +1,5 @@ /** - * Loader generated by glad 2.0.4 on Mon May 22 13:18:29 2023 + * Loader generated by glad 2.0.6 on Fri Apr 5 08:14:31 2024 * * SPDX-License-Identifier: (WTFPL OR CC0-1.0) AND Apache-2.0 * @@ -152,7 +152,7 @@ extern "C" { #define GLAD_VERSION_MAJOR(version) (version / 10000) #define GLAD_VERSION_MINOR(version) (version % 10000) -#define GLAD_GENERATOR_VERSION "2.0.4" +#define GLAD_GENERATOR_VERSION "2.0.6" typedef void (*GLADapiproc)(void); diff --git a/thirdparty/glad/patches/patch_enable_both_gl_and_gles.diff b/thirdparty/glad/patches/patch_enable_both_gl_and_gles.diff index a98efe51d8..88c5510166 100644 --- a/thirdparty/glad/patches/patch_enable_both_gl_and_gles.diff +++ b/thirdparty/glad/patches/patch_enable_both_gl_and_gles.diff @@ -1,8 +1,8 @@ diff --git a/thirdparty/glad/gl.c b/thirdparty/glad/gl.c -index a0b59dbbfb..9f10f6544a 100644 +index 3f0884a3dc..38ecb514bd 100644 --- a/thirdparty/glad/gl.c +++ b/thirdparty/glad/gl.c -@@ -2475,7 +2475,7 @@ static GLADapiproc glad_gl_get_proc(void *vuserptr, const char *name) { +@@ -2462,7 +2462,7 @@ static GLADapiproc glad_gl_get_proc(void *vuserptr, const char *name) { return result; } @@ -11,7 +11,7 @@ index a0b59dbbfb..9f10f6544a 100644 static void* glad_gl_dlopen_handle(void) { #if GLAD_PLATFORM_APPLE -@@ -2497,11 +2497,11 @@ static void* glad_gl_dlopen_handle(void) { +@@ -2484,11 +2484,11 @@ static void* glad_gl_dlopen_handle(void) { }; #endif @@ -26,7 +26,7 @@ index a0b59dbbfb..9f10f6544a 100644 } static struct _glad_gl_userptr glad_gl_build_userptr(void *handle) { -@@ -2527,7 +2527,7 @@ int gladLoaderLoadGL(void) { +@@ -2514,7 +2514,7 @@ int gladLoaderLoadGL(void) { int did_load = 0; struct _glad_gl_userptr userptr; @@ -35,7 +35,7 @@ index a0b59dbbfb..9f10f6544a 100644 handle = glad_gl_dlopen_handle(); if (handle) { userptr = glad_gl_build_userptr(handle); -@@ -2545,9 +2545,9 @@ int gladLoaderLoadGL(void) { +@@ -2532,9 +2532,9 @@ int gladLoaderLoadGL(void) { void gladLoaderUnloadGL(void) { @@ -49,7 +49,7 @@ index a0b59dbbfb..9f10f6544a 100644 } diff --git a/thirdparty/glad/glad/gl.h b/thirdparty/glad/glad/gl.h -index 905c16aeed..f3cb7d8cb5 100644 +index 77c6f33cab..1301d10b65 100644 --- a/thirdparty/glad/glad/gl.h +++ b/thirdparty/glad/glad/gl.h @@ -67,6 +67,7 @@ diff --git a/thirdparty/harfbuzz/src/OT/Color/COLR/COLR.hh b/thirdparty/harfbuzz/src/OT/Color/COLR/COLR.hh index b632a1d9eb..623775a771 100644 --- a/thirdparty/harfbuzz/src/OT/Color/COLR/COLR.hh +++ b/thirdparty/harfbuzz/src/OT/Color/COLR/COLR.hh @@ -68,7 +68,7 @@ public: hb_font_t *font; unsigned int palette_index; hb_color_t foreground; - VarStoreInstancer &instancer; + ItemVarStoreInstancer &instancer; hb_map_t current_glyphs; hb_map_t current_layers; int depth_left = HB_MAX_NESTING_LEVEL; @@ -80,7 +80,7 @@ public: hb_font_t *font_, unsigned int palette_, hb_color_t foreground_, - VarStoreInstancer &instancer_) : + ItemVarStoreInstancer &instancer_) : base (base_), funcs (funcs_), data (data_), @@ -245,7 +245,7 @@ struct Variable { value.closurev1 (c); } bool subset (hb_subset_context_t *c, - const VarStoreInstancer &instancer) const + const ItemVarStoreInstancer &instancer) const { TRACE_SUBSET (this); if (!value.subset (c, instancer, varIdxBase)) return_trace (false); @@ -270,7 +270,7 @@ struct Variable void get_color_stop (hb_paint_context_t *c, hb_color_stop_t *stop, - const VarStoreInstancer &instancer) const + const ItemVarStoreInstancer &instancer) const { value.get_color_stop (c, stop, varIdxBase, instancer); } @@ -305,7 +305,7 @@ struct NoVariable { value.closurev1 (c); } bool subset (hb_subset_context_t *c, - const VarStoreInstancer &instancer) const + const ItemVarStoreInstancer &instancer) const { TRACE_SUBSET (this); return_trace (value.subset (c, instancer, varIdxBase)); @@ -325,7 +325,7 @@ struct NoVariable void get_color_stop (hb_paint_context_t *c, hb_color_stop_t *stop, - const VarStoreInstancer &instancer) const + const ItemVarStoreInstancer &instancer) const { value.get_color_stop (c, stop, VarIdx::NO_VARIATION, instancer); } @@ -348,7 +348,7 @@ struct ColorStop { c->add_palette_index (paletteIndex); } bool subset (hb_subset_context_t *c, - const VarStoreInstancer &instancer, + const ItemVarStoreInstancer &instancer, uint32_t varIdxBase) const { TRACE_SUBSET (this); @@ -374,7 +374,7 @@ struct ColorStop void get_color_stop (hb_paint_context_t *c, hb_color_stop_t *out, uint32_t varIdx, - const VarStoreInstancer &instancer) const + const ItemVarStoreInstancer &instancer) const { out->offset = stopOffset.to_float(instancer (varIdx, 0)); out->color = c->get_color (paletteIndex, @@ -410,7 +410,7 @@ struct ColorLine } bool subset (hb_subset_context_t *c, - const VarStoreInstancer &instancer) const + const ItemVarStoreInstancer &instancer) const { TRACE_SUBSET (this); auto *out = c->serializer->start_embed (this); @@ -439,7 +439,7 @@ struct ColorLine unsigned int start, unsigned int *count, hb_color_stop_t *color_stops, - const VarStoreInstancer &instancer) const + const ItemVarStoreInstancer &instancer) const { unsigned int len = stops.len; @@ -543,7 +543,7 @@ struct Affine2x3 } bool subset (hb_subset_context_t *c, - const VarStoreInstancer &instancer, + const ItemVarStoreInstancer &instancer, uint32_t varIdxBase) const { TRACE_SUBSET (this); @@ -588,7 +588,7 @@ struct PaintColrLayers void closurev1 (hb_colrv1_closure_context_t* c) const; bool subset (hb_subset_context_t *c, - const VarStoreInstancer &instancer HB_UNUSED) const + const ItemVarStoreInstancer &instancer HB_UNUSED) const { TRACE_SUBSET (this); auto *out = c->serializer->embed (this); @@ -620,7 +620,7 @@ struct PaintSolid { c->add_palette_index (paletteIndex); } bool subset (hb_subset_context_t *c, - const VarStoreInstancer &instancer, + const ItemVarStoreInstancer &instancer, uint32_t varIdxBase) const { TRACE_SUBSET (this); @@ -669,7 +669,7 @@ struct PaintLinearGradient { (this+colorLine).closurev1 (c); } bool subset (hb_subset_context_t *c, - const VarStoreInstancer &instancer, + const ItemVarStoreInstancer &instancer, uint32_t varIdxBase) const { TRACE_SUBSET (this); @@ -736,7 +736,7 @@ struct PaintRadialGradient { (this+colorLine).closurev1 (c); } bool subset (hb_subset_context_t *c, - const VarStoreInstancer &instancer, + const ItemVarStoreInstancer &instancer, uint32_t varIdxBase) const { TRACE_SUBSET (this); @@ -803,7 +803,7 @@ struct PaintSweepGradient { (this+colorLine).closurev1 (c); } bool subset (hb_subset_context_t *c, - const VarStoreInstancer &instancer, + const ItemVarStoreInstancer &instancer, uint32_t varIdxBase) const { TRACE_SUBSET (this); @@ -863,7 +863,7 @@ struct PaintGlyph void closurev1 (hb_colrv1_closure_context_t* c) const; bool subset (hb_subset_context_t *c, - const VarStoreInstancer &instancer) const + const ItemVarStoreInstancer &instancer) const { TRACE_SUBSET (this); auto *out = c->serializer->embed (this); @@ -906,7 +906,7 @@ struct PaintColrGlyph void closurev1 (hb_colrv1_closure_context_t* c) const; bool subset (hb_subset_context_t *c, - const VarStoreInstancer &instancer HB_UNUSED) const + const ItemVarStoreInstancer &instancer HB_UNUSED) const { TRACE_SUBSET (this); auto *out = c->serializer->embed (this); @@ -936,7 +936,7 @@ struct PaintTransform HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const; bool subset (hb_subset_context_t *c, - const VarStoreInstancer &instancer) const + const ItemVarStoreInstancer &instancer) const { TRACE_SUBSET (this); auto *out = c->serializer->embed (this); @@ -975,7 +975,7 @@ struct PaintTranslate HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const; bool subset (hb_subset_context_t *c, - const VarStoreInstancer &instancer, + const ItemVarStoreInstancer &instancer, uint32_t varIdxBase) const { TRACE_SUBSET (this); @@ -1024,7 +1024,7 @@ struct PaintScale HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const; bool subset (hb_subset_context_t *c, - const VarStoreInstancer &instancer, + const ItemVarStoreInstancer &instancer, uint32_t varIdxBase) const { TRACE_SUBSET (this); @@ -1073,7 +1073,7 @@ struct PaintScaleAroundCenter HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const; bool subset (hb_subset_context_t *c, - const VarStoreInstancer &instancer, + const ItemVarStoreInstancer &instancer, uint32_t varIdxBase) const { TRACE_SUBSET (this); @@ -1132,7 +1132,7 @@ struct PaintScaleUniform HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const; bool subset (hb_subset_context_t *c, - const VarStoreInstancer &instancer, + const ItemVarStoreInstancer &instancer, uint32_t varIdxBase) const { TRACE_SUBSET (this); @@ -1176,7 +1176,7 @@ struct PaintScaleUniformAroundCenter HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const; bool subset (hb_subset_context_t *c, - const VarStoreInstancer &instancer, + const ItemVarStoreInstancer &instancer, uint32_t varIdxBase) const { TRACE_SUBSET (this); @@ -1232,7 +1232,7 @@ struct PaintRotate HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const; bool subset (hb_subset_context_t *c, - const VarStoreInstancer &instancer, + const ItemVarStoreInstancer &instancer, uint32_t varIdxBase) const { TRACE_SUBSET (this); @@ -1276,7 +1276,7 @@ struct PaintRotateAroundCenter HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const; bool subset (hb_subset_context_t *c, - const VarStoreInstancer &instancer, + const ItemVarStoreInstancer &instancer, uint32_t varIdxBase) const { TRACE_SUBSET (this); @@ -1332,7 +1332,7 @@ struct PaintSkew HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const; bool subset (hb_subset_context_t *c, - const VarStoreInstancer &instancer, + const ItemVarStoreInstancer &instancer, uint32_t varIdxBase) const { TRACE_SUBSET (this); @@ -1381,7 +1381,7 @@ struct PaintSkewAroundCenter HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const; bool subset (hb_subset_context_t *c, - const VarStoreInstancer &instancer, + const ItemVarStoreInstancer &instancer, uint32_t varIdxBase) const { TRACE_SUBSET (this); @@ -1440,7 +1440,7 @@ struct PaintComposite void closurev1 (hb_colrv1_closure_context_t* c) const; bool subset (hb_subset_context_t *c, - const VarStoreInstancer &instancer) const + const ItemVarStoreInstancer &instancer) const { TRACE_SUBSET (this); auto *out = c->serializer->embed (this); @@ -1491,7 +1491,7 @@ struct ClipBoxFormat1 return_trace (c->check_struct (this)); } - void get_clip_box (ClipBoxData &clip_box, const VarStoreInstancer &instancer HB_UNUSED) const + void get_clip_box (ClipBoxData &clip_box, const ItemVarStoreInstancer &instancer HB_UNUSED) const { clip_box.xMin = xMin; clip_box.yMin = yMin; @@ -1500,7 +1500,7 @@ struct ClipBoxFormat1 } bool subset (hb_subset_context_t *c, - const VarStoreInstancer &instancer, + const ItemVarStoreInstancer &instancer, uint32_t varIdxBase) const { TRACE_SUBSET (this); @@ -1533,7 +1533,7 @@ struct ClipBoxFormat1 struct ClipBoxFormat2 : Variable<ClipBoxFormat1> { - void get_clip_box (ClipBoxData &clip_box, const VarStoreInstancer &instancer) const + void get_clip_box (ClipBoxData &clip_box, const ItemVarStoreInstancer &instancer) const { value.get_clip_box(clip_box, instancer); if (instancer) @@ -1549,7 +1549,7 @@ struct ClipBoxFormat2 : Variable<ClipBoxFormat1> struct ClipBox { bool subset (hb_subset_context_t *c, - const VarStoreInstancer &instancer) const + const ItemVarStoreInstancer &instancer) const { TRACE_SUBSET (this); switch (u.format) { @@ -1572,7 +1572,7 @@ struct ClipBox } bool get_extents (hb_glyph_extents_t *extents, - const VarStoreInstancer &instancer) const + const ItemVarStoreInstancer &instancer) const { ClipBoxData clip_box; switch (u.format) { @@ -1608,7 +1608,7 @@ struct ClipRecord bool subset (hb_subset_context_t *c, const void *base, - const VarStoreInstancer &instancer) const + const ItemVarStoreInstancer &instancer) const { TRACE_SUBSET (this); auto *out = c->serializer->embed (*this); @@ -1625,7 +1625,7 @@ struct ClipRecord bool get_extents (hb_glyph_extents_t *extents, const void *base, - const VarStoreInstancer &instancer) const + const ItemVarStoreInstancer &instancer) const { return (base+clipBox).get_extents (extents, instancer); } @@ -1642,7 +1642,7 @@ DECLARE_NULL_NAMESPACE_BYTES (OT, ClipRecord); struct ClipList { unsigned serialize_clip_records (hb_subset_context_t *c, - const VarStoreInstancer &instancer, + const ItemVarStoreInstancer &instancer, const hb_set_t& gids, const hb_map_t& gid_offset_map) const { @@ -1695,7 +1695,7 @@ struct ClipList } bool subset (hb_subset_context_t *c, - const VarStoreInstancer &instancer) const + const ItemVarStoreInstancer &instancer) const { TRACE_SUBSET (this); auto *out = c->serializer->start_embed (*this); @@ -1735,7 +1735,7 @@ struct ClipList bool get_extents (hb_codepoint_t gid, hb_glyph_extents_t *extents, - const VarStoreInstancer &instancer) const + const ItemVarStoreInstancer &instancer) const { auto *rec = clips.as_array ().bsearch (gid); if (rec) @@ -1855,7 +1855,7 @@ struct BaseGlyphPaintRecord bool serialize (hb_serialize_context_t *s, const hb_map_t* glyph_map, const void* src_base, hb_subset_context_t *c, - const VarStoreInstancer &instancer) const + const ItemVarStoreInstancer &instancer) const { TRACE_SERIALIZE (this); auto *out = s->embed (this); @@ -1884,7 +1884,7 @@ struct BaseGlyphPaintRecord struct BaseGlyphList : SortedArray32Of<BaseGlyphPaintRecord> { bool subset (hb_subset_context_t *c, - const VarStoreInstancer &instancer) const + const ItemVarStoreInstancer &instancer) const { TRACE_SUBSET (this); auto *out = c->serializer->start_embed (this); @@ -1916,7 +1916,7 @@ struct LayerList : Array32OfOffset32To<Paint> { return this+(*this)[i]; } bool subset (hb_subset_context_t *c, - const VarStoreInstancer &instancer) const + const ItemVarStoreInstancer &instancer) const { TRACE_SUBSET (this); auto *out = c->serializer->start_embed (this); @@ -2206,7 +2206,7 @@ struct COLR auto snap = c->serializer->snapshot (); if (!c->serializer->allocate_size<void> (5 * HBUINT32::static_size)) return_trace (false); - VarStoreInstancer instancer (varStore ? &(this+varStore) : nullptr, + ItemVarStoreInstancer instancer (varStore ? &(this+varStore) : nullptr, varIdxMap ? &(this+varIdxMap) : nullptr, c->plan->normalized_coords.as_array ()); @@ -2250,7 +2250,7 @@ struct COLR if (version != 1) return false; - VarStoreInstancer instancer (&(this+varStore), + ItemVarStoreInstancer instancer (&(this+varStore), &(this+varIdxMap), hb_array (font->coords, font->num_coords)); @@ -2301,7 +2301,7 @@ struct COLR bool get_clip (hb_codepoint_t glyph, hb_glyph_extents_t *extents, - const VarStoreInstancer instancer) const + const ItemVarStoreInstancer instancer) const { return (this+clipList).get_extents (glyph, extents, @@ -2312,7 +2312,7 @@ struct COLR bool paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data, unsigned int palette_index, hb_color_t foreground, bool clip = true) const { - VarStoreInstancer instancer (&(this+varStore), + ItemVarStoreInstancer instancer (&(this+varStore), &(this+varIdxMap), hb_array (font->coords, font->num_coords)); hb_paint_context_t c (this, funcs, data, font, palette_index, foreground, instancer); @@ -2327,7 +2327,7 @@ struct COLR { // COLRv1 glyph - VarStoreInstancer instancer (&(this+varStore), + ItemVarStoreInstancer instancer (&(this+varStore), &(this+varIdxMap), hb_array (font->coords, font->num_coords)); @@ -2413,7 +2413,7 @@ struct COLR Offset32To<LayerList> layerList; Offset32To<ClipList> clipList; // Offset to ClipList table (may be NULL) Offset32To<DeltaSetIndexMap> varIdxMap; // Offset to DeltaSetIndexMap table (may be NULL) - Offset32To<VariationStore> varStore; + Offset32To<ItemVariationStore> varStore; public: DEFINE_SIZE_MIN (14); }; diff --git a/thirdparty/harfbuzz/src/OT/Layout/GDEF/GDEF.hh b/thirdparty/harfbuzz/src/OT/Layout/GDEF/GDEF.hh index 14a9b5e5cd..317b96c714 100644 --- a/thirdparty/harfbuzz/src/OT/Layout/GDEF/GDEF.hh +++ b/thirdparty/harfbuzz/src/OT/Layout/GDEF/GDEF.hh @@ -189,7 +189,7 @@ struct CaretValueFormat3 friend struct CaretValue; hb_position_t get_caret_value (hb_font_t *font, hb_direction_t direction, - const VariationStore &var_store) const + const ItemVariationStore &var_store) const { return HB_DIRECTION_IS_HORIZONTAL (direction) ? font->em_scale_x (coordinate) + (this+deviceTable).get_x_delta (font, var_store) : @@ -251,7 +251,7 @@ struct CaretValue hb_position_t get_caret_value (hb_font_t *font, hb_direction_t direction, hb_codepoint_t glyph_id, - const VariationStore &var_store) const + const ItemVariationStore &var_store) const { switch (u.format) { case 1: return u.format1.get_caret_value (font, direction); @@ -316,7 +316,7 @@ struct LigGlyph unsigned get_lig_carets (hb_font_t *font, hb_direction_t direction, hb_codepoint_t glyph_id, - const VariationStore &var_store, + const ItemVariationStore &var_store, unsigned start_offset, unsigned *caret_count /* IN/OUT */, hb_position_t *caret_array /* OUT */) const @@ -372,7 +372,7 @@ struct LigCaretList unsigned int get_lig_carets (hb_font_t *font, hb_direction_t direction, hb_codepoint_t glyph_id, - const VariationStore &var_store, + const ItemVariationStore &var_store, unsigned int start_offset, unsigned int *caret_count /* IN/OUT */, hb_position_t *caret_array /* OUT */) const @@ -609,7 +609,7 @@ struct GDEFVersion1_2 * definitions--from beginning of GDEF * header (may be NULL). Introduced * in version 0x00010002. */ - Offset32To<VariationStore> + Offset32To<ItemVariationStore> varStore; /* Offset to the table of Item Variation * Store--from beginning of GDEF * header (may be NULL). Introduced @@ -663,21 +663,16 @@ struct GDEFVersion1_2 auto *out = c->serializer->start_embed (*this); if (unlikely (!c->serializer->extend_min (out))) return_trace (false); - out->version.major = version.major; - out->version.minor = version.minor; - bool subset_glyphclassdef = out->glyphClassDef.serialize_subset (c, glyphClassDef, this, nullptr, false, true); - bool subset_attachlist = out->attachList.serialize_subset (c, attachList, this); - bool subset_markattachclassdef = out->markAttachClassDef.serialize_subset (c, markAttachClassDef, this, nullptr, false, true); - - bool subset_markglyphsetsdef = false; + // Push var store first (if it's needed) so that it's last in the + // serialization order. Some font consumers assume that varstore runs to + // the end of the GDEF table. + // See: https://github.com/harfbuzz/harfbuzz/issues/4636 auto snapshot_version0 = c->serializer->snapshot (); - if (version.to_int () >= 0x00010002u) - { - if (unlikely (!c->serializer->embed (markGlyphSetsDef))) return_trace (false); - subset_markglyphsetsdef = out->markGlyphSetsDef.serialize_subset (c, markGlyphSetsDef, this); - } + if (unlikely (version.to_int () >= 0x00010002u && !c->serializer->embed (markGlyphSetsDef))) + return_trace (false); bool subset_varstore = false; + unsigned varstore_index = (unsigned) -1; auto snapshot_version2 = c->serializer->snapshot (); if (version.to_int () >= 0x00010003u) { @@ -690,35 +685,58 @@ struct GDEFVersion1_2 { item_variations_t item_vars; if (item_vars.instantiate (this+varStore, c->plan, true, true, - c->plan->gdef_varstore_inner_maps.as_array ())) + c->plan->gdef_varstore_inner_maps.as_array ())) { subset_varstore = out->varStore.serialize_serialize (c->serializer, item_vars.has_long_word (), c->plan->axis_tags, item_vars.get_region_list (), item_vars.get_vardata_encodings ()); + varstore_index = c->serializer->last_added_child_index(); + } remap_varidx_after_instantiation (item_vars.get_varidx_map (), c->plan->layout_variation_idx_delta_map); } } else + { subset_varstore = out->varStore.serialize_subset (c, varStore, this, c->plan->gdef_varstore_inner_maps.as_array ()); + varstore_index = c->serializer->last_added_child_index(); + } + } + + out->version.major = version.major; + out->version.minor = version.minor; + + if (!subset_varstore && version.to_int () >= 0x00010002u) { + c->serializer->revert (snapshot_version2); } + bool subset_markglyphsetsdef = false; + if (version.to_int () >= 0x00010002u) + { + subset_markglyphsetsdef = out->markGlyphSetsDef.serialize_subset (c, markGlyphSetsDef, this); + } if (subset_varstore) { out->version.minor = 3; c->plan->has_gdef_varstore = true; } else if (subset_markglyphsetsdef) { - out->version.minor = 2; - c->serializer->revert (snapshot_version2); + out->version.minor = 2; } else { out->version.minor = 0; c->serializer->revert (snapshot_version0); } + bool subset_glyphclassdef = out->glyphClassDef.serialize_subset (c, glyphClassDef, this, nullptr, false, true); + bool subset_attachlist = out->attachList.serialize_subset (c, attachList, this); + bool subset_markattachclassdef = out->markAttachClassDef.serialize_subset (c, markAttachClassDef, this, nullptr, false, true); bool subset_ligcaretlist = out->ligCaretList.serialize_subset (c, ligCaretList, this); + if (subset_varstore && varstore_index != (unsigned) -1) { + c->serializer->repack_last(varstore_index); + } + return_trace (subset_glyphclassdef || subset_attachlist || subset_ligcaretlist || subset_markattachclassdef || (out->version.to_int () >= 0x00010002u && subset_markglyphsetsdef) || @@ -884,14 +902,14 @@ struct GDEF default: return false; } } - const VariationStore &get_var_store () const + const ItemVariationStore &get_var_store () const { switch (u.version.major) { - case 1: return u.version.to_int () >= 0x00010003u ? this+u.version1.varStore : Null(VariationStore); + case 1: return u.version.to_int () >= 0x00010003u ? this+u.version1.varStore : Null(ItemVariationStore); #ifndef HB_NO_BEYOND_64K case 2: return this+u.version2.varStore; #endif - default: return Null(VariationStore); + default: return Null(ItemVariationStore); } } @@ -1011,9 +1029,9 @@ struct GDEF hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *layout_variation_idx_delta_map /* OUT */) const { if (!has_var_store ()) return; - const VariationStore &var_store = get_var_store (); + const ItemVariationStore &var_store = get_var_store (); float *store_cache = var_store.create_cache (); - + unsigned new_major = 0, new_minor = 0; unsigned last_major = (layout_variation_indices->get_min ()) >> 16; for (unsigned idx : layout_variation_indices->iter ()) diff --git a/thirdparty/harfbuzz/src/OT/Layout/GPOS/PairPosFormat2.hh b/thirdparty/harfbuzz/src/OT/Layout/GPOS/PairPosFormat2.hh index dd02da887d..9c805b39a1 100644 --- a/thirdparty/harfbuzz/src/OT/Layout/GPOS/PairPosFormat2.hh +++ b/thirdparty/harfbuzz/src/OT/Layout/GPOS/PairPosFormat2.hh @@ -324,17 +324,8 @@ struct PairPosFormat2_4 : ValueBase } } - const hb_set_t &glyphset = *c->plan->glyphset_gsub (); - const hb_map_t &glyph_map = *c->plan->glyph_map; - - auto it = - + hb_iter (this+coverage) - | hb_filter (glyphset) - | hb_map_retains_sorting (glyph_map) - ; - - out->coverage.serialize_serialize (c->serializer, it); - return_trace (out->class1Count && out->class2Count && bool (it)); + bool ret = out->coverage.serialize_subset(c, coverage, this); + return_trace (out->class1Count && out->class2Count && ret); } diff --git a/thirdparty/harfbuzz/src/OT/Layout/GPOS/ValueFormat.hh b/thirdparty/harfbuzz/src/OT/Layout/GPOS/ValueFormat.hh index 17f57db1f5..9442cc1cc5 100644 --- a/thirdparty/harfbuzz/src/OT/Layout/GPOS/ValueFormat.hh +++ b/thirdparty/harfbuzz/src/OT/Layout/GPOS/ValueFormat.hh @@ -116,7 +116,7 @@ struct ValueFormat : HBUINT16 if (!use_x_device && !use_y_device) return ret; - const VariationStore &store = c->var_store; + const ItemVariationStore &store = c->var_store; auto *cache = c->var_store_cache; /* pixel -> fractional pixel */ diff --git a/thirdparty/harfbuzz/src/OT/glyf/CompositeGlyph.hh b/thirdparty/harfbuzz/src/OT/glyf/CompositeGlyph.hh index 60858a5a58..5c0ecd5133 100644 --- a/thirdparty/harfbuzz/src/OT/glyf/CompositeGlyph.hh +++ b/thirdparty/harfbuzz/src/OT/glyf/CompositeGlyph.hh @@ -240,7 +240,8 @@ struct CompositeGlyphRecord } if (is_anchored ()) tx = ty = 0; - trans.init ((float) tx, (float) ty); + /* set is_end_point flag to true, used by IUP delta optimization */ + trans.init ((float) tx, (float) ty, true); { const F2DOT14 *points = (const F2DOT14 *) p; diff --git a/thirdparty/harfbuzz/src/OT/glyf/Glyph.hh b/thirdparty/harfbuzz/src/OT/glyf/Glyph.hh index 5ea611948f..69a0b625c7 100644 --- a/thirdparty/harfbuzz/src/OT/glyf/Glyph.hh +++ b/thirdparty/harfbuzz/src/OT/glyf/Glyph.hh @@ -103,6 +103,9 @@ struct Glyph } } + bool is_composite () const + { return type == COMPOSITE; } + bool get_all_points_without_var (const hb_face_t *face, contour_point_vector_t &points /* OUT */) const { diff --git a/thirdparty/harfbuzz/src/OT/glyf/glyf-helpers.hh b/thirdparty/harfbuzz/src/OT/glyf/glyf-helpers.hh index d0a5a132f0..f157bf0020 100644 --- a/thirdparty/harfbuzz/src/OT/glyf/glyf-helpers.hh +++ b/thirdparty/harfbuzz/src/OT/glyf/glyf-helpers.hh @@ -38,7 +38,7 @@ _write_loca (IteratorIn&& it, unsigned padded_size = *it++; offset += padded_size; - DEBUG_MSG (SUBSET, nullptr, "loca entry gid %u offset %u padded-size %u", gid, offset, padded_size); + DEBUG_MSG (SUBSET, nullptr, "loca entry gid %" PRIu32 " offset %u padded-size %u", gid, offset, padded_size); value = offset >> right_shift; *dest++ = value; diff --git a/thirdparty/harfbuzz/src/graph/classdef-graph.hh b/thirdparty/harfbuzz/src/graph/classdef-graph.hh index 9cf845a82d..da6378820b 100644 --- a/thirdparty/harfbuzz/src/graph/classdef-graph.hh +++ b/thirdparty/harfbuzz/src/graph/classdef-graph.hh @@ -134,20 +134,23 @@ struct ClassDef : public OT::ClassDef struct class_def_size_estimator_t { + // TODO(garretrieger): update to support beyond64k coverage/classdef tables. + constexpr static unsigned class_def_format1_base_size = 6; + constexpr static unsigned class_def_format2_base_size = 4; + constexpr static unsigned coverage_base_size = 4; + constexpr static unsigned bytes_per_range = 6; + constexpr static unsigned bytes_per_glyph = 2; + template<typename It> class_def_size_estimator_t (It glyph_and_class) - : gids_consecutive (true), num_ranges_per_class (), glyphs_per_class () + : num_ranges_per_class (), glyphs_per_class () { - unsigned last_gid = (unsigned) -1; + reset(); for (auto p : + glyph_and_class) { unsigned gid = p.first; unsigned klass = p.second; - if (last_gid != (unsigned) -1 && gid != last_gid + 1) - gids_consecutive = false; - last_gid = gid; - hb_set_t* glyphs; if (glyphs_per_class.has (klass, &glyphs) && glyphs) { glyphs->add (gid); @@ -177,28 +180,54 @@ struct class_def_size_estimator_t } } - // Incremental increase in the Coverage and ClassDef table size - // (worst case) if all glyphs associated with 'klass' were added. - unsigned incremental_coverage_size (unsigned klass) const + void reset() { + class_def_1_size = class_def_format1_base_size; + class_def_2_size = class_def_format2_base_size; + included_glyphs.clear(); + included_classes.clear(); + } + + // Compute the size of coverage for all glyphs added via 'add_class_def_size'. + unsigned coverage_size () const { - // Coverage takes 2 bytes per glyph worst case, - return 2 * glyphs_per_class.get (klass).get_population (); + unsigned format1_size = coverage_base_size + bytes_per_glyph * included_glyphs.get_population(); + unsigned format2_size = coverage_base_size + bytes_per_range * num_glyph_ranges(); + return hb_min(format1_size, format2_size); } - // Incremental increase in the Coverage and ClassDef table size - // (worst case) if all glyphs associated with 'klass' were added. - unsigned incremental_class_def_size (unsigned klass) const + // Compute the new size of the ClassDef table if all glyphs associated with 'klass' were added. + unsigned add_class_def_size (unsigned klass) { - // ClassDef takes 6 bytes per range - unsigned class_def_2_size = 6 * num_ranges_per_class.get (klass); - if (gids_consecutive) - { - // ClassDef1 takes 2 bytes per glyph, but only can be used - // when gids are consecutive. - return hb_min (2 * glyphs_per_class.get (klass).get_population (), class_def_2_size); + if (!included_classes.has(klass)) { + hb_set_t* glyphs = nullptr; + if (glyphs_per_class.has(klass, &glyphs)) { + included_glyphs.union_(*glyphs); + } + + class_def_1_size = class_def_format1_base_size; + if (!included_glyphs.is_empty()) { + unsigned min_glyph = included_glyphs.get_min(); + unsigned max_glyph = included_glyphs.get_max(); + class_def_1_size += bytes_per_glyph * (max_glyph - min_glyph + 1); + } + + class_def_2_size += bytes_per_range * num_ranges_per_class.get (klass); + + included_classes.add(klass); } - return class_def_2_size; + return hb_min (class_def_1_size, class_def_2_size); + } + + unsigned num_glyph_ranges() const { + hb_codepoint_t start = HB_SET_VALUE_INVALID; + hb_codepoint_t end = HB_SET_VALUE_INVALID; + + unsigned count = 0; + while (included_glyphs.next_range (&start, &end)) { + count++; + } + return count; } bool in_error () @@ -214,9 +243,12 @@ struct class_def_size_estimator_t } private: - bool gids_consecutive; hb_hashmap_t<unsigned, unsigned> num_ranges_per_class; hb_hashmap_t<unsigned, hb_set_t> glyphs_per_class; + hb_set_t included_classes; + hb_set_t included_glyphs; + unsigned class_def_1_size; + unsigned class_def_2_size; }; diff --git a/thirdparty/harfbuzz/src/graph/graph.hh b/thirdparty/harfbuzz/src/graph/graph.hh index 26ad00bdd9..2a9d8346c0 100644 --- a/thirdparty/harfbuzz/src/graph/graph.hh +++ b/thirdparty/harfbuzz/src/graph/graph.hh @@ -195,6 +195,15 @@ struct graph_t return incoming_edges_; } + unsigned incoming_edges_from_parent (unsigned parent_index) const { + if (single_parent != (unsigned) -1) { + return single_parent == parent_index ? 1 : 0; + } + + unsigned* count; + return parents.has(parent_index, &count) ? *count : 0; + } + void reset_parents () { incoming_edges_ = 0; @@ -334,6 +343,16 @@ struct graph_t return true; } + bool give_max_priority () + { + bool result = false; + while (!has_max_priority()) { + result = true; + priority++; + } + return result; + } + bool has_max_priority () const { return priority >= 3; } @@ -1023,6 +1042,11 @@ struct graph_t * Creates a copy of child and re-assigns the link from * parent to the clone. The copy is a shallow copy, objects * linked from child are not duplicated. + * + * Returns the index of the newly created duplicate. + * + * If the child_idx only has incoming edges from parent_idx, this + * will do nothing and return the original child_idx. */ unsigned duplicate_if_shared (unsigned parent_idx, unsigned child_idx) { @@ -1036,18 +1060,20 @@ struct graph_t * Creates a copy of child and re-assigns the link from * parent to the clone. The copy is a shallow copy, objects * linked from child are not duplicated. + * + * Returns the index of the newly created duplicate. + * + * If the child_idx only has incoming edges from parent_idx, + * duplication isn't possible and this will return -1. */ unsigned duplicate (unsigned parent_idx, unsigned child_idx) { update_parents (); - unsigned links_to_child = 0; - for (const auto& l : vertices_[parent_idx].obj.all_links ()) - { - if (l.objidx == child_idx) links_to_child++; - } + const auto& child = vertices_[child_idx]; + unsigned links_to_child = child.incoming_edges_from_parent(parent_idx); - if (vertices_[child_idx].incoming_edges () <= links_to_child) + if (child.incoming_edges () <= links_to_child) { // Can't duplicate this node, doing so would orphan the original one as all remaining links // to child are from parent. @@ -1060,7 +1086,7 @@ struct graph_t parent_idx, child_idx); unsigned clone_idx = duplicate (child_idx); - if (clone_idx == (unsigned) -1) return false; + if (clone_idx == (unsigned) -1) return -1; // duplicate shifts the root node idx, so if parent_idx was root update it. if (parent_idx == clone_idx) parent_idx++; @@ -1076,6 +1102,62 @@ struct graph_t return clone_idx; } + /* + * Creates a copy of child and re-assigns the links from + * parents to the clone. The copy is a shallow copy, objects + * linked from child are not duplicated. + * + * Returns the index of the newly created duplicate. + * + * If the child_idx only has incoming edges from parents, + * duplication isn't possible or duplication fails and this will + * return -1. + */ + unsigned duplicate (const hb_set_t* parents, unsigned child_idx) + { + if (parents->is_empty()) { + return -1; + } + + update_parents (); + + const auto& child = vertices_[child_idx]; + unsigned links_to_child = 0; + unsigned last_parent = parents->get_max(); + unsigned first_parent = parents->get_min(); + for (unsigned parent_idx : *parents) { + links_to_child += child.incoming_edges_from_parent(parent_idx); + } + + if (child.incoming_edges () <= links_to_child) + { + // Can't duplicate this node, doing so would orphan the original one as all remaining links + // to child are from parent. + DEBUG_MSG (SUBSET_REPACK, nullptr, " Not duplicating %u, ..., %u => %u", first_parent, last_parent, child_idx); + return -1; + } + + DEBUG_MSG (SUBSET_REPACK, nullptr, " Duplicating %u, ..., %u => %u", first_parent, last_parent, child_idx); + + unsigned clone_idx = duplicate (child_idx); + if (clone_idx == (unsigned) -1) return false; + + for (unsigned parent_idx : *parents) { + // duplicate shifts the root node idx, so if parent_idx was root update it. + if (parent_idx == clone_idx) parent_idx++; + auto& parent = vertices_[parent_idx]; + for (auto& l : parent.obj.all_links_writer ()) + { + if (l.objidx != child_idx) + continue; + + reassign_link (l, parent_idx, clone_idx); + } + } + + return clone_idx; + } + /* * Adds a new node to the graph, not connected to anything. diff --git a/thirdparty/harfbuzz/src/graph/pairpos-graph.hh b/thirdparty/harfbuzz/src/graph/pairpos-graph.hh index f7f74b18c9..fd46861de4 100644 --- a/thirdparty/harfbuzz/src/graph/pairpos-graph.hh +++ b/thirdparty/harfbuzz/src/graph/pairpos-graph.hh @@ -247,8 +247,8 @@ struct PairPosFormat2 : public OT::Layout::GPOS_impl::PairPosFormat2_4<SmallType for (unsigned i = 0; i < class1_count; i++) { unsigned accumulated_delta = class1_record_size; - coverage_size += estimator.incremental_coverage_size (i); - class_def_1_size += estimator.incremental_class_def_size (i); + class_def_1_size = estimator.add_class_def_size (i); + coverage_size = estimator.coverage_size (); max_coverage_size = hb_max (max_coverage_size, coverage_size); max_class_def_1_size = hb_max (max_class_def_1_size, class_def_1_size); @@ -280,8 +280,10 @@ struct PairPosFormat2 : public OT::Layout::GPOS_impl::PairPosFormat2_4<SmallType split_points.push (i); // split does not include i, so add the size for i when we reset the size counters. accumulated = base_size + accumulated_delta; - coverage_size = 4 + estimator.incremental_coverage_size (i); - class_def_1_size = 4 + estimator.incremental_class_def_size (i); + + estimator.reset(); + class_def_1_size = estimator.add_class_def_size(i); + coverage_size = estimator.coverage_size(); visited.clear (); // node sharing isn't allowed between splits. } } diff --git a/thirdparty/harfbuzz/src/hb-aat-layout-morx-table.hh b/thirdparty/harfbuzz/src/hb-aat-layout-morx-table.hh index 06c9334b37..8436551324 100644 --- a/thirdparty/harfbuzz/src/hb-aat-layout-morx-table.hh +++ b/thirdparty/harfbuzz/src/hb-aat-layout-morx-table.hh @@ -552,6 +552,7 @@ struct LigatureSubtable { DEBUG_MSG (APPLY, nullptr, "Skipping ligature component"); if (unlikely (!buffer->move_to (match_positions[--match_length % ARRAY_LENGTH (match_positions)]))) return; + buffer->cur().unicode_props() |= UPROPS_MASK_IGNORABLE; if (unlikely (!buffer->replace_glyph (DELETED_GLYPH))) return; } diff --git a/thirdparty/harfbuzz/src/hb-algs.hh b/thirdparty/harfbuzz/src/hb-algs.hh index ea97057165..efa6074a42 100644 --- a/thirdparty/harfbuzz/src/hb-algs.hh +++ b/thirdparty/harfbuzz/src/hb-algs.hh @@ -671,7 +671,7 @@ struct hb_pair_t return 0; } - friend void swap (hb_pair_t& a, hb_pair_t& b) + friend void swap (hb_pair_t& a, hb_pair_t& b) noexcept { hb_swap (a.first, b.first); hb_swap (a.second, b.second); @@ -1053,6 +1053,18 @@ _hb_cmp_method (const void *pkey, const void *pval, Ts... ds) return val.cmp (key, ds...); } +template <typename K, typename V> +static int +_hb_cmp_operator (const void *pkey, const void *pval) +{ + const K& key = * (const K*) pkey; + const V& val = * (const V*) pval; + + if (key < val) return -1; + if (key > val) return 1; + return 0; +} + template <typename V, typename K, typename ...Ts> static inline bool hb_bsearch_impl (unsigned *pos, /* Out */ diff --git a/thirdparty/harfbuzz/src/hb-bit-set-invertible.hh b/thirdparty/harfbuzz/src/hb-bit-set-invertible.hh index 2626251807..d5d1326d9f 100644 --- a/thirdparty/harfbuzz/src/hb-bit-set-invertible.hh +++ b/thirdparty/harfbuzz/src/hb-bit-set-invertible.hh @@ -39,10 +39,10 @@ struct hb_bit_set_invertible_t hb_bit_set_invertible_t () = default; hb_bit_set_invertible_t (const hb_bit_set_invertible_t& o) = default; - hb_bit_set_invertible_t (hb_bit_set_invertible_t&& other) : hb_bit_set_invertible_t () { hb_swap (*this, other); } + hb_bit_set_invertible_t (hb_bit_set_invertible_t&& other) noexcept : hb_bit_set_invertible_t () { hb_swap (*this, other); } hb_bit_set_invertible_t& operator= (const hb_bit_set_invertible_t& o) = default; - hb_bit_set_invertible_t& operator= (hb_bit_set_invertible_t&& other) { hb_swap (*this, other); return *this; } - friend void swap (hb_bit_set_invertible_t &a, hb_bit_set_invertible_t &b) + hb_bit_set_invertible_t& operator= (hb_bit_set_invertible_t&& other) noexcept { hb_swap (*this, other); return *this; } + friend void swap (hb_bit_set_invertible_t &a, hb_bit_set_invertible_t &b) noexcept { if (likely (!a.s.successful || !b.s.successful)) return; diff --git a/thirdparty/harfbuzz/src/hb-bit-set.hh b/thirdparty/harfbuzz/src/hb-bit-set.hh index 1dbcce5cbd..5f4c6f0afe 100644 --- a/thirdparty/harfbuzz/src/hb-bit-set.hh +++ b/thirdparty/harfbuzz/src/hb-bit-set.hh @@ -38,10 +38,10 @@ struct hb_bit_set_t ~hb_bit_set_t () = default; hb_bit_set_t (const hb_bit_set_t& other) : hb_bit_set_t () { set (other, true); } - hb_bit_set_t ( hb_bit_set_t&& other) : hb_bit_set_t () { hb_swap (*this, other); } + hb_bit_set_t ( hb_bit_set_t&& other) noexcept : hb_bit_set_t () { hb_swap (*this, other); } hb_bit_set_t& operator= (const hb_bit_set_t& other) { set (other); return *this; } - hb_bit_set_t& operator= (hb_bit_set_t&& other) { hb_swap (*this, other); return *this; } - friend void swap (hb_bit_set_t &a, hb_bit_set_t &b) + hb_bit_set_t& operator= (hb_bit_set_t&& other) noexcept { hb_swap (*this, other); return *this; } + friend void swap (hb_bit_set_t &a, hb_bit_set_t &b) noexcept { if (likely (!a.successful || !b.successful)) return; diff --git a/thirdparty/harfbuzz/src/hb-blob.cc b/thirdparty/harfbuzz/src/hb-blob.cc index 265effba03..873d9b257a 100644 --- a/thirdparty/harfbuzz/src/hb-blob.cc +++ b/thirdparty/harfbuzz/src/hb-blob.cc @@ -598,6 +598,11 @@ _open_resource_fork (const char *file_name, hb_mapped_file_t *file) * Creates a new blob containing the data from the * specified binary font file. * + * The filename is passed directly to the system on all platforms, + * except on Windows, where the filename is interpreted as UTF-8. + * Only if the filename is not valid UTF-8, it will be interpreted + * according to the system codepage. + * * Returns: An #hb_blob_t pointer with the content of the file, * or hb_blob_get_empty() if failed. * @@ -617,6 +622,11 @@ hb_blob_create_from_file (const char *file_name) * Creates a new blob containing the data from the * specified binary font file. * + * The filename is passed directly to the system on all platforms, + * except on Windows, where the filename is interpreted as UTF-8. + * Only if the filename is not valid UTF-8, it will be interpreted + * according to the system codepage. + * * Returns: An #hb_blob_t pointer with the content of the file, * or `NULL` if failed. * @@ -672,10 +682,19 @@ fail_without_close: if (unlikely (!file)) return nullptr; HANDLE fd; + int conversion; unsigned int size = strlen (file_name) + 1; wchar_t * wchar_file_name = (wchar_t *) hb_malloc (sizeof (wchar_t) * size); if (unlikely (!wchar_file_name)) goto fail_without_close; - mbstowcs (wchar_file_name, file_name, size); + + /* Assume file name is given in UTF-8 encoding */ + conversion = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, file_name, -1, wchar_file_name, size); + if (conversion <= 0) + { + /* Conversion failed due to invalid UTF-8 characters, + Repeat conversion based on system code page */ + mbstowcs(wchar_file_name, file_name, size); + } #if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) { CREATEFILE2_EXTENDED_PARAMETERS ceparams = { 0 }; diff --git a/thirdparty/harfbuzz/src/hb-buffer-verify.cc b/thirdparty/harfbuzz/src/hb-buffer-verify.cc index 15a53919de..671d6eda8c 100644 --- a/thirdparty/harfbuzz/src/hb-buffer-verify.cc +++ b/thirdparty/harfbuzz/src/hb-buffer-verify.cc @@ -149,7 +149,7 @@ buffer_verify_unsafe_to_break (hb_buffer_t *buffer, } assert (text_start < text_end); - if (0) + if (false) printf("start %u end %u text start %u end %u\n", start, end, text_start, text_end); hb_buffer_clear_contents (fragment); @@ -288,7 +288,7 @@ buffer_verify_unsafe_to_concat (hb_buffer_t *buffer, } assert (text_start < text_end); - if (0) + if (false) printf("start %u end %u text start %u end %u\n", start, end, text_start, text_end); #if 0 diff --git a/thirdparty/harfbuzz/src/hb-buffer.cc b/thirdparty/harfbuzz/src/hb-buffer.cc index 934c6c2129..d621a7cc55 100644 --- a/thirdparty/harfbuzz/src/hb-buffer.cc +++ b/thirdparty/harfbuzz/src/hb-buffer.cc @@ -309,6 +309,7 @@ hb_buffer_t::clear () deallocate_var_all (); serial = 0; + random_state = 1; scratch_flags = HB_BUFFER_SCRATCH_FLAG_DEFAULT; } @@ -1359,6 +1360,49 @@ hb_buffer_get_not_found_glyph (const hb_buffer_t *buffer) return buffer->not_found; } +/** + * hb_buffer_set_random_state: + * @buffer: An #hb_buffer_t + * @state: the new random state + * + * Sets the random state of the buffer. The state changes + * every time a glyph uses randomness (eg. the `rand` + * OpenType feature). This function together with + * hb_buffer_get_random_state() allow for transferring + * the current random state to a subsequent buffer, to + * get better randomness distribution. + * + * Defaults to 1 and when buffer contents are cleared. + * A value of 0 disables randomness during shaping. + * + * Since: 8.4.0 + **/ +void +hb_buffer_set_random_state (hb_buffer_t *buffer, + unsigned state) +{ + if (unlikely (hb_object_is_immutable (buffer))) + return; + + buffer->random_state = state; +} + +/** + * hb_buffer_get_random_state: + * @buffer: An #hb_buffer_t + * + * See hb_buffer_set_random_state(). + * + * Return value: + * The @buffer random state + * + * Since: 8.4.0 + **/ +unsigned +hb_buffer_get_random_state (const hb_buffer_t *buffer) +{ + return buffer->random_state; +} /** * hb_buffer_clear_contents: diff --git a/thirdparty/harfbuzz/src/hb-buffer.h b/thirdparty/harfbuzz/src/hb-buffer.h index 3573127ff0..f75fe96b21 100644 --- a/thirdparty/harfbuzz/src/hb-buffer.h +++ b/thirdparty/harfbuzz/src/hb-buffer.h @@ -487,6 +487,12 @@ hb_buffer_set_not_found_glyph (hb_buffer_t *buffer, HB_EXTERN hb_codepoint_t hb_buffer_get_not_found_glyph (const hb_buffer_t *buffer); +HB_EXTERN void +hb_buffer_set_random_state (hb_buffer_t *buffer, + unsigned state); + +HB_EXTERN unsigned +hb_buffer_get_random_state (const hb_buffer_t *buffer); /* * Content API. diff --git a/thirdparty/harfbuzz/src/hb-buffer.hh b/thirdparty/harfbuzz/src/hb-buffer.hh index f04ad58f11..0a198722d6 100644 --- a/thirdparty/harfbuzz/src/hb-buffer.hh +++ b/thirdparty/harfbuzz/src/hb-buffer.hh @@ -116,6 +116,7 @@ struct hb_buffer_t uint8_t allocated_var_bits; uint8_t serial; + uint32_t random_state; hb_buffer_scratch_flags_t scratch_flags; /* Have space-fallback, etc. */ unsigned int max_len; /* Maximum allowed len. */ int max_ops; /* Maximum allowed operations. */ diff --git a/thirdparty/harfbuzz/src/hb-cff-interp-dict-common.hh b/thirdparty/harfbuzz/src/hb-cff-interp-dict-common.hh index 53226b227e..a08b10b5ff 100644 --- a/thirdparty/harfbuzz/src/hb-cff-interp-dict-common.hh +++ b/thirdparty/harfbuzz/src/hb-cff-interp-dict-common.hh @@ -54,8 +54,8 @@ struct top_dict_values_t : dict_values_t<OPSTR> } void fini () { dict_values_t<OPSTR>::fini (); } - unsigned int charStringsOffset; - unsigned int FDArrayOffset; + int charStringsOffset; + int FDArrayOffset; }; struct dict_opset_t : opset_t<number_t> @@ -157,11 +157,11 @@ struct top_dict_opset_t : dict_opset_t { switch (op) { case OpCode_CharStrings: - dictval.charStringsOffset = env.argStack.pop_uint (); + dictval.charStringsOffset = env.argStack.pop_int (); env.clear_args (); break; case OpCode_FDArray: - dictval.FDArrayOffset = env.argStack.pop_uint (); + dictval.FDArrayOffset = env.argStack.pop_int (); env.clear_args (); break; case OpCode_FontMatrix: diff --git a/thirdparty/harfbuzz/src/hb-cff2-interp-cs.hh b/thirdparty/harfbuzz/src/hb-cff2-interp-cs.hh index 915b10cf39..55b1d3bf8d 100644 --- a/thirdparty/harfbuzz/src/hb-cff2-interp-cs.hh +++ b/thirdparty/harfbuzz/src/hb-cff2-interp-cs.hh @@ -168,7 +168,7 @@ struct cff2_cs_interp_env_t : cs_interp_env_t<ELEM, CFF2Subrs> protected: const int *coords; unsigned int num_coords; - const CFF2VariationStore *varStore; + const CFF2ItemVariationStore *varStore; unsigned int region_count; unsigned int ivs; hb_vector_t<float> scalars; diff --git a/thirdparty/harfbuzz/src/hb-common.cc b/thirdparty/harfbuzz/src/hb-common.cc index 0c13c7d171..4b8bae4422 100644 --- a/thirdparty/harfbuzz/src/hb-common.cc +++ b/thirdparty/harfbuzz/src/hb-common.cc @@ -996,7 +996,7 @@ hb_feature_to_string (hb_feature_t *feature, if (feature->value > 1) { s[len++] = '='; - len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->value)); + len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%" PRIu32, feature->value)); } assert (len < ARRAY_LENGTH (s)); len = hb_min (len, size - 1); diff --git a/thirdparty/harfbuzz/src/hb-common.h b/thirdparty/harfbuzz/src/hb-common.h index a9fe666b39..533de91562 100644 --- a/thirdparty/harfbuzz/src/hb-common.h +++ b/thirdparty/harfbuzz/src/hb-common.h @@ -47,14 +47,10 @@ # endif /* !__cplusplus */ #endif -#if defined (_SVR4) || defined (SVR4) || defined (__OpenBSD__) || \ - defined (_sgi) || defined (__sun) || defined (sun) || \ - defined (__digital__) || defined (__HP_cc) -# include <inttypes.h> -#elif defined (_AIX) +#if defined (_AIX) # include <sys/inttypes.h> #elif defined (_MSC_VER) && _MSC_VER < 1600 -/* VS 2010 (_MSC_VER 1600) has stdint.h */ +/* VS 2010 (_MSC_VER 1600) has stdint.h */ typedef __int8 int8_t; typedef unsigned __int8 uint8_t; typedef __int16 int16_t; @@ -63,10 +59,11 @@ typedef __int32 int32_t; typedef unsigned __int32 uint32_t; typedef __int64 int64_t; typedef unsigned __int64 uint64_t; -#elif defined (__KERNEL__) -# include <linux/types.h> -#else +#elif defined (_MSC_VER) && _MSC_VER < 1800 +/* VS 2013 (_MSC_VER 1800) has inttypes.h */ # include <stdint.h> +#else +# include <inttypes.h> #endif #if defined(__GNUC__) && ((__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) diff --git a/thirdparty/harfbuzz/src/hb-cplusplus.hh b/thirdparty/harfbuzz/src/hb-cplusplus.hh index 531ef1b7c8..a640e192de 100644 --- a/thirdparty/harfbuzz/src/hb-cplusplus.hh +++ b/thirdparty/harfbuzz/src/hb-cplusplus.hh @@ -56,15 +56,15 @@ struct shared_ptr explicit shared_ptr (T *p = nullptr) : p (p) {} shared_ptr (const shared_ptr &o) : p (v::reference (o.p)) {} - shared_ptr (shared_ptr &&o) : p (o.p) { o.p = nullptr; } + shared_ptr (shared_ptr &&o) noexcept : p (o.p) { o.p = nullptr; } shared_ptr& operator = (const shared_ptr &o) { if (p != o.p) { destroy (); p = o.p; reference (); } return *this; } - shared_ptr& operator = (shared_ptr &&o) { v::destroy (p); p = o.p; o.p = nullptr; return *this; } + shared_ptr& operator = (shared_ptr &&o) noexcept { v::destroy (p); p = o.p; o.p = nullptr; return *this; } ~shared_ptr () { v::destroy (p); p = nullptr; } T* get() const { return p; } - void swap (shared_ptr &o) { std::swap (p, o.p); } - friend void swap (shared_ptr &a, shared_ptr &b) { std::swap (a.p, b.p); } + void swap (shared_ptr &o) noexcept { std::swap (p, o.p); } + friend void swap (shared_ptr &a, shared_ptr &b) noexcept { std::swap (a.p, b.p); } operator T * () const { return p; } T& operator * () const { return *get (); } @@ -98,16 +98,16 @@ struct unique_ptr explicit unique_ptr (T *p = nullptr) : p (p) {} unique_ptr (const unique_ptr &o) = delete; - unique_ptr (unique_ptr &&o) : p (o.p) { o.p = nullptr; } + unique_ptr (unique_ptr &&o) noexcept : p (o.p) { o.p = nullptr; } unique_ptr& operator = (const unique_ptr &o) = delete; - unique_ptr& operator = (unique_ptr &&o) { v::destroy (p); p = o.p; o.p = nullptr; return *this; } + unique_ptr& operator = (unique_ptr &&o) noexcept { v::destroy (p); p = o.p; o.p = nullptr; return *this; } ~unique_ptr () { v::destroy (p); p = nullptr; } T* get() const { return p; } T* release () { T* v = p; p = nullptr; return v; } - void swap (unique_ptr &o) { std::swap (p, o.p); } - friend void swap (unique_ptr &a, unique_ptr &b) { std::swap (a.p, b.p); } + void swap (unique_ptr &o) noexcept { std::swap (p, o.p); } + friend void swap (unique_ptr &a, unique_ptr &b) noexcept { std::swap (a.p, b.p); } operator T * () const { return p; } T& operator * () const { return *get (); } diff --git a/thirdparty/harfbuzz/src/hb-directwrite.cc b/thirdparty/harfbuzz/src/hb-directwrite.cc index 42764a244b..6c90265d0b 100644 --- a/thirdparty/harfbuzz/src/hb-directwrite.cc +++ b/thirdparty/harfbuzz/src/hb-directwrite.cc @@ -173,7 +173,7 @@ _hb_directwrite_shaper_face_data_create (hb_face_t *face) t_DWriteCreateFactory p_DWriteCreateFactory; -#if defined(__GNUC__) +#if defined(__GNUC__) || defined(__clang__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wcast-function-type" #endif @@ -181,7 +181,7 @@ _hb_directwrite_shaper_face_data_create (hb_face_t *face) p_DWriteCreateFactory = (t_DWriteCreateFactory) GetProcAddress (data->dwrite_dll, "DWriteCreateFactory"); -#if defined(__GNUC__) +#if defined(__GNUC__) || defined(__clang__) #pragma GCC diagnostic pop #endif diff --git a/thirdparty/harfbuzz/src/hb-features.h b/thirdparty/harfbuzz/src/hb-features.h new file mode 100644 index 0000000000..9199864195 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-features.h @@ -0,0 +1,119 @@ +/* + * Copyright © 2022 Red Hat, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#ifndef HB_FEATURES_H +#define HB_FEATURES_H + +HB_BEGIN_DECLS + +/** + * SECTION: hb-features + * @title: hb-features + * @short_description: Feature detection + * @include: hb-features.h + * + * Macros for detecting optional HarfBuzz features at build time. + **/ + +/** + * HB_HAS_CAIRO: + * + * Defined if Harfbuzz has been built with cairo support. + */ +# + +/** + * HB_HAS_CORETEXT: + * + * Defined if Harfbuzz has been built with CoreText support. + */ +#undef HB_HAS_CORETEXT + +/** + * HB_HAS_DIRECTWRITE: + * + * Defined if Harfbuzz has been built with DirectWrite support. + */ +#undef HB_HAS_DIRECTWRITE + +/** + * HB_HAS_FREETYPE: + * + * Defined if Harfbuzz has been built with Freetype support. + */ +#define HB_HAS_FREETYPE 1 + +/** + * HB_HAS_GDI: + * + * Defined if Harfbuzz has been built with GDI support. + */ +#undef HB_HAS_GDI + +/** + * HB_HAS_GLIB: + * + * Defined if Harfbuzz has been built with GLib support. + */ +#define HB_HAS_GLIB 1 + +/** + * HB_HAS_GOBJECT: + * + * Defined if Harfbuzz has been built with GObject support. + */ +#undef HB_HAS_GOBJECT + +/** + * HB_HAS_GRAPHITE: + * + * Defined if Harfbuzz has been built with Graphite support. + */ +#undef HB_HAS_GRAPHITE + +/** + * HB_HAS_ICU: + * + * Defined if Harfbuzz has been built with ICU support. + */ +#undef HB_HAS_ICU + +/** + * HB_HAS_UNISCRIBE: + * + * Defined if Harfbuzz has been built with Uniscribe support. + */ +#undef HB_HAS_UNISCRIBE + +/** + * HB_HAS_WASM: + * + * Defined if Harfbuzz has been built with WebAssembly support. + */ +#undef HB_HAS_WASM + + +HB_END_DECLS + +#endif /* HB_FEATURES_H */ diff --git a/thirdparty/harfbuzz/src/hb-font.hh b/thirdparty/harfbuzz/src/hb-font.hh index f503575c34..4c8190b0dd 100644 --- a/thirdparty/harfbuzz/src/hb-font.hh +++ b/thirdparty/harfbuzz/src/hb-font.hh @@ -651,7 +651,7 @@ struct hb_font_t { if (get_glyph_name (glyph, s, size)) return; - if (size && snprintf (s, size, "gid%u", glyph) < 0) + if (size && snprintf (s, size, "gid%" PRIu32, glyph) < 0) *s = '\0'; } diff --git a/thirdparty/harfbuzz/src/hb-ft.cc b/thirdparty/harfbuzz/src/hb-ft.cc index 955a9081e0..3de4a6d5d4 100644 --- a/thirdparty/harfbuzz/src/hb-ft.cc +++ b/thirdparty/harfbuzz/src/hb-ft.cc @@ -224,7 +224,7 @@ _hb_ft_hb_font_check_changed (hb_font_t *font, * * Sets the FT_Load_Glyph load flags for the specified #hb_font_t. * - * For more information, see + * For more information, see * <https://freetype.org/freetype2/docs/reference/ft2-glyph_retrieval.html#ft_load_xxx> * * This function works with #hb_font_t objects created by @@ -252,7 +252,7 @@ hb_ft_font_set_load_flags (hb_font_t *font, int load_flags) * * Fetches the FT_Load_Glyph load flags of the specified #hb_font_t. * - * For more information, see + * For more information, see * <https://freetype.org/freetype2/docs/reference/ft2-glyph_retrieval.html#ft_load_xxx> * * This function works with #hb_font_t objects created by @@ -1118,10 +1118,10 @@ _hb_ft_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data * This variant of the function does not provide any life-cycle management. * * Most client programs should use hb_ft_face_create_referenced() - * (or, perhaps, hb_ft_face_create_cached()) instead. + * (or, perhaps, hb_ft_face_create_cached()) instead. * * If you know you have valid reasons not to use hb_ft_face_create_referenced(), - * then it is the client program's responsibility to destroy @ft_face + * then it is the client program's responsibility to destroy @ft_face * after the #hb_face_t face object has been destroyed. * * Return value: (transfer full): the new #hb_face_t face object @@ -1215,7 +1215,7 @@ hb_ft_face_finalize (void *arg) hb_face_t * hb_ft_face_create_cached (FT_Face ft_face) { - if (unlikely (!ft_face->generic.data || ft_face->generic.finalizer != (FT_Generic_Finalizer) hb_ft_face_finalize)) + if (unlikely (!ft_face->generic.data || ft_face->generic.finalizer != hb_ft_face_finalize)) { if (ft_face->generic.finalizer) ft_face->generic.finalizer (ft_face); @@ -1241,13 +1241,13 @@ hb_ft_face_create_cached (FT_Face ft_face) * This variant of the function does not provide any life-cycle management. * * Most client programs should use hb_ft_font_create_referenced() - * instead. + * instead. * * If you know you have valid reasons not to use hb_ft_font_create_referenced(), - * then it is the client program's responsibility to destroy @ft_face + * then it is the client program's responsibility to destroy @ft_face * after the #hb_font_t font object has been destroyed. * - * HarfBuzz will use the @destroy callback on the #hb_font_t font object + * HarfBuzz will use the @destroy callback on the #hb_font_t font object * if it is supplied when you use this function. However, even if @destroy * is provided, it is the client program's responsibility to destroy @ft_face, * and it is the client program's responsibility to ensure that @ft_face is diff --git a/thirdparty/harfbuzz/src/hb-icu.cc b/thirdparty/harfbuzz/src/hb-icu.cc index e46401f7a6..3707ec30f8 100644 --- a/thirdparty/harfbuzz/src/hb-icu.cc +++ b/thirdparty/harfbuzz/src/hb-icu.cc @@ -93,15 +93,16 @@ hb_icu_script_to_script (UScriptCode script) UScriptCode hb_icu_script_from_script (hb_script_t script) { + UScriptCode out = USCRIPT_INVALID_CODE; + if (unlikely (script == HB_SCRIPT_INVALID)) - return USCRIPT_INVALID_CODE; + return out; - unsigned int numScriptCode = 1 + u_getIntPropertyMaxValue (UCHAR_SCRIPT); - for (unsigned int i = 0; i < numScriptCode; i++) - if (unlikely (hb_icu_script_to_script ((UScriptCode) i) == script)) - return (UScriptCode) i; + UErrorCode icu_err = U_ZERO_ERROR; + const unsigned char buf[5] = {HB_UNTAG (script), 0}; + uscript_getCode ((const char *) buf, &out, 1, &icu_err); - return USCRIPT_UNKNOWN; + return out; } diff --git a/thirdparty/harfbuzz/src/hb-limits.hh b/thirdparty/harfbuzz/src/hb-limits.hh index 25c1e71e13..7efc893eae 100644 --- a/thirdparty/harfbuzz/src/hb-limits.hh +++ b/thirdparty/harfbuzz/src/hb-limits.hh @@ -106,7 +106,7 @@ #endif #ifndef HB_COLRV1_MAX_EDGE_COUNT -#define HB_COLRV1_MAX_EDGE_COUNT 65536 +#define HB_COLRV1_MAX_EDGE_COUNT 2048 #endif diff --git a/thirdparty/harfbuzz/src/hb-map.hh b/thirdparty/harfbuzz/src/hb-map.hh index 45a02b830c..6521b1a41d 100644 --- a/thirdparty/harfbuzz/src/hb-map.hh +++ b/thirdparty/harfbuzz/src/hb-map.hh @@ -70,9 +70,9 @@ struct hb_hashmap_t alloc (o.population); hb_copy (o, *this); } - hb_hashmap_t (hb_hashmap_t&& o) : hb_hashmap_t () { hb_swap (*this, o); } + hb_hashmap_t (hb_hashmap_t&& o) noexcept : hb_hashmap_t () { hb_swap (*this, o); } hb_hashmap_t& operator= (const hb_hashmap_t& o) { reset (); alloc (o.population); hb_copy (o, *this); return *this; } - hb_hashmap_t& operator= (hb_hashmap_t&& o) { hb_swap (*this, o); return *this; } + hb_hashmap_t& operator= (hb_hashmap_t&& o) noexcept { hb_swap (*this, o); return *this; } hb_hashmap_t (std::initializer_list<hb_pair_t<K, V>> lst) : hb_hashmap_t () { @@ -137,26 +137,23 @@ struct hb_hashmap_t }; hb_object_header_t header; - unsigned int successful : 1; /* Allocations successful */ - unsigned int population : 31; /* Not including tombstones. */ + bool successful; /* Allocations successful */ + unsigned short max_chain_length; + unsigned int population; /* Not including tombstones. */ unsigned int occupancy; /* Including tombstones. */ unsigned int mask; unsigned int prime; - unsigned int max_chain_length; item_t *items; - friend void swap (hb_hashmap_t& a, hb_hashmap_t& b) + friend void swap (hb_hashmap_t& a, hb_hashmap_t& b) noexcept { if (unlikely (!a.successful || !b.successful)) return; - unsigned tmp = a.population; - a.population = b.population; - b.population = tmp; - //hb_swap (a.population, b.population); + hb_swap (a.max_chain_length, b.max_chain_length); + hb_swap (a.population, b.population); hb_swap (a.occupancy, b.occupancy); hb_swap (a.mask, b.mask); hb_swap (a.prime, b.prime); - hb_swap (a.max_chain_length, b.max_chain_length); hb_swap (a.items, b.items); } void init () @@ -164,10 +161,10 @@ struct hb_hashmap_t hb_object_init (this); successful = true; + max_chain_length = 0; population = occupancy = 0; mask = 0; prime = 0; - max_chain_length = 0; items = nullptr; } void fini () @@ -558,7 +555,7 @@ struct hb_map_t : hb_hashmap_t<hb_codepoint_t, ~hb_map_t () = default; hb_map_t () : hashmap () {} hb_map_t (const hb_map_t &o) : hashmap ((hashmap &) o) {} - hb_map_t (hb_map_t &&o) : hashmap (std::move ((hashmap &) o)) {} + hb_map_t (hb_map_t &&o) noexcept : hashmap (std::move ((hashmap &) o)) {} hb_map_t& operator= (const hb_map_t&) = default; hb_map_t& operator= (hb_map_t&&) = default; hb_map_t (std::initializer_list<hb_codepoint_pair_t> lst) : hashmap (lst) {} diff --git a/thirdparty/harfbuzz/src/hb-object.hh b/thirdparty/harfbuzz/src/hb-object.hh index e2c2c3394c..5cffe1666b 100644 --- a/thirdparty/harfbuzz/src/hb-object.hh +++ b/thirdparty/harfbuzz/src/hb-object.hh @@ -325,7 +325,7 @@ retry: hb_user_data_array_t *user_data = obj->header.user_data.get_acquire (); if (unlikely (!user_data)) { - user_data = (hb_user_data_array_t *) hb_calloc (sizeof (hb_user_data_array_t), 1); + user_data = (hb_user_data_array_t *) hb_calloc (1, sizeof (hb_user_data_array_t)); if (unlikely (!user_data)) return false; user_data->init (); diff --git a/thirdparty/harfbuzz/src/hb-open-type.hh b/thirdparty/harfbuzz/src/hb-open-type.hh index 6967bca3d4..9c11f14344 100644 --- a/thirdparty/harfbuzz/src/hb-open-type.hh +++ b/thirdparty/harfbuzz/src/hb-open-type.hh @@ -985,6 +985,13 @@ struct SortedArrayOf : ArrayOf<Type, LenType> return_trace (ret); } + SortedArrayOf* copy (hb_serialize_context_t *c) const + { + TRACE_SERIALIZE (this); + SortedArrayOf* out = reinterpret_cast<SortedArrayOf *> (ArrayOf<Type, LenType>::copy (c)); + return_trace (out); + } + template <typename T> Type &bsearch (const T &x, Type ¬_found = Crap (Type)) { return *as_array ().bsearch (x, ¬_found); } diff --git a/thirdparty/harfbuzz/src/hb-ot-cff-common.hh b/thirdparty/harfbuzz/src/hb-ot-cff-common.hh index 4fdba197ac..c7c3264c08 100644 --- a/thirdparty/harfbuzz/src/hb-ot-cff-common.hh +++ b/thirdparty/harfbuzz/src/hb-ot-cff-common.hh @@ -41,10 +41,21 @@ using namespace OT; using objidx_t = hb_serialize_context_t::objidx_t; using whence_t = hb_serialize_context_t::whence_t; -/* utility macro */ -template<typename Type> -static inline const Type& StructAtOffsetOrNull (const void *P, unsigned int offset) -{ return offset ? StructAtOffset<Type> (P, offset) : Null (Type); } +/* CFF offsets can technically be negative */ +template<typename Type, typename ...Ts> +static inline const Type& StructAtOffsetOrNull (const void *P, int offset, hb_sanitize_context_t &sc, Ts&&... ds) +{ + if (!offset) return Null (Type); + + const char *p = (const char *) P + offset; + if (!sc.check_point (p)) return Null (Type); + + const Type &obj = *reinterpret_cast<const Type *> (p); + if (!obj.sanitize (&sc, std::forward<Ts> (ds)...)) return Null (Type); + + return obj; +} + struct code_pair_t { diff --git a/thirdparty/harfbuzz/src/hb-ot-cff1-table.hh b/thirdparty/harfbuzz/src/hb-ot-cff1-table.hh index c869e90554..1bbd463841 100644 --- a/thirdparty/harfbuzz/src/hb-ot-cff1-table.hh +++ b/thirdparty/harfbuzz/src/hb-ot-cff1-table.hh @@ -763,9 +763,9 @@ struct cff1_top_dict_values_t : top_dict_values_t<cff1_top_dict_val_t> unsigned int ros_supplement; unsigned int cidCount; - unsigned int EncodingOffset; - unsigned int CharsetOffset; - unsigned int FDSelectOffset; + int EncodingOffset; + int CharsetOffset; + int FDSelectOffset; table_info_t privateDictInfo; }; @@ -821,24 +821,24 @@ struct cff1_top_dict_opset_t : top_dict_opset_t<cff1_top_dict_val_t> break; case OpCode_Encoding: - dictval.EncodingOffset = env.argStack.pop_uint (); + dictval.EncodingOffset = env.argStack.pop_int (); env.clear_args (); if (unlikely (dictval.EncodingOffset == 0)) return; break; case OpCode_charset: - dictval.CharsetOffset = env.argStack.pop_uint (); + dictval.CharsetOffset = env.argStack.pop_int (); env.clear_args (); if (unlikely (dictval.CharsetOffset == 0)) return; break; case OpCode_FDSelect: - dictval.FDSelectOffset = env.argStack.pop_uint (); + dictval.FDSelectOffset = env.argStack.pop_int (); env.clear_args (); break; case OpCode_Private: - dictval.privateDictInfo.offset = env.argStack.pop_uint (); + dictval.privateDictInfo.offset = env.argStack.pop_int (); dictval.privateDictInfo.size = env.argStack.pop_uint (); env.clear_args (); break; @@ -913,7 +913,7 @@ struct cff1_private_dict_values_base_t : dict_values_t<VAL> } void fini () { dict_values_t<VAL>::fini (); } - unsigned int subrsOffset; + int subrsOffset; const CFF1Subrs *localSubrs; }; @@ -948,7 +948,7 @@ struct cff1_private_dict_opset_t : dict_opset_t env.clear_args (); break; case OpCode_Subrs: - dictval.subrsOffset = env.argStack.pop_uint (); + dictval.subrsOffset = env.argStack.pop_int (); env.clear_args (); break; @@ -990,7 +990,7 @@ struct cff1_private_dict_opset_subset_t : dict_opset_t break; case OpCode_Subrs: - dictval.subrsOffset = env.argStack.pop_uint (); + dictval.subrsOffset = env.argStack.pop_int (); env.clear_args (); break; @@ -1090,8 +1090,8 @@ struct cff1 goto fail; hb_barrier (); - topDictIndex = &StructAtOffset<CFF1TopDictIndex> (nameIndex, nameIndex->get_size ()); - if ((topDictIndex == &Null (CFF1TopDictIndex)) || !topDictIndex->sanitize (&sc) || (topDictIndex->count == 0)) + topDictIndex = &StructAtOffsetOrNull<CFF1TopDictIndex> (nameIndex, nameIndex->get_size (), sc); + if (topDictIndex == &Null (CFF1TopDictIndex) || (topDictIndex->count == 0)) goto fail; hb_barrier (); @@ -1108,20 +1108,18 @@ struct cff1 charset = &Null (Charset); else { - charset = &StructAtOffsetOrNull<Charset> (cff, topDict.CharsetOffset); - if (unlikely ((charset == &Null (Charset)) || !charset->sanitize (&sc, &num_charset_entries))) goto fail; - hb_barrier (); + charset = &StructAtOffsetOrNull<Charset> (cff, topDict.CharsetOffset, sc, &num_charset_entries); + if (unlikely (charset == &Null (Charset))) goto fail; } fdCount = 1; if (is_CID ()) { - fdArray = &StructAtOffsetOrNull<CFF1FDArray> (cff, topDict.FDArrayOffset); - fdSelect = &StructAtOffsetOrNull<CFF1FDSelect> (cff, topDict.FDSelectOffset); - if (unlikely ((fdArray == &Null (CFF1FDArray)) || !fdArray->sanitize (&sc) || - (fdSelect == &Null (CFF1FDSelect)) || !fdSelect->sanitize (&sc, fdArray->count))) + fdArray = &StructAtOffsetOrNull<CFF1FDArray> (cff, topDict.FDArrayOffset, sc); + fdSelect = &StructAtOffsetOrNull<CFF1FDSelect> (cff, topDict.FDSelectOffset, sc, fdArray->count); + if (unlikely (fdArray == &Null (CFF1FDArray) || + fdSelect == &Null (CFF1FDSelect))) goto fail; - hb_barrier (); fdCount = fdArray->count; } @@ -1140,27 +1138,19 @@ struct cff1 { if (!is_predef_encoding ()) { - encoding = &StructAtOffsetOrNull<Encoding> (cff, topDict.EncodingOffset); - if (unlikely ((encoding == &Null (Encoding)) || !encoding->sanitize (&sc))) goto fail; - hb_barrier (); + encoding = &StructAtOffsetOrNull<Encoding> (cff, topDict.EncodingOffset, sc); + if (unlikely (encoding == &Null (Encoding))) goto fail; } } - stringIndex = &StructAtOffset<CFF1StringIndex> (topDictIndex, topDictIndex->get_size ()); - if ((stringIndex == &Null (CFF1StringIndex)) || !stringIndex->sanitize (&sc)) + stringIndex = &StructAtOffsetOrNull<CFF1StringIndex> (topDictIndex, topDictIndex->get_size (), sc); + if (stringIndex == &Null (CFF1StringIndex)) goto fail; - hb_barrier (); - globalSubrs = &StructAtOffset<CFF1Subrs> (stringIndex, stringIndex->get_size ()); - if ((globalSubrs != &Null (CFF1Subrs)) && !globalSubrs->sanitize (&sc)) + globalSubrs = &StructAtOffsetOrNull<CFF1Subrs> (stringIndex, stringIndex->get_size (), sc); + charStrings = &StructAtOffsetOrNull<CFF1CharStrings> (cff, topDict.charStringsOffset, sc); + if (charStrings == &Null (CFF1CharStrings)) goto fail; - hb_barrier (); - - charStrings = &StructAtOffsetOrNull<CFF1CharStrings> (cff, topDict.charStringsOffset); - - if ((charStrings == &Null (CFF1CharStrings)) || unlikely (!charStrings->sanitize (&sc))) - goto fail; - hb_barrier (); num_glyphs = charStrings->count; if (num_glyphs != sc.get_num_glyphs ()) @@ -1188,19 +1178,13 @@ struct cff1 font->init (); if (unlikely (!font_interp.interpret (*font))) goto fail; PRIVDICTVAL *priv = &privateDicts[i]; - const hb_ubytes_t privDictStr = StructAtOffset<UnsizedByteStr> (cff, font->privateDictInfo.offset).as_ubytes (font->privateDictInfo.size); - if (unlikely (!privDictStr.sanitize (&sc))) goto fail; - hb_barrier (); + const hb_ubytes_t privDictStr = StructAtOffsetOrNull<UnsizedByteStr> (cff, font->privateDictInfo.offset, sc, font->privateDictInfo.size).as_ubytes (font->privateDictInfo.size); num_interp_env_t env2 (privDictStr); dict_interpreter_t<PRIVOPSET, PRIVDICTVAL> priv_interp (env2); priv->init (); if (unlikely (!priv_interp.interpret (*priv))) goto fail; - priv->localSubrs = &StructAtOffsetOrNull<CFF1Subrs> (&privDictStr, priv->subrsOffset); - if (priv->localSubrs != &Null (CFF1Subrs) && - unlikely (!priv->localSubrs->sanitize (&sc))) - goto fail; - hb_barrier (); + priv->localSubrs = &StructAtOffsetOrNull<CFF1Subrs> (&privDictStr, priv->subrsOffset, sc); } } else /* non-CID */ @@ -1208,18 +1192,13 @@ struct cff1 cff1_top_dict_values_t *font = &topDict; PRIVDICTVAL *priv = &privateDicts[0]; - const hb_ubytes_t privDictStr = StructAtOffset<UnsizedByteStr> (cff, font->privateDictInfo.offset).as_ubytes (font->privateDictInfo.size); - if (unlikely (!privDictStr.sanitize (&sc))) goto fail; - hb_barrier (); + const hb_ubytes_t privDictStr = StructAtOffsetOrNull<UnsizedByteStr> (cff, font->privateDictInfo.offset, sc, font->privateDictInfo.size).as_ubytes (font->privateDictInfo.size); num_interp_env_t env (privDictStr); dict_interpreter_t<PRIVOPSET, PRIVDICTVAL> priv_interp (env); priv->init (); if (unlikely (!priv_interp.interpret (*priv))) goto fail; - priv->localSubrs = &StructAtOffsetOrNull<CFF1Subrs> (&privDictStr, priv->subrsOffset); - if (priv->localSubrs != &Null (CFF1Subrs) && - unlikely (!priv->localSubrs->sanitize (&sc))) - goto fail; + priv->localSubrs = &StructAtOffsetOrNull<CFF1Subrs> (&privDictStr, priv->subrsOffset, sc); hb_barrier (); } @@ -1437,7 +1416,7 @@ struct cff1 hb_sorted_vector_t<gname_t> *names = glyph_names.get_acquire (); if (unlikely (!names)) { - names = (hb_sorted_vector_t<gname_t> *) hb_calloc (sizeof (hb_sorted_vector_t<gname_t>), 1); + names = (hb_sorted_vector_t<gname_t> *) hb_calloc (1, sizeof (hb_sorted_vector_t<gname_t>)); if (likely (names)) { names->init (); diff --git a/thirdparty/harfbuzz/src/hb-ot-cff2-table.hh b/thirdparty/harfbuzz/src/hb-ot-cff2-table.hh index 652748b737..4b3bdc9315 100644 --- a/thirdparty/harfbuzz/src/hb-ot-cff2-table.hh +++ b/thirdparty/harfbuzz/src/hb-ot-cff2-table.hh @@ -111,7 +111,7 @@ struct CFF2FDSelect DEFINE_SIZE_MIN (2); }; -struct CFF2VariationStore +struct CFF2ItemVariationStore { bool sanitize (hb_sanitize_context_t *c) const { @@ -122,11 +122,11 @@ struct CFF2VariationStore varStore.sanitize (c)); } - bool serialize (hb_serialize_context_t *c, const CFF2VariationStore *varStore) + bool serialize (hb_serialize_context_t *c, const CFF2ItemVariationStore *varStore) { TRACE_SERIALIZE (this); unsigned int size_ = varStore->get_size (); - CFF2VariationStore *dest = c->allocate_size<CFF2VariationStore> (size_); + CFF2ItemVariationStore *dest = c->allocate_size<CFF2ItemVariationStore> (size_); if (unlikely (!dest)) return_trace (false); hb_memcpy (dest, varStore, size_); return_trace (true); @@ -135,9 +135,9 @@ struct CFF2VariationStore unsigned int get_size () const { return HBUINT16::static_size + size; } HBUINT16 size; - VariationStore varStore; + ItemVariationStore varStore; - DEFINE_SIZE_MIN (2 + VariationStore::min_size); + DEFINE_SIZE_MIN (2 + ItemVariationStore::min_size); }; struct cff2_top_dict_values_t : top_dict_values_t<> @@ -150,8 +150,8 @@ struct cff2_top_dict_values_t : top_dict_values_t<> } void fini () { top_dict_values_t<>::fini (); } - unsigned int vstoreOffset; - unsigned int FDSelectOffset; + int vstoreOffset; + int FDSelectOffset; }; struct cff2_top_dict_opset_t : top_dict_opset_t<> @@ -169,11 +169,11 @@ struct cff2_top_dict_opset_t : top_dict_opset_t<> break; case OpCode_vstore: - dictval.vstoreOffset = env.argStack.pop_uint (); + dictval.vstoreOffset = env.argStack.pop_int (); env.clear_args (); break; case OpCode_FDSelect: - dictval.FDSelectOffset = env.argStack.pop_uint (); + dictval.FDSelectOffset = env.argStack.pop_int (); env.clear_args (); break; @@ -241,7 +241,7 @@ struct cff2_private_dict_values_base_t : dict_values_t<VAL> } void fini () { dict_values_t<VAL>::fini (); } - unsigned int subrsOffset; + int subrsOffset; const CFF2Subrs *localSubrs; unsigned int ivs; }; @@ -295,7 +295,7 @@ struct cff2_private_dict_opset_t : dict_opset_t env.clear_args (); break; case OpCode_Subrs: - dictval.subrsOffset = env.argStack.pop_uint (); + dictval.subrsOffset = env.argStack.pop_int (); env.clear_args (); break; case OpCode_vsindexdict: @@ -344,7 +344,7 @@ struct cff2_private_dict_opset_subset_t : dict_opset_t return; case OpCode_Subrs: - dictval.subrsOffset = env.argStack.pop_uint (); + dictval.subrsOffset = env.argStack.pop_int (); env.clear_args (); break; @@ -426,18 +426,15 @@ struct cff2 if (unlikely (!top_interp.interpret (topDict))) goto fail; } - globalSubrs = &StructAtOffset<CFF2Subrs> (cff2, cff2->topDict + cff2->topDictSize); - varStore = &StructAtOffsetOrNull<CFF2VariationStore> (cff2, topDict.vstoreOffset); - charStrings = &StructAtOffsetOrNull<CFF2CharStrings> (cff2, topDict.charStringsOffset); - fdArray = &StructAtOffsetOrNull<CFF2FDArray> (cff2, topDict.FDArrayOffset); - fdSelect = &StructAtOffsetOrNull<CFF2FDSelect> (cff2, topDict.FDSelectOffset); - - if (((varStore != &Null (CFF2VariationStore)) && unlikely (!varStore->sanitize (&sc))) || - (charStrings == &Null (CFF2CharStrings)) || unlikely (!charStrings->sanitize (&sc)) || - (globalSubrs == &Null (CFF2Subrs)) || unlikely (!globalSubrs->sanitize (&sc)) || - (fdArray == &Null (CFF2FDArray)) || unlikely (!fdArray->sanitize (&sc)) || - !hb_barrier () || - (((fdSelect != &Null (CFF2FDSelect)) && unlikely (!fdSelect->sanitize (&sc, fdArray->count))))) + globalSubrs = &StructAtOffsetOrNull<CFF2Subrs> (cff2, cff2->topDict + cff2->topDictSize, sc); + varStore = &StructAtOffsetOrNull<CFF2ItemVariationStore> (cff2, topDict.vstoreOffset, sc); + charStrings = &StructAtOffsetOrNull<CFF2CharStrings> (cff2, topDict.charStringsOffset, sc); + fdArray = &StructAtOffsetOrNull<CFF2FDArray> (cff2, topDict.FDArrayOffset, sc); + fdSelect = &StructAtOffsetOrNull<CFF2FDSelect> (cff2, topDict.FDSelectOffset, sc, fdArray->count); + + if (charStrings == &Null (CFF2CharStrings) || + globalSubrs == &Null (CFF2Subrs) || + fdArray == &Null (CFF2FDArray)) goto fail; num_glyphs = charStrings->count; @@ -462,19 +459,13 @@ struct cff2 font->init (); if (unlikely (!font_interp.interpret (*font))) goto fail; - const hb_ubytes_t privDictStr = StructAtOffsetOrNull<UnsizedByteStr> (cff2, font->privateDictInfo.offset).as_ubytes (font->privateDictInfo.size); - if (unlikely (!privDictStr.sanitize (&sc))) goto fail; - hb_barrier (); + const hb_ubytes_t privDictStr = StructAtOffsetOrNull<UnsizedByteStr> (cff2, font->privateDictInfo.offset, sc, font->privateDictInfo.size).as_ubytes (font->privateDictInfo.size); cff2_priv_dict_interp_env_t env2 (privDictStr); dict_interpreter_t<PRIVOPSET, PRIVDICTVAL, cff2_priv_dict_interp_env_t> priv_interp (env2); privateDicts[i].init (); if (unlikely (!priv_interp.interpret (privateDicts[i]))) goto fail; - privateDicts[i].localSubrs = &StructAtOffsetOrNull<CFF2Subrs> (&privDictStr[0], privateDicts[i].subrsOffset); - if (privateDicts[i].localSubrs != &Null (CFF2Subrs) && - unlikely (!privateDicts[i].localSubrs->sanitize (&sc))) - goto fail; - hb_barrier (); + privateDicts[i].localSubrs = &StructAtOffsetOrNull<CFF2Subrs> (&privDictStr[0], privateDicts[i].subrsOffset, sc); } return; @@ -509,7 +500,7 @@ struct cff2 hb_blob_t *blob = nullptr; cff2_top_dict_values_t topDict; const CFF2Subrs *globalSubrs = nullptr; - const CFF2VariationStore *varStore = nullptr; + const CFF2ItemVariationStore *varStore = nullptr; const CFF2CharStrings *charStrings = nullptr; const CFF2FDArray *fdArray = nullptr; const CFF2FDSelect *fdSelect = nullptr; diff --git a/thirdparty/harfbuzz/src/hb-ot-cmap-table.hh b/thirdparty/harfbuzz/src/hb-ot-cmap-table.hh index e2e2581855..64d2b13880 100644 --- a/thirdparty/harfbuzz/src/hb-ot-cmap-table.hh +++ b/thirdparty/harfbuzz/src/hb-ot-cmap-table.hh @@ -41,6 +41,30 @@ namespace OT { +static inline uint8_t unicode_to_macroman (hb_codepoint_t u) +{ + uint16_t mapping[] = { + 0x00C4, 0x00C5, 0x00C7, 0x00C9, 0x00D1, 0x00D6, 0x00DC, 0x00E1, + 0x00E0, 0x00E2, 0x00E4, 0x00E3, 0x00E5, 0x00E7, 0x00E9, 0x00E8, + 0x00EA, 0x00EB, 0x00ED, 0x00EC, 0x00EE, 0x00EF, 0x00F1, 0x00F3, + 0x00F2, 0x00F4, 0x00F6, 0x00F5, 0x00FA, 0x00F9, 0x00FB, 0x00FC, + 0x2020, 0x00B0, 0x00A2, 0x00A3, 0x00A7, 0x2022, 0x00B6, 0x00DF, + 0x00AE, 0x00A9, 0x2122, 0x00B4, 0x00A8, 0x2260, 0x00C6, 0x00D8, + 0x221E, 0x00B1, 0x2264, 0x2265, 0x00A5, 0x00B5, 0x2202, 0x2211, + 0x220F, 0x03C0, 0x222B, 0x00AA, 0x00BA, 0x03A9, 0x00E6, 0x00F8, + 0x00BF, 0x00A1, 0x00AC, 0x221A, 0x0192, 0x2248, 0x2206, 0x00AB, + 0x00BB, 0x2026, 0x00A0, 0x00C0, 0x00C3, 0x00D5, 0x0152, 0x0153, + 0x2013, 0x2014, 0x201C, 0x201D, 0x2018, 0x2019, 0x00F7, 0x25CA, + 0x00FF, 0x0178, 0x2044, 0x20AC, 0x2039, 0x203A, 0xFB01, 0xFB02, + 0x2021, 0x00B7, 0x201A, 0x201E, 0x2030, 0x00C2, 0x00CA, 0x00C1, + 0x00CB, 0x00C8, 0x00CD, 0x00CE, 0x00CF, 0x00CC, 0x00D3, 0x00D4, + 0xF8FF, 0x00D2, 0x00DA, 0x00DB, 0x00D9, 0x0131, 0x02C6, 0x02DC, + 0x00AF, 0x02D8, 0x02D9, 0x02DA, 0x00B8, 0x02DD, 0x02DB, 0x02C7 + }; + uint16_t *c = hb_bsearch (u, mapping, ARRAY_LENGTH (mapping), sizeof (mapping[0]), + _hb_cmp_operator<uint16_t, uint16_t>); + return c ? (c - mapping) + 0x7F : 0; +} struct CmapSubtableFormat0 { @@ -1465,8 +1489,11 @@ struct EncodingRecord int ret; ret = platformID.cmp (other.platformID); if (ret) return ret; - ret = encodingID.cmp (other.encodingID); - if (ret) return ret; + if (other.encodingID != 0xFFFF) + { + ret = encodingID.cmp (other.encodingID); + if (ret) return ret; + } return 0; } @@ -1814,9 +1841,13 @@ struct cmap c->plan)); } - const CmapSubtable *find_best_subtable (bool *symbol = nullptr) const + const CmapSubtable *find_best_subtable (bool *symbol = nullptr, + bool *mac = nullptr, + bool *macroman = nullptr) const { if (symbol) *symbol = false; + if (mac) *mac = false; + if (macroman) *macroman = false; const CmapSubtable *subtable; @@ -1841,6 +1872,20 @@ struct cmap if ((subtable = this->find_subtable (0, 1))) return subtable; if ((subtable = this->find_subtable (0, 0))) return subtable; + /* MacRoman subtable. */ + if ((subtable = this->find_subtable (1, 0))) + { + if (mac) *mac = true; + if (macroman) *macroman = true; + return subtable; + } + /* Any other Mac subtable; we just map ASCII for these. */ + if ((subtable = this->find_subtable (1, 0xFFFF))) + { + if (mac) *mac = true; + return subtable; + } + /* Meh. */ return &Null (CmapSubtable); } @@ -1852,8 +1897,8 @@ struct cmap accelerator_t (hb_face_t *face) { this->table = hb_sanitize_context_t ().reference_table<cmap> (face); - bool symbol; - this->subtable = table->find_best_subtable (&symbol); + bool symbol, mac, macroman; + this->subtable = table->find_best_subtable (&symbol, &mac, ¯oman); this->subtable_uvs = &Null (CmapSubtableFormat14); { const CmapSubtable *st = table->find_subtable (0, 5); @@ -1862,6 +1907,7 @@ struct cmap } this->get_glyph_data = subtable; +#ifndef HB_NO_CMAP_LEGACY_SUBTABLES if (unlikely (symbol)) { switch ((unsigned) face->table.OS2->get_font_page ()) { @@ -1881,7 +1927,16 @@ struct cmap break; } } + else if (unlikely (macroman)) + { + this->get_glyph_funcZ = get_glyph_from_macroman<CmapSubtable>; + } + else if (unlikely (mac)) + { + this->get_glyph_funcZ = get_glyph_from_ascii<CmapSubtable>; + } else +#endif { switch (subtable->u.format) { /* Accelerate format 4 and format 12. */ @@ -1924,7 +1979,7 @@ struct cmap hb_codepoint_t *glyph, cache_t *cache = nullptr) const { - if (unlikely (!this->get_glyph_funcZ)) return 0; + if (unlikely (!this->get_glyph_funcZ)) return false; return _cached_get (unicode, glyph, cache); } @@ -2006,6 +2061,28 @@ struct cmap return false; } + template <typename Type> + HB_INTERNAL static bool get_glyph_from_ascii (const void *obj, + hb_codepoint_t codepoint, + hb_codepoint_t *glyph) + { + const Type *typed_obj = (const Type *) obj; + return codepoint < 0x80 && typed_obj->get_glyph (codepoint, glyph); + } + + template <typename Type> + HB_INTERNAL static bool get_glyph_from_macroman (const void *obj, + hb_codepoint_t codepoint, + hb_codepoint_t *glyph) + { + if (get_glyph_from_ascii<Type> (obj, codepoint, glyph)) + return true; + + const Type *typed_obj = (const Type *) obj; + unsigned c = unicode_to_macroman (codepoint); + return c && typed_obj->get_glyph (c, glyph); + } + private: hb_nonnull_ptr_t<const CmapSubtable> subtable; hb_nonnull_ptr_t<const CmapSubtableFormat14> subtable_uvs; @@ -2035,28 +2112,6 @@ struct cmap return &(this+result.subtable); } - const EncodingRecord *find_encodingrec (unsigned int platform_id, - unsigned int encoding_id) const - { - EncodingRecord key; - key.platformID = platform_id; - key.encodingID = encoding_id; - - return encodingRecord.as_array ().bsearch (key); - } - - bool find_subtable (unsigned format) const - { - auto it = - + hb_iter (encodingRecord) - | hb_map (&EncodingRecord::subtable) - | hb_map (hb_add (this)) - | hb_filter ([&] (const CmapSubtable& _) { return _.u.format == format; }) - ; - - return it.len (); - } - public: bool sanitize (hb_sanitize_context_t *c) const diff --git a/thirdparty/harfbuzz/src/hb-ot-font.cc b/thirdparty/harfbuzz/src/hb-ot-font.cc index b3677c6a4c..1da869d697 100644 --- a/thirdparty/harfbuzz/src/hb-ot-font.cc +++ b/thirdparty/harfbuzz/src/hb-ot-font.cc @@ -208,12 +208,12 @@ hb_ot_get_glyph_h_advances (hb_font_t* font, void* font_data, #if !defined(HB_NO_VAR) && !defined(HB_NO_OT_FONT_ADVANCE_CACHE) const OT::HVAR &HVAR = *hmtx.var_table; - const OT::VariationStore &varStore = &HVAR + HVAR.varStore; - OT::VariationStore::cache_t *varStore_cache = font->num_coords * count >= 128 ? varStore.create_cache () : nullptr; + const OT::ItemVariationStore &varStore = &HVAR + HVAR.varStore; + OT::ItemVariationStore::cache_t *varStore_cache = font->num_coords * count >= 128 ? varStore.create_cache () : nullptr; bool use_cache = font->num_coords; #else - OT::VariationStore::cache_t *varStore_cache = nullptr; + OT::ItemVariationStore::cache_t *varStore_cache = nullptr; bool use_cache = false; #endif @@ -277,7 +277,7 @@ hb_ot_get_glyph_h_advances (hb_font_t* font, void* font_data, } #if !defined(HB_NO_VAR) && !defined(HB_NO_OT_FONT_ADVANCE_CACHE) - OT::VariationStore::destroy_cache (varStore_cache); + OT::ItemVariationStore::destroy_cache (varStore_cache); #endif if (font->x_strength && !font->embolden_in_place) @@ -313,10 +313,10 @@ hb_ot_get_glyph_v_advances (hb_font_t* font, void* font_data, { #if !defined(HB_NO_VAR) && !defined(HB_NO_OT_FONT_ADVANCE_CACHE) const OT::VVAR &VVAR = *vmtx.var_table; - const OT::VariationStore &varStore = &VVAR + VVAR.varStore; - OT::VariationStore::cache_t *varStore_cache = font->num_coords ? varStore.create_cache () : nullptr; + const OT::ItemVariationStore &varStore = &VVAR + VVAR.varStore; + OT::ItemVariationStore::cache_t *varStore_cache = font->num_coords ? varStore.create_cache () : nullptr; #else - OT::VariationStore::cache_t *varStore_cache = nullptr; + OT::ItemVariationStore::cache_t *varStore_cache = nullptr; #endif for (unsigned int i = 0; i < count; i++) @@ -327,7 +327,7 @@ hb_ot_get_glyph_v_advances (hb_font_t* font, void* font_data, } #if !defined(HB_NO_VAR) && !defined(HB_NO_OT_FONT_ADVANCE_CACHE) - OT::VariationStore::destroy_cache (varStore_cache); + OT::ItemVariationStore::destroy_cache (varStore_cache); #endif } else diff --git a/thirdparty/harfbuzz/src/hb-ot-hmtx-table.hh b/thirdparty/harfbuzz/src/hb-ot-hmtx-table.hh index 89640b43f1..48bd536121 100644 --- a/thirdparty/harfbuzz/src/hb-ot-hmtx-table.hh +++ b/thirdparty/harfbuzz/src/hb-ot-hmtx-table.hh @@ -145,6 +145,29 @@ struct hmtxvmtx table->minTrailingBearing = min_rsb; table->maxExtent = max_extent; } + + if (T::is_horizontal) + { + const auto &OS2 = *c->plan->source->table.OS2; + if (OS2.has_data () && + table->ascender == OS2.sTypoAscender && + table->descender == OS2.sTypoDescender && + table->lineGap == OS2.sTypoLineGap) + { + table->ascender = static_cast<int> (roundf (OS2.sTypoAscender + + MVAR.get_var (HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER, + c->plan->normalized_coords.arrayZ, + c->plan->normalized_coords.length))); + table->descender = static_cast<int> (roundf (OS2.sTypoDescender + + MVAR.get_var (HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER, + c->plan->normalized_coords.arrayZ, + c->plan->normalized_coords.length))); + table->lineGap = static_cast<int> (roundf (OS2.sTypoLineGap + + MVAR.get_var (HB_OT_METRICS_TAG_HORIZONTAL_LINE_GAP, + c->plan->normalized_coords.arrayZ, + c->plan->normalized_coords.length))); + } + } } #endif @@ -374,7 +397,7 @@ struct hmtxvmtx unsigned get_advance_with_var_unscaled (hb_codepoint_t glyph, hb_font_t *font, - VariationStore::cache_t *store_cache = nullptr) const + ItemVariationStore::cache_t *store_cache = nullptr) const { unsigned int advance = get_advance_without_var_unscaled (glyph); diff --git a/thirdparty/harfbuzz/src/hb-ot-layout-base-table.hh b/thirdparty/harfbuzz/src/hb-ot-layout-base-table.hh index a23b6377d1..0278399069 100644 --- a/thirdparty/harfbuzz/src/hb-ot-layout-base-table.hh +++ b/thirdparty/harfbuzz/src/hb-ot-layout-base-table.hh @@ -46,6 +46,12 @@ struct BaseCoordFormat1 return HB_DIRECTION_IS_HORIZONTAL (direction) ? font->em_scale_y (coordinate) : font->em_scale_x (coordinate); } + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + return_trace ((bool) c->serializer->embed (*this)); + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -67,6 +73,17 @@ struct BaseCoordFormat2 return HB_DIRECTION_IS_HORIZONTAL (direction) ? font->em_scale_y (coordinate) : font->em_scale_x (coordinate); } + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (*this); + if (unlikely (!out)) return_trace (false); + + return_trace (c->serializer->check_assign (out->referenceGlyph, + c->plan->glyph_map->get (referenceGlyph), + HB_SERIALIZE_ERROR_INT_OVERFLOW)); + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -86,7 +103,7 @@ struct BaseCoordFormat2 struct BaseCoordFormat3 { hb_position_t get_coord (hb_font_t *font, - const VariationStore &var_store, + const ItemVariationStore &var_store, hb_direction_t direction) const { const Device &device = this+deviceTable; @@ -96,6 +113,23 @@ struct BaseCoordFormat3 : font->em_scale_x (coordinate) + device.get_x_delta (font, var_store); } + void collect_variation_indices (hb_set_t& varidx_set /* OUT */) const + { + unsigned varidx = (this+deviceTable).get_variation_index (); + varidx_set.add (varidx); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (*this); + if (unlikely (!out)) return_trace (false); + + return_trace (out->deviceTable.serialize_copy (c->serializer, deviceTable, + this, 0, + hb_serialize_context_t::Head, + &c->plan->base_variation_idx_map)); + } bool sanitize (hb_sanitize_context_t *c) const { @@ -120,7 +154,7 @@ struct BaseCoord bool has_data () const { return u.format; } hb_position_t get_coord (hb_font_t *font, - const VariationStore &var_store, + const ItemVariationStore &var_store, hb_direction_t direction) const { switch (u.format) { @@ -131,6 +165,27 @@ struct BaseCoord } } + void collect_variation_indices (hb_set_t& varidx_set /* OUT */) const + { + switch (u.format) { + case 3: u.format3.collect_variation_indices (varidx_set); + default:return; + } + } + + template <typename context_t, typename ...Ts> + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { + if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format); + switch (u.format) { + case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...)); + case 2: return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...)); + case 3: return_trace (c->dispatch (u.format3, std::forward<Ts> (ds)...)); + default:return_trace (c->default_return_value ()); + } + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -161,12 +216,37 @@ struct FeatMinMaxRecord bool has_data () const { return tag; } + hb_tag_t get_feature_tag () const { return tag; } + void get_min_max (const BaseCoord **min, const BaseCoord **max) const { if (likely (min)) *min = &(this+minCoord); if (likely (max)) *max = &(this+maxCoord); } + void collect_variation_indices (const hb_subset_plan_t* plan, + const void *base, + hb_set_t& varidx_set /* OUT */) const + { + if (!plan->layout_features.has (tag)) + return; + + (base+minCoord).collect_variation_indices (varidx_set); + (base+maxCoord).collect_variation_indices (varidx_set); + } + + bool subset (hb_subset_context_t *c, + const void *base) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (*this); + if (unlikely (!out)) return_trace (false); + if (!(out->minCoord.serialize_subset (c, minCoord, base))) + return_trace (false); + + return_trace (out->maxCoord.serialize_subset (c, maxCoord, base)); + } + bool sanitize (hb_sanitize_context_t *c, const void *base) const { TRACE_SANITIZE (this); @@ -206,6 +286,39 @@ struct MinMax } } + void collect_variation_indices (const hb_subset_plan_t* plan, + hb_set_t& varidx_set /* OUT */) const + { + (this+minCoord).collect_variation_indices (varidx_set); + (this+maxCoord).collect_variation_indices (varidx_set); + for (const FeatMinMaxRecord& record : featMinMaxRecords) + record.collect_variation_indices (plan, this, varidx_set); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false); + + if (!(out->minCoord.serialize_subset (c, minCoord, this)) || + !(out->maxCoord.serialize_subset (c, maxCoord, this))) + return_trace (false); + + unsigned len = 0; + for (const FeatMinMaxRecord& _ : featMinMaxRecords) + { + hb_tag_t feature_tag = _.get_feature_tag (); + if (!c->plan->layout_features.has (feature_tag)) + continue; + + if (!_.subset (c, this)) return false; + len++; + } + return_trace (c->serializer->check_assign (out->featMinMaxRecords.len, len, + HB_SERIALIZE_ERROR_INT_OVERFLOW)); + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -240,6 +353,26 @@ struct BaseValues return this+baseCoords[baseline_tag_index]; } + void collect_variation_indices (hb_set_t& varidx_set /* OUT */) const + { + for (const auto& _ : baseCoords) + (this+_).collect_variation_indices (varidx_set); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false); + out->defaultIndex = defaultIndex; + + for (const auto& _ : baseCoords) + if (!subset_offset_array (c, out->baseCoords, this) (_)) + return_trace (false); + + return_trace (bool (out->baseCoords)); + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -270,6 +403,20 @@ struct BaseLangSysRecord const MinMax &get_min_max () const { return this+minMax; } + void collect_variation_indices (const hb_subset_plan_t* plan, + hb_set_t& varidx_set /* OUT */) const + { (this+minMax).collect_variation_indices (plan, varidx_set); } + + bool subset (hb_subset_context_t *c, + const void *base) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (*this); + if (unlikely (!out)) return_trace (false); + + return_trace (out->minMax.serialize_subset (c, minMax, base)); + } + bool sanitize (hb_sanitize_context_t *c, const void *base) const { TRACE_SANITIZE (this); @@ -300,6 +447,35 @@ struct BaseScript bool has_values () const { return baseValues; } bool has_min_max () const { return defaultMinMax; /* TODO What if only per-language is present? */ } + void collect_variation_indices (const hb_subset_plan_t* plan, + hb_set_t& varidx_set /* OUT */) const + { + (this+baseValues).collect_variation_indices (varidx_set); + (this+defaultMinMax).collect_variation_indices (plan, varidx_set); + + for (const BaseLangSysRecord& _ : baseLangSysRecords) + _.collect_variation_indices (plan, varidx_set); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false); + + if (baseValues && !out->baseValues.serialize_subset (c, baseValues, this)) + return_trace (false); + + if (defaultMinMax && !out->defaultMinMax.serialize_subset (c, defaultMinMax, this)) + return_trace (false); + + for (const auto& _ : baseLangSysRecords) + if (!_.subset (c, this)) return_trace (false); + + return_trace (c->serializer->check_assign (out->baseLangSysRecords.len, baseLangSysRecords.len, + HB_SERIALIZE_ERROR_INT_OVERFLOW)); + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -332,9 +508,31 @@ struct BaseScriptRecord bool has_data () const { return baseScriptTag; } + hb_tag_t get_script_tag () const { return baseScriptTag; } + const BaseScript &get_base_script (const BaseScriptList *list) const { return list+baseScript; } + void collect_variation_indices (const hb_subset_plan_t* plan, + const void* list, + hb_set_t& varidx_set /* OUT */) const + { + if (!plan->layout_scripts.has (baseScriptTag)) + return; + + (list+baseScript).collect_variation_indices (plan, varidx_set); + } + + bool subset (hb_subset_context_t *c, + const void *base) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (*this); + if (unlikely (!out)) return_trace (false); + + return_trace (out->baseScript.serialize_subset (c, baseScript, base)); + } + bool sanitize (hb_sanitize_context_t *c, const void *base) const { TRACE_SANITIZE (this); @@ -361,6 +559,33 @@ struct BaseScriptList return record->has_data () ? record->get_base_script (this) : Null (BaseScript); } + void collect_variation_indices (const hb_subset_plan_t* plan, + hb_set_t& varidx_set /* OUT */) const + { + for (const BaseScriptRecord& _ : baseScriptRecords) + _.collect_variation_indices (plan, this, varidx_set); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false); + + unsigned len = 0; + for (const BaseScriptRecord& _ : baseScriptRecords) + { + hb_tag_t script_tag = _.get_script_tag (); + if (!c->plan->layout_scripts.has (script_tag)) + continue; + + if (!_.subset (c, this)) return false; + len++; + } + return_trace (c->serializer->check_assign (out->baseScriptRecords.len, len, + HB_SERIALIZE_ERROR_INT_OVERFLOW)); + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -422,6 +647,20 @@ struct Axis return true; } + void collect_variation_indices (const hb_subset_plan_t* plan, + hb_set_t& varidx_set /* OUT */) const + { (this+baseScriptList).collect_variation_indices (plan, varidx_set); } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (*this); + if (unlikely (!out)) return_trace (false); + + out->baseTagList.serialize_copy (c->serializer, baseTagList, this); + return_trace (out->baseScriptList.serialize_subset (c, baseScriptList, this)); + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -453,8 +692,41 @@ struct BASE const Axis &get_axis (hb_direction_t direction) const { return HB_DIRECTION_IS_VERTICAL (direction) ? this+vAxis : this+hAxis; } - const VariationStore &get_var_store () const - { return version.to_int () < 0x00010001u ? Null (VariationStore) : this+varStore; } + bool has_var_store () const + { return version.to_int () >= 0x00010001u && varStore != 0; } + + const ItemVariationStore &get_var_store () const + { return version.to_int () < 0x00010001u ? Null (ItemVariationStore) : this+varStore; } + + void collect_variation_indices (const hb_subset_plan_t* plan, + hb_set_t& varidx_set /* OUT */) const + { + (this+hAxis).collect_variation_indices (plan, varidx_set); + (this+vAxis).collect_variation_indices (plan, varidx_set); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false); + + out->version = version; + if (hAxis && !out->hAxis.serialize_subset (c, hAxis, this)) + return_trace (false); + + if (vAxis && !out->vAxis.serialize_subset (c, vAxis, this)) + return_trace (false); + + if (has_var_store ()) + { + if (!c->serializer->allocate_size<Offset32To<ItemVariationStore>> (Offset32To<ItemVariationStore>::static_size)) + return_trace (false); + return_trace (out->varStore.serialize_subset (c, varStore, this, c->plan->base_varstore_inner_maps.as_array ())); + } + + return_trace (true); + } bool get_baseline (hb_font_t *font, hb_tag_t baseline_tag, @@ -487,7 +759,7 @@ struct BASE &min_coord, &max_coord)) return false; - const VariationStore &var_store = get_var_store (); + const ItemVariationStore &var_store = get_var_store (); if (likely (min && min_coord)) *min = min_coord->get_coord (font, var_store, direction); if (likely (max && max_coord)) *max = max_coord->get_coord (font, var_store, direction); return true; @@ -510,7 +782,7 @@ struct BASE * of BASE table (may be NULL) */ Offset16To<Axis>vAxis; /* Offset to vertical Axis table, from beginning * of BASE table (may be NULL) */ - Offset32To<VariationStore> + Offset32To<ItemVariationStore> varStore; /* Offset to the table of Item Variation * Store--from beginning of BASE * header (may be NULL). Introduced diff --git a/thirdparty/harfbuzz/src/hb-ot-layout-common.hh b/thirdparty/harfbuzz/src/hb-ot-layout-common.hh index 6b359cceb7..aba427368c 100644 --- a/thirdparty/harfbuzz/src/hb-ot-layout-common.hh +++ b/thirdparty/harfbuzz/src/hb-ot-layout-common.hh @@ -188,7 +188,7 @@ struct hb_subset_layout_context_t : unsigned lookup_index_count; }; -struct VariationStore; +struct ItemVariationStore; struct hb_collect_variation_indices_context_t : hb_dispatch_context_t<hb_collect_variation_indices_context_t> { @@ -3036,7 +3036,7 @@ struct VarData DEFINE_SIZE_ARRAY (6, regionIndices); }; -struct VariationStore +struct ItemVariationStore { friend struct item_variations_t; using cache_t = VarRegionList::cache_t; @@ -3141,7 +3141,7 @@ struct VariationStore } bool serialize (hb_serialize_context_t *c, - const VariationStore *src, + const ItemVariationStore *src, const hb_array_t <const hb_inc_bimap_t> &inner_maps) { TRACE_SERIALIZE (this); @@ -3197,7 +3197,7 @@ struct VariationStore return_trace (true); } - VariationStore *copy (hb_serialize_context_t *c) const + ItemVariationStore *copy (hb_serialize_context_t *c) const { TRACE_SERIALIZE (this); auto *out = c->start_embed (this); @@ -3227,7 +3227,7 @@ struct VariationStore return_trace (false); #endif - VariationStore *varstore_prime = c->serializer->start_embed<VariationStore> (); + ItemVariationStore *varstore_prime = c->serializer->start_embed<ItemVariationStore> (); if (unlikely (!varstore_prime)) return_trace (false); varstore_prime->serialize (c->serializer, this, inner_maps); @@ -4030,13 +4030,13 @@ struct VariationDevice private: hb_position_t get_x_delta (hb_font_t *font, - const VariationStore &store, - VariationStore::cache_t *store_cache = nullptr) const + const ItemVariationStore &store, + ItemVariationStore::cache_t *store_cache = nullptr) const { return font->em_scalef_x (get_delta (font, store, store_cache)); } hb_position_t get_y_delta (hb_font_t *font, - const VariationStore &store, - VariationStore::cache_t *store_cache = nullptr) const + const ItemVariationStore &store, + ItemVariationStore::cache_t *store_cache = nullptr) const { return font->em_scalef_y (get_delta (font, store, store_cache)); } VariationDevice* copy (hb_serialize_context_t *c, @@ -4070,10 +4070,10 @@ struct VariationDevice private: float get_delta (hb_font_t *font, - const VariationStore &store, - VariationStore::cache_t *store_cache = nullptr) const + const ItemVariationStore &store, + ItemVariationStore::cache_t *store_cache = nullptr) const { - return store.get_delta (varIdx, font->coords, font->num_coords, (VariationStore::cache_t *) store_cache); + return store.get_delta (varIdx, font->coords, font->num_coords, (ItemVariationStore::cache_t *) store_cache); } protected: @@ -4097,8 +4097,8 @@ struct DeviceHeader struct Device { hb_position_t get_x_delta (hb_font_t *font, - const VariationStore &store=Null (VariationStore), - VariationStore::cache_t *store_cache = nullptr) const + const ItemVariationStore &store=Null (ItemVariationStore), + ItemVariationStore::cache_t *store_cache = nullptr) const { switch (u.b.format) { @@ -4115,8 +4115,8 @@ struct Device } } hb_position_t get_y_delta (hb_font_t *font, - const VariationStore &store=Null (VariationStore), - VariationStore::cache_t *store_cache = nullptr) const + const ItemVariationStore &store=Null (ItemVariationStore), + ItemVariationStore::cache_t *store_cache = nullptr) const { switch (u.b.format) { diff --git a/thirdparty/harfbuzz/src/hb-ot-layout-gsubgpos.hh b/thirdparty/harfbuzz/src/hb-ot-layout-gsubgpos.hh index 499ad673e4..c65ea32b8a 100644 --- a/thirdparty/harfbuzz/src/hb-ot-layout-gsubgpos.hh +++ b/thirdparty/harfbuzz/src/hb-ot-layout-gsubgpos.hh @@ -708,8 +708,8 @@ struct hb_ot_apply_context_t : recurse_func_t recurse_func = nullptr; const GDEF &gdef; const GDEF::accelerator_t &gdef_accel; - const VariationStore &var_store; - VariationStore::cache_t *var_store_cache; + const ItemVariationStore &var_store; + ItemVariationStore::cache_t *var_store_cache; hb_set_digest_t digest; hb_direction_t direction; @@ -723,7 +723,6 @@ struct hb_ot_apply_context_t : bool auto_zwj = true; bool per_syllable = false; bool random = false; - uint32_t random_state = 1; unsigned new_syllables = (unsigned) -1; signed last_base = -1; // GPOS uses @@ -766,7 +765,7 @@ struct hb_ot_apply_context_t : ~hb_ot_apply_context_t () { #ifndef HB_NO_VAR - VariationStore::destroy_cache (var_store_cache); + ItemVariationStore::destroy_cache (var_store_cache); #endif } @@ -788,8 +787,8 @@ struct hb_ot_apply_context_t : uint32_t random_number () { /* http://www.cplusplus.com/reference/random/minstd_rand/ */ - random_state = random_state * 48271 % 2147483647; - return random_state; + buffer->random_state = buffer->random_state * 48271 % 2147483647; + return buffer->random_state; } bool match_properties_mark (hb_codepoint_t glyph, diff --git a/thirdparty/harfbuzz/src/hb-ot-layout.cc b/thirdparty/harfbuzz/src/hb-ot-layout.cc index 2eb8535db5..a4c13abadf 100644 --- a/thirdparty/harfbuzz/src/hb-ot-layout.cc +++ b/thirdparty/harfbuzz/src/hb-ot-layout.cc @@ -2127,7 +2127,7 @@ hb_ot_layout_get_font_extents (hb_font_t *font, hb_tag_t language_tag, hb_font_extents_t *extents) { - hb_position_t min, max; + hb_position_t min = 0, max = 0; if (font->face->table.BASE->get_min_max (font, direction, script_tag, language_tag, HB_TAG_NONE, &min, &max)) { diff --git a/thirdparty/harfbuzz/src/hb-ot-math-table.hh b/thirdparty/harfbuzz/src/hb-ot-math-table.hh index 32e497aef6..5839059fde 100644 --- a/thirdparty/harfbuzz/src/hb-ot-math-table.hh +++ b/thirdparty/harfbuzz/src/hb-ot-math-table.hh @@ -344,27 +344,20 @@ struct MathKern const MathValueRecord* kernValue = mathValueRecordsZ.arrayZ + heightCount; int sign = font->y_scale < 0 ? -1 : +1; - /* The description of the MathKern table is a ambiguous, but interpreting - * "between the two heights found at those indexes" for 0 < i < len as - * - * correctionHeight[i-1] < correction_height <= correctionHeight[i] - * - * makes the result consistent with the limit cases and we can just use the - * binary search algorithm of std::upper_bound: + /* According to OpenType spec (v1.9), except for the boundary cases, the index + * chosen for kern value should be i such that + * correctionHeight[i-1] <= correction_height < correctionHeight[i] + * We can use the binary search algorithm of std::upper_bound(). Or, we can + * use the internal hb_bsearch_impl. */ - unsigned int i = 0; - unsigned int count = heightCount; - while (count > 0) - { - unsigned int half = count / 2; - hb_position_t height = correctionHeight[i + half].get_y_value (font, this); - if (sign * height < sign * correction_height) - { - i += half + 1; - count -= half + 1; - } else - count = half; - } + unsigned int pos; + auto cmp = +[](const void* key, const void* p, + int sign, hb_font_t* font, const MathKern* mathKern) -> int { + return sign * *(hb_position_t*)key - sign * ((MathValueRecord*)p)->get_y_value(font, mathKern); + }; + unsigned int i = hb_bsearch_impl(&pos, correction_height, correctionHeight, + heightCount, MathValueRecord::static_size, + cmp, sign, font, this) ? pos + 1 : pos; return kernValue[i].get_x_value (font, this); } diff --git a/thirdparty/harfbuzz/src/hb-ot-os2-table.hh b/thirdparty/harfbuzz/src/hb-ot-os2-table.hh index 8c2e696f56..43b58d9bbf 100644 --- a/thirdparty/harfbuzz/src/hb-ot-os2-table.hh +++ b/thirdparty/harfbuzz/src/hb-ot-os2-table.hh @@ -223,7 +223,7 @@ struct OS2 } } - return num ? (unsigned) roundf (total_width / num) : 0; + return num ? (unsigned) roundf ((double) total_width / (double) num) : 0; } bool subset (hb_subset_context_t *c) const @@ -284,12 +284,12 @@ struct OS2 os2_prime->usWidthClass = width_class; } - if (c->plan->flags & HB_SUBSET_FLAGS_NO_PRUNE_UNICODE_RANGES) - return_trace (true); - os2_prime->usFirstCharIndex = hb_min (0xFFFFu, c->plan->unicodes.get_min ()); os2_prime->usLastCharIndex = hb_min (0xFFFFu, c->plan->unicodes.get_max ()); + if (c->plan->flags & HB_SUBSET_FLAGS_NO_PRUNE_UNICODE_RANGES) + return_trace (true); + _update_unicode_ranges (&c->plan->unicodes, os2_prime->ulUnicodeRange); return_trace (true); diff --git a/thirdparty/harfbuzz/src/hb-ot-shape.cc b/thirdparty/harfbuzz/src/hb-ot-shape.cc index 90f596ae79..148830022e 100644 --- a/thirdparty/harfbuzz/src/hb-ot-shape.cc +++ b/thirdparty/harfbuzz/src/hb-ot-shape.cc @@ -155,7 +155,7 @@ hb_ot_shape_planner_t::compile (hb_ot_shape_plan_t &plan, #endif bool has_gpos = !disable_gpos && hb_ot_layout_has_positioning (face); if (false) - ; + {} #ifndef HB_NO_AAT_SHAPE /* Prefer GPOS over kerx if GSUB is present; * https://github.com/harfbuzz/harfbuzz/issues/3008 */ @@ -167,15 +167,16 @@ hb_ot_shape_planner_t::compile (hb_ot_shape_plan_t &plan, if (!plan.apply_kerx && (!has_gpos_kern || !plan.apply_gpos)) { + if (false) {} #ifndef HB_NO_AAT_SHAPE - if (has_kerx) + else if (has_kerx) plan.apply_kerx = true; - else #endif #ifndef HB_NO_OT_KERN - if (hb_ot_layout_has_kerning (face)) + else if (hb_ot_layout_has_kerning (face)) plan.apply_kern = true; #endif + else {} } plan.apply_fallback_kern = !(plan.apply_gpos || plan.apply_kerx || plan.apply_kern); diff --git a/thirdparty/harfbuzz/src/hb-ot-shaper-arabic.cc b/thirdparty/harfbuzz/src/hb-ot-shaper-arabic.cc index 72dcc84df5..d70746ed2b 100644 --- a/thirdparty/harfbuzz/src/hb-ot-shaper-arabic.cc +++ b/thirdparty/harfbuzz/src/hb-ot-shaper-arabic.cc @@ -560,9 +560,9 @@ apply_stch (const hb_ot_shape_plan_t *plan HB_UNUSED, DEBUG_MSG (ARABIC, nullptr, "%s stretch at (%u,%u,%u)", step == MEASURE ? "measuring" : "cutting", context, start, end); - DEBUG_MSG (ARABIC, nullptr, "rest of word: count=%u width %d", start - context, w_total); - DEBUG_MSG (ARABIC, nullptr, "fixed tiles: count=%d width=%d", n_fixed, w_fixed); - DEBUG_MSG (ARABIC, nullptr, "repeating tiles: count=%d width=%d", n_repeating, w_repeating); + DEBUG_MSG (ARABIC, nullptr, "rest of word: count=%u width %" PRId32, start - context, w_total); + DEBUG_MSG (ARABIC, nullptr, "fixed tiles: count=%d width=%" PRId32, n_fixed, w_fixed); + DEBUG_MSG (ARABIC, nullptr, "repeating tiles: count=%d width=%" PRId32, n_repeating, w_repeating); /* Number of additional times to repeat each repeating tile. */ int n_copies = 0; @@ -602,7 +602,7 @@ apply_stch (const hb_ot_shape_plan_t *plan HB_UNUSED, if (info[k - 1].arabic_shaping_action() == STCH_REPEATING) repeat += n_copies; - DEBUG_MSG (ARABIC, nullptr, "appending %u copies of glyph %u; j=%u", + DEBUG_MSG (ARABIC, nullptr, "appending %u copies of glyph %" PRIu32 "; j=%u", repeat, info[k - 1].codepoint, j); pos[k - 1].x_advance = 0; for (unsigned int n = 0; n < repeat; n++) diff --git a/thirdparty/harfbuzz/src/hb-ot-stat-table.hh b/thirdparty/harfbuzz/src/hb-ot-stat-table.hh index 58b3cd74df..e88c82a13c 100644 --- a/thirdparty/harfbuzz/src/hb-ot-stat-table.hh +++ b/thirdparty/harfbuzz/src/hb-ot-stat-table.hh @@ -349,7 +349,7 @@ struct AxisValueFormat4 struct AxisValue { - bool get_value (unsigned int axis_index) const + float get_value (unsigned int axis_index) const { switch (u.format) { @@ -357,7 +357,7 @@ struct AxisValue case 2: return u.format2.get_value (); case 3: return u.format3.get_value (); case 4: return u.format4.get_axis_record (axis_index).get_value (); - default:return 0; + default:return 0.f; } } @@ -485,7 +485,7 @@ struct STAT hb_array_t<const Offset16To<AxisValue>> axis_values = get_axis_value_offsets (); for (unsigned int i = 0; i < axis_values.length; i++) { - const AxisValue& axis_value = this+axis_values[i]; + const AxisValue& axis_value = this+offsetToAxisValueOffsets+axis_values[i]; if (axis_value.get_axis_index () == axis_index) { if (value) diff --git a/thirdparty/harfbuzz/src/hb-ot-tag-table.hh b/thirdparty/harfbuzz/src/hb-ot-tag-table.hh index 032a7c866c..db92f4664a 100644 --- a/thirdparty/harfbuzz/src/hb-ot-tag-table.hh +++ b/thirdparty/harfbuzz/src/hb-ot-tag-table.hh @@ -6,8 +6,8 @@ * * on files with these headers: * - * <meta name="updated_at" content="2022-09-30 11:47 PM" /> - * File-Date: 2023-08-02 + * <meta name="updated_at" content="2023-09-30 01:21 AM" /> + * File-Date: 2024-03-07 */ #ifndef HB_OT_TAG_TABLE_HH @@ -31,7 +31,7 @@ static const LangTag ot_languages2[] = { {HB_TAG('b','i',' ',' '), HB_TAG('B','I','S',' ')}, /* Bislama */ {HB_TAG('b','i',' ',' '), HB_TAG('C','P','P',' ')}, /* Bislama -> Creoles */ {HB_TAG('b','m',' ',' '), HB_TAG('B','M','B',' ')}, /* Bambara (Bamanankan) */ - {HB_TAG('b','n',' ',' '), HB_TAG('B','E','N',' ')}, /* Bengali */ + {HB_TAG('b','n',' ',' '), HB_TAG('B','E','N',' ')}, /* Bangla */ {HB_TAG('b','o',' ',' '), HB_TAG('T','I','B',' ')}, /* Tibetan */ {HB_TAG('b','r',' ',' '), HB_TAG('B','R','E',' ')}, /* Breton */ {HB_TAG('b','s',' ',' '), HB_TAG('B','O','S',' ')}, /* Bosnian */ @@ -64,7 +64,7 @@ static const LangTag ot_languages2[] = { {HB_TAG('f','r',' ',' '), HB_TAG('F','R','A',' ')}, /* French */ {HB_TAG('f','y',' ',' '), HB_TAG('F','R','I',' ')}, /* Western Frisian -> Frisian */ {HB_TAG('g','a',' ',' '), HB_TAG('I','R','I',' ')}, /* Irish */ - {HB_TAG('g','d',' ',' '), HB_TAG('G','A','E',' ')}, /* Scottish Gaelic (Gaelic) */ + {HB_TAG('g','d',' ',' '), HB_TAG('G','A','E',' ')}, /* Scottish Gaelic */ {HB_TAG('g','l',' ',' '), HB_TAG('G','A','L',' ')}, /* Galician */ {HB_TAG('g','n',' ',' '), HB_TAG('G','U','A',' ')}, /* Guarani [macrolanguage] */ {HB_TAG('g','u',' ',' '), HB_TAG('G','U','J',' ')}, /* Gujarati */ @@ -132,7 +132,7 @@ static const LangTag ot_languages2[] = { {HB_TAG('m','l',' ',' '), HB_TAG('M','A','L',' ')}, /* Malayalam -> Malayalam Traditional */ {HB_TAG('m','l',' ',' '), HB_TAG('M','L','R',' ')}, /* Malayalam -> Malayalam Reformed */ {HB_TAG('m','n',' ',' '), HB_TAG('M','N','G',' ')}, /* Mongolian [macrolanguage] */ - {HB_TAG('m','o',' ',' '), HB_TAG('M','O','L',' ')}, /* Moldavian (retired code) */ + {HB_TAG('m','o',' ',' '), HB_TAG('M','O','L',' ')}, /* Moldavian (retired code) -> Romanian (Moldova) */ {HB_TAG('m','o',' ',' '), HB_TAG('R','O','M',' ')}, /* Moldavian (retired code) -> Romanian */ {HB_TAG('m','r',' ',' '), HB_TAG('M','A','R',' ')}, /* Marathi */ {HB_TAG('m','s',' ',' '), HB_TAG('M','L','Y',' ')}, /* Malay [macrolanguage] */ @@ -153,7 +153,7 @@ static const LangTag ot_languages2[] = { {HB_TAG('o','c',' ',' '), HB_TAG('O','C','I',' ')}, /* Occitan (post 1500) */ {HB_TAG('o','j',' ',' '), HB_TAG('O','J','B',' ')}, /* Ojibwa [macrolanguage] -> Ojibway */ {HB_TAG('o','m',' ',' '), HB_TAG('O','R','O',' ')}, /* Oromo [macrolanguage] */ - {HB_TAG('o','r',' ',' '), HB_TAG('O','R','I',' ')}, /* Odia (formerly Oriya) [macrolanguage] */ + {HB_TAG('o','r',' ',' '), HB_TAG('O','R','I',' ')}, /* Odia [macrolanguage] */ {HB_TAG('o','s',' ',' '), HB_TAG('O','S','S',' ')}, /* Ossetian */ {HB_TAG('p','a',' ',' '), HB_TAG('P','A','N',' ')}, /* Punjabi */ {HB_TAG('p','i',' ',' '), HB_TAG('P','A','L',' ')}, /* Pali */ @@ -166,7 +166,7 @@ static const LangTag ot_languages2[] = { {HB_TAG('r','o',' ',' '), HB_TAG('R','O','M',' ')}, /* Romanian */ {HB_TAG('r','u',' ',' '), HB_TAG('R','U','S',' ')}, /* Russian */ {HB_TAG('r','w',' ',' '), HB_TAG('R','U','A',' ')}, /* Kinyarwanda */ - {HB_TAG('s','a',' ',' '), HB_TAG('S','A','N',' ')}, /* Sanskrit */ + {HB_TAG('s','a',' ',' '), HB_TAG('S','A','N',' ')}, /* Sanskrit [macrolanguage] */ {HB_TAG('s','c',' ',' '), HB_TAG('S','R','D',' ')}, /* Sardinian [macrolanguage] */ {HB_TAG('s','d',' ',' '), HB_TAG('S','N','D',' ')}, /* Sindhi */ {HB_TAG('s','e',' ',' '), HB_TAG('N','S','M',' ')}, /* Northern Sami */ @@ -465,6 +465,7 @@ static const LangTag ot_languages3[] = { {HB_TAG('c','l','d',' '), HB_TAG('S','Y','R',' ')}, /* Chaldean Neo-Aramaic -> Syriac */ {HB_TAG('c','l','e',' '), HB_TAG('C','C','H','N')}, /* Lealao Chinantec -> Chinantec */ {HB_TAG('c','l','j',' '), HB_TAG('Q','I','N',' ')}, /* Laitu Chin -> Chin */ + {HB_TAG('c','l','s',' '), HB_TAG('S','A','N',' ')}, /* Classical Sanskrit -> Sanskrit */ {HB_TAG('c','l','t',' '), HB_TAG('Q','I','N',' ')}, /* Lautu Chin -> Chin */ {HB_TAG('c','m','n',' '), HB_TAG('Z','H','S',' ')}, /* Mandarin Chinese -> Chinese, Simplified */ {HB_TAG('c','m','r',' '), HB_TAG('Q','I','N',' ')}, /* Mro-Khimi Chin -> Chin */ @@ -637,7 +638,7 @@ static const LangTag ot_languages3[] = { {HB_TAG('g','a','a',' '), HB_TAG('G','A','D',' ')}, /* Ga */ {HB_TAG('g','a','c',' '), HB_TAG('C','P','P',' ')}, /* Mixed Great Andamanese -> Creoles */ {HB_TAG('g','a','d',' '), HB_TAG_NONE }, /* Gaddang != Ga */ - {HB_TAG('g','a','e',' '), HB_TAG_NONE }, /* Guarequena != Scottish Gaelic (Gaelic) */ + {HB_TAG('g','a','e',' '), HB_TAG_NONE }, /* Guarequena != Scottish Gaelic */ /*{HB_TAG('g','a','g',' '), HB_TAG('G','A','G',' ')},*/ /* Gagauz */ {HB_TAG('g','a','l',' '), HB_TAG_NONE }, /* Galolen != Galician */ {HB_TAG('g','a','n',' '), HB_TAG('Z','H','S',' ')}, /* Gan Chinese -> Chinese, Simplified */ @@ -1160,7 +1161,7 @@ static const LangTag ot_languages3[] = { {HB_TAG('o','r','o',' '), HB_TAG_NONE }, /* Orokolo != Oromo */ {HB_TAG('o','r','r',' '), HB_TAG('I','J','O',' ')}, /* Oruma -> Ijo */ {HB_TAG('o','r','s',' '), HB_TAG('M','L','Y',' ')}, /* Orang Seletar -> Malay */ - {HB_TAG('o','r','y',' '), HB_TAG('O','R','I',' ')}, /* Odia (formerly Oriya) */ + {HB_TAG('o','r','y',' '), HB_TAG('O','R','I',' ')}, /* Odia */ {HB_TAG('o','t','w',' '), HB_TAG('O','J','B',' ')}, /* Ottawa -> Ojibway */ {HB_TAG('o','u','a',' '), HB_TAG('B','B','R',' ')}, /* Tagargrent -> Berber */ {HB_TAG('p','a','a',' '), HB_TAG_NONE }, /* Papuan [collection] != Palestinian Aramaic */ @@ -1395,7 +1396,7 @@ static const LangTag ot_languages3[] = { /*{HB_TAG('s','n','k',' '), HB_TAG('S','N','K',' ')},*/ /* Soninke */ {HB_TAG('s','o','g',' '), HB_TAG_NONE }, /* Sogdian != Sodo Gurage */ /*{HB_TAG('s','o','p',' '), HB_TAG('S','O','P',' ')},*/ /* Songe */ - {HB_TAG('s','p','v',' '), HB_TAG('O','R','I',' ')}, /* Sambalpuri -> Odia (formerly Oriya) */ + {HB_TAG('s','p','v',' '), HB_TAG('O','R','I',' ')}, /* Sambalpuri -> Odia */ {HB_TAG('s','p','y',' '), HB_TAG('K','A','L',' ')}, /* Sabaot -> Kalenjin */ {HB_TAG('s','r','b',' '), HB_TAG_NONE }, /* Sora != Serbian */ {HB_TAG('s','r','c',' '), HB_TAG('S','R','D',' ')}, /* Logudorese Sardinian -> Sardinian */ @@ -1533,6 +1534,7 @@ static const LangTag ot_languages3[] = { {HB_TAG('v','l','s',' '), HB_TAG('F','L','E',' ')}, /* Vlaams -> Dutch (Flemish) */ {HB_TAG('v','m','w',' '), HB_TAG('M','A','K',' ')}, /* Makhuwa */ /*{HB_TAG('v','r','o',' '), HB_TAG('V','R','O',' ')},*/ /* Võro */ + {HB_TAG('v','s','n',' '), HB_TAG('S','A','N',' ')}, /* Vedic Sanskrit -> Sanskrit */ {HB_TAG('w','a','g',' '), HB_TAG_NONE }, /* Wa'ema != Wagdi */ /*{HB_TAG('w','a','r',' '), HB_TAG('W','A','R',' ')},*/ /* Waray (Philippines) -> Waray-Waray */ {HB_TAG('w','b','m',' '), HB_TAG('W','A',' ',' ')}, /* Wa */ @@ -2643,7 +2645,7 @@ out: /* Romanian; Moldova */ unsigned int i; hb_tag_t possible_tags[] = { - HB_TAG('M','O','L',' '), /* Moldavian */ + HB_TAG('M','O','L',' '), /* Romanian (Moldova) */ HB_TAG('R','O','M',' '), /* Romanian */ }; for (i = 0; i < 2 && i < *count; i++) @@ -2920,7 +2922,7 @@ hb_ot_ambiguous_tag_to_language (hb_tag_t tag) return hb_language_from_string ("mn", -1); /* Mongolian [macrolanguage] */ case HB_TAG('M','N','K',' '): /* Maninka */ return hb_language_from_string ("man", -1); /* Mandingo [macrolanguage] */ - case HB_TAG('M','O','L',' '): /* Moldavian */ + case HB_TAG('M','O','L',' '): /* Romanian (Moldova) */ return hb_language_from_string ("ro-MD", -1); /* Romanian; Moldova */ case HB_TAG('M','O','N','T'): /* Thailand Mon */ return hb_language_from_string ("mnw-TH", -1); /* Mon; Thailand */ @@ -2958,6 +2960,8 @@ hb_ot_ambiguous_tag_to_language (hb_tag_t tag) return hb_language_from_string ("ro", -1); /* Romanian */ case HB_TAG('R','O','Y',' '): /* Romany */ return hb_language_from_string ("rom", -1); /* Romany [macrolanguage] */ + case HB_TAG('S','A','N',' '): /* Sanskrit */ + return hb_language_from_string ("sa", -1); /* Sanskrit [macrolanguage] */ case HB_TAG('S','Q','I',' '): /* Albanian */ return hb_language_from_string ("sq", -1); /* Albanian [macrolanguage] */ case HB_TAG('S','R','B',' '): /* Serbian */ diff --git a/thirdparty/harfbuzz/src/hb-ot-tag.cc b/thirdparty/harfbuzz/src/hb-ot-tag.cc index 53b6b38f66..0c63756b14 100644 --- a/thirdparty/harfbuzz/src/hb-ot-tag.cc +++ b/thirdparty/harfbuzz/src/hb-ot-tag.cc @@ -547,7 +547,7 @@ hb_ot_tag_to_language (hb_tag_t tag) buf[3] = '-'; str += 4; } - snprintf (str, 16, "x-hbot-%08x", tag); + snprintf (str, 16, "x-hbot-%08" PRIx32, tag); return hb_language_from_string (&*buf, -1); } } diff --git a/thirdparty/harfbuzz/src/hb-ot-var-avar-table.hh b/thirdparty/harfbuzz/src/hb-ot-var-avar-table.hh index b2e5d87a3c..9149959d79 100644 --- a/thirdparty/harfbuzz/src/hb-ot-var-avar-table.hh +++ b/thirdparty/harfbuzz/src/hb-ot-var-avar-table.hh @@ -57,7 +57,7 @@ struct avarV2Tail protected: Offset32To<DeltaSetIndexMap> varIdxMap; /* Offset from the beginning of 'avar' table. */ - Offset32To<VariationStore> varStore; /* Offset from the beginning of 'avar' table. */ + Offset32To<ItemVariationStore> varStore; /* Offset from the beginning of 'avar' table. */ public: DEFINE_SIZE_STATIC (8); @@ -230,7 +230,7 @@ struct SegmentMaps : Array16Of<AxisValueMap> * duplicates here */ if (mapping.must_include ()) continue; - value_mappings.push (std::move (mapping)); + value_mappings.push (mapping); } AxisValueMap m; @@ -343,7 +343,7 @@ struct avar for (unsigned i = 0; i < coords_length; i++) coords[i] = out[i]; - OT::VariationStore::destroy_cache (var_store_cache); + OT::ItemVariationStore::destroy_cache (var_store_cache); #endif } diff --git a/thirdparty/harfbuzz/src/hb-ot-var-common.hh b/thirdparty/harfbuzz/src/hb-ot-var-common.hh index eff6df380f..379e164059 100644 --- a/thirdparty/harfbuzz/src/hb-ot-var-common.hh +++ b/thirdparty/harfbuzz/src/hb-ot-var-common.hh @@ -28,6 +28,7 @@ #include "hb-ot-layout-common.hh" #include "hb-priority-queue.hh" +#include "hb-subset-instancer-iup.hh" namespace OT { @@ -221,9 +222,9 @@ struct DeltaSetIndexMap }; -struct VarStoreInstancer +struct ItemVarStoreInstancer { - VarStoreInstancer (const VariationStore *varStore, + ItemVarStoreInstancer (const ItemVariationStore *varStore, const DeltaSetIndexMap *varIdxMap, hb_array_t<int> coords) : varStore (varStore), varIdxMap (varIdxMap), coords (coords) {} @@ -235,7 +236,7 @@ struct VarStoreInstancer float operator() (uint32_t varIdx, unsigned short offset = 0) const { return coords ? varStore->get_delta (varIdxMap ? varIdxMap->map (VarIdx::add (varIdx, offset)) : varIdx + offset, coords) : 0; } - const VariationStore *varStore; + const ItemVariationStore *varStore; const DeltaSetIndexMap *varIdxMap; hb_array_t<int> coords; }; @@ -460,7 +461,7 @@ struct tuple_delta_t tuple_delta_t () = default; tuple_delta_t (const tuple_delta_t& o) = default; - friend void swap (tuple_delta_t& a, tuple_delta_t& b) + friend void swap (tuple_delta_t& a, tuple_delta_t& b) noexcept { hb_swap (a.axis_tuples, b.axis_tuples); hb_swap (a.indices, b.indices); @@ -471,10 +472,10 @@ struct tuple_delta_t hb_swap (a.compiled_peak_coords, b.compiled_peak_coords); } - tuple_delta_t (tuple_delta_t&& o) : tuple_delta_t () + tuple_delta_t (tuple_delta_t&& o) noexcept : tuple_delta_t () { hb_swap (*this, o); } - tuple_delta_t& operator = (tuple_delta_t&& o) + tuple_delta_t& operator = (tuple_delta_t&& o) noexcept { hb_swap (*this, o); return *this; @@ -609,7 +610,9 @@ struct tuple_delta_t const hb_map_t& axes_old_index_tag_map, const hb_hashmap_t<const hb_vector_t<char>*, unsigned>* shared_tuples_idx_map) { - if (!compiled_deltas) return false; + /* compiled_deltas could be empty after iup delta optimization, we can skip + * compiling this tuple and return true */ + if (!compiled_deltas) return true; unsigned cur_axis_count = axes_index_map.get_population (); /* allocate enough memory: 1 peak + 2 intermediate coords + fixed header size */ @@ -723,22 +726,28 @@ struct tuple_delta_t } bool compile_deltas () + { return compile_deltas (indices, deltas_x, deltas_y, compiled_deltas); } + + bool compile_deltas (const hb_vector_t<bool> &point_indices, + const hb_vector_t<float> &x_deltas, + const hb_vector_t<float> &y_deltas, + hb_vector_t<char> &compiled_deltas /* OUT */) { hb_vector_t<int> rounded_deltas; - if (unlikely (!rounded_deltas.alloc (indices.length))) + if (unlikely (!rounded_deltas.alloc (point_indices.length))) return false; - for (unsigned i = 0; i < indices.length; i++) + for (unsigned i = 0; i < point_indices.length; i++) { - if (!indices[i]) continue; - int rounded_delta = (int) roundf (deltas_x[i]); + if (!point_indices[i]) continue; + int rounded_delta = (int) roundf (x_deltas.arrayZ[i]); rounded_deltas.push (rounded_delta); } - if (!rounded_deltas) return false; + if (!rounded_deltas) return true; /* allocate enough memories 3 * num_deltas */ unsigned alloc_len = 3 * rounded_deltas.length; - if (deltas_y) + if (y_deltas) alloc_len *= 2; if (unlikely (!compiled_deltas.resize (alloc_len))) return false; @@ -746,14 +755,14 @@ struct tuple_delta_t unsigned i = 0; unsigned encoded_len = encode_delta_run (i, compiled_deltas.as_array (), rounded_deltas); - if (deltas_y) + if (y_deltas) { - /* reuse the rounded_deltas vector, check that deltas_y have the same num of deltas as deltas_x */ + /* reuse the rounded_deltas vector, check that y_deltas have the same num of deltas as x_deltas */ unsigned j = 0; - for (unsigned idx = 0; idx < indices.length; idx++) + for (unsigned idx = 0; idx < point_indices.length; idx++) { - if (!indices[idx]) continue; - int rounded_delta = (int) roundf (deltas_y[idx]); + if (!point_indices[idx]) continue; + int rounded_delta = (int) roundf (y_deltas.arrayZ[idx]); if (j >= rounded_deltas.length) return false; @@ -761,7 +770,7 @@ struct tuple_delta_t } if (j != rounded_deltas.length) return false; - /* reset i because we reuse rounded_deltas for deltas_y */ + /* reset i because we reuse rounded_deltas for y_deltas */ i = 0; encoded_len += encode_delta_run (i, compiled_deltas.as_array ().sub_array (encoded_len), rounded_deltas); } @@ -1020,6 +1029,171 @@ struct tuple_delta_t return true; } + bool optimize (const contour_point_vector_t& contour_points, + bool is_composite, + float tolerance = 0.5f) + { + unsigned count = contour_points.length; + if (deltas_x.length != count || + deltas_y.length != count) + return false; + + hb_vector_t<bool> opt_indices; + hb_vector_t<int> rounded_x_deltas, rounded_y_deltas; + + if (unlikely (!rounded_x_deltas.alloc (count) || + !rounded_y_deltas.alloc (count))) + return false; + + for (unsigned i = 0; i < count; i++) + { + int rounded_x_delta = (int) roundf (deltas_x.arrayZ[i]); + int rounded_y_delta = (int) roundf (deltas_y.arrayZ[i]); + rounded_x_deltas.push (rounded_x_delta); + rounded_y_deltas.push (rounded_y_delta); + } + + if (!iup_delta_optimize (contour_points, rounded_x_deltas, rounded_y_deltas, opt_indices, tolerance)) + return false; + + unsigned ref_count = 0; + for (bool ref_flag : opt_indices) + ref_count += ref_flag; + + if (ref_count == count) return true; + + hb_vector_t<float> opt_deltas_x, opt_deltas_y; + bool is_comp_glyph_wo_deltas = (is_composite && ref_count == 0); + if (is_comp_glyph_wo_deltas) + { + if (unlikely (!opt_deltas_x.resize (count) || + !opt_deltas_y.resize (count))) + return false; + + opt_indices.arrayZ[0] = true; + for (unsigned i = 1; i < count; i++) + opt_indices.arrayZ[i] = false; + } + + hb_vector_t<char> opt_point_data; + if (!compile_point_set (opt_indices, opt_point_data)) + return false; + hb_vector_t<char> opt_deltas_data; + if (!compile_deltas (opt_indices, + is_comp_glyph_wo_deltas ? opt_deltas_x : deltas_x, + is_comp_glyph_wo_deltas ? opt_deltas_y : deltas_y, + opt_deltas_data)) + return false; + + hb_vector_t<char> point_data; + if (!compile_point_set (indices, point_data)) + return false; + hb_vector_t<char> deltas_data; + if (!compile_deltas (indices, deltas_x, deltas_y, deltas_data)) + return false; + + if (opt_point_data.length + opt_deltas_data.length < point_data.length + deltas_data.length) + { + indices.fini (); + indices = std::move (opt_indices); + + if (is_comp_glyph_wo_deltas) + { + deltas_x.fini (); + deltas_x = std::move (opt_deltas_x); + + deltas_y.fini (); + deltas_y = std::move (opt_deltas_y); + } + } + return !indices.in_error () && !deltas_x.in_error () && !deltas_y.in_error (); + } + + static bool compile_point_set (const hb_vector_t<bool> &point_indices, + hb_vector_t<char>& compiled_points /* OUT */) + { + unsigned num_points = 0; + for (bool i : point_indices) + if (i) num_points++; + + /* when iup optimization is enabled, num of referenced points could be 0 */ + if (!num_points) return true; + + unsigned indices_length = point_indices.length; + /* If the points set consists of all points in the glyph, it's encoded with a + * single zero byte */ + if (num_points == indices_length) + return compiled_points.resize (1); + + /* allocate enough memories: 2 bytes for count + 3 bytes for each point */ + unsigned num_bytes = 2 + 3 *num_points; + if (unlikely (!compiled_points.resize (num_bytes, false))) + return false; + + unsigned pos = 0; + /* binary data starts with the total number of reference points */ + if (num_points < 0x80) + compiled_points.arrayZ[pos++] = num_points; + else + { + compiled_points.arrayZ[pos++] = ((num_points >> 8) | 0x80); + compiled_points.arrayZ[pos++] = num_points & 0xFF; + } + + const unsigned max_run_length = 0x7F; + unsigned i = 0; + unsigned last_value = 0; + unsigned num_encoded = 0; + while (i < indices_length && num_encoded < num_points) + { + unsigned run_length = 0; + unsigned header_pos = pos; + compiled_points.arrayZ[pos++] = 0; + + bool use_byte_encoding = false; + bool new_run = true; + while (i < indices_length && num_encoded < num_points && + run_length <= max_run_length) + { + // find out next referenced point index + while (i < indices_length && !point_indices[i]) + i++; + + if (i >= indices_length) break; + + unsigned cur_value = i; + unsigned delta = cur_value - last_value; + + if (new_run) + { + use_byte_encoding = (delta <= 0xFF); + new_run = false; + } + + if (use_byte_encoding && delta > 0xFF) + break; + + if (use_byte_encoding) + compiled_points.arrayZ[pos++] = delta; + else + { + compiled_points.arrayZ[pos++] = delta >> 8; + compiled_points.arrayZ[pos++] = delta & 0xFF; + } + i++; + last_value = cur_value; + run_length++; + num_encoded++; + } + + if (use_byte_encoding) + compiled_points.arrayZ[header_pos] = run_length - 1; + else + compiled_points.arrayZ[header_pos] = (run_length - 1) | 0x80; + } + return compiled_points.resize (pos, false); + } + static float infer_delta (float target_val, float prev_val, float next_val, float prev_delta, float next_delta) { if (prev_val == next_val) @@ -1071,41 +1245,41 @@ struct TupleVariationData private: /* referenced point set->compiled point data map */ - hb_hashmap_t<const hb_vector_t<bool>*, hb_bytes_t> point_data_map; + hb_hashmap_t<const hb_vector_t<bool>*, hb_vector_t<char>> point_data_map; /* referenced point set-> count map, used in finding shared points */ hb_hashmap_t<const hb_vector_t<bool>*, unsigned> point_set_count_map; /* empty for non-gvar tuples. - * shared_points_bytes is just a copy of some value in the point_data_map, + * shared_points_bytes is a pointer to some value in the point_data_map, * which will be freed during map destruction. Save it for serialization, so * no need to do find_shared_points () again */ - hb_bytes_t shared_points_bytes; + hb_vector_t<char> *shared_points_bytes = nullptr; /* total compiled byte size as TupleVariationData format, initialized to its * min_size: 4 */ unsigned compiled_byte_size = 4; + /* for gvar iup delta optimization: whether this is a composite glyph */ + bool is_composite = false; + public: tuple_variations_t () = default; tuple_variations_t (const tuple_variations_t&) = delete; tuple_variations_t& operator=(const tuple_variations_t&) = delete; tuple_variations_t (tuple_variations_t&&) = default; tuple_variations_t& operator=(tuple_variations_t&&) = default; - ~tuple_variations_t () { fini (); } - void fini () - { - for (auto _ : point_data_map.values ()) - _.fini (); - - point_set_count_map.fini (); - tuple_vars.fini (); - } + ~tuple_variations_t () = default; explicit operator bool () const { return bool (tuple_vars); } unsigned get_var_count () const { - unsigned count = tuple_vars.length; - if (shared_points_bytes.length) + unsigned count = 0; + /* when iup delta opt is enabled, compiled_deltas could be empty and we + * should skip this tuple */ + for (auto& tuple: tuple_vars) + if (tuple.compiled_deltas) count++; + + if (shared_points_bytes && shared_points_bytes->length) count |= TupleVarCount::SharedPointNumbers; return count; } @@ -1119,26 +1293,27 @@ struct TupleVariationData bool is_gvar, const hb_map_t *axes_old_index_tag_map, const hb_vector_t<unsigned> &shared_indices, - const hb_array_t<const F2DOT14> shared_tuples) + const hb_array_t<const F2DOT14> shared_tuples, + bool is_composite_glyph) { do { const HBUINT8 *p = iterator.get_serialized_data (); unsigned int length = iterator.current_tuple->get_data_size (); if (unlikely (!iterator.var_data_bytes.check_range (p, length))) - { fini (); return false; } + return false; hb_hashmap_t<hb_tag_t, Triple> axis_tuples; if (!iterator.current_tuple->unpack_axis_tuples (iterator.get_axis_count (), shared_tuples, axes_old_index_tag_map, axis_tuples) || axis_tuples.is_empty ()) - { fini (); return false; } + return false; hb_vector_t<unsigned> private_indices; bool has_private_points = iterator.current_tuple->has_private_points (); const HBUINT8 *end = p + length; if (has_private_points && !TupleVariationData::unpack_points (p, private_indices, end)) - { fini (); return false; } + return false; const hb_vector_t<unsigned> &indices = has_private_points ? private_indices : shared_indices; bool apply_to_all = (indices.length == 0); @@ -1148,24 +1323,24 @@ struct TupleVariationData if (unlikely (!deltas_x.resize (num_deltas, false) || !TupleVariationData::unpack_deltas (p, deltas_x, end))) - { fini (); return false; } + return false; hb_vector_t<int> deltas_y; if (is_gvar) { if (unlikely (!deltas_y.resize (num_deltas, false) || !TupleVariationData::unpack_deltas (p, deltas_y, end))) - { fini (); return false; } + return false; } tuple_delta_t var; var.axis_tuples = std::move (axis_tuples); if (unlikely (!var.indices.resize (point_count) || !var.deltas_x.resize (point_count, false))) - { fini (); return false; } + return false; if (is_gvar && unlikely (!var.deltas_y.resize (point_count, false))) - { fini (); return false; } + return false; for (unsigned i = 0; i < num_deltas; i++) { @@ -1178,6 +1353,8 @@ struct TupleVariationData } tuple_vars.push (std::move (var)); } while (iterator.move_to_next ()); + + is_composite = is_composite_glyph; return true; } @@ -1261,7 +1438,7 @@ struct TupleVariationData unsigned new_len = new_vars.length + out.length; if (unlikely (!new_vars.alloc (new_len, false))) - { fini (); return false;} + return false; for (unsigned i = 0; i < out.length; i++) new_vars.push (std::move (out[i])); @@ -1272,8 +1449,9 @@ struct TupleVariationData return true; } - /* merge tuple variations with overlapping tents */ - void merge_tuple_variations () + /* merge tuple variations with overlapping tents, if iup delta optimization + * is enabled, add default deltas to contour_points */ + bool merge_tuple_variations (contour_point_vector_t* contour_points = nullptr) { hb_vector_t<tuple_delta_t> new_vars; hb_hashmap_t<const hb_hashmap_t<hb_tag_t, Triple>*, unsigned> m; @@ -1281,7 +1459,15 @@ struct TupleVariationData for (const tuple_delta_t& var : tuple_vars) { /* if all axes are pinned, drop the tuple variation */ - if (var.axis_tuples.is_empty ()) continue; + if (var.axis_tuples.is_empty ()) + { + /* if iup_delta_optimize is enabled, add deltas to contour coords */ + if (contour_points && !contour_points->add_deltas (var.deltas_x, + var.deltas_y, + var.indices)) + return false; + continue; + } unsigned *idx; if (m.has (&(var.axis_tuples), &idx)) @@ -1291,98 +1477,14 @@ struct TupleVariationData else { new_vars.push (var); - m.set (&(var.axis_tuples), i); + if (!m.set (&(var.axis_tuples), i)) + return false; i++; } } tuple_vars.fini (); tuple_vars = std::move (new_vars); - } - - hb_bytes_t compile_point_set (const hb_vector_t<bool> &point_indices) - { - unsigned num_points = 0; - for (bool i : point_indices) - if (i) num_points++; - - unsigned indices_length = point_indices.length; - /* If the points set consists of all points in the glyph, it's encoded with a - * single zero byte */ - if (num_points == indices_length) - { - char *p = (char *) hb_calloc (1, sizeof (char)); - if (unlikely (!p)) return hb_bytes_t (); - - return hb_bytes_t (p, 1); - } - - /* allocate enough memories: 2 bytes for count + 3 bytes for each point */ - unsigned num_bytes = 2 + 3 *num_points; - char *p = (char *) hb_calloc (num_bytes, sizeof (char)); - if (unlikely (!p)) return hb_bytes_t (); - - unsigned pos = 0; - /* binary data starts with the total number of reference points */ - if (num_points < 0x80) - p[pos++] = num_points; - else - { - p[pos++] = ((num_points >> 8) | 0x80); - p[pos++] = num_points & 0xFF; - } - - const unsigned max_run_length = 0x7F; - unsigned i = 0; - unsigned last_value = 0; - unsigned num_encoded = 0; - while (i < indices_length && num_encoded < num_points) - { - unsigned run_length = 0; - unsigned header_pos = pos; - p[pos++] = 0; - - bool use_byte_encoding = false; - bool new_run = true; - while (i < indices_length && num_encoded < num_points && - run_length <= max_run_length) - { - // find out next referenced point index - while (i < indices_length && !point_indices[i]) - i++; - - if (i >= indices_length) break; - - unsigned cur_value = i; - unsigned delta = cur_value - last_value; - - if (new_run) - { - use_byte_encoding = (delta <= 0xFF); - new_run = false; - } - - if (use_byte_encoding && delta > 0xFF) - break; - - if (use_byte_encoding) - p[pos++] = delta; - else - { - p[pos++] = delta >> 8; - p[pos++] = delta & 0xFF; - } - i++; - last_value = cur_value; - run_length++; - num_encoded++; - } - - if (use_byte_encoding) - p[header_pos] = run_length - 1; - else - p[header_pos] = (run_length - 1) | 0x80; - } - return hb_bytes_t (p, pos); + return true; } /* compile all point set and store byte data in a point_set->hb_bytes_t hashmap, @@ -1402,11 +1504,11 @@ struct TupleVariationData continue; } - hb_bytes_t compiled_data = compile_point_set (*points_set); - if (unlikely (compiled_data == hb_bytes_t ())) + hb_vector_t<char> compiled_point_data; + if (!tuple_delta_t::compile_point_set (*points_set, compiled_point_data)) return false; - if (!point_data_map.set (points_set, compiled_data) || + if (!point_data_map.set (points_set, std::move (compiled_point_data)) || !point_set_count_map.set (points_set, 1)) return false; } @@ -1414,31 +1516,33 @@ struct TupleVariationData } /* find shared points set which saves most bytes */ - hb_bytes_t find_shared_points () + void find_shared_points () { unsigned max_saved_bytes = 0; - hb_bytes_t res{}; - for (const auto& _ : point_data_map.iter ()) + for (const auto& _ : point_data_map.iter_ref ()) { const hb_vector_t<bool>* points_set = _.first; unsigned data_length = _.second.length; + if (!data_length) continue; unsigned *count; if (unlikely (!point_set_count_map.has (points_set, &count) || *count <= 1)) - return hb_bytes_t (); + { + shared_points_bytes = nullptr; + return; + } unsigned saved_bytes = data_length * ((*count) -1); if (saved_bytes > max_saved_bytes) { max_saved_bytes = saved_bytes; - res = _.second; + shared_points_bytes = &(_.second); } } - return res; } - bool calc_inferred_deltas (contour_point_vector_t& contour_points) + bool calc_inferred_deltas (const contour_point_vector_t& contour_points) { for (tuple_delta_t& var : tuple_vars) if (!var.calc_inferred_deltas (contour_points)) @@ -1447,10 +1551,21 @@ struct TupleVariationData return true; } + bool iup_optimize (const contour_point_vector_t& contour_points) + { + for (tuple_delta_t& var : tuple_vars) + { + if (!var.optimize (contour_points, is_composite)) + return false; + } + return true; + } + public: bool instantiate (const hb_hashmap_t<hb_tag_t, Triple>& normalized_axes_location, const hb_hashmap_t<hb_tag_t, TripleDistances>& axes_triple_distances, - contour_point_vector_t* contour_points = nullptr) + contour_point_vector_t* contour_points = nullptr, + bool optimize = false) { if (!tuple_vars) return true; if (!change_tuple_variations_axis_limits (normalized_axes_location, axes_triple_distances)) @@ -1460,7 +1575,14 @@ struct TupleVariationData if (!calc_inferred_deltas (*contour_points)) return false; - merge_tuple_variations (); + /* if iup delta opt is on, contour_points can't be null */ + if (optimize && !contour_points) + return false; + + if (!merge_tuple_variations (optimize ? contour_points : nullptr)) + return false; + + if (optimize && !iup_optimize (*contour_points)) return false; return !tuple_vars.in_error (); } @@ -1475,21 +1597,27 @@ struct TupleVariationData if (use_shared_points) { - shared_points_bytes = find_shared_points (); - compiled_byte_size += shared_points_bytes.length; + find_shared_points (); + if (shared_points_bytes) + compiled_byte_size += shared_points_bytes->length; } // compile delta and tuple var header for each tuple variation for (auto& tuple: tuple_vars) { const hb_vector_t<bool>* points_set = &(tuple.indices); - hb_bytes_t *points_data; + hb_vector_t<char> *points_data; if (unlikely (!point_data_map.has (points_set, &points_data))) return false; + /* when iup optimization is enabled, num of referenced points could be 0 + * and thus the compiled points bytes is empty, we should skip compiling + * this tuple */ + if (!points_data->length) + continue; if (!tuple.compile_deltas ()) return false; - unsigned points_data_length = (*points_data != shared_points_bytes) ? points_data->length : 0; + unsigned points_data_length = (points_data != shared_points_bytes) ? points_data->length : 0; if (!tuple.compile_tuple_var_header (axes_index_map, points_data_length, axes_old_index_tag_map, shared_tuples_idx_map)) return false; @@ -1513,18 +1641,24 @@ struct TupleVariationData bool serialize_var_data (hb_serialize_context_t *c, bool is_gvar) const { TRACE_SERIALIZE (this); - if (is_gvar) - shared_points_bytes.copy (c); + if (is_gvar && shared_points_bytes) + { + hb_bytes_t s (shared_points_bytes->arrayZ, shared_points_bytes->length); + s.copy (c); + } for (const auto& tuple: tuple_vars) { const hb_vector_t<bool>* points_set = &(tuple.indices); - hb_bytes_t *point_data; + hb_vector_t<char> *point_data; if (!point_data_map.has (points_set, &point_data)) return_trace (false); - if (!is_gvar || *point_data != shared_points_bytes) - point_data->copy (c); + if (!is_gvar || point_data != shared_points_bytes) + { + hb_bytes_t s (point_data->arrayZ, point_data->length); + s.copy (c); + } tuple.compiled_deltas.as_array ().copy (c); if (c->in_error ()) return_trace (false); @@ -1711,13 +1845,15 @@ struct TupleVariationData const hb_map_t *axes_old_index_tag_map, const hb_vector_t<unsigned> &shared_indices, const hb_array_t<const F2DOT14> shared_tuples, - tuple_variations_t& tuple_variations /* OUT */) const + tuple_variations_t& tuple_variations, /* OUT */ + bool is_composite_glyph = false) const { return tuple_variations.create_from_tuple_var_data (iterator, tupleVarCount, point_count, is_gvar, axes_old_index_tag_map, shared_indices, - shared_tuples); + shared_tuples, + is_composite_glyph); } bool serialize (hb_serialize_context_t *c, @@ -1831,7 +1967,7 @@ struct item_variations_t const hb_map_t& get_varidx_map () const { return varidx_map; } - bool instantiate (const VariationStore& varStore, + bool instantiate (const ItemVariationStore& varStore, const hb_subset_plan_t *plan, bool optimize=true, bool use_no_variation_idx=true, @@ -1845,7 +1981,7 @@ struct item_variations_t } /* keep below APIs public only for unit test: test-item-varstore */ - bool create_from_item_varstore (const VariationStore& varStore, + bool create_from_item_varstore (const ItemVariationStore& varStore, const hb_map_t& axes_old_index_tag_map, const hb_array_t <const hb_inc_bimap_t> inner_maps = hb_array_t<const hb_inc_bimap_t> ()) { diff --git a/thirdparty/harfbuzz/src/hb-ot-var-gvar-table.hh b/thirdparty/harfbuzz/src/hb-ot-var-gvar-table.hh index 1c7a1f6c1e..59aad57e37 100644 --- a/thirdparty/harfbuzz/src/hb-ot-var-gvar-table.hh +++ b/thirdparty/harfbuzz/src/hb-ot-var-gvar-table.hh @@ -101,10 +101,15 @@ struct glyph_variations_t continue; } + bool is_composite_glyph = false; +#ifdef HB_EXPERIMENTAL_API + is_composite_glyph = plan->composite_new_gids.has (new_gid); +#endif if (!p->decompile_tuple_variations (all_contour_points->length, true /* is_gvar */, iterator, &(plan->axes_old_index_tag_map), shared_indices, shared_tuples, - tuple_vars /* OUT */)) + tuple_vars, /* OUT */ + is_composite_glyph)) return false; glyph_variations.push (std::move (tuple_vars)); } @@ -114,13 +119,17 @@ struct glyph_variations_t bool instantiate (const hb_subset_plan_t *plan) { unsigned count = plan->new_to_old_gid_list.length; + bool iup_optimize = false; +#ifdef HB_EXPERIMENTAL_API + iup_optimize = plan->flags & HB_SUBSET_FLAGS_OPTIMIZE_IUP_DELTAS; +#endif for (unsigned i = 0; i < count; i++) { hb_codepoint_t new_gid = plan->new_to_old_gid_list[i].first; contour_point_vector_t *all_points; if (!plan->new_gid_contour_points_map.has (new_gid, &all_points)) return false; - if (!glyph_variations[i].instantiate (plan->axes_location, plan->axes_triple_distances, all_points)) + if (!glyph_variations[i].instantiate (plan->axes_location, plan->axes_triple_distances, all_points, iup_optimize)) return false; } return true; @@ -340,7 +349,8 @@ struct gvar const glyph_variations_t& glyph_vars, Iterator it, unsigned axis_count, - unsigned num_glyphs) const + unsigned num_glyphs, + bool force_long_offsets) const { TRACE_SERIALIZE (this); gvar *out = c->allocate_min<gvar> (); @@ -352,7 +362,7 @@ struct gvar out->glyphCountX = hb_min (0xFFFFu, num_glyphs); unsigned glyph_var_data_size = glyph_vars.compiled_byte_size (); - bool long_offset = glyph_var_data_size & ~0xFFFFu; + bool long_offset = glyph_var_data_size & ~0xFFFFu || force_long_offsets; out->flags = long_offset ? 1 : 0; HBUINT8 *glyph_var_data_offsets = c->allocate_size<HBUINT8> ((long_offset ? 4 : 2) * (num_glyphs + 1), false); @@ -393,7 +403,12 @@ struct gvar unsigned axis_count = c->plan->axes_index_map.get_population (); unsigned num_glyphs = c->plan->num_output_glyphs (); auto it = hb_iter (c->plan->new_to_old_gid_list); - return_trace (serialize (c->serializer, glyph_vars, it, axis_count, num_glyphs)); + + bool force_long_offsets = false; +#ifdef HB_EXPERIMENTAL_API + force_long_offsets = c->plan->flags & HB_SUBSET_FLAGS_IFTB_REQUIREMENTS; +#endif + return_trace (serialize (c->serializer, glyph_vars, it, axis_count, num_glyphs, force_long_offsets)); } bool subset (hb_subset_context_t *c) const @@ -429,7 +444,7 @@ struct gvar } bool long_offset = (subset_data_size & ~0xFFFFu); - #ifdef HB_EXPERIMENTAL_API +#ifdef HB_EXPERIMENTAL_API long_offset = long_offset || (c->plan->flags & HB_SUBSET_FLAGS_IFTB_REQUIREMENTS); #endif out->flags = long_offset ? 1 : 0; diff --git a/thirdparty/harfbuzz/src/hb-ot-var-hvar-table.hh b/thirdparty/harfbuzz/src/hb-ot-var-hvar-table.hh index 53a4642d38..33a4e1a40e 100644 --- a/thirdparty/harfbuzz/src/hb-ot-var-hvar-table.hh +++ b/thirdparty/harfbuzz/src/hb-ot-var-hvar-table.hh @@ -188,7 +188,7 @@ struct hvarvvar_subset_plan_t ~hvarvvar_subset_plan_t() { fini (); } void init (const hb_array_t<const DeltaSetIndexMap *> &index_maps, - const VariationStore &_var_store, + const ItemVariationStore &_var_store, const hb_subset_plan_t *plan) { index_map_plans.resize (index_maps.length); @@ -263,7 +263,7 @@ struct hvarvvar_subset_plan_t hb_inc_bimap_t outer_map; hb_vector_t<hb_inc_bimap_t> inner_maps; hb_vector_t<index_map_subset_plan_t> index_map_plans; - const VariationStore *var_store; + const ItemVariationStore *var_store; protected: hb_vector_t<hb_set_t *> inner_sets; @@ -296,7 +296,7 @@ struct HVARVVAR rsbMap.sanitize (c, this)); } - const VariationStore& get_var_store () const + const ItemVariationStore& get_var_store () const { return this+varStore; } void listup_index_maps (hb_vector_t<const DeltaSetIndexMap *> &index_maps) const @@ -384,7 +384,7 @@ struct HVARVVAR float get_advance_delta_unscaled (hb_codepoint_t glyph, const int *coords, unsigned int coord_count, - VariationStore::cache_t *store_cache = nullptr) const + ItemVariationStore::cache_t *store_cache = nullptr) const { uint32_t varidx = (this+advMap).map (glyph); return (this+varStore).get_delta (varidx, @@ -405,7 +405,7 @@ struct HVARVVAR public: FixedVersion<>version; /* Version of the metrics variation table * initially set to 0x00010000u */ - Offset32To<VariationStore> + Offset32To<ItemVariationStore> varStore; /* Offset to item variation store table. */ Offset32To<DeltaSetIndexMap> advMap; /* Offset to advance var-idx mapping. */ diff --git a/thirdparty/harfbuzz/src/hb-ot-var-mvar-table.hh b/thirdparty/harfbuzz/src/hb-ot-var-mvar-table.hh index 6d69777618..1f0401d1d3 100644 --- a/thirdparty/harfbuzz/src/hb-ot-var-mvar-table.hh +++ b/thirdparty/harfbuzz/src/hb-ot-var-mvar-table.hh @@ -56,7 +56,7 @@ struct VariationValueRecord public: Tag valueTag; /* Four-byte tag identifying a font-wide measure. */ - VarIdx varIdx; /* Outer/inner index into VariationStore item. */ + VarIdx varIdx; /* Outer/inner index into ItemVariationStore item. */ public: DEFINE_SIZE_STATIC (8); @@ -106,7 +106,7 @@ struct MVAR out->valueRecordCount = valueRecordCount; item_variations_t item_vars; - const VariationStore& src_var_store = this+varStore; + const ItemVariationStore& src_var_store = this+varStore; if (!item_vars.instantiate (src_var_store, c->plan)) return_trace (false); @@ -159,7 +159,7 @@ protected: HBUINT16 valueRecordSize;/* The size in bytes of each value record — * must be greater than zero. */ HBUINT16 valueRecordCount;/* The number of value records — may be zero. */ - Offset16To<VariationStore> + Offset16To<ItemVariationStore> varStore; /* Offset to item variation store table. */ UnsizedArrayOf<HBUINT8> valuesZ; /* Array of value records. The records must be diff --git a/thirdparty/harfbuzz/src/hb-priority-queue.hh b/thirdparty/harfbuzz/src/hb-priority-queue.hh index 9b962a29d9..274d5df4c5 100644 --- a/thirdparty/harfbuzz/src/hb-priority-queue.hh +++ b/thirdparty/harfbuzz/src/hb-priority-queue.hh @@ -163,7 +163,7 @@ struct hb_priority_queue_t goto repeat; } - void swap (unsigned a, unsigned b) + void swap (unsigned a, unsigned b) noexcept { assert (a < heap.length); assert (b < heap.length); diff --git a/thirdparty/harfbuzz/src/hb-repacker.hh b/thirdparty/harfbuzz/src/hb-repacker.hh index e9cd376ad3..ed40f271cc 100644 --- a/thirdparty/harfbuzz/src/hb-repacker.hh +++ b/thirdparty/harfbuzz/src/hb-repacker.hh @@ -239,6 +239,54 @@ bool _try_isolating_subgraphs (const hb_vector_t<graph::overflow_record_t>& over } static inline +bool _resolve_shared_overflow(const hb_vector_t<graph::overflow_record_t>& overflows, + int overflow_index, + graph_t& sorted_graph) +{ + const graph::overflow_record_t& r = overflows[overflow_index]; + + // Find all of the parents in overflowing links that link to this + // same child node. We will then try duplicating the child node and + // re-assigning all of these parents to the duplicate. + hb_set_t parents; + parents.add(r.parent); + for (int i = overflow_index - 1; i >= 0; i--) { + const graph::overflow_record_t& r2 = overflows[i]; + if (r2.child == r.child) { + parents.add(r2.parent); + } + } + + unsigned result = sorted_graph.duplicate(&parents, r.child); + if (result == (unsigned) -1 && parents.get_population() > 2) { + // All links to the child are overflowing, so we can't include all + // in the duplication. Remove one parent from the duplication. + // Remove the lowest index parent, which will be the closest to the child. + parents.del(parents.get_min()); + result = sorted_graph.duplicate(&parents, r.child); + } + + if (result == (unsigned) -1) return result; + + if (parents.get_population() > 1) { + // If the duplicated node has more than one parent pre-emptively raise it's priority to the maximum. + // This will place it close to the parents. Node's with only one parent, don't need this as normal overflow + // resolution will raise priority if needed. + // + // Reasoning: most of the parents to this child are likely at the same layer in the graph. Duplicating + // the child will theoretically allow it to be placed closer to it's parents. However, due to the shortest + // distance sort by default it's placement will remain in the same layer, thus it will remain in roughly the + // same position (and distance from parents) as the original child node. The overflow resolution will attempt + // to move nodes closer, but only for non-shared nodes. Since this node is shared, it will simply be given + // further duplication which defeats the attempt to duplicate with multiple parents. To fix this we + // pre-emptively raise priority now which allows the duplicated node to pack into the same layer as it's parents. + sorted_graph.vertices_[result].give_max_priority(); + } + + return result; +} + +static inline bool _process_overflows (const hb_vector_t<graph::overflow_record_t>& overflows, hb_set_t& priority_bumped_parents, graph_t& sorted_graph) @@ -254,7 +302,7 @@ bool _process_overflows (const hb_vector_t<graph::overflow_record_t>& overflows, { // The child object is shared, we may be able to eliminate the overflow // by duplicating it. - if (sorted_graph.duplicate (r.parent, r.child) == (unsigned) -1) continue; + if (!_resolve_shared_overflow(overflows, i, sorted_graph)) continue; return true; } @@ -388,7 +436,7 @@ template<typename T> inline hb_blob_t* hb_resolve_overflows (const T& packed, hb_tag_t table_tag, - unsigned max_rounds = 20, + unsigned max_rounds = 32, bool recalculate_extensions = false) { graph_t sorted_graph (packed); if (sorted_graph.in_error ()) diff --git a/thirdparty/harfbuzz/src/hb-serialize.hh b/thirdparty/harfbuzz/src/hb-serialize.hh index 15eccb6a09..e988451eb3 100644 --- a/thirdparty/harfbuzz/src/hb-serialize.hh +++ b/thirdparty/harfbuzz/src/hb-serialize.hh @@ -91,7 +91,27 @@ struct hb_serialize_context_t } #endif - friend void swap (object_t& a, object_t& b) + bool add_virtual_link (objidx_t objidx) + { + if (!objidx) + return false; + + auto& link = *virtual_links.push (); + if (virtual_links.in_error ()) + return false; + + link.objidx = objidx; + // Remaining fields were previously zero'd by push(): + // link.width = 0; + // link.is_signed = 0; + // link.whence = 0; + // link.position = 0; + // link.bias = 0; + + return true; + } + + friend void swap (object_t& a, object_t& b) noexcept { hb_swap (a.head, b.head); hb_swap (a.tail, b.tail); @@ -156,9 +176,9 @@ struct hb_serialize_context_t object_t *next; auto all_links () const HB_AUTO_RETURN - (( hb_concat (this->real_links, this->virtual_links) )); + (( hb_concat (real_links, virtual_links) )); auto all_links_writer () HB_AUTO_RETURN - (( hb_concat (this->real_links.writer (), this->virtual_links.writer ()) )); + (( hb_concat (real_links.writer (), virtual_links.writer ()) )); }; struct snapshot_t @@ -469,16 +489,40 @@ struct hb_serialize_context_t assert (current); - auto& link = *current->virtual_links.push (); - if (current->virtual_links.in_error ()) + if (!current->add_virtual_link(objidx)) err (HB_SERIALIZE_ERROR_OTHER); + } - link.width = 0; - link.objidx = objidx; - link.is_signed = 0; - link.whence = 0; - link.position = 0; - link.bias = 0; + objidx_t last_added_child_index() const { + if (unlikely (in_error ())) return (objidx_t) -1; + + assert (current); + if (!bool(current->real_links)) { + return (objidx_t) -1; + } + + return current->real_links[current->real_links.length - 1].objidx; + } + + // For the current object ensure that the sub-table bytes for child objidx are always placed + // after the subtable bytes for any other existing children. This only ensures that the + // repacker will not move the target subtable before the other children + // (by adding virtual links). It is up to the caller to ensure the initial serialization + // order is correct. + void repack_last(objidx_t objidx) { + if (unlikely (in_error ())) return; + + if (!objidx) + return; + + assert (current); + for (auto& l : current->real_links) { + if (l.objidx == objidx) { + continue; + } + + packed[l.objidx]->add_virtual_link(objidx); + } } template <typename T> diff --git a/thirdparty/harfbuzz/src/hb-set.hh b/thirdparty/harfbuzz/src/hb-set.hh index ff2a170d2d..ce69ea2c9b 100644 --- a/thirdparty/harfbuzz/src/hb-set.hh +++ b/thirdparty/harfbuzz/src/hb-set.hh @@ -44,10 +44,10 @@ struct hb_sparseset_t ~hb_sparseset_t () { fini (); } hb_sparseset_t (const hb_sparseset_t& other) : hb_sparseset_t () { set (other); } - hb_sparseset_t (hb_sparseset_t&& other) : hb_sparseset_t () { s = std::move (other.s); } + hb_sparseset_t (hb_sparseset_t&& other) noexcept : hb_sparseset_t () { s = std::move (other.s); } hb_sparseset_t& operator = (const hb_sparseset_t& other) { set (other); return *this; } - hb_sparseset_t& operator = (hb_sparseset_t&& other) { s = std::move (other.s); return *this; } - friend void swap (hb_sparseset_t& a, hb_sparseset_t& b) { hb_swap (a.s, b.s); } + hb_sparseset_t& operator = (hb_sparseset_t&& other) noexcept { s = std::move (other.s); return *this; } + friend void swap (hb_sparseset_t& a, hb_sparseset_t& b) noexcept { hb_swap (a.s, b.s); } hb_sparseset_t (std::initializer_list<hb_codepoint_t> lst) : hb_sparseset_t () { @@ -166,7 +166,7 @@ struct hb_set_t : hb_sparseset_t<hb_bit_set_invertible_t> ~hb_set_t () = default; hb_set_t () : sparseset () {}; hb_set_t (const hb_set_t &o) : sparseset ((sparseset &) o) {}; - hb_set_t (hb_set_t&& o) : sparseset (std::move ((sparseset &) o)) {} + hb_set_t (hb_set_t&& o) noexcept : sparseset (std::move ((sparseset &) o)) {} hb_set_t& operator = (const hb_set_t&) = default; hb_set_t& operator = (hb_set_t&&) = default; hb_set_t (std::initializer_list<hb_codepoint_t> lst) : sparseset (lst) {} diff --git a/thirdparty/harfbuzz/src/hb-subset-cff2.cc b/thirdparty/harfbuzz/src/hb-subset-cff2.cc index abc108e571..eb5cb0c625 100644 --- a/thirdparty/harfbuzz/src/hb-subset-cff2.cc +++ b/thirdparty/harfbuzz/src/hb-subset-cff2.cc @@ -248,7 +248,7 @@ struct cff2_subr_subsetter_t : subr_subsetter_t<cff2_subr_subsetter_t, CFF2Subrs struct cff2_private_blend_encoder_param_t { cff2_private_blend_encoder_param_t (hb_serialize_context_t *c, - const CFF2VariationStore *varStore, + const CFF2ItemVariationStore *varStore, hb_array_t<int> normalized_coords) : c (c), varStore (varStore), normalized_coords (normalized_coords) {} @@ -284,7 +284,7 @@ struct cff2_private_blend_encoder_param_t unsigned ivs = 0; unsigned region_count = 0; hb_vector_t<float> scalars; - const CFF2VariationStore *varStore = nullptr; + const CFF2ItemVariationStore *varStore = nullptr; hb_array_t<int> normalized_coords; }; @@ -378,7 +378,7 @@ struct cff2_private_dict_blend_opset_t : dict_opset_t struct cff2_private_dict_op_serializer_t : op_serializer_t { cff2_private_dict_op_serializer_t (bool desubroutinize_, bool drop_hints_, bool pinned_, - const CFF::CFF2VariationStore* varStore_, + const CFF::CFF2ItemVariationStore* varStore_, hb_array_t<int> normalized_coords_) : desubroutinize (desubroutinize_), drop_hints (drop_hints_), pinned (pinned_), varStore (varStore_), normalized_coords (normalized_coords_) {} @@ -416,7 +416,7 @@ struct cff2_private_dict_op_serializer_t : op_serializer_t const bool desubroutinize; const bool drop_hints; const bool pinned; - const CFF::CFF2VariationStore* varStore; + const CFF::CFF2ItemVariationStore* varStore; hb_array_t<int> normalized_coords; }; @@ -628,10 +628,10 @@ OT::cff2::accelerator_subset_t::serialize (hb_serialize_context_t *c, } /* variation store */ - if (varStore != &Null (CFF2VariationStore) && + if (varStore != &Null (CFF2ItemVariationStore) && !plan.pinned) { - auto *dest = c->push<CFF2VariationStore> (); + auto *dest = c->push<CFF2ItemVariationStore> (); if (unlikely (!dest->serialize (c, varStore))) { c->pop_discard (); diff --git a/thirdparty/harfbuzz/src/hb-subset-input.cc b/thirdparty/harfbuzz/src/hb-subset-input.cc index 1e0a89a630..68a3e77788 100644 --- a/thirdparty/harfbuzz/src/hb-subset-input.cc +++ b/thirdparty/harfbuzz/src/hb-subset-input.cc @@ -24,6 +24,7 @@ * Google Author(s): Garret Rieger, Rod Sheeter, Behdad Esfahbod */ +#include "hb-subset-instancer-solver.hh" #include "hb-subset.hh" #include "hb-set.hh" #include "hb-utf.hh" @@ -50,7 +51,6 @@ hb_subset_input_t::hb_subset_input_t () HB_TAG ('k', 'e', 'r', 'n'), // Copied from fontTools: - HB_TAG ('B', 'A', 'S', 'E'), HB_TAG ('J', 'S', 'T', 'F'), HB_TAG ('D', 'S', 'I', 'G'), HB_TAG ('E', 'B', 'D', 'T'), @@ -418,6 +418,46 @@ hb_subset_input_keep_everything (hb_subset_input_t *input) #ifndef HB_NO_VAR /** + * hb_subset_input_pin_all_axes_to_default: (skip) + * @input: a #hb_subset_input_t object. + * @face: a #hb_face_t object. + * + * Pin all axes to default locations in the given subset input object. + * + * All axes in a font must be pinned. Additionally, `CFF2` table, if present, + * will be de-subroutinized. + * + * Return value: `true` if success, `false` otherwise + * + * Since: 8.3.1 + **/ +HB_EXTERN hb_bool_t +hb_subset_input_pin_all_axes_to_default (hb_subset_input_t *input, + hb_face_t *face) +{ + unsigned axis_count = hb_ot_var_get_axis_count (face); + if (!axis_count) return false; + + hb_ot_var_axis_info_t *axis_infos = (hb_ot_var_axis_info_t *) hb_calloc (axis_count, sizeof (hb_ot_var_axis_info_t)); + if (unlikely (!axis_infos)) return false; + + (void) hb_ot_var_get_axis_infos (face, 0, &axis_count, axis_infos); + + for (unsigned i = 0; i < axis_count; i++) + { + hb_tag_t axis_tag = axis_infos[i].tag; + float default_val = axis_infos[i].default_value; + if (!input->axes_location.set (axis_tag, Triple (default_val, default_val, default_val))) + { + hb_free (axis_infos); + return false; + } + } + hb_free (axis_infos); + return true; +} + +/** * hb_subset_input_pin_axis_to_default: (skip) * @input: a #hb_subset_input_t object. * @face: a #hb_face_t object. @@ -481,16 +521,13 @@ hb_subset_input_pin_axis_location (hb_subset_input_t *input, * @input: a #hb_subset_input_t object. * @face: a #hb_face_t object. * @axis_tag: Tag of the axis - * @axis_min_value: Minimum value of the axis variation range to set - * @axis_max_value: Maximum value of the axis variation range to set - * @axis_def_value: Default value of the axis variation range to set, in case of - * null, it'll be determined automatically + * @axis_min_value: Minimum value of the axis variation range to set, if NaN the existing min will be used. + * @axis_max_value: Maximum value of the axis variation range to set if NaN the existing max will be used. + * @axis_def_value: Default value of the axis variation range to set, if NaN the existing default will be used. * * Restricting the range of variation on an axis in the given subset input object. * New min/default/max values will be clamped if they're not within the fvar axis range. - * If the new default value is null: - * If the fvar axis default value is within the new range, then new default - * value is the same as original default value. + * * If the fvar axis default value is not within the new range, the new default * value will be changed to the new min or max value, whichever is closer to the fvar * axis default. @@ -509,21 +546,57 @@ hb_subset_input_set_axis_range (hb_subset_input_t *input, hb_tag_t axis_tag, float axis_min_value, float axis_max_value, - float *axis_def_value /* IN, maybe NULL */) + float axis_def_value) { - if (axis_min_value > axis_max_value) - return false; - hb_ot_var_axis_info_t axis_info; if (!hb_ot_var_find_axis_info (face, axis_tag, &axis_info)) return false; - float new_min_val = hb_clamp(axis_min_value, axis_info.min_value, axis_info.max_value); - float new_max_val = hb_clamp(axis_max_value, axis_info.min_value, axis_info.max_value); - float new_default_val = axis_def_value ? *axis_def_value : axis_info.default_value; - new_default_val = hb_clamp(new_default_val, new_min_val, new_max_val); + float min = !std::isnan(axis_min_value) ? axis_min_value : axis_info.min_value; + float max = !std::isnan(axis_max_value) ? axis_max_value : axis_info.max_value; + float def = !std::isnan(axis_def_value) ? axis_def_value : axis_info.default_value; + + if (min > max) + return false; + + float new_min_val = hb_clamp(min, axis_info.min_value, axis_info.max_value); + float new_max_val = hb_clamp(max, axis_info.min_value, axis_info.max_value); + float new_default_val = hb_clamp(def, new_min_val, new_max_val); return input->axes_location.set (axis_tag, Triple (new_min_val, new_default_val, new_max_val)); } + +/** + * hb_subset_input_get_axis_range: (skip) + * @input: a #hb_subset_input_t object. + * @axis_tag: Tag of the axis + * @axis_min_value: Set to the previously configured minimum value of the axis variation range. + * @axis_max_value: Set to the previously configured maximum value of the axis variation range. + * @axis_def_value: Set to the previously configured default value of the axis variation range. + * + * Gets the axis range assigned by previous calls to hb_subset_input_set_axis_range. + * + * Return value: `true` if a range has been set for this axis tag, `false` otherwise. + * + * XSince: EXPERIMENTAL + **/ +HB_EXTERN hb_bool_t +hb_subset_input_get_axis_range (hb_subset_input_t *input, + hb_tag_t axis_tag, + float *axis_min_value, + float *axis_max_value, + float *axis_def_value) + +{ + Triple* triple; + if (!input->axes_location.has(axis_tag, &triple)) { + return false; + } + + *axis_min_value = triple->minimum; + *axis_def_value = triple->middle; + *axis_max_value = triple->maximum; + return true; +} #endif #endif diff --git a/thirdparty/harfbuzz/src/hb-subset-instancer-iup.cc b/thirdparty/harfbuzz/src/hb-subset-instancer-iup.cc new file mode 100644 index 0000000000..35a964d082 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-subset-instancer-iup.cc @@ -0,0 +1,532 @@ +/* + * Copyright © 2024 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#include "hb-subset-instancer-iup.hh" + +/* This file is a straight port of the following: + * + * https://github.com/fonttools/fonttools/blob/main/Lib/fontTools/varLib/iup.py + * + * Where that file returns optimzied deltas vector, we return optimized + * referenced point indices. + */ + +constexpr static unsigned MAX_LOOKBACK = 8; + +static void _iup_contour_bound_forced_set (const hb_array_t<const contour_point_t> contour_points, + const hb_array_t<const int> x_deltas, + const hb_array_t<const int> y_deltas, + hb_set_t& forced_set, /* OUT */ + float tolerance = 0.f) +{ + unsigned len = contour_points.length; + unsigned next_i = 0; + for (int i = len - 1; i >= 0; i--) + { + unsigned last_i = (len + i -1) % len; + for (unsigned j = 0; j < 2; j++) + { + float cj, lcj, ncj; + int dj, ldj, ndj; + if (j == 0) + { + cj = contour_points.arrayZ[i].x; + dj = x_deltas.arrayZ[i]; + lcj = contour_points.arrayZ[last_i].x; + ldj = x_deltas.arrayZ[last_i]; + ncj = contour_points.arrayZ[next_i].x; + ndj = x_deltas.arrayZ[next_i]; + } + else + { + cj = contour_points.arrayZ[i].y; + dj = y_deltas.arrayZ[i]; + lcj = contour_points.arrayZ[last_i].y; + ldj = y_deltas.arrayZ[last_i]; + ncj = contour_points.arrayZ[next_i].y; + ndj = y_deltas.arrayZ[next_i]; + } + + float c1, c2; + int d1, d2; + if (lcj <= ncj) + { + c1 = lcj; + c2 = ncj; + d1 = ldj; + d2 = ndj; + } + else + { + c1 = ncj; + c2 = lcj; + d1 = ndj; + d2 = ldj; + } + + bool force = false; + if (c1 == c2) + { + if (abs (d1 - d2) > tolerance && abs (dj) > tolerance) + force = true; + } + else if (c1 <= cj && cj <= c2) + { + if (!(hb_min (d1, d2) - tolerance <= dj && + dj <= hb_max (d1, d2) + tolerance)) + force = true; + } + else + { + if (d1 != d2) + { + if (cj < c1) + { + if (abs (dj) > tolerance && + abs (dj - d1) > tolerance && + ((dj - tolerance < d1) != (d1 < d2))) + force = true; + } + else + { + if (abs (dj) > tolerance && + abs (dj - d2) > tolerance && + ((d2 < dj + tolerance) != (d1 < d2))) + force = true; + } + } + } + + if (force) + { + forced_set.add (i); + break; + } + } + next_i = i; + } +} + +template <typename T, + hb_enable_if (hb_is_trivially_copyable (T))> +static bool rotate_array (const hb_array_t<const T>& org_array, + int k, + hb_vector_t<T>& out) +{ + unsigned n = org_array.length; + if (!n) return true; + if (unlikely (!out.resize (n, false))) + return false; + + unsigned item_size = hb_static_size (T); + if (k < 0) + k = n - (-k) % n; + else + k %= n; + + hb_memcpy ((void *) out.arrayZ, (const void *) (org_array.arrayZ + n - k), k * item_size); + hb_memcpy ((void *) (out.arrayZ + k), (const void *) org_array.arrayZ, (n - k) * item_size); + return true; +} + +static bool rotate_set (const hb_set_t& org_set, + int k, + unsigned n, + hb_set_t& out) +{ + if (!n) return false; + k %= n; + if (k < 0) + k = n + k; + + if (k == 0) + { + out.set (org_set); + } + else + { + for (auto v : org_set) + out.add ((v + k) % n); + } + return !out.in_error (); +} + +/* Given two reference coordinates (start and end of contour_points array), + * output interpolated deltas for points in between */ +static bool _iup_segment (const hb_array_t<const contour_point_t> contour_points, + const hb_array_t<const int> x_deltas, + const hb_array_t<const int> y_deltas, + const contour_point_t& p1, const contour_point_t& p2, + int p1_dx, int p2_dx, + int p1_dy, int p2_dy, + hb_vector_t<float>& interp_x_deltas, /* OUT */ + hb_vector_t<float>& interp_y_deltas /* OUT */) +{ + unsigned n = contour_points.length; + if (unlikely (!interp_x_deltas.resize (n, false) || + !interp_y_deltas.resize (n, false))) + return false; + + for (unsigned j = 0; j < 2; j++) + { + float x1, x2, d1, d2; + float *out; + if (j == 0) + { + x1 = p1.x; + x2 = p2.x; + d1 = p1_dx; + d2 = p2_dx; + out = interp_x_deltas.arrayZ; + } + else + { + x1 = p1.y; + x2 = p2.y; + d1 = p1_dy; + d2 = p2_dy; + out = interp_y_deltas.arrayZ; + } + + if (x1 == x2) + { + if (d1 == d2) + { + for (unsigned i = 0; i < n; i++) + out[i] = d1; + } + else + { + for (unsigned i = 0; i < n; i++) + out[i] = 0.f; + } + continue; + } + + if (x1 > x2) + { + hb_swap (x1, x2); + hb_swap (d1, d2); + } + + float scale = (d2 - d1) / (x2 - x1); + for (unsigned i = 0; i < n; i++) + { + float x = j == 0 ? contour_points.arrayZ[i].x : contour_points.arrayZ[i].y; + float d; + if (x <= x1) + d = d1; + else if (x >= x2) + d = d2; + else + d = d1 + (x - x1) * scale; + + out[i] = d; + } + } + return true; +} + +static bool _can_iup_in_between (const hb_array_t<const contour_point_t> contour_points, + const hb_array_t<const int> x_deltas, + const hb_array_t<const int> y_deltas, + const contour_point_t& p1, const contour_point_t& p2, + int p1_dx, int p2_dx, + int p1_dy, int p2_dy, + float tolerance) +{ + hb_vector_t<float> interp_x_deltas, interp_y_deltas; + if (!_iup_segment (contour_points, x_deltas, y_deltas, + p1, p2, p1_dx, p2_dx, p1_dy, p2_dy, + interp_x_deltas, interp_y_deltas)) + return false; + + unsigned num = contour_points.length; + + for (unsigned i = 0; i < num; i++) + { + float dx = x_deltas.arrayZ[i] - interp_x_deltas.arrayZ[i]; + float dy = y_deltas.arrayZ[i] - interp_y_deltas.arrayZ[i]; + + if (sqrtf ((float)dx * dx + (float)dy * dy) > tolerance) + return false; + } + return true; +} + +static bool _iup_contour_optimize_dp (const contour_point_vector_t& contour_points, + const hb_vector_t<int>& x_deltas, + const hb_vector_t<int>& y_deltas, + const hb_set_t& forced_set, + float tolerance, + unsigned lookback, + hb_vector_t<unsigned>& costs, /* OUT */ + hb_vector_t<int>& chain /* OUT */) +{ + unsigned n = contour_points.length; + if (unlikely (!costs.resize (n, false) || + !chain.resize (n, false))) + return false; + + lookback = hb_min (lookback, MAX_LOOKBACK); + + for (unsigned i = 0; i < n; i++) + { + unsigned best_cost = (i == 0 ? 1 : costs.arrayZ[i-1] + 1); + + costs.arrayZ[i] = best_cost; + chain.arrayZ[i] = (i == 0 ? -1 : i - 1); + + if (i > 0 && forced_set.has (i - 1)) + continue; + + int lookback_index = hb_max ((int) i - (int) lookback + 1, -1); + for (int j = i - 2; j >= lookback_index; j--) + { + unsigned cost = j == -1 ? 1 : costs.arrayZ[j] + 1; + /* num points between i and j */ + unsigned num_points = i - j - 1; + unsigned p1 = (j == -1 ? n - 1 : j); + if (cost < best_cost && + _can_iup_in_between (contour_points.as_array ().sub_array (j + 1, num_points), + x_deltas.as_array ().sub_array (j + 1, num_points), + y_deltas.as_array ().sub_array (j + 1, num_points), + contour_points.arrayZ[p1], contour_points.arrayZ[i], + x_deltas.arrayZ[p1], x_deltas.arrayZ[i], + y_deltas.arrayZ[p1], y_deltas.arrayZ[i], + tolerance)) + { + best_cost = cost; + costs.arrayZ[i] = best_cost; + chain.arrayZ[i] = j; + } + + if (j > 0 && forced_set.has (j)) + break; + } + } + return true; +} + +static bool _iup_contour_optimize (const hb_array_t<const contour_point_t> contour_points, + const hb_array_t<const int> x_deltas, + const hb_array_t<const int> y_deltas, + hb_array_t<bool> opt_indices, /* OUT */ + float tolerance = 0.f) +{ + unsigned n = contour_points.length; + if (opt_indices.length != n || + x_deltas.length != n || + y_deltas.length != n) + return false; + + bool all_within_tolerance = true; + for (unsigned i = 0; i < n; i++) + { + int dx = x_deltas.arrayZ[i]; + int dy = y_deltas.arrayZ[i]; + if (sqrtf ((float)dx * dx + (float)dy * dy) > tolerance) + { + all_within_tolerance = false; + break; + } + } + + /* If all are within tolerance distance, do nothing, opt_indices is + * initilized to false */ + if (all_within_tolerance) + return true; + + /* If there's exactly one point, return it */ + if (n == 1) + { + opt_indices.arrayZ[0] = true; + return true; + } + + /* If all deltas are exactly the same, return just one (the first one) */ + bool all_deltas_are_equal = true; + for (unsigned i = 1; i < n; i++) + if (x_deltas.arrayZ[i] != x_deltas.arrayZ[0] || + y_deltas.arrayZ[i] != y_deltas.arrayZ[0]) + { + all_deltas_are_equal = false; + break; + } + + if (all_deltas_are_equal) + { + opt_indices.arrayZ[0] = true; + return true; + } + + /* else, solve the general problem using Dynamic Programming */ + hb_set_t forced_set; + _iup_contour_bound_forced_set (contour_points, x_deltas, y_deltas, forced_set, tolerance); + + if (!forced_set.is_empty ()) + { + int k = n - 1 - forced_set.get_max (); + if (k < 0) + return false; + + hb_vector_t<int> rot_x_deltas, rot_y_deltas; + contour_point_vector_t rot_points; + hb_set_t rot_forced_set; + if (!rotate_array (contour_points, k, rot_points) || + !rotate_array (x_deltas, k, rot_x_deltas) || + !rotate_array (y_deltas, k, rot_y_deltas) || + !rotate_set (forced_set, k, n, rot_forced_set)) + return false; + + hb_vector_t<unsigned> costs; + hb_vector_t<int> chain; + + if (!_iup_contour_optimize_dp (rot_points, rot_x_deltas, rot_y_deltas, + rot_forced_set, tolerance, n, + costs, chain)) + return false; + + hb_set_t solution; + int index = n - 1; + while (index != -1) + { + solution.add (index); + index = chain.arrayZ[index]; + } + + if (solution.is_empty () || + forced_set.get_population () > solution.get_population ()) + return false; + + for (unsigned i : solution) + opt_indices.arrayZ[i] = true; + + hb_vector_t<bool> rot_indices; + const hb_array_t<const bool> opt_indices_array (opt_indices.arrayZ, opt_indices.length); + rotate_array (opt_indices_array, -k, rot_indices); + + for (unsigned i = 0; i < n; i++) + opt_indices.arrayZ[i] = rot_indices.arrayZ[i]; + } + else + { + hb_vector_t<int> repeat_x_deltas, repeat_y_deltas; + contour_point_vector_t repeat_points; + + if (unlikely (!repeat_x_deltas.resize (n * 2, false) || + !repeat_y_deltas.resize (n * 2, false) || + !repeat_points.resize (n * 2, false))) + return false; + + unsigned contour_point_size = hb_static_size (contour_point_t); + for (unsigned i = 0; i < n; i++) + { + hb_memcpy ((void *) repeat_x_deltas.arrayZ, (const void *) x_deltas.arrayZ, n * sizeof (float)); + hb_memcpy ((void *) (repeat_x_deltas.arrayZ + n), (const void *) x_deltas.arrayZ, n * sizeof (float)); + + hb_memcpy ((void *) repeat_y_deltas.arrayZ, (const void *) y_deltas.arrayZ, n * sizeof (float)); + hb_memcpy ((void *) (repeat_y_deltas.arrayZ + n), (const void *) y_deltas.arrayZ, n * sizeof (float)); + + hb_memcpy ((void *) repeat_points.arrayZ, (const void *) contour_points.arrayZ, n * contour_point_size); + hb_memcpy ((void *) (repeat_points.arrayZ + n), (const void *) contour_points.arrayZ, n * contour_point_size); + } + + hb_vector_t<unsigned> costs; + hb_vector_t<int> chain; + if (!_iup_contour_optimize_dp (repeat_points, repeat_x_deltas, repeat_y_deltas, + forced_set, tolerance, n, + costs, chain)) + return false; + + unsigned best_cost = n + 1; + int len = costs.length; + hb_set_t best_sol; + for (int start = n - 1; start < len; start++) + { + hb_set_t solution; + int i = start; + int lookback = start - (int) n; + while (i > lookback) + { + solution.add (i % n); + i = chain.arrayZ[i]; + } + if (i == lookback) + { + unsigned cost_i = i < 0 ? 0 : costs.arrayZ[i]; + unsigned cost = costs.arrayZ[start] - cost_i; + if (cost <= best_cost) + { + best_sol.set (solution); + best_cost = cost; + } + } + } + + for (unsigned i = 0; i < n; i++) + if (best_sol.has (i)) + opt_indices.arrayZ[i] = true; + } + return true; +} + +bool iup_delta_optimize (const contour_point_vector_t& contour_points, + const hb_vector_t<int>& x_deltas, + const hb_vector_t<int>& y_deltas, + hb_vector_t<bool>& opt_indices, /* OUT */ + float tolerance) +{ + if (!opt_indices.resize (contour_points.length)) + return false; + + hb_vector_t<unsigned> end_points; + unsigned count = contour_points.length; + if (unlikely (!end_points.alloc (count))) + return false; + + for (unsigned i = 0; i < count - 4; i++) + if (contour_points.arrayZ[i].is_end_point) + end_points.push (i); + + /* phantom points */ + for (unsigned i = count - 4; i < count; i++) + end_points.push (i); + + if (end_points.in_error ()) return false; + + unsigned start = 0; + for (unsigned end : end_points) + { + unsigned len = end - start + 1; + if (!_iup_contour_optimize (contour_points.as_array ().sub_array (start, len), + x_deltas.as_array ().sub_array (start, len), + y_deltas.as_array ().sub_array (start, len), + opt_indices.as_array ().sub_array (start, len), + tolerance)) + return false; + start = end + 1; + } + return true; +} diff --git a/thirdparty/harfbuzz/src/hb-subset-instancer-iup.hh b/thirdparty/harfbuzz/src/hb-subset-instancer-iup.hh new file mode 100644 index 0000000000..7eac5935a4 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-subset-instancer-iup.hh @@ -0,0 +1,37 @@ +/* + * Copyright © 2024 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#ifndef HB_SUBSET_INSTANCER_IUP_HH +#define HB_SUBSET_INSTANCER_IUP_HH + +#include "hb-subset-plan.hh" +/* given contour points and deltas, optimize a set of referenced points within error + * tolerance. Returns optimized referenced point indices */ +HB_INTERNAL bool iup_delta_optimize (const contour_point_vector_t& contour_points, + const hb_vector_t<int>& x_deltas, + const hb_vector_t<int>& y_deltas, + hb_vector_t<bool>& opt_indices, /* OUT */ + float tolerance = 0.f); + +#endif /* HB_SUBSET_INSTANCER_IUP_HH */ diff --git a/thirdparty/harfbuzz/src/hb-subset-instancer-solver.cc b/thirdparty/harfbuzz/src/hb-subset-instancer-solver.cc index 4876bc4379..70783c0a0d 100644 --- a/thirdparty/harfbuzz/src/hb-subset-instancer-solver.cc +++ b/thirdparty/harfbuzz/src/hb-subset-instancer-solver.cc @@ -256,7 +256,10 @@ _solve (Triple tent, Triple axisLimit, bool negative = false) */ float newUpper = peak + (1 - gain) * (upper - peak); assert (axisMax <= newUpper); // Because outGain > gain - if (newUpper <= axisDef + (axisMax - axisDef) * 2) + /* Disabled because ots doesn't like us: + * https://github.com/fonttools/fonttools/issues/3350 */ + + if (false && (newUpper <= axisDef + (axisMax - axisDef) * 2)) { upper = newUpper; if (!negative && axisDef + (axisMax - axisDef) * MAX_F2DOT14 < upper) diff --git a/thirdparty/harfbuzz/src/hb-subset-plan-member-list.hh b/thirdparty/harfbuzz/src/hb-subset-plan-member-list.hh index 71da80e387..74416b92f9 100644 --- a/thirdparty/harfbuzz/src/hb-subset-plan-member-list.hh +++ b/thirdparty/harfbuzz/src/hb-subset-plan-member-list.hh @@ -140,6 +140,15 @@ HB_SUBSET_PLAN_MEMBER (mutable hb_vector_t<unsigned>, bounds_height_vec) //map: new_gid -> contour points vector HB_SUBSET_PLAN_MEMBER (mutable hb_hashmap_t E(<hb_codepoint_t, contour_point_vector_t>), new_gid_contour_points_map) +//new gids set for composite glyphs +HB_SUBSET_PLAN_MEMBER (hb_set_t, composite_new_gids) + +//Old BASE item variation index -> (New varidx, 0) mapping +HB_SUBSET_PLAN_MEMBER (hb_hashmap_t E(<unsigned, hb_pair_t E(<unsigned, int>)>), base_variation_idx_map) + +//BASE table varstore retained varidx mapping +HB_SUBSET_PLAN_MEMBER (hb_vector_t<hb_inc_bimap_t>, base_varstore_inner_maps) + #ifdef HB_EXPERIMENTAL_API // name table overrides map: hb_ot_name_record_ids_t-> name string new value or // None to indicate should remove diff --git a/thirdparty/harfbuzz/src/hb-subset-plan.cc b/thirdparty/harfbuzz/src/hb-subset-plan.cc index 5786223196..068fddaedd 100644 --- a/thirdparty/harfbuzz/src/hb-subset-plan.cc +++ b/thirdparty/harfbuzz/src/hb-subset-plan.cc @@ -32,6 +32,7 @@ #include "hb-ot-cmap-table.hh" #include "hb-ot-glyf-table.hh" +#include "hb-ot-layout-base-table.hh" #include "hb-ot-layout-gdef-table.hh" #include "hb-ot-layout-gpos-table.hh" #include "hb-ot-layout-gsub-table.hh" @@ -431,6 +432,52 @@ _collect_layout_variation_indices (hb_subset_plan_t* plan) gdef.destroy (); gpos.destroy (); } + +#ifndef HB_NO_BASE +/* used by BASE table only, delta is always set to 0 in the output map */ +static inline void +_remap_variation_indices (const hb_set_t& indices, + unsigned subtable_count, + hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>>& variation_idx_delta_map /* OUT */) +{ + unsigned new_major = 0, new_minor = 0; + unsigned last_major = (indices.get_min ()) >> 16; + for (unsigned idx : indices) + { + uint16_t major = idx >> 16; + if (major >= subtable_count) break; + if (major != last_major) + { + new_minor = 0; + ++new_major; + } + + unsigned new_idx = (new_major << 16) + new_minor; + variation_idx_delta_map.set (idx, hb_pair_t<unsigned, int> (new_idx, 0)); + ++new_minor; + last_major = major; + } +} + +static inline void +_collect_base_variation_indices (hb_subset_plan_t* plan) +{ + hb_blob_ptr_t<OT::BASE> base = plan->source_table<OT::BASE> (); + if (!base->has_var_store ()) + { + base.destroy (); + return; + } + + hb_set_t varidx_set; + base->collect_variation_indices (plan, varidx_set); + unsigned subtable_count = base->get_var_store ().get_sub_table_count (); + base.destroy (); + + _remap_variation_indices (varidx_set, subtable_count, plan->base_variation_idx_map); + _generate_varstore_inner_maps (varidx_set, subtable_count, plan->base_varstore_inner_maps); +} +#endif #endif static inline void @@ -994,8 +1041,8 @@ _update_instance_metrics_map_from_cff2 (hb_subset_plan_t *plan) OT::cff2::accelerator_t cff2 (plan->source); if (!cff2.is_valid ()) return; - hb_font_t *font = nullptr; - if (unlikely (!plan->check_success (font = _get_hb_font_with_variations (plan)))) + hb_font_t *font = _get_hb_font_with_variations (plan); + if (unlikely (!plan->check_success (font != nullptr))) { hb_font_destroy (font); return; @@ -1073,8 +1120,8 @@ _update_instance_metrics_map_from_cff2 (hb_subset_plan_t *plan) static bool _get_instance_glyphs_contour_points (hb_subset_plan_t *plan) { - /* contour_points vector only needed for updating gvar table (infer delta) - * during partial instancing */ + /* contour_points vector only needed for updating gvar table (infer delta and + * iup delta optimization) during partial instancing */ if (plan->user_axes_location.is_empty () || plan->all_axes_pinned) return true; @@ -1092,10 +1139,17 @@ _get_instance_glyphs_contour_points (hb_subset_plan_t *plan) } hb_codepoint_t old_gid = _.second; - if (unlikely (!glyf.glyph_for_gid (old_gid).get_all_points_without_var (plan->source, all_points))) + auto glyph = glyf.glyph_for_gid (old_gid); + if (unlikely (!glyph.get_all_points_without_var (plan->source, all_points))) return false; if (unlikely (!plan->new_gid_contour_points_map.set (new_gid, all_points))) return false; + +#ifdef HB_EXPERIMENTAL_API + /* composite new gids are only needed by iup delta optimization */ + if ((plan->flags & HB_SUBSET_FLAGS_OPTIMIZE_IUP_DELTAS) && glyph.is_composite ()) + plan->composite_new_gids.add (new_gid); +#endif } return true; } @@ -1205,6 +1259,13 @@ hb_subset_plan_t::hb_subset_plan_t (hb_face_t *face, if (!drop_tables.has (HB_OT_TAG_GDEF)) _remap_used_mark_sets (this, used_mark_sets_map); +#ifndef HB_NO_VAR +#ifndef HB_NO_BASE + if (!drop_tables.has (HB_OT_TAG_BASE)) + _collect_base_variation_indices (this); +#endif +#endif + if (unlikely (in_error ())) return; diff --git a/thirdparty/harfbuzz/src/hb-subset-plan.hh b/thirdparty/harfbuzz/src/hb-subset-plan.hh index 1f19a58c1e..19a9fa6918 100644 --- a/thirdparty/harfbuzz/src/hb-subset-plan.hh +++ b/thirdparty/harfbuzz/src/hb-subset-plan.hh @@ -78,6 +78,13 @@ struct contour_point_t y = x * matrix[1] + y * matrix[3]; x = x_; } + + void add_delta (float delta_x, float delta_y) + { + x += delta_x; + y += delta_y; + } + HB_ALWAYS_INLINE void translate (const contour_point_t &p) { x += p.x; y += p.y; } @@ -99,6 +106,22 @@ struct contour_point_vector_t : hb_vector_t<contour_point_t> unsigned count = a.length; hb_memcpy (arrayZ, a.arrayZ, count * sizeof (arrayZ[0])); } + + bool add_deltas (const hb_vector_t<float> deltas_x, + const hb_vector_t<float> deltas_y, + const hb_vector_t<bool> indices) + { + if (indices.length != deltas_x.length || + indices.length != deltas_y.length) + return false; + + for (unsigned i = 0; i < indices.length; i++) + { + if (!indices.arrayZ[i]) continue; + arrayZ[i].add_delta (deltas_x.arrayZ[i], deltas_y.arrayZ[i]); + } + return true; + } }; namespace OT { @@ -147,7 +170,7 @@ struct hb_subset_plan_t bool gsub_insert_catch_all_feature_variation_rec; bool gpos_insert_catch_all_feature_variation_rec; - // whether GDEF VarStore is retained + // whether GDEF ItemVariationStore is retained mutable bool has_gdef_varstore; #define HB_SUBSET_PLAN_MEMBER(Type, Name) Type Name; diff --git a/thirdparty/harfbuzz/src/hb-subset.cc b/thirdparty/harfbuzz/src/hb-subset.cc index 06e77dd8eb..f10ef54dbd 100644 --- a/thirdparty/harfbuzz/src/hb-subset.cc +++ b/thirdparty/harfbuzz/src/hb-subset.cc @@ -48,6 +48,7 @@ #include "hb-ot-cff2-table.hh" #include "hb-ot-vorg-table.hh" #include "hb-ot-name-table.hh" +#include "hb-ot-layout-base-table.hh" #include "hb-ot-layout-gsub-table.hh" #include "hb-ot-layout-gpos-table.hh" #include "hb-ot-var-avar-table.hh" @@ -503,6 +504,7 @@ _subset_table (hb_subset_plan_t *plan, case HB_OT_TAG_CBLC: return _subset<const OT::CBLC> (plan, buf); case HB_OT_TAG_CBDT: return true; /* skip CBDT, handled by CBLC */ case HB_OT_TAG_MATH: return _subset<const OT::MATH> (plan, buf); + case HB_OT_TAG_BASE: return _subset<const OT::BASE> (plan, buf); #ifndef HB_NO_SUBSET_CFF case HB_OT_TAG_CFF1: return _subset<const OT::cff1> (plan, buf); @@ -548,6 +550,7 @@ _subset_table (hb_subset_plan_t *plan, } #endif return _passthrough (plan, tag); + default: if (plan->flags & HB_SUBSET_FLAGS_PASSTHROUGH_UNRECOGNIZED) return _passthrough (plan, tag); diff --git a/thirdparty/harfbuzz/src/hb-subset.h b/thirdparty/harfbuzz/src/hb-subset.h index d79e7f762a..73dcae4660 100644 --- a/thirdparty/harfbuzz/src/hb-subset.h +++ b/thirdparty/harfbuzz/src/hb-subset.h @@ -76,6 +76,8 @@ typedef struct hb_subset_plan_t hb_subset_plan_t; * @HB_SUBSET_FLAGS_IFTB_REQUIREMENTS: If set enforce requirements on the output subset * to allow it to be used with incremental font transfer IFTB patches. Primarily, * this forces all outline data to use long (32 bit) offsets. Since: EXPERIMENTAL + * @HB_SUBSET_FLAGS_OPTIMIZE_IUP_DELTAS: If set perform IUP delta optimization on the + * remaining gvar table's deltas. Since: EXPERIMENTAL * * List of boolean properties that can be configured on the subset input. * @@ -95,6 +97,7 @@ typedef enum { /*< flags >*/ HB_SUBSET_FLAGS_NO_LAYOUT_CLOSURE = 0x00000200u, #ifdef HB_EXPERIMENTAL_API HB_SUBSET_FLAGS_IFTB_REQUIREMENTS = 0x00000400u, + HB_SUBSET_FLAGS_OPTIMIZE_IUP_DELTAS = 0x00000800u, #endif } hb_subset_flags_t; @@ -171,6 +174,10 @@ hb_subset_input_set_flags (hb_subset_input_t *input, unsigned value); HB_EXTERN hb_bool_t +hb_subset_input_pin_all_axes_to_default (hb_subset_input_t *input, + hb_face_t *face); + +HB_EXTERN hb_bool_t hb_subset_input_pin_axis_to_default (hb_subset_input_t *input, hb_face_t *face, hb_tag_t axis_tag); @@ -183,12 +190,19 @@ hb_subset_input_pin_axis_location (hb_subset_input_t *input, #ifdef HB_EXPERIMENTAL_API HB_EXTERN hb_bool_t +hb_subset_input_get_axis_range (hb_subset_input_t *input, + hb_tag_t axis_tag, + float *axis_min_value, + float *axis_max_value, + float *axis_def_value); + +HB_EXTERN hb_bool_t hb_subset_input_set_axis_range (hb_subset_input_t *input, hb_face_t *face, hb_tag_t axis_tag, float axis_min_value, float axis_max_value, - float *axis_def_value); + float axis_def_value); HB_EXTERN hb_bool_t hb_subset_input_override_name_table (hb_subset_input_t *input, diff --git a/thirdparty/harfbuzz/src/hb-vector.hh b/thirdparty/harfbuzz/src/hb-vector.hh index dfe1b7d1c7..c0cc7063ff 100644 --- a/thirdparty/harfbuzz/src/hb-vector.hh +++ b/thirdparty/harfbuzz/src/hb-vector.hh @@ -78,7 +78,7 @@ struct hb_vector_t if (unlikely (in_error ())) return; copy_array (o); } - hb_vector_t (hb_vector_t &&o) + hb_vector_t (hb_vector_t &&o) noexcept { allocated = o.allocated; length = o.length; @@ -122,7 +122,7 @@ struct hb_vector_t resize (0); } - friend void swap (hb_vector_t& a, hb_vector_t& b) + friend void swap (hb_vector_t& a, hb_vector_t& b) noexcept { hb_swap (a.allocated, b.allocated); hb_swap (a.length, b.length); @@ -139,7 +139,7 @@ struct hb_vector_t return *this; } - hb_vector_t& operator = (hb_vector_t &&o) + hb_vector_t& operator = (hb_vector_t &&o) noexcept { hb_swap (*this, o); return *this; diff --git a/thirdparty/harfbuzz/src/hb-version.h b/thirdparty/harfbuzz/src/hb-version.h index b08dd1f09f..68681874ca 100644 --- a/thirdparty/harfbuzz/src/hb-version.h +++ b/thirdparty/harfbuzz/src/hb-version.h @@ -47,7 +47,7 @@ HB_BEGIN_DECLS * * The minor component of the library version available at compile-time. */ -#define HB_VERSION_MINOR 3 +#define HB_VERSION_MINOR 4 /** * HB_VERSION_MICRO: * @@ -60,7 +60,7 @@ HB_BEGIN_DECLS * * A string literal containing the library version available at compile-time. */ -#define HB_VERSION_STRING "8.3.0" +#define HB_VERSION_STRING "8.4.0" /** * HB_VERSION_ATLEAST: diff --git a/thirdparty/harfbuzz/src/hb.hh b/thirdparty/harfbuzz/src/hb.hh index 972608d6a3..0ceeb99f50 100644 --- a/thirdparty/harfbuzz/src/hb.hh +++ b/thirdparty/harfbuzz/src/hb.hh @@ -64,6 +64,7 @@ #pragma GCC diagnostic error "-Wbitwise-instead-of-logical" #pragma GCC diagnostic error "-Wcast-align" #pragma GCC diagnostic error "-Wcast-function-type" +#pragma GCC diagnostic error "-Wcast-function-type-strict" #pragma GCC diagnostic error "-Wconstant-conversion" #pragma GCC diagnostic error "-Wcomma" #pragma GCC diagnostic error "-Wdelete-non-virtual-dtor" @@ -177,6 +178,11 @@ #define HB_EXTERN __declspec (dllexport) extern #endif +// https://github.com/harfbuzz/harfbuzz/pull/4619 +#ifndef __STDC_FORMAT_MACROS +#define __STDC_FORMAT_MACROS 1 +#endif + #include "hb.h" #define HB_H_IN #include "hb-ot.h" @@ -212,6 +218,12 @@ #include <winapifamily.h> #endif +#ifndef PRId32 +# define PRId32 "d" +# define PRIu32 "u" +# define PRIx32 "x" +#endif + #define HB_PASTE1(a,b) a##b #define HB_PASTE(a,b) HB_PASTE1(a,b) diff --git a/thirdparty/mbedtls/include/mbedtls/aesni.h b/thirdparty/mbedtls/include/mbedtls/aesni.h index b636c100ae..93f067304d 100644 --- a/thirdparty/mbedtls/include/mbedtls/aesni.h +++ b/thirdparty/mbedtls/include/mbedtls/aesni.h @@ -46,7 +46,7 @@ * macros that may change in future releases. */ #undef MBEDTLS_AESNI_HAVE_INTRINSICS -#if defined(_MSC_VER) +#if defined(_MSC_VER) && !defined(__clang__) /* Visual Studio supports AESNI intrinsics since VS 2008 SP1. We only support * VS 2013 and up for other reasons anyway, so no need to check the version. */ #define MBEDTLS_AESNI_HAVE_INTRINSICS @@ -54,7 +54,7 @@ /* GCC-like compilers: currently, we only support intrinsics if the requisite * target flag is enabled when building the library (e.g. `gcc -mpclmul -msse2` * or `clang -maes -mpclmul`). */ -#if defined(__GNUC__) && defined(__AES__) && defined(__PCLMUL__) +#if (defined(__GNUC__) || defined(__clang__)) && defined(__AES__) && defined(__PCLMUL__) #define MBEDTLS_AESNI_HAVE_INTRINSICS #endif diff --git a/thirdparty/mbedtls/include/mbedtls/config.h b/thirdparty/mbedtls/include/mbedtls/config.h index ac2146ea11..4842fd494c 100644 --- a/thirdparty/mbedtls/include/mbedtls/config.h +++ b/thirdparty/mbedtls/include/mbedtls/config.h @@ -1571,6 +1571,26 @@ //#define MBEDTLS_PSA_INJECT_ENTROPY /** + * \def MBEDTLS_PSA_ASSUME_EXCLUSIVE_BUFFERS + * + * Assume all buffers passed to PSA functions are owned exclusively by the + * PSA function and are not stored in shared memory. + * + * This option may be enabled if all buffers passed to any PSA function reside + * in memory that is accessible only to the PSA function during its execution. + * + * This option MUST be disabled whenever buffer arguments are in memory shared + * with an untrusted party, for example where arguments to PSA calls are passed + * across a trust boundary. + * + * \note Enabling this option reduces memory usage and code size. + * + * \note Enabling this option causes overlap of input and output buffers + * not to be supported by PSA functions. + */ +//#define MBEDTLS_PSA_ASSUME_EXCLUSIVE_BUFFERS + +/** * \def MBEDTLS_RSA_NO_CRT * * Do not use the Chinese Remainder Theorem diff --git a/thirdparty/mbedtls/include/mbedtls/ecp.h b/thirdparty/mbedtls/include/mbedtls/ecp.h index e4e40c003c..33ea14d7e2 100644 --- a/thirdparty/mbedtls/include/mbedtls/ecp.h +++ b/thirdparty/mbedtls/include/mbedtls/ecp.h @@ -1265,6 +1265,8 @@ int mbedtls_ecp_gen_key(mbedtls_ecp_group_id grp_id, mbedtls_ecp_keypair *key, /** * \brief This function reads an elliptic curve private key. * + * \note This function does not support Curve448 yet. + * * \param grp_id The ECP group identifier. * \param key The destination key. * \param buf The buffer containing the binary representation of the @@ -1286,17 +1288,43 @@ int mbedtls_ecp_read_key(mbedtls_ecp_group_id grp_id, mbedtls_ecp_keypair *key, /** * \brief This function exports an elliptic curve private key. * + * \note Note that although this function accepts an output + * buffer that is smaller or larger than the key, most key + * import interfaces require the output to have exactly + * key's nominal length. It is generally simplest to + * pass the key's nominal length as \c buflen, after + * checking that the output buffer is large enough. + * See the description of the \p buflen parameter for + * how to calculate the nominal length. + * + * \note If the private key was not set in \p key, + * the output is unspecified. Future versions + * may return an error in that case. + * + * \note This function does not support Curve448 yet. + * * \param key The private key. * \param buf The output buffer for containing the binary representation - * of the key. (Big endian integer for Weierstrass curves, byte - * string for Montgomery curves.) + * of the key. + * For Weierstrass curves, this is the big-endian + * representation, padded with null bytes at the beginning + * to reach \p buflen bytes. + * For Montgomery curves, this is the standard byte string + * representation (which is little-endian), padded with + * null bytes at the end to reach \p buflen bytes. * \param buflen The total length of the buffer in bytes. + * The length of the output is + * (`grp->nbits` + 7) / 8 bytes + * where `grp->nbits` is the private key size in bits. + * For Weierstrass keys, if the output buffer is smaller, + * leading zeros are trimmed to fit if possible. For + * Montgomery keys, the output buffer must always be large + * enough for the nominal length. * * \return \c 0 on success. - * \return #MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL if the \p key - representation is larger than the available space in \p buf. - * \return #MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE if the operation for - * the group is not implemented. + * \return #MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL or + * #MBEDTLS_ERR_MPI_BUFFER_TOO_SMALL if the \p key + * representation is larger than the available space in \p buf. * \return Another negative error code on different kinds of failure. */ int mbedtls_ecp_write_key(mbedtls_ecp_keypair *key, diff --git a/thirdparty/mbedtls/include/mbedtls/net_sockets.h b/thirdparty/mbedtls/include/mbedtls/net_sockets.h index 2d3fe3f949..1a12c9c803 100644 --- a/thirdparty/mbedtls/include/mbedtls/net_sockets.h +++ b/thirdparty/mbedtls/include/mbedtls/net_sockets.h @@ -140,7 +140,7 @@ int mbedtls_net_bind(mbedtls_net_context *ctx, const char *bind_ip, const char * * \param client_ctx Will contain the connected client socket * \param client_ip Will contain the client IP address, can be NULL * \param buf_size Size of the client_ip buffer - * \param ip_len Will receive the size of the client IP written, + * \param cip_len Will receive the size of the client IP written, * can be NULL if client_ip is null * * \return 0 if successful, or @@ -153,7 +153,7 @@ int mbedtls_net_bind(mbedtls_net_context *ctx, const char *bind_ip, const char * */ int mbedtls_net_accept(mbedtls_net_context *bind_ctx, mbedtls_net_context *client_ctx, - void *client_ip, size_t buf_size, size_t *ip_len); + void *client_ip, size_t buf_size, size_t *cip_len); /** * \brief Check and wait for the context to be ready for read/write diff --git a/thirdparty/mbedtls/include/mbedtls/version.h b/thirdparty/mbedtls/include/mbedtls/version.h index 0533bca681..bbe76b1739 100644 --- a/thirdparty/mbedtls/include/mbedtls/version.h +++ b/thirdparty/mbedtls/include/mbedtls/version.h @@ -26,16 +26,16 @@ */ #define MBEDTLS_VERSION_MAJOR 2 #define MBEDTLS_VERSION_MINOR 28 -#define MBEDTLS_VERSION_PATCH 7 +#define MBEDTLS_VERSION_PATCH 8 /** * The single version number has the following structure: * MMNNPP00 * Major version | Minor version | Patch version */ -#define MBEDTLS_VERSION_NUMBER 0x021C0700 -#define MBEDTLS_VERSION_STRING "2.28.7" -#define MBEDTLS_VERSION_STRING_FULL "Mbed TLS 2.28.7" +#define MBEDTLS_VERSION_NUMBER 0x021C0800 +#define MBEDTLS_VERSION_STRING "2.28.8" +#define MBEDTLS_VERSION_STRING_FULL "Mbed TLS 2.28.8" #if defined(MBEDTLS_VERSION_C) diff --git a/thirdparty/mbedtls/library/aes.c b/thirdparty/mbedtls/library/aes.c index 24d7ab92fb..836367cea7 100644 --- a/thirdparty/mbedtls/library/aes.c +++ b/thirdparty/mbedtls/library/aes.c @@ -322,7 +322,7 @@ static const uint32_t RT3[256] = { RT }; /* * Round constants */ -static const uint32_t RCON[10] = +static const uint32_t round_constants[10] = { 0x00000001, 0x00000002, 0x00000004, 0x00000008, 0x00000010, 0x00000020, 0x00000040, 0x00000080, @@ -369,7 +369,7 @@ static uint32_t RT3[256]; /* * Round constants */ -static uint32_t RCON[10]; +static uint32_t round_constants[10]; /* * Tables generation code @@ -399,7 +399,7 @@ static void aes_gen_tables(void) * calculate the round constants */ for (i = 0, x = 1; i < 10; i++) { - RCON[i] = (uint32_t) x; + round_constants[i] = (uint32_t) x; x = MBEDTLS_BYTE_0(XTIME(x)); } @@ -625,7 +625,7 @@ int mbedtls_aes_setkey_enc(mbedtls_aes_context *ctx, const unsigned char *key, case 10: for (i = 0; i < 10; i++, RK += 4) { - RK[4] = RK[0] ^ RCON[i] ^ + RK[4] = RK[0] ^ round_constants[i] ^ ((uint32_t) FSb[MBEDTLS_BYTE_1(RK[3])]) ^ ((uint32_t) FSb[MBEDTLS_BYTE_2(RK[3])] << 8) ^ ((uint32_t) FSb[MBEDTLS_BYTE_3(RK[3])] << 16) ^ @@ -640,7 +640,7 @@ int mbedtls_aes_setkey_enc(mbedtls_aes_context *ctx, const unsigned char *key, case 12: for (i = 0; i < 8; i++, RK += 6) { - RK[6] = RK[0] ^ RCON[i] ^ + RK[6] = RK[0] ^ round_constants[i] ^ ((uint32_t) FSb[MBEDTLS_BYTE_1(RK[5])]) ^ ((uint32_t) FSb[MBEDTLS_BYTE_2(RK[5])] << 8) ^ ((uint32_t) FSb[MBEDTLS_BYTE_3(RK[5])] << 16) ^ @@ -657,7 +657,7 @@ int mbedtls_aes_setkey_enc(mbedtls_aes_context *ctx, const unsigned char *key, case 14: for (i = 0; i < 7; i++, RK += 8) { - RK[8] = RK[0] ^ RCON[i] ^ + RK[8] = RK[0] ^ round_constants[i] ^ ((uint32_t) FSb[MBEDTLS_BYTE_1(RK[7])]) ^ ((uint32_t) FSb[MBEDTLS_BYTE_2(RK[7])] << 8) ^ ((uint32_t) FSb[MBEDTLS_BYTE_3(RK[7])] << 16) ^ diff --git a/thirdparty/mbedtls/library/aesni.c b/thirdparty/mbedtls/library/aesni.c index dd84c2b4ea..74bae91f5e 100644 --- a/thirdparty/mbedtls/library/aesni.c +++ b/thirdparty/mbedtls/library/aesni.c @@ -27,10 +27,12 @@ #if defined(MBEDTLS_AESNI_HAVE_CODE) #if MBEDTLS_AESNI_HAVE_CODE == 2 -#if !defined(_WIN32) +#if defined(__GNUC__) #include <cpuid.h> -#else +#elif defined(_MSC_VER) #include <intrin.h> +#else +#error "`__cpuid` required by MBEDTLS_AESNI_C is not supported by the compiler" #endif #include <immintrin.h> #endif @@ -45,7 +47,7 @@ int mbedtls_aesni_has_support(unsigned int what) if (!done) { #if MBEDTLS_AESNI_HAVE_CODE == 2 - static unsigned info[4] = { 0, 0, 0, 0 }; + static int info[4] = { 0, 0, 0, 0 }; #if defined(_MSC_VER) __cpuid(info, 1); #else @@ -179,7 +181,7 @@ void mbedtls_aesni_gcm_mult(unsigned char c[16], const unsigned char a[16], const unsigned char b[16]) { - __m128i aa, bb, cc, dd; + __m128i aa = { 0 }, bb = { 0 }, cc, dd; /* The inputs are in big-endian order, so byte-reverse them */ for (size_t i = 0; i < 16; i++) { diff --git a/thirdparty/mbedtls/library/common.h b/thirdparty/mbedtls/library/common.h index bf18d725cc..49e2c97ea0 100644 --- a/thirdparty/mbedtls/library/common.h +++ b/thirdparty/mbedtls/library/common.h @@ -350,4 +350,31 @@ static inline const unsigned char *mbedtls_buffer_offset_const( #define MBEDTLS_STATIC_ASSERT(expr, msg) #endif +/* Suppress compiler warnings for unused functions and variables. */ +#if !defined(MBEDTLS_MAYBE_UNUSED) && defined(__has_attribute) +# if __has_attribute(unused) +# define MBEDTLS_MAYBE_UNUSED __attribute__((unused)) +# endif +#endif +#if !defined(MBEDTLS_MAYBE_UNUSED) && defined(__GNUC__) +# define MBEDTLS_MAYBE_UNUSED __attribute__((unused)) +#endif +#if !defined(MBEDTLS_MAYBE_UNUSED) && defined(__IAR_SYSTEMS_ICC__) && defined(__VER__) +/* IAR does support __attribute__((unused)), but only if the -e flag (extended language support) + * is given; the pragma always works. + * Unfortunately the pragma affects the rest of the file where it is used, but this is harmless. + * Check for version 5.2 or later - this pragma may be supported by earlier versions, but I wasn't + * able to find documentation). + */ +# if (__VER__ >= 5020000) +# define MBEDTLS_MAYBE_UNUSED _Pragma("diag_suppress=Pe177") +# endif +#endif +#if !defined(MBEDTLS_MAYBE_UNUSED) && defined(_MSC_VER) +# define MBEDTLS_MAYBE_UNUSED __pragma(warning(suppress:4189)) +#endif +#if !defined(MBEDTLS_MAYBE_UNUSED) +# define MBEDTLS_MAYBE_UNUSED +#endif + #endif /* MBEDTLS_LIBRARY_COMMON_H */ diff --git a/thirdparty/mbedtls/library/ecp.c b/thirdparty/mbedtls/library/ecp.c index 31a6b9e305..cfe02b0d2c 100644 --- a/thirdparty/mbedtls/library/ecp.c +++ b/thirdparty/mbedtls/library/ecp.c @@ -927,7 +927,7 @@ int mbedtls_ecp_point_read_binary(const mbedtls_ecp_group *grp, size_t plen; ECP_VALIDATE_RET(grp != NULL); ECP_VALIDATE_RET(pt != NULL); - ECP_VALIDATE_RET(buf != NULL); + ECP_VALIDATE_RET(ilen == 0 || buf != NULL); if (ilen < 1) { return MBEDTLS_ERR_ECP_BAD_INPUT_DATA; @@ -996,7 +996,7 @@ int mbedtls_ecp_tls_read_point(const mbedtls_ecp_group *grp, ECP_VALIDATE_RET(grp != NULL); ECP_VALIDATE_RET(pt != NULL); ECP_VALIDATE_RET(buf != NULL); - ECP_VALIDATE_RET(*buf != NULL); + ECP_VALIDATE_RET(buf_len == 0 || *buf != NULL); /* * We must have at least two bytes (1 for length, at least one for data) @@ -1068,7 +1068,7 @@ int mbedtls_ecp_tls_read_group(mbedtls_ecp_group *grp, mbedtls_ecp_group_id grp_id; ECP_VALIDATE_RET(grp != NULL); ECP_VALIDATE_RET(buf != NULL); - ECP_VALIDATE_RET(*buf != NULL); + ECP_VALIDATE_RET(len == 0 || *buf != NULL); if ((ret = mbedtls_ecp_tls_read_group_id(&grp_id, buf, len)) != 0) { return ret; @@ -1088,7 +1088,7 @@ int mbedtls_ecp_tls_read_group_id(mbedtls_ecp_group_id *grp, const mbedtls_ecp_curve_info *curve_info; ECP_VALIDATE_RET(grp != NULL); ECP_VALIDATE_RET(buf != NULL); - ECP_VALIDATE_RET(*buf != NULL); + ECP_VALIDATE_RET(len == 0 || *buf != NULL); /* * We expect at least three bytes (see below) @@ -2614,8 +2614,8 @@ static int ecp_mul_mxz(mbedtls_ecp_group *grp, mbedtls_ecp_point *R, /* RP.X might be slightly larger than P, so reduce it */ MOD_ADD(RP.X); + /* Randomize coordinates of the starting point */ #if defined(MBEDTLS_ECP_NO_INTERNAL_RNG) - /* Derandomize coordinates of the starting point */ if (f_rng == NULL) { have_rng = 0; } @@ -3358,10 +3358,10 @@ cleanup: int mbedtls_ecp_write_key(mbedtls_ecp_keypair *key, unsigned char *buf, size_t buflen) { - int ret = MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; ECP_VALIDATE_RET(key != NULL); - ECP_VALIDATE_RET(buf != NULL); + ECP_VALIDATE_RET(buflen == 0 || buf != NULL); #if defined(MBEDTLS_ECP_MONTGOMERY_ENABLED) if (mbedtls_ecp_get_type(&key->grp) == MBEDTLS_ECP_TYPE_MONTGOMERY) { diff --git a/thirdparty/mbedtls/library/ecp_curves.c b/thirdparty/mbedtls/library/ecp_curves.c index c7565cce5d..61a1046f3a 100644 --- a/thirdparty/mbedtls/library/ecp_curves.c +++ b/thirdparty/mbedtls/library/ecp_curves.c @@ -535,10 +535,10 @@ static inline void ecp_mpi_load(mbedtls_mpi *X, const mbedtls_mpi_uint *p, size_ */ static inline void ecp_mpi_set1(mbedtls_mpi *X) { - static mbedtls_mpi_uint one[] = { 1 }; + static const mbedtls_mpi_uint one[] = { 1 }; X->s = 1; X->n = 1; - X->p = one; + X->p = (mbedtls_mpi_uint *) one; /* X->p will not be modified so the cast is safe */ } /* @@ -1348,7 +1348,7 @@ cleanup: */ #define P_KOBLITZ_MAX (256 / 8 / sizeof(mbedtls_mpi_uint)) // Max limbs in P #define P_KOBLITZ_R (8 / sizeof(mbedtls_mpi_uint)) // Limbs in R -static inline int ecp_mod_koblitz(mbedtls_mpi *N, mbedtls_mpi_uint *Rp, size_t p_limbs, +static inline int ecp_mod_koblitz(mbedtls_mpi *N, const mbedtls_mpi_uint *Rp, size_t p_limbs, size_t adjust, size_t shift, mbedtls_mpi_uint mask) { int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; @@ -1362,7 +1362,7 @@ static inline int ecp_mod_koblitz(mbedtls_mpi *N, mbedtls_mpi_uint *Rp, size_t p /* Init R */ R.s = 1; - R.p = Rp; + R.p = (mbedtls_mpi_uint *) Rp; /* R.p will not be modified so the cast is safe */ R.n = P_KOBLITZ_R; /* Common setup for M */ @@ -1433,7 +1433,7 @@ cleanup: */ static int ecp_mod_p192k1(mbedtls_mpi *N) { - static mbedtls_mpi_uint Rp[] = { + static const mbedtls_mpi_uint Rp[] = { MBEDTLS_BYTES_TO_T_UINT_8(0xC9, 0x11, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00) }; @@ -1450,7 +1450,7 @@ static int ecp_mod_p192k1(mbedtls_mpi *N) */ static int ecp_mod_p224k1(mbedtls_mpi *N) { - static mbedtls_mpi_uint Rp[] = { + static const mbedtls_mpi_uint Rp[] = { MBEDTLS_BYTES_TO_T_UINT_8(0x93, 0x1A, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00) }; @@ -1472,7 +1472,7 @@ static int ecp_mod_p224k1(mbedtls_mpi *N) */ static int ecp_mod_p256k1(mbedtls_mpi *N) { - static mbedtls_mpi_uint Rp[] = { + static const mbedtls_mpi_uint Rp[] = { MBEDTLS_BYTES_TO_T_UINT_8(0xD1, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00) }; diff --git a/thirdparty/mbedtls/library/entropy_poll.c b/thirdparty/mbedtls/library/entropy_poll.c index 4c5184686e..727f848b93 100644 --- a/thirdparty/mbedtls/library/entropy_poll.c +++ b/thirdparty/mbedtls/library/entropy_poll.c @@ -5,7 +5,7 @@ * SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */ -#if defined(__linux__) && !defined(_GNU_SOURCE) +#if defined(__linux__) || defined(__midipix__) && !defined(_GNU_SOURCE) /* Ensure that syscall() is available even when compiling with -std=c99 */ #define _GNU_SOURCE #endif diff --git a/thirdparty/mbedtls/library/gcm.c b/thirdparty/mbedtls/library/gcm.c index 86d5fa2b5f..d3e773278f 100644 --- a/thirdparty/mbedtls/library/gcm.c +++ b/thirdparty/mbedtls/library/gcm.c @@ -241,7 +241,7 @@ int mbedtls_gcm_starts(mbedtls_gcm_context *ctx, uint64_t iv_bits; GCM_VALIDATE_RET(ctx != NULL); - GCM_VALIDATE_RET(iv != NULL); + GCM_VALIDATE_RET(iv_len == 0 || iv != NULL); GCM_VALIDATE_RET(add_len == 0 || add != NULL); /* IV and AD are limited to 2^64 bits, so 2^61 bytes */ @@ -433,7 +433,7 @@ int mbedtls_gcm_crypt_and_tag(mbedtls_gcm_context *ctx, int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; GCM_VALIDATE_RET(ctx != NULL); - GCM_VALIDATE_RET(iv != NULL); + GCM_VALIDATE_RET(iv_len == 0 || iv != NULL); GCM_VALIDATE_RET(add_len == 0 || add != NULL); GCM_VALIDATE_RET(length == 0 || input != NULL); GCM_VALIDATE_RET(length == 0 || output != NULL); @@ -470,7 +470,7 @@ int mbedtls_gcm_auth_decrypt(mbedtls_gcm_context *ctx, int diff; GCM_VALIDATE_RET(ctx != NULL); - GCM_VALIDATE_RET(iv != NULL); + GCM_VALIDATE_RET(iv_len == 0 || iv != NULL); GCM_VALIDATE_RET(add_len == 0 || add != NULL); GCM_VALIDATE_RET(tag != NULL); GCM_VALIDATE_RET(length == 0 || input != NULL); diff --git a/thirdparty/mbedtls/library/net_sockets.c b/thirdparty/mbedtls/library/net_sockets.c index 8140eeade4..5d985ef001 100644 --- a/thirdparty/mbedtls/library/net_sockets.c +++ b/thirdparty/mbedtls/library/net_sockets.c @@ -321,7 +321,7 @@ static int net_would_block(const mbedtls_net_context *ctx) */ int mbedtls_net_accept(mbedtls_net_context *bind_ctx, mbedtls_net_context *client_ctx, - void *client_ip, size_t buf_size, size_t *ip_len) + void *client_ip, size_t buf_size, size_t *cip_len) { int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; int type; @@ -404,22 +404,22 @@ int mbedtls_net_accept(mbedtls_net_context *bind_ctx, if (client_ip != NULL) { if (client_addr.ss_family == AF_INET) { struct sockaddr_in *addr4 = (struct sockaddr_in *) &client_addr; - *ip_len = sizeof(addr4->sin_addr.s_addr); + *cip_len = sizeof(addr4->sin_addr.s_addr); - if (buf_size < *ip_len) { + if (buf_size < *cip_len) { return MBEDTLS_ERR_NET_BUFFER_TOO_SMALL; } - memcpy(client_ip, &addr4->sin_addr.s_addr, *ip_len); + memcpy(client_ip, &addr4->sin_addr.s_addr, *cip_len); } else { struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *) &client_addr; - *ip_len = sizeof(addr6->sin6_addr.s6_addr); + *cip_len = sizeof(addr6->sin6_addr.s6_addr); - if (buf_size < *ip_len) { + if (buf_size < *cip_len) { return MBEDTLS_ERR_NET_BUFFER_TOO_SMALL; } - memcpy(client_ip, &addr6->sin6_addr.s6_addr, *ip_len); + memcpy(client_ip, &addr6->sin6_addr.s6_addr, *cip_len); } } diff --git a/thirdparty/mbedtls/library/pk_wrap.c b/thirdparty/mbedtls/library/pk_wrap.c index 14c6d3f99c..dd460a6a0c 100644 --- a/thirdparty/mbedtls/library/pk_wrap.c +++ b/thirdparty/mbedtls/library/pk_wrap.c @@ -53,7 +53,23 @@ static int rsa_can_do(mbedtls_pk_type_t type) static size_t rsa_get_bitlen(const void *ctx) { const mbedtls_rsa_context *rsa = (const mbedtls_rsa_context *) ctx; - return 8 * mbedtls_rsa_get_len(rsa); + /* Unfortunately, the rsa.h interface does not have a direct way + * to access the bit-length that works with MBEDTLS_RSA_ALT. + * So we have to do a little work here. + */ + mbedtls_mpi N; + mbedtls_mpi_init(&N); + int ret = mbedtls_rsa_export(rsa, &N, NULL, NULL, NULL, NULL); + /* If the export fails for some reason (e.g. the RSA_ALT implementation + * does not support export, or there is not enough memory), + * we have no way of returning an error from this function. + * As a fallback, return the byte-length converted in bits, which is + * the correct value if the modulus size is a multiple of 8 bits, which + * is very often the case in practice. */ + size_t bitlen = (ret == 0 ? mbedtls_mpi_bitlen(&N) : + 8 * mbedtls_rsa_get_len(rsa)); + mbedtls_mpi_free(&N); + return bitlen; } static int rsa_verify_wrap(void *ctx, mbedtls_md_type_t md_alg, diff --git a/thirdparty/mbedtls/library/pkcs12.c b/thirdparty/mbedtls/library/pkcs12.c index 712488233f..55de216edb 100644 --- a/thirdparty/mbedtls/library/pkcs12.c +++ b/thirdparty/mbedtls/library/pkcs12.c @@ -244,21 +244,22 @@ int mbedtls_pkcs12_pbe_ext(mbedtls_asn1_buf *pbe_params, int mode, } #if defined(MBEDTLS_CIPHER_MODE_WITH_PADDING) - /* PKCS12 uses CBC with PKCS7 padding */ - - mbedtls_cipher_padding_t padding = MBEDTLS_PADDING_PKCS7; + { + /* PKCS12 uses CBC with PKCS7 padding */ + mbedtls_cipher_padding_t padding = MBEDTLS_PADDING_PKCS7; #if !defined(MBEDTLS_CIPHER_PADDING_PKCS7) - /* For historical reasons, when decrypting, this function works when - * decrypting even when support for PKCS7 padding is disabled. In this - * case, it ignores the padding, and so will never report a - * password mismatch. - */ - if (mode == MBEDTLS_PKCS12_PBE_DECRYPT) { - padding = MBEDTLS_PADDING_NONE; - } + /* For historical reasons, when decrypting, this function works when + * decrypting even when support for PKCS7 padding is disabled. In this + * case, it ignores the padding, and so will never report a + * password mismatch. + */ + if (mode == MBEDTLS_PKCS12_PBE_DECRYPT) { + padding = MBEDTLS_PADDING_NONE; + } #endif - if ((ret = mbedtls_cipher_set_padding_mode(&cipher_ctx, padding)) != 0) { - goto exit; + if ((ret = mbedtls_cipher_set_padding_mode(&cipher_ctx, padding)) != 0) { + goto exit; + } } #endif /* MBEDTLS_CIPHER_MODE_WITH_PADDING */ diff --git a/thirdparty/mbedtls/library/pkcs5.c b/thirdparty/mbedtls/library/pkcs5.c index 8e5b751a38..90703c45f9 100644 --- a/thirdparty/mbedtls/library/pkcs5.c +++ b/thirdparty/mbedtls/library/pkcs5.c @@ -239,23 +239,25 @@ int mbedtls_pkcs5_pbes2_ext(const mbedtls_asn1_buf *pbe_params, int mode, } #if defined(MBEDTLS_CIPHER_MODE_WITH_PADDING) - /* PKCS5 uses CBC with PKCS7 padding (which is the same as - * "PKCS5 padding" except that it's typically only called PKCS5 - * with 64-bit-block ciphers). - */ - mbedtls_cipher_padding_t padding = MBEDTLS_PADDING_PKCS7; + { + /* PKCS5 uses CBC with PKCS7 padding (which is the same as + * "PKCS5 padding" except that it's typically only called PKCS5 + * with 64-bit-block ciphers). + */ + mbedtls_cipher_padding_t padding = MBEDTLS_PADDING_PKCS7; #if !defined(MBEDTLS_CIPHER_PADDING_PKCS7) - /* For historical reasons, when decrypting, this function works when - * decrypting even when support for PKCS7 padding is disabled. In this - * case, it ignores the padding, and so will never report a - * password mismatch. - */ - if (mode == MBEDTLS_DECRYPT) { - padding = MBEDTLS_PADDING_NONE; - } + /* For historical reasons, when decrypting, this function works when + * decrypting even when support for PKCS7 padding is disabled. In this + * case, it ignores the padding, and so will never report a + * password mismatch. + */ + if (mode == MBEDTLS_DECRYPT) { + padding = MBEDTLS_PADDING_NONE; + } #endif - if ((ret = mbedtls_cipher_set_padding_mode(&cipher_ctx, padding)) != 0) { - goto exit; + if ((ret = mbedtls_cipher_set_padding_mode(&cipher_ctx, padding)) != 0) { + goto exit; + } } #endif /* MBEDTLS_CIPHER_MODE_WITH_PADDING */ if ((ret = mbedtls_cipher_crypt(&cipher_ctx, iv, enc_scheme_params.len, diff --git a/thirdparty/mbedtls/library/pkwrite.c b/thirdparty/mbedtls/library/pkwrite.c index fafcf0e1a7..534290df4e 100644 --- a/thirdparty/mbedtls/library/pkwrite.c +++ b/thirdparty/mbedtls/library/pkwrite.c @@ -559,38 +559,49 @@ end_of_export: int mbedtls_pk_write_pubkey_pem(mbedtls_pk_context *key, unsigned char *buf, size_t size) { int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; - unsigned char output_buf[PUB_DER_MAX_BYTES]; + unsigned char *output_buf = NULL; + output_buf = mbedtls_calloc(1, PUB_DER_MAX_BYTES); + if (output_buf == NULL) { + return MBEDTLS_ERR_PK_ALLOC_FAILED; + } size_t olen = 0; PK_VALIDATE_RET(key != NULL); PK_VALIDATE_RET(buf != NULL || size == 0); if ((ret = mbedtls_pk_write_pubkey_der(key, output_buf, - sizeof(output_buf))) < 0) { - return ret; + PUB_DER_MAX_BYTES)) < 0) { + goto cleanup; } if ((ret = mbedtls_pem_write_buffer(PEM_BEGIN_PUBLIC_KEY, PEM_END_PUBLIC_KEY, - output_buf + sizeof(output_buf) - ret, + output_buf + PUB_DER_MAX_BYTES - ret, ret, buf, size, &olen)) != 0) { - return ret; + goto cleanup; } - return 0; + ret = 0; +cleanup: + mbedtls_free(output_buf); + return ret; } int mbedtls_pk_write_key_pem(mbedtls_pk_context *key, unsigned char *buf, size_t size) { int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; - unsigned char output_buf[PRV_DER_MAX_BYTES]; + unsigned char *output_buf = NULL; + output_buf = mbedtls_calloc(1, PRV_DER_MAX_BYTES); + if (output_buf == NULL) { + return MBEDTLS_ERR_PK_ALLOC_FAILED; + } const char *begin, *end; size_t olen = 0; PK_VALIDATE_RET(key != NULL); PK_VALIDATE_RET(buf != NULL || size == 0); - if ((ret = mbedtls_pk_write_key_der(key, output_buf, sizeof(output_buf))) < 0) { - return ret; + if ((ret = mbedtls_pk_write_key_der(key, output_buf, PRV_DER_MAX_BYTES)) < 0) { + goto cleanup; } #if defined(MBEDTLS_RSA_C) @@ -605,15 +616,22 @@ int mbedtls_pk_write_key_pem(mbedtls_pk_context *key, unsigned char *buf, size_t end = PEM_END_PRIVATE_KEY_EC; } else #endif - return MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE; + { + ret = MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE; + goto cleanup; + } if ((ret = mbedtls_pem_write_buffer(begin, end, - output_buf + sizeof(output_buf) - ret, + output_buf + PRV_DER_MAX_BYTES - ret, ret, buf, size, &olen)) != 0) { - return ret; + goto cleanup; } - return 0; + ret = 0; +cleanup: + mbedtls_platform_zeroize(output_buf, PRV_DER_MAX_BYTES); + mbedtls_free(output_buf); + return ret; } #endif /* MBEDTLS_PEM_WRITE_C */ diff --git a/thirdparty/mbedtls/library/platform_util.c b/thirdparty/mbedtls/library/platform_util.c index a86b07fa3f..df34167a8f 100644 --- a/thirdparty/mbedtls/library/platform_util.c +++ b/thirdparty/mbedtls/library/platform_util.c @@ -66,10 +66,10 @@ void mbedtls_platform_zeroize(void *buf, size_t len) #include <time.h> #if !defined(_WIN32) && (defined(unix) || \ defined(__unix) || defined(__unix__) || (defined(__APPLE__) && \ - defined(__MACH__))) + defined(__MACH__)) || defined(__midipix__)) #include <unistd.h> #endif /* !_WIN32 && (unix || __unix || __unix__ || - * (__APPLE__ && __MACH__)) */ + * (__APPLE__ && __MACH__)) || __midipix__ */ #if !((defined(_POSIX_VERSION) && _POSIX_VERSION >= 200809L) || \ (defined(_POSIX_THREAD_SAFE_FUNCTIONS) && \ diff --git a/thirdparty/mbedtls/library/ssl_tls.c b/thirdparty/mbedtls/library/ssl_tls.c index 1a2bc7bc9e..c667a2923b 100644 --- a/thirdparty/mbedtls/library/ssl_tls.c +++ b/thirdparty/mbedtls/library/ssl_tls.c @@ -992,8 +992,7 @@ static int ssl_populate_transform(mbedtls_ssl_transform *transform, !defined(MBEDTLS_SSL_EXPORT_KEYS) && \ !defined(MBEDTLS_SSL_DTLS_CONNECTION_ID) && \ !defined(MBEDTLS_DEBUG_C) - ssl = NULL; /* make sure we don't use it except for those cases */ - (void) ssl; + (void) ssl; /* ssl is unused except for those cases */ #endif /* @@ -5205,6 +5204,12 @@ const mbedtls_ssl_session *mbedtls_ssl_get_session_pointer(const mbedtls_ssl_con #define SSL_SERIALIZED_SESSION_CONFIG_CRT 0 #endif /* MBEDTLS_X509_CRT_PARSE_C */ +#if defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) +#define SSL_SERIALIZED_SESSION_CONFIG_KEEP_PEER_CRT 1 +#else +#define SSL_SERIALIZED_SESSION_CONFIG_KEEP_PEER_CRT 0 +#endif /* MBEDTLS_SSL_SESSION_TICKETS */ + #if defined(MBEDTLS_SSL_CLI_C) && defined(MBEDTLS_SSL_SESSION_TICKETS) #define SSL_SERIALIZED_SESSION_CONFIG_CLIENT_TICKET 1 #else @@ -5242,6 +5247,7 @@ const mbedtls_ssl_session *mbedtls_ssl_get_session_pointer(const mbedtls_ssl_con #define SSL_SERIALIZED_SESSION_CONFIG_TRUNC_HMAC_BIT 4 #define SSL_SERIALIZED_SESSION_CONFIG_ETM_BIT 5 #define SSL_SERIALIZED_SESSION_CONFIG_TICKET_BIT 6 +#define SSL_SERIALIZED_SESSION_CONFIG_KEEP_PEER_CRT_BIT 7 #define SSL_SERIALIZED_SESSION_CONFIG_BITFLAG \ ((uint16_t) ( \ @@ -5253,9 +5259,11 @@ const mbedtls_ssl_session *mbedtls_ssl_get_session_pointer(const mbedtls_ssl_con (SSL_SERIALIZED_SESSION_CONFIG_TRUNC_HMAC << \ SSL_SERIALIZED_SESSION_CONFIG_TRUNC_HMAC_BIT) | \ (SSL_SERIALIZED_SESSION_CONFIG_ETM << SSL_SERIALIZED_SESSION_CONFIG_ETM_BIT) | \ - (SSL_SERIALIZED_SESSION_CONFIG_TICKET << SSL_SERIALIZED_SESSION_CONFIG_TICKET_BIT))) + (SSL_SERIALIZED_SESSION_CONFIG_TICKET << SSL_SERIALIZED_SESSION_CONFIG_TICKET_BIT) | \ + (SSL_SERIALIZED_SESSION_CONFIG_KEEP_PEER_CRT << \ + SSL_SERIALIZED_SESSION_CONFIG_KEEP_PEER_CRT_BIT))) -static unsigned char ssl_serialized_session_header[] = { +static const unsigned char ssl_serialized_session_header[] = { MBEDTLS_VERSION_MAJOR, MBEDTLS_VERSION_MINOR, MBEDTLS_VERSION_PATCH, @@ -5279,19 +5287,36 @@ static unsigned char ssl_serialized_session_header[] = { * // the setting of those compile-time * // configuration options which influence * // the structure of mbedtls_ssl_session. - * uint64 start_time; - * uint8 ciphersuite[2]; // defined by the standard - * uint8 compression; // 0 or 1 - * uint8 session_id_len; // at most 32 - * opaque session_id[32]; - * opaque master[48]; // fixed length in the standard - * uint32 verify_result; - * opaque peer_cert<0..2^24-1>; // length 0 means no peer cert - * opaque ticket<0..2^24-1>; // length 0 means no ticket - * uint32 ticket_lifetime; - * uint8 mfl_code; // up to 255 according to standard - * uint8 trunc_hmac; // 0 or 1 - * uint8 encrypt_then_mac; // 0 or 1 + * #if defined(MBEDTLS_HAVE_TIME) + * uint64 start_time; + * #endif + * uint8 ciphersuite[2]; // defined by the standard + * uint8 compression; // 0 or 1 + * uint8 session_id_len; // at most 32 + * opaque session_id[32]; + * opaque master[48]; // fixed length in the standard + * uint32 verify_result; + * #if defined(MBEDTLS_X509_CRT_PARSE_C) + * #if defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) + * opaque peer_cert<0..2^24-1>; // length 0 means no peer cert + * #else + * uint8 peer_cert_digest_type; + * opaque peer_cert_digest<0..2^8-1> + * #endif + * #endif + * #if defined(MBEDTLS_SSL_SESSION_TICKETS) && defined(MBEDTLS_SSL_CLI_C) + * opaque ticket<0..2^24-1>; // length 0 means no ticket + * uint32 ticket_lifetime; + * #endif + * #if defined(MBEDTLS_SSL_MAX_FRAGMENT_LENGTH) + * uint8 mfl_code; // up to 255 according to standard + * #endif + * #if defined(MBEDTLS_SSL_TRUNCATED_HMAC) + * uint8 trunc_hmac; // 0 or 1 + * #endif + * #if defined(MBEDTLS_SSL_ENCRYPT_THEN_MAC) + * uint8 encrypt_then_mac; // 0 or 1 + * #endif * * The order is the same as in the definition of the structure, except * verify_result is put before peer_cert so that all mandatory fields come @@ -6124,7 +6149,7 @@ void mbedtls_ssl_session_free(mbedtls_ssl_session *session) (SSL_SERIALIZED_CONTEXT_CONFIG_ALPN << SSL_SERIALIZED_CONTEXT_CONFIG_ALPN_BIT) | \ 0u)) -static unsigned char ssl_serialized_context_header[] = { +static const unsigned char ssl_serialized_context_header[] = { MBEDTLS_VERSION_MAJOR, MBEDTLS_VERSION_MINOR, MBEDTLS_VERSION_PATCH, @@ -6655,7 +6680,7 @@ static int ssl_context_load(mbedtls_ssl_context *ssl, /* alpn_chosen should point to an item in the configured list */ for (cur = ssl->conf->alpn_list; *cur != NULL; cur++) { if (strlen(*cur) == alpn_len && - memcmp(p, cur, alpn_len) == 0) { + memcmp(p, *cur, alpn_len) == 0) { ssl->alpn_chosen = *cur; break; } @@ -6822,7 +6847,7 @@ void mbedtls_ssl_config_init(mbedtls_ssl_config *conf) } #if defined(MBEDTLS_KEY_EXCHANGE_WITH_CERT_ENABLED) -static int ssl_preset_default_hashes[] = { +static const int ssl_preset_default_hashes[] = { #if defined(MBEDTLS_SHA512_C) MBEDTLS_MD_SHA512, #endif @@ -6840,14 +6865,14 @@ static int ssl_preset_default_hashes[] = { }; #endif -static int ssl_preset_suiteb_ciphersuites[] = { +static const int ssl_preset_suiteb_ciphersuites[] = { MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, 0 }; #if defined(MBEDTLS_KEY_EXCHANGE_WITH_CERT_ENABLED) -static int ssl_preset_suiteb_hashes[] = { +static const int ssl_preset_suiteb_hashes[] = { MBEDTLS_MD_SHA256, MBEDTLS_MD_SHA384, MBEDTLS_MD_NONE @@ -6855,7 +6880,7 @@ static int ssl_preset_suiteb_hashes[] = { #endif #if defined(MBEDTLS_ECP_C) -static mbedtls_ecp_group_id ssl_preset_suiteb_curves[] = { +static const mbedtls_ecp_group_id ssl_preset_suiteb_curves[] = { #if defined(MBEDTLS_ECP_DP_SECP256R1_ENABLED) MBEDTLS_ECP_DP_SECP256R1, #endif diff --git a/thirdparty/mbedtls/library/timing.c b/thirdparty/mbedtls/library/timing.c index 7ef9f473b5..ec2cff1084 100644 --- a/thirdparty/mbedtls/library/timing.c +++ b/thirdparty/mbedtls/library/timing.c @@ -403,6 +403,7 @@ int mbedtls_timing_self_test(int verbose) uint32_t a = 0, b = 0; mbedtls_timing_delay_context ctx; + memset(&ctx, 0, sizeof(ctx)); if (verbose != 0) { mbedtls_printf(" TIMING tests note: will take some time!\n"); } diff --git a/thirdparty/mbedtls/library/version_features.c b/thirdparty/mbedtls/library/version_features.c index 779325744b..6f663b12a7 100644 --- a/thirdparty/mbedtls/library/version_features.c +++ b/thirdparty/mbedtls/library/version_features.c @@ -456,6 +456,9 @@ static const char * const features[] = { #if defined(MBEDTLS_PSA_INJECT_ENTROPY) "MBEDTLS_PSA_INJECT_ENTROPY", #endif /* MBEDTLS_PSA_INJECT_ENTROPY */ +#if defined(MBEDTLS_PSA_ASSUME_EXCLUSIVE_BUFFERS) + "MBEDTLS_PSA_ASSUME_EXCLUSIVE_BUFFERS", +#endif /* MBEDTLS_PSA_ASSUME_EXCLUSIVE_BUFFERS */ #if defined(MBEDTLS_RSA_NO_CRT) "MBEDTLS_RSA_NO_CRT", #endif /* MBEDTLS_RSA_NO_CRT */ diff --git a/thirdparty/squish/LICENSE.txt b/thirdparty/squish/LICENSE.txt new file mode 100644 index 0000000000..e491e36226 --- /dev/null +++ b/thirdparty/squish/LICENSE.txt @@ -0,0 +1,20 @@ +Copyright (c) 2006 Simon Brown si@sjbrown.co.uk + +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. diff --git a/thirdparty/squish/godot-changes.patch b/thirdparty/squish/godot-changes.patch deleted file mode 100644 index 555fbc51d0..0000000000 --- a/thirdparty/squish/godot-changes.patch +++ /dev/null @@ -1,211 +0,0 @@ -diff --git a/thirdparty/squish/colourblock.cpp b/thirdparty/squish/colourblock.cpp -index af8b98036..3d87adaa7 100644 ---- a/thirdparty/squish/colourblock.cpp -+++ b/thirdparty/squish/colourblock.cpp -@@ -24,6 +24,9 @@ - -------------------------------------------------------------------------- */ - - #include "colourblock.h" -+// -- Godot start -- -+#include "alpha.h" -+// -- Godot end -- - - namespace squish { - -@@ -211,4 +214,23 @@ void DecompressColour( u8* rgba, void const* block, bool isDxt1 ) - } - } - -+// -- Godot start -- -+void DecompressColourBc5( u8* rgba, void const* block) -+{ -+ void const* rblock = block; -+ void const* gblock = reinterpret_cast< u8 const* >( block ) + 8; -+ DecompressAlphaDxt5(rgba,rblock); -+ for ( int i = 0; i < 16; ++i ) { -+ rgba[i*4] = rgba[i*4 + 3]; -+ } -+ DecompressAlphaDxt5(rgba,gblock); -+ for ( int i = 0; i < 16; ++i ) { -+ rgba[i*4+1] = rgba[i*4 + 3]; -+ rgba[i*4 + 2] = 0; -+ rgba[i*4 + 3] = 255; -+ } -+} -+// -- GODOT end -- -+ -+ - } // namespace squish -diff --git a/thirdparty/squish/colourblock.h b/thirdparty/squish/colourblock.h -index fee2cd7c5..3cb9b7e3b 100644 ---- a/thirdparty/squish/colourblock.h -+++ b/thirdparty/squish/colourblock.h -@@ -35,6 +35,9 @@ void WriteColourBlock3( Vec3::Arg start, Vec3::Arg end, u8 const* indices, void* - void WriteColourBlock4( Vec3::Arg start, Vec3::Arg end, u8 const* indices, void* block ); - - void DecompressColour( u8* rgba, void const* block, bool isDxt1 ); -+// -- GODOT start -- -+void DecompressColourBc5( u8* rgba, void const* block ); -+// -- GODOT end -- - - } // namespace squish - -diff --git a/thirdparty/squish/config.h b/thirdparty/squish/config.h -index 92edefe96..05f8d7259 100644 ---- a/thirdparty/squish/config.h -+++ b/thirdparty/squish/config.h -@@ -32,6 +32,26 @@ - #endif - - // Set to 1 or 2 when building squish to use SSE or SSE2 instructions. -+// -- GODOT start -- -+#ifdef _MSC_VER -+ #if defined(_M_IX86_FP) -+ #if _M_IX86_FP >= 2 -+ #define SQUISH_USE_SSE 2 -+ #elif _M_IX86_FP >= 1 -+ #define SQUISH_USE_SSE 1 -+ #endif -+ #elif defined(_M_X64) -+ #define SQUISH_USE_SSE 2 -+ #endif -+#else -+ #if defined(__SSE2__) -+ #define SQUISH_USE_SSE 2 -+ #elif defined(__SSE__) -+ #define SQUISH_USE_SSE 1 -+ #endif -+#endif -+// -- GODOT end -- -+ - #ifndef SQUISH_USE_SSE - #define SQUISH_USE_SSE 0 - #endif -diff --git a/thirdparty/squish/squish.cpp b/thirdparty/squish/squish.cpp -index 1d22a64ad..fd11a147d 100644 ---- a/thirdparty/squish/squish.cpp -+++ b/thirdparty/squish/squish.cpp -@@ -135,7 +135,13 @@ void Decompress( u8* rgba, void const* block, int flags ) - colourBlock = reinterpret_cast< u8 const* >( block ) + 8; - - // decompress colour -- DecompressColour( rgba, colourBlock, ( flags & kDxt1 ) != 0 ); -+ // -- GODOT start -- -+ //DecompressColour( rgba, colourBlock, ( flags & kDxt1 ) != 0 ); -+ if(( flags & ( kBc5 ) ) != 0) -+ DecompressColourBc5( rgba, colourBlock); -+ else -+ DecompressColour( rgba, colourBlock, ( flags & kDxt1 ) != 0 ); -+ // -- GODOT end -- - - // decompress alpha separately if necessary - if( ( flags & kDxt3 ) != 0 ) - -diff --git a/thirdparty/squish/colourblock.cpp b/thirdparty/squish/colourblock.cpp -index 49401358bc..f14c9362bd 100644 ---- a/thirdparty/squish/colourblock.cpp -+++ b/thirdparty/squish/colourblock.cpp -@@ -24,9 +24,9 @@ - -------------------------------------------------------------------------- */ - - #include "colourblock.h" --// -- Godot start -- -+// -- GODOT start -- - #include "alpha.h" --// -- Godot end -- -+// -- GODOT end -- - - namespace squish { - -diff --git a/thirdparty/squish/godot-changes.patch b/thirdparty/squish/godot-changes.patch -index ef7bafb4b4..655a8cffc2 100644 ---- a/thirdparty/squish/godot-changes.patch -+++ b/thirdparty/squish/godot-changes.patch -@@ -1,22 +1,33 @@ - diff --git a/thirdparty/squish/colourblock.cpp b/thirdparty/squish/colourblock.cpp --index af8b98036..3d87adaa7 100644 -+index af8b980365..f14c9362bd 100644 - --- a/thirdparty/squish/colourblock.cpp - +++ b/thirdparty/squish/colourblock.cpp - @@ -24,6 +24,9 @@ - -------------------------------------------------------------------------- */ - - #include "colourblock.h" --+// -- Godot start -- -++// -- GODOT start -- - +#include "alpha.h" --+// -- Godot end -- -++// -- GODOT end -- - - namespace squish { - --@@ -211,4 +214,23 @@ void DecompressColour( u8* rgba, void const* block, bool isDxt1 ) -+@@ -211,4 +214,34 @@ void DecompressColour( u8* rgba, void const* block, bool isDxt1 ) - } - } - --+// -- Godot start -- -++// -- GODOT start -- -++void DecompressColourBc4( u8* rgba, void const* block) -++{ -++ DecompressAlphaDxt5(rgba,block); -++ for ( int i = 0; i < 16; ++i ) { -++ rgba[i*4] = rgba[i*4 + 3]; -++ rgba[i*4 + 1] = 0; -++ rgba[i*4 + 2] = 0; -++ rgba[i*4 + 3] = 255; -++ } -++} -++ - +void DecompressColourBc5( u8* rgba, void const* block) - +{ - + void const* rblock = block; -@@ -37,21 +48,22 @@ index af8b98036..3d87adaa7 100644 - + - } // namespace squish - diff --git a/thirdparty/squish/colourblock.h b/thirdparty/squish/colourblock.h --index fee2cd7c5..3cb9b7e3b 100644 -+index fee2cd7c5d..e1eb9e4917 100644 - --- a/thirdparty/squish/colourblock.h - +++ b/thirdparty/squish/colourblock.h --@@ -35,6 +35,9 @@ void WriteColourBlock3( Vec3::Arg start, Vec3::Arg end, u8 const* indices, void* -+@@ -35,6 +35,10 @@ void WriteColourBlock3( Vec3::Arg start, Vec3::Arg end, u8 const* indices, void* - void WriteColourBlock4( Vec3::Arg start, Vec3::Arg end, u8 const* indices, void* block ); - - void DecompressColour( u8* rgba, void const* block, bool isDxt1 ); - +// -- GODOT start -- -++void DecompressColourBc4( u8* rgba, void const* block ); - +void DecompressColourBc5( u8* rgba, void const* block ); - +// -- GODOT end -- - - } // namespace squish - - diff --git a/thirdparty/squish/config.h b/thirdparty/squish/config.h --index 92edefe96..05f8d7259 100644 -+index 92edefe966..05f8d72598 100644 - --- a/thirdparty/squish/config.h - +++ b/thirdparty/squish/config.h - @@ -32,6 +32,26 @@ -@@ -82,17 +94,19 @@ index 92edefe96..05f8d7259 100644 - #define SQUISH_USE_SSE 0 - #endif - diff --git a/thirdparty/squish/squish.cpp b/thirdparty/squish/squish.cpp --index 1d22a64ad..fd11a147d 100644 -+index 1d22a64ad6..086ba11cd0 100644 - --- a/thirdparty/squish/squish.cpp - +++ b/thirdparty/squish/squish.cpp --@@ -135,7 +135,13 @@ void Decompress( u8* rgba, void const* block, int flags ) -+@@ -135,7 +135,15 @@ void Decompress( u8* rgba, void const* block, int flags ) - colourBlock = reinterpret_cast< u8 const* >( block ) + 8; - - // decompress colour - - DecompressColour( rgba, colourBlock, ( flags & kDxt1 ) != 0 ); - + // -- GODOT start -- - + //DecompressColour( rgba, colourBlock, ( flags & kDxt1 ) != 0 ); --+ if(( flags & ( kBc5 ) ) != 0) -++ if(( flags & ( kBc4 ) ) != 0) -++ DecompressColourBc4( rgba, colourBlock); -++ else if(( flags & ( kBc5 ) ) != 0) - + DecompressColourBc5( rgba, colourBlock); - + else - + DecompressColour( rgba, colourBlock, ( flags & kDxt1 ) != 0 );
\ No newline at end of file diff --git a/thirdparty/squish/patches/config_sse.patch b/thirdparty/squish/patches/config_sse.patch new file mode 100644 index 0000000000..047701ee32 --- /dev/null +++ b/thirdparty/squish/patches/config_sse.patch @@ -0,0 +1,31 @@ +diff --git a/thirdparty/squish/config.h b/thirdparty/squish/config.h +index 92edefe966..05f8d72598 100644 +--- a/thirdparty/squish/config.h ++++ b/thirdparty/squish/config.h +@@ -32,6 +32,26 @@ + #endif + + // Set to 1 or 2 when building squish to use SSE or SSE2 instructions. ++// -- GODOT start -- ++#ifdef _MSC_VER ++ #if defined(_M_IX86_FP) ++ #if _M_IX86_FP >= 2 ++ #define SQUISH_USE_SSE 2 ++ #elif _M_IX86_FP >= 1 ++ #define SQUISH_USE_SSE 1 ++ #endif ++ #elif defined(_M_X64) ++ #define SQUISH_USE_SSE 2 ++ #endif ++#else ++ #if defined(__SSE2__) ++ #define SQUISH_USE_SSE 2 ++ #elif defined(__SSE__) ++ #define SQUISH_USE_SSE 1 ++ #endif ++#endif ++// -- GODOT end -- ++ + #ifndef SQUISH_USE_SSE + #define SQUISH_USE_SSE 0 + #endif diff --git a/thirdparty/squish/patches/decompress_bc4_bc5.patch b/thirdparty/squish/patches/decompress_bc4_bc5.patch new file mode 100644 index 0000000000..949375560c --- /dev/null +++ b/thirdparty/squish/patches/decompress_bc4_bc5.patch @@ -0,0 +1,85 @@ +diff --git a/thirdparty/squish/colourblock.cpp b/thirdparty/squish/colourblock.cpp +index af8b980365..f14c9362bd 100644 +--- a/thirdparty/squish/colourblock.cpp ++++ b/thirdparty/squish/colourblock.cpp +@@ -24,6 +24,9 @@ + -------------------------------------------------------------------------- */ + + #include "colourblock.h" ++// -- GODOT start -- ++#include "alpha.h" ++// -- GODOT end -- + + namespace squish { + +@@ -211,4 +214,34 @@ void DecompressColour( u8* rgba, void const* block, bool isDxt1 ) + } + } + ++// -- GODOT start -- ++void DecompressColourBc4( u8* rgba, void const* block) ++{ ++ DecompressAlphaDxt5(rgba,block); ++ for ( int i = 0; i < 16; ++i ) { ++ rgba[i*4] = rgba[i*4 + 3]; ++ rgba[i*4 + 1] = 0; ++ rgba[i*4 + 2] = 0; ++ rgba[i*4 + 3] = 255; ++ } ++} ++ ++void DecompressColourBc5( u8* rgba, void const* block) ++{ ++ void const* rblock = block; ++ void const* gblock = reinterpret_cast< u8 const* >( block ) + 8; ++ DecompressAlphaDxt5(rgba,rblock); ++ for ( int i = 0; i < 16; ++i ) { ++ rgba[i*4] = rgba[i*4 + 3]; ++ } ++ DecompressAlphaDxt5(rgba,gblock); ++ for ( int i = 0; i < 16; ++i ) { ++ rgba[i*4+1] = rgba[i*4 + 3]; ++ rgba[i*4 + 2] = 0; ++ rgba[i*4 + 3] = 255; ++ } ++} ++// -- GODOT end -- ++ ++ + } // namespace squish +diff --git a/thirdparty/squish/colourblock.h b/thirdparty/squish/colourblock.h +index fee2cd7c5d..e1eb9e4917 100644 +--- a/thirdparty/squish/colourblock.h ++++ b/thirdparty/squish/colourblock.h +@@ -35,6 +35,10 @@ void WriteColourBlock3( Vec3::Arg start, Vec3::Arg end, u8 const* indices, void* + void WriteColourBlock4( Vec3::Arg start, Vec3::Arg end, u8 const* indices, void* block ); + + void DecompressColour( u8* rgba, void const* block, bool isDxt1 ); ++// -- GODOT start -- ++void DecompressColourBc4( u8* rgba, void const* block ); ++void DecompressColourBc5( u8* rgba, void const* block ); ++// -- GODOT end -- + + } // namespace squish + +diff --git a/thirdparty/squish/squish.cpp b/thirdparty/squish/squish.cpp +index 1d22a64ad6..086ba11cd0 100644 +--- a/thirdparty/squish/squish.cpp ++++ b/thirdparty/squish/squish.cpp +@@ -135,7 +135,15 @@ void Decompress( u8* rgba, void const* block, int flags ) + colourBlock = reinterpret_cast< u8 const* >( block ) + 8; + + // decompress colour +- DecompressColour( rgba, colourBlock, ( flags & kDxt1 ) != 0 ); ++ // -- GODOT start -- ++ //DecompressColour( rgba, colourBlock, ( flags & kDxt1 ) != 0 ); ++ if(( flags & ( kBc4 ) ) != 0) ++ DecompressColourBc4( rgba, colourBlock); ++ else if(( flags & ( kBc5 ) ) != 0) ++ DecompressColourBc5( rgba, colourBlock); ++ else ++ DecompressColour( rgba, colourBlock, ( flags & kDxt1 ) != 0 ); ++ // -- GODOT end -- + + // decompress alpha separately if necessary + if( ( flags & kDxt3 ) != 0 ) diff --git a/thirdparty/thorvg/inc/config.h b/thirdparty/thorvg/inc/config.h index 67716b66cc..31baceca67 100644 --- a/thirdparty/thorvg/inc/config.h +++ b/thirdparty/thorvg/inc/config.h @@ -10,5 +10,5 @@ // For internal debugging: //#define THORVG_LOG_ENABLED -#define THORVG_VERSION_STRING "0.12.9" +#define THORVG_VERSION_STRING "0.12.10" #endif diff --git a/thirdparty/thorvg/src/common/tvgMath.cpp b/thirdparty/thorvg/src/common/tvgMath.cpp index 42bc2cf4aa..37a8879cb5 100644 --- a/thirdparty/thorvg/src/common/tvgMath.cpp +++ b/thirdparty/thorvg/src/common/tvgMath.cpp @@ -71,7 +71,7 @@ void mathRotate(Matrix* m, float degree) { if (degree == 0.0f) return; - auto radian = degree / 180.0f * M_PI; + auto radian = degree / 180.0f * MATH_PI; auto cosVal = cosf(radian); auto sinVal = sinf(radian); diff --git a/thirdparty/thorvg/src/common/tvgMath.h b/thirdparty/thorvg/src/common/tvgMath.h index 7f6708262b..32f4e6b7d1 100644 --- a/thirdparty/thorvg/src/common/tvgMath.h +++ b/thirdparty/thorvg/src/common/tvgMath.h @@ -70,7 +70,7 @@ static inline bool mathEqual(const Matrix& a, const Matrix& b) static inline bool mathRightAngle(const Matrix* m) { auto radian = fabsf(atan2f(m->e21, m->e11)); - if (radian < FLT_EPSILON || mathEqual(radian, float(M_PI_2)) || mathEqual(radian, float(M_PI))) return true; + if (radian < FLT_EPSILON || mathEqual(radian, MATH_PI2) || mathEqual(radian, MATH_PI)) return true; return false; } diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp index 7a4f544539..f2fbc07b4a 100644 --- a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp +++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp @@ -743,10 +743,10 @@ static Matrix* _parseTransformationMatrix(const char* value) } else goto error; } else if (state == MatrixState::Rotate) { //Transform to signed. - points[0] = fmod(points[0], 360); - if (points[0] < 0) points[0] += 360; - auto c = cosf(points[0] * (M_PI / 180.0)); - auto s = sinf(points[0] * (M_PI / 180.0)); + points[0] = fmodf(points[0], 360.0f); + if (points[0] < 0) points[0] += 360.0f; + auto c = cosf(points[0] * (MATH_PI / 180.0f)); + auto s = sinf(points[0] * (MATH_PI / 180.0f)); if (ptCount == 1) { Matrix tmp = { c, -s, 0, s, c, 0, 0, 0, 1 }; *matrix = mathMultiply(matrix, &tmp); @@ -769,12 +769,12 @@ static Matrix* _parseTransformationMatrix(const char* value) *matrix = mathMultiply(matrix, &tmp); } else if (state == MatrixState::SkewX) { if (ptCount != 1) goto error; - auto deg = tanf(points[0] * (M_PI / 180.0)); + auto deg = tanf(points[0] * (MATH_PI / 180.0f)); Matrix tmp = { 1, deg, 0, 0, 1, 0, 0, 0, 1 }; *matrix = mathMultiply(matrix, &tmp); } else if (state == MatrixState::SkewY) { if (ptCount != 1) goto error; - auto deg = tanf(points[0] * (M_PI / 180.0)); + auto deg = tanf(points[0] * (MATH_PI / 180.0f)); Matrix tmp = { 1, 0, 0, deg, 1, 0, 0, 0, 1 }; *matrix = mathMultiply(matrix, &tmp); } @@ -1919,6 +1919,19 @@ static SvgNode* _findNodeById(SvgNode *node, const char* id) } +static SvgNode* _findParentById(SvgNode* node, char* id, SvgNode* doc) +{ + SvgNode *parent = node->parent; + while (parent != nullptr && parent != doc) { + if (parent->id && !strcmp(parent->id, id)) { + return parent; + } + parent = parent->parent; + } + return nullptr; +} + + static constexpr struct { const char* tag; @@ -1959,8 +1972,12 @@ static bool _attrParseUseNode(void* data, const char* key, const char* value) defs = _getDefsNode(node); nodeFrom = _findNodeById(defs, id); if (nodeFrom) { - _cloneNode(nodeFrom, node, 0); - if (nodeFrom->type == SvgNodeType::Symbol) use->symbol = nodeFrom; + if (!_findParentById(node, id, loader->doc)) { + _cloneNode(nodeFrom, node, 0); + if (nodeFrom->type == SvgNodeType::Symbol) use->symbol = nodeFrom; + } else { + TVGLOG("SVG", "%s is ancestor element. This reference is invalid.", id); + } free(id); } else { //some svg export software include <defs> element at the end of the file @@ -2669,7 +2686,7 @@ static void _inheritGradient(SvgLoaderData* loader, SvgStyleGradient* to, SvgSty if (to->transform) memcpy(to->transform, from->transform, sizeof(Matrix)); } - if (to->type == SvgGradientType::Linear && from->type == SvgGradientType::Linear) { + if (to->type == SvgGradientType::Linear) { for (unsigned int i = 0; i < sizeof(linear_tags) / sizeof(linear_tags[0]); i++) { bool coordSet = to->flags & linear_tags[i].flag; if (!(to->flags & linear_tags[i].flag) && (from->flags & linear_tags[i].flag)) { @@ -2686,7 +2703,7 @@ static void _inheritGradient(SvgLoaderData* loader, SvgStyleGradient* to, SvgSty linear_tags[i].tagInheritedRecalc(loader, to->linear, to->userSpace); } } - } else if (to->type == SvgGradientType::Radial && from->type == SvgGradientType::Radial) { + } else if (to->type == SvgGradientType::Radial) { for (unsigned int i = 0; i < sizeof(radialTags) / sizeof(radialTags[0]); i++) { bool coordSet = (to->flags & radialTags[i].flag); if (!(to->flags & radialTags[i].flag) && (from->flags & radialTags[i].flag)) { @@ -2696,10 +2713,16 @@ static void _inheritGradient(SvgLoaderData* loader, SvgStyleGradient* to, SvgSty //GradUnits not set directly, coord set if (!gradUnitSet && coordSet) { radialTags[i].tagRecalc(loader, to->radial, to->userSpace); + //If fx and fy are not set, set cx and cy. + if (!strcmp(radialTags[i].tag, "cx") && !(to->flags & SvgGradientFlags::Fx)) to->radial->fx = to->radial->cx; + if (!strcmp(radialTags[i].tag, "cy") && !(to->flags & SvgGradientFlags::Fy)) to->radial->fy = to->radial->cy; } //GradUnits set, coord not set directly if (to->userSpace == from->userSpace) continue; if (gradUnitSet && !coordSet) { + //If fx and fx are not set, do not call recalc. + if (!strcmp(radialTags[i].tag, "fx") && !(to->flags & SvgGradientFlags::Fx)) continue; + if (!strcmp(radialTags[i].tag, "fy") && !(to->flags & SvgGradientFlags::Fy)) continue; radialTags[i].tagInheritedRecalc(loader, to->radial, to->userSpace); } } @@ -3018,9 +3041,13 @@ static void _clonePostponedNodes(Array<SvgNodeIdPair>* cloneNodes, SvgNode* doc) auto defs = _getDefsNode(nodeIdPair.node); auto nodeFrom = _findNodeById(defs, nodeIdPair.id); if (!nodeFrom) nodeFrom = _findNodeById(doc, nodeIdPair.id); - _cloneNode(nodeFrom, nodeIdPair.node, 0); - if (nodeFrom && nodeFrom->type == SvgNodeType::Symbol && nodeIdPair.node->type == SvgNodeType::Use) { - nodeIdPair.node->node.use.symbol = nodeFrom; + if (!_findParentById(nodeIdPair.node, nodeIdPair.id, doc)) { + _cloneNode(nodeFrom, nodeIdPair.node, 0); + if (nodeFrom && nodeFrom->type == SvgNodeType::Symbol && nodeIdPair.node->type == SvgNodeType::Use) { + nodeIdPair.node->node.use.symbol = nodeFrom; + } + } else { + TVGLOG("SVG", "%s is ancestor element. This reference is invalid.", nodeIdPair.id); } free(nodeIdPair.id); } diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp index f9780749a9..691cde1fc5 100644 --- a/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp +++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp @@ -126,7 +126,7 @@ void _pathAppendArcTo(Array<PathCommand>* cmds, Array<Point>* pts, Point* cur, P rx = fabsf(rx); ry = fabsf(ry); - angle = angle * M_PI / 180.0f; + angle = angle * MATH_PI / 180.0f; cosPhi = cosf(angle); sinPhi = sinf(angle); dx2 = (sx - x) / 2.0f; @@ -195,24 +195,24 @@ void _pathAppendArcTo(Array<PathCommand>* cmds, Array<Point>* pts, Point* cur, P //http://www.euclideanspace.com/maths/algebra/vectors/angleBetween/index.htm //Note: atan2 (0.0, 1.0) == 0.0 at = atan2(((y1p - cyp) / ry), ((x1p - cxp) / rx)); - theta1 = (at < 0.0f) ? 2.0f * M_PI + at : at; + theta1 = (at < 0.0f) ? 2.0f * MATH_PI + at : at; nat = atan2(((-y1p - cyp) / ry), ((-x1p - cxp) / rx)); - deltaTheta = (nat < at) ? 2.0f * M_PI - at + nat : nat - at; + deltaTheta = (nat < at) ? 2.0f * MATH_PI - at + nat : nat - at; if (sweep) { //Ensure delta theta < 0 or else add 360 degrees - if (deltaTheta < 0.0f) deltaTheta += (float)(2.0f * M_PI); + if (deltaTheta < 0.0f) deltaTheta += 2.0f * MATH_PI; } else { //Ensure delta theta > 0 or else substract 360 degrees - if (deltaTheta > 0.0f) deltaTheta -= (float)(2.0f * M_PI); + if (deltaTheta > 0.0f) deltaTheta -= 2.0f * MATH_PI; } //Add several cubic bezier to approximate the arc //(smaller than 90 degrees) //We add one extra segment because we want something //Smaller than 90deg (i.e. not 90 itself) - segments = static_cast<int>(fabsf(deltaTheta / float(M_PI_2)) + 1.0f); + segments = static_cast<int>(fabsf(deltaTheta / MATH_PI2) + 1.0f); delta = deltaTheta / segments; //http://www.stillhq.com/ctpfaq/2001/comp.text.pdf-faq-2001-04.txt (section 2.13) diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwShape.cpp b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwShape.cpp index 03261a4b7f..5e79bb6ffa 100644 --- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwShape.cpp +++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwShape.cpp @@ -28,6 +28,7 @@ /* Internal Class Implementation */ /************************************************************************/ + struct Line { Point pt1; @@ -55,23 +56,24 @@ static void _lineSplitAt(const Line& cur, float at, Line& left, Line& right) } -static void _outlineEnd(SwOutline& outline) +static bool _outlineEnd(SwOutline& outline) { - if (outline.pts.empty()) return; + //Make a contour if lineTo/curveTo without calling close/moveTo beforehand. + if (outline.pts.empty()) return false; outline.cntrs.push(outline.pts.count - 1); outline.closed.push(false); + return false; } -static void _outlineMoveTo(SwOutline& outline, const Point* to, const Matrix* transform) +static bool _outlineMoveTo(SwOutline& outline, const Point* to, const Matrix* transform, bool closed = false) { - if (outline.pts.count > 0) { - outline.cntrs.push(outline.pts.count - 1); - outline.closed.push(false); - } + //make it a contour, if the last contour is not closed yet. + if (!closed) _outlineEnd(outline); outline.pts.push(mathTransform(to, transform)); outline.types.push(SW_CURVE_TYPE_POINT); + return false; } @@ -95,20 +97,22 @@ static void _outlineCubicTo(SwOutline& outline, const Point* ctrl1, const Point* } -static void _outlineClose(SwOutline& outline) +static bool _outlineClose(SwOutline& outline) { - uint32_t i = 0; - + uint32_t i; if (outline.cntrs.count > 0) i = outline.cntrs.last() + 1; - else i = 0; //First Path + else i = 0; //Make sure there is at least one point in the current path - if (outline.pts.count == i) return; + if (outline.pts.count == i) return false; //Close the path outline.pts.push(outline.pts[i]); + outline.cntrs.push(outline.pts.count - 1); outline.types.push(SW_CURVE_TYPE_POINT); outline.closed.push(true); + + return true; } @@ -306,7 +310,7 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* trans bool isOdd = dash.cnt % 2; if (isOdd) patternLength *= 2; - offset = fmod(offset, patternLength); + offset = fmodf(offset, patternLength); if (offset < 0) offset += patternLength; for (size_t i = 0; i < dash.cnt * (1 + (size_t)isOdd); ++i, ++offIdx) { @@ -425,25 +429,28 @@ static bool _genOutline(SwShape* shape, const RenderShape* rshape, const Matrix* shape->outline = mpoolReqOutline(mpool, tid); auto outline = shape->outline; + bool closed = false; //Generate Outlines while (cmdCnt-- > 0) { switch (*cmds) { case PathCommand::Close: { - _outlineClose(*outline); + if (!closed) closed = _outlineClose(*outline); break; } case PathCommand::MoveTo: { - _outlineMoveTo(*outline, pts, transform); + closed = _outlineMoveTo(*outline, pts, transform, closed); ++pts; break; } case PathCommand::LineTo: { + if (closed) closed = _outlineEnd(*outline); _outlineLineTo(*outline, pts, transform); ++pts; break; } case PathCommand::CubicTo: { + if (closed) closed = _outlineEnd(*outline); _outlineCubicTo(*outline, pts, pts + 1, pts + 2, transform); pts += 3; break; @@ -452,7 +459,7 @@ static bool _genOutline(SwShape* shape, const RenderShape* rshape, const Matrix* ++cmds; } - _outlineEnd(*outline); + if (!closed) _outlineEnd(*outline); outline->fillRule = rshape->rule; shape->outline = outline; diff --git a/thirdparty/thorvg/src/renderer/tvgPaint.cpp b/thirdparty/thorvg/src/renderer/tvgPaint.cpp index 13ec4183d6..227ce10a0d 100644 --- a/thirdparty/thorvg/src/renderer/tvgPaint.cpp +++ b/thirdparty/thorvg/src/renderer/tvgPaint.cpp @@ -41,20 +41,26 @@ } -static bool _compFastTrack(Paint* cmpTarget, const RenderTransform* pTransform, RenderTransform* rTransform, RenderRegion& viewport) + +static Result _compFastTrack(Paint* cmpTarget, const RenderTransform* pTransform, RenderTransform* rTransform, RenderRegion& viewport) { /* Access Shape class by Paint is bad... but it's ok still it's an internal usage. */ auto shape = static_cast<Shape*>(cmpTarget); //Rectangle Candidates? const Point* pts; - if (shape->pathCoords(&pts) != 4) return false; + auto ptsCnt = shape->pathCoords(&pts); + + //nothing to clip + if (ptsCnt == 0) return Result::InvalidArguments; + + if (ptsCnt != 4) return Result::InsufficientCondition; if (rTransform) rTransform->update(); //No rotation and no skewing - if (pTransform && (!mathRightAngle(&pTransform->m) || mathSkewed(&pTransform->m))) return false; - if (rTransform && (!mathRightAngle(&rTransform->m) || mathSkewed(&rTransform->m))) return false; + if (pTransform && (!mathRightAngle(&pTransform->m) || mathSkewed(&pTransform->m))) return Result::InsufficientCondition; + if (rTransform && (!mathRightAngle(&rTransform->m) || mathSkewed(&rTransform->m))) return Result::InsufficientCondition; //Perpendicular Rectangle? auto pt1 = pts + 0; @@ -99,10 +105,9 @@ static bool _compFastTrack(Paint* cmpTarget, const RenderTransform* pTransform, if (viewport.w < 0) viewport.w = 0; if (viewport.h < 0) viewport.h = 0; - return true; + return Result::Success; } - - return false; + return Result::InsufficientCondition; } @@ -235,7 +240,7 @@ RenderData Paint::Impl::update(RenderMethod* renderer, const RenderTransform* pT /* 1. Composition Pre Processing */ RenderData trd = nullptr; //composite target render data RenderRegion viewport; - bool compFastTrack = false; + Result compFastTrack = Result::InsufficientCondition; bool childClipper = false; if (compData) { @@ -260,7 +265,7 @@ RenderData Paint::Impl::update(RenderMethod* renderer, const RenderTransform* pT } if (tryFastTrack) { RenderRegion viewport2; - if ((compFastTrack = _compFastTrack(target, pTransform, target->pImpl->rTransform, viewport2))) { + if ((compFastTrack = _compFastTrack(target, pTransform, target->pImpl->rTransform, viewport2)) == Result::Success) { viewport = renderer->viewport(); viewport2.intersect(viewport); renderer->viewport(viewport2); @@ -268,7 +273,7 @@ RenderData Paint::Impl::update(RenderMethod* renderer, const RenderTransform* pT } } } - if (!compFastTrack) { + if (compFastTrack == Result::InsufficientCondition) { childClipper = compData->method == CompositeMethod::ClipPath ? true : false; trd = target->pImpl->update(renderer, pTransform, clips, 255, pFlag, childClipper); if (childClipper) clips.push(trd); @@ -285,7 +290,7 @@ RenderData Paint::Impl::update(RenderMethod* renderer, const RenderTransform* pT PAINT_METHOD(rd, update(renderer, &outTransform, clips, opacity, newFlag, clipper)); /* 3. Composition Post Processing */ - if (compFastTrack) renderer->viewport(viewport); + if (compFastTrack == Result::Success) renderer->viewport(viewport); else if (childClipper) clips.pop(); return rd; diff --git a/thirdparty/thorvg/src/renderer/tvgShape.cpp b/thirdparty/thorvg/src/renderer/tvgShape.cpp index ab1f378b47..a42060e241 100644 --- a/thirdparty/thorvg/src/renderer/tvgShape.cpp +++ b/thirdparty/thorvg/src/renderer/tvgShape.cpp @@ -164,7 +164,7 @@ Result Shape::appendArc(float cx, float cy, float radius, float startAngle, floa } for (int i = 0; i < nCurves; ++i) { - auto endAngle = startAngle + ((i != nCurves - 1) ? float(M_PI_2) * sweepSign : fract); + auto endAngle = startAngle + ((i != nCurves - 1) ? MATH_PI2 * sweepSign : fract); Point end = {radius * cosf(endAngle), radius * sinf(endAngle)}; //variables needed to calculate bezier control points diff --git a/thirdparty/thorvg/update-thorvg.sh b/thirdparty/thorvg/update-thorvg.sh index 7a754c09b9..afe05e629e 100755 --- a/thirdparty/thorvg/update-thorvg.sh +++ b/thirdparty/thorvg/update-thorvg.sh @@ -1,6 +1,6 @@ #!/bin/bash -e -VERSION=0.12.9 +VERSION=0.12.10 cd thirdparty/thorvg/ || true rm -rf AUTHORS LICENSE inc/ src/ *.zip *.tar.gz tmp/ |