diff options
86 files changed, 2490 insertions, 1442 deletions
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/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/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/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/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/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/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/Skeleton3D.xml b/doc/classes/Skeleton3D.xml index c9ad204247..17c93af652 100644 --- a/doc/classes/Skeleton3D.xml +++ b/doc/classes/Skeleton3D.xml @@ -27,7 +27,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 +58,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 +81,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 +125,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 +183,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 +191,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 +199,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 +207,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 +233,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 +247,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 +273,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 +285,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 +334,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 +352,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 +378,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..46b3d79795 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,13 +16,13 @@ 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> @@ -56,9 +56,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/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/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/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_help.cpp b/editor/editor_help.cpp index 022cf82426..bba1939f72 100644 --- a/editor/editor_help.cpp +++ b/editor/editor_help.cpp @@ -3471,7 +3471,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 +3563,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 +3582,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/filesystem_dock.cpp b/editor/filesystem_dock.cpp index 4521f4d3ff..9937b3fc51 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) { @@ -2757,7 +2757,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 +2872,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 +2923,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 +2940,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 +3464,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/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/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/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/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/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/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/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/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/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/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/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/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/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp index f5174d7d46..cc1ceec41e 100644 --- a/modules/text_server_adv/text_server_adv.cpp +++ b/modules/text_server_adv/text_server_adv.cpp @@ -6976,6 +6976,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..160eb27528 100644 --- a/modules/text_server_adv/text_server_adv.h +++ b/modules/text_server_adv/text_server_adv.h @@ -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..1a0a4ec356 100644 --- a/modules/text_server_fb/text_server_fb.cpp +++ b/modules/text_server_fb/text_server_fb.cpp @@ -4439,6 +4439,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..9fd5af8b51 100644 --- a/modules/text_server_fb/text_server_fb.h +++ b/modules/text_server_fb/text_server_fb.h @@ -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/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/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/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..566801c9f7 100644 --- a/scene/3d/skeleton_3d.cpp +++ b/scene/3d/skeleton_3d.cpp @@ -32,10 +32,11 @@ #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/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 +71,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 +141,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 +264,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 +379,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 +609,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 +649,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 +728,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 +736,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 +765,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 +838,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 +877,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 +925,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 +1021,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 +1036,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 +1048,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..757ac68e46 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 { @@ -930,6 +923,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(); } @@ -2228,19 +2222,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 +2261,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..ed291bfe63 100644 --- a/scene/animation/animation_mixer.h +++ b/scene/animation/animation_mixer.h @@ -341,6 +341,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 +379,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 +437,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/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/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/thirdparty/README.md b/thirdparty/README.md index 79ceee52c3..3b920d7247 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 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----- |