diff options
111 files changed, 1373 insertions, 1136 deletions
diff --git a/.github/workflows/macos_builds.yml b/.github/workflows/macos_builds.yml index 04bdf971ca..70031ec4c3 100644 --- a/.github/workflows/macos_builds.yml +++ b/.github/workflows/macos_builds.yml @@ -14,7 +14,7 @@ concurrency: jobs: build-macos: - runs-on: "macos-12" + runs-on: "macos-latest" name: ${{ matrix.name }} strategy: fail-fast: false @@ -24,7 +24,7 @@ jobs: cache-name: macos-editor target: editor tests: true - bin: "./bin/godot.macos.editor.x86_64" + bin: "./bin/godot.macos.editor.universal" - name: Template (target=template_release) cache-name: macos-template @@ -50,16 +50,26 @@ jobs: run: | sh misc/scripts/install_vulkan_sdk_macos.sh - - name: Compilation + - name: Compilation (x86_64) uses: ./.github/actions/godot-build with: - sconsflags: ${{ env.SCONSFLAGS }} + sconsflags: ${{ env.SCONSFLAGS }} arch=x86_64 + platform: macos + target: ${{ matrix.target }} + tests: ${{ matrix.tests }} + + - name: Compilation (arm64) + uses: ./.github/actions/godot-build + with: + sconsflags: ${{ env.SCONSFLAGS }} arch=arm64 platform: macos target: ${{ matrix.target }} tests: ${{ matrix.tests }} - name: Prepare artifact run: | + lipo -create ./bin/godot.macos.${{ matrix.target }}.x86_64 ./bin/godot.macos.${{ matrix.target }}.arm64 -output ./bin/godot.macos.${{ matrix.target }}.universal + rm ./bin/godot.macos.${{ matrix.target }}.x86_64 ./bin/godot.macos.${{ matrix.target }}.arm64 strip bin/godot.* chmod +x bin/godot.* diff --git a/SConstruct b/SConstruct index 05e69c431d..18511ff5ee 100644 --- a/SConstruct +++ b/SConstruct @@ -14,9 +14,6 @@ from types import ModuleType from collections import OrderedDict from importlib.util import spec_from_file_location, module_from_spec from SCons import __version__ as scons_raw_version -from SCons.Script.SConscript import SConsEnvironment - -scons_ver = SConsEnvironment._get_major_minor_revision(scons_raw_version) # Explicitly resolve the helper modules, this is done to avoid clash with # modules of the same name that might be randomly added (e.g. someone adding @@ -134,6 +131,7 @@ if "TERM" in os.environ: # Used for colored output. env.disabled_modules = [] env.module_version_string = "" env.msvc = False +env.scons_version = env._get_major_minor_revision(scons_raw_version) env.__class__.disable_module = methods.disable_module @@ -172,7 +170,7 @@ if profile: opts = Variables(customs, ARGUMENTS) # Target build options -if scons_ver >= (4, 3): +if env.scons_version >= (4, 3): opts.Add(["platform", "p"], "Target platform (%s)" % "|".join(platform_list), "") else: opts.Add("platform", "Target platform (%s)" % "|".join(platform_list), "") @@ -294,7 +292,7 @@ if env["import_env_vars"]: selected_platform = env["platform"] -if scons_ver < (4, 3) and not selected_platform: +if env.scons_version < (4, 3) and not selected_platform: selected_platform = env["p"] if selected_platform == "": @@ -983,16 +981,16 @@ if env["vsproj"]: env.vs_incs = [] env.vs_srcs = [] -if env["compiledb"] and scons_ver < (4, 0, 0): +if env["compiledb"] and env.scons_version < (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): +if env.scons_version >= (4, 0, 0): env.Tool("compilation_db") env.Alias("compiledb", env.CompilationDatabase()) if env["ninja"]: - if scons_ver < (4, 2, 0): + if env.scons_version < (4, 2, 0): print("The `ninja=yes` option requires SCons 4.2 or later, but your version is %s." % scons_raw_version) Exit(255) diff --git a/core/extension/gdextension.cpp b/core/extension/gdextension.cpp index 3ea5791282..22a5df9935 100644 --- a/core/extension/gdextension.cpp +++ b/core/extension/gdextension.cpp @@ -275,7 +275,7 @@ public: ret_opaque = r_ret->get_type() == Variant::NIL ? r_ret : VariantInternal::get_opaque_pointer(r_ret); } - ptrcall(p_object, argptrs, ret_opaque); + ptrcall_func(method_userdata, extension_instance, reinterpret_cast<GDExtensionConstTypePtr *>(argptrs), (GDExtensionTypePtr)ret_opaque); if (r_ret && r_ret->get_type() == Variant::OBJECT) { VariantInternal::update_object_id(r_ret); @@ -289,7 +289,7 @@ public: ERR_FAIL_COND_MSG(p_object && p_object->is_extension_placeholder(), vformat("Cannot call GDExtension method bind '%s' on placeholder instance.", name)); #endif ERR_FAIL_COND_MSG(vararg, "Vararg methods don't have ptrcall support. This is most likely an engine bug."); - GDExtensionClassInstancePtr extension_instance = p_object->_get_extension_instance(); + GDExtensionClassInstancePtr extension_instance = is_static() ? nullptr : p_object->_get_extension_instance(); ptrcall_func(method_userdata, extension_instance, reinterpret_cast<GDExtensionConstTypePtr *>(p_args), (GDExtensionTypePtr)r_ret); } diff --git a/core/input/gamecontrollerdb.txt b/core/input/gamecontrollerdb.txt index a17e15a62c..7150911e75 100644 --- a/core/input/gamecontrollerdb.txt +++ b/core/input/gamecontrollerdb.txt @@ -917,6 +917,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 030000000d0f00003801000008010000,Hori PC Engine Mini Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b9,platform:Mac OS X, 030000000d0f00009200000000010000,Hori Pokken Tournament DX Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X, 030000000d0f0000aa00000072050000,Hori Real Arcade Pro for Nintendo Switch,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X, +030000000d0f00000002000017010000,Hori Split Pad Fit,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 030000000d0f00000002000015010000,Hori Switch Split Pad Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 030000000d0f00006e00000000010000,Horipad 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 030000000d0f00006600000000010000,Horipad 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, @@ -1282,6 +1283,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 030000000d0f00006b00000011010000,Hori Real Arcade Pro 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 030000000d0f00001600000000010000,Hori Real Arcade Pro EXSE,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b2,y:b3,platform:Linux, 030000000d0f0000aa00000011010000,Hori Real Arcade Pro for Nintendo Switch,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, +030000000d0f00008501000017010000,Hori Split Pad Fit,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000000d0f00008501000015010000,Hori Switch Split Pad Pro,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000000d0f00006e00000011010000,Horipad 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 030000000d0f00006600000011010000,Horipad 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, @@ -1696,6 +1698,8 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 060000005e040000120b00000f050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 050000005e040000130b000017050000,Xbox Series X Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 060000005e040000120b00000d050000,Xbox Series X Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e040000120b000011050000,Xbox Series X Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e040000120b000014050000,Xbox Series X Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 050000005e040000200b000013050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 050000005e040000200b000017050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 050000005e040000220b000017050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, diff --git a/core/io/logger.cpp b/core/io/logger.cpp index 441df7f5d1..1476b8ccac 100644 --- a/core/io/logger.cpp +++ b/core/io/logger.cpp @@ -37,6 +37,8 @@ #include "core/os/time.h" #include "core/string/print_string.h" +#include "modules/modules_enabled.gen.h" // For regex. + #if defined(MINGW_ENABLED) || defined(_MSC_VER) #define sprintf sprintf_s #endif @@ -180,6 +182,12 @@ RotatedFileLogger::RotatedFileLogger(const String &p_base_path, int p_max_files) base_path(p_base_path.simplify_path()), max_files(p_max_files > 0 ? p_max_files : 1) { rotate_file(); + +#ifdef MODULE_REGEX_ENABLED + strip_ansi_regex.instantiate(); + strip_ansi_regex->detach_from_objectdb(); // Note: This RegEx instance will exist longer than ObjectDB, therefore can't be registered in ObjectDB. + strip_ansi_regex->compile("\u001b\\[((?:\\d|;)*)([a-zA-Z])"); +#endif // MODULE_REGEX_ENABLED } void RotatedFileLogger::logv(const char *p_format, va_list p_list, bool p_err) { @@ -199,7 +207,15 @@ void RotatedFileLogger::logv(const char *p_format, va_list p_list, bool p_err) { vsnprintf(buf, len + 1, p_format, list_copy); } va_end(list_copy); + +#ifdef MODULE_REGEX_ENABLED + // Strip ANSI escape codes (such as those inserted by `print_rich()`) + // before writing to file, as text editors cannot display those + // correctly. + file->store_string(strip_ansi_regex->sub(String(buf), "", true)); +#else file->store_buffer((uint8_t *)buf, len); +#endif // MODULE_REGEX_ENABLED if (len >= static_buf_size) { Memory::free_static(buf); diff --git a/core/io/logger.h b/core/io/logger.h index 3cd18965c5..85ef3031ec 100644 --- a/core/io/logger.h +++ b/core/io/logger.h @@ -34,6 +34,10 @@ #include "core/io/file_access.h" #include "core/string/ustring.h" #include "core/templates/vector.h" +#include "modules/modules_enabled.gen.h" // For regex. +#ifdef MODULE_REGEX_ENABLED +#include "modules/regex/regex.h" +#endif // MODULE_REGEX_ENABLED #include <stdarg.h> @@ -86,6 +90,10 @@ class RotatedFileLogger : public Logger { void clear_old_backups(); void rotate_file(); +#ifdef MODULE_REGEX_ENABLED + Ref<RegEx> strip_ansi_regex; +#endif // MODULE_REGEX_ENABLED + public: explicit RotatedFileLogger(const String &p_base_path, int p_max_files = 10); diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp index eea6357084..c3c37aa89d 100644 --- a/core/io/resource_loader.cpp +++ b/core/io/resource_loader.cpp @@ -242,16 +242,20 @@ ResourceLoader::LoadToken::~LoadToken() { } Ref<Resource> ResourceLoader::_load(const String &p_path, const String &p_original_path, const String &p_type_hint, ResourceFormatLoader::CacheMode p_cache_mode, Error *r_error, bool p_use_sub_threads, float *r_progress) { + const String &original_path = p_original_path.is_empty() ? p_path : p_original_path; load_nesting++; if (load_paths_stack->size()) { thread_load_mutex.lock(); - HashMap<String, ThreadLoadTask>::Iterator E = thread_load_tasks.find(load_paths_stack->get(load_paths_stack->size() - 1)); - if (E) { + const String &parent_task_path = load_paths_stack->get(load_paths_stack->size() - 1); + HashMap<String, ThreadLoadTask>::Iterator E = thread_load_tasks.find(parent_task_path); + // Avoid double-tracking, for progress reporting, resources that boil down to a remapped path containing the real payload (e.g., imported resources). + bool is_remapped_load = original_path == parent_task_path; + if (E && !is_remapped_load) { E->value.sub_tasks.insert(p_original_path); } thread_load_mutex.unlock(); } - load_paths_stack->push_back(p_original_path); + load_paths_stack->push_back(original_path); // Try all loaders and pick the first match for the type hint bool found = false; @@ -261,7 +265,7 @@ Ref<Resource> ResourceLoader::_load(const String &p_path, const String &p_origin continue; } found = true; - res = loader[i]->load(p_path, !p_original_path.is_empty() ? p_original_path : p_path, r_error, p_use_sub_threads, r_progress, p_cache_mode); + res = loader[i]->load(p_path, original_path, r_error, p_use_sub_threads, r_progress, p_cache_mode); if (!res.is_null()) { break; } diff --git a/core/object/class_db.h b/core/object/class_db.h index adb525cbe8..37a864c109 100644 --- a/core/object/class_db.h +++ b/core/object/class_db.h @@ -195,7 +195,7 @@ public: template <typename T> static void register_class(bool p_virtual = false) { GLOBAL_LOCK_FUNCTION; - static_assert(types_are_same_v<typename T::self_type, T>, "Class not declared properly, please use GDCLASS."); + static_assert(std::is_same_v<typename T::self_type, T>, "Class not declared properly, please use GDCLASS."); T::initialize_class(); ClassInfo *t = classes.getptr(T::get_class_static()); ERR_FAIL_NULL(t); @@ -210,7 +210,7 @@ public: template <typename T> static void register_abstract_class() { GLOBAL_LOCK_FUNCTION; - static_assert(types_are_same_v<typename T::self_type, T>, "Class not declared properly, please use GDCLASS."); + static_assert(std::is_same_v<typename T::self_type, T>, "Class not declared properly, please use GDCLASS."); T::initialize_class(); ClassInfo *t = classes.getptr(T::get_class_static()); ERR_FAIL_NULL(t); @@ -223,7 +223,7 @@ public: template <typename T> static void register_internal_class() { GLOBAL_LOCK_FUNCTION; - static_assert(types_are_same_v<typename T::self_type, T>, "Class not declared properly, please use GDCLASS."); + static_assert(std::is_same_v<typename T::self_type, T>, "Class not declared properly, please use GDCLASS."); T::initialize_class(); ClassInfo *t = classes.getptr(T::get_class_static()); ERR_FAIL_NULL(t); @@ -238,7 +238,7 @@ public: template <typename T> static void register_runtime_class() { GLOBAL_LOCK_FUNCTION; - static_assert(types_are_same_v<typename T::self_type, T>, "Class not declared properly, please use GDCLASS."); + static_assert(std::is_same_v<typename T::self_type, T>, "Class not declared properly, please use GDCLASS."); T::initialize_class(); ClassInfo *t = classes.getptr(T::get_class_static()); ERR_FAIL_NULL(t); @@ -263,7 +263,7 @@ public: template <typename T> static void register_custom_instance_class() { GLOBAL_LOCK_FUNCTION; - static_assert(types_are_same_v<typename T::self_type, T>, "Class not declared properly, please use GDCLASS."); + static_assert(std::is_same_v<typename T::self_type, T>, "Class not declared properly, please use GDCLASS."); T::initialize_class(); ClassInfo *t = classes.getptr(T::get_class_static()); ERR_FAIL_NULL(t); diff --git a/core/variant/type_info.h b/core/variant/type_info.h index 9c52db3345..15cb6c9c1a 100644 --- a/core/variant/type_info.h +++ b/core/variant/type_info.h @@ -33,31 +33,7 @@ #include "core/typedefs.h" -template <bool C, typename T = void> -struct EnableIf { - typedef T type; -}; - -template <typename T> -struct EnableIf<false, T> { -}; - -template <typename, typename> -inline constexpr bool types_are_same_v = false; - -template <typename T> -inline constexpr bool types_are_same_v<T, T> = true; - -template <typename B, typename D> -struct TypeInherits { - static D *get_d(); - - static char (&test(B *))[1]; - static char (&test(...))[2]; - - static bool const value = sizeof(test(get_d())) == sizeof(char) && - !types_are_same_v<B volatile const, void volatile const>; -}; +#include <type_traits> namespace GodotTypeInfo { enum Metadata { @@ -223,16 +199,7 @@ MAKE_TEMPLATE_TYPE_INFO(Vector, Face3, Variant::PACKED_VECTOR3_ARRAY) MAKE_TEMPLATE_TYPE_INFO(Vector, StringName, Variant::PACKED_STRING_ARRAY) template <typename T> -struct GetTypeInfo<T *, typename EnableIf<TypeInherits<Object, T>::value>::type> { - static const Variant::Type VARIANT_TYPE = Variant::OBJECT; - static const GodotTypeInfo::Metadata METADATA = GodotTypeInfo::METADATA_NONE; - static inline PropertyInfo get_class_info() { - return PropertyInfo(StringName(T::get_class_static())); - } -}; - -template <typename T> -struct GetTypeInfo<const T *, typename EnableIf<TypeInherits<Object, T>::value>::type> { +struct GetTypeInfo<T *, std::enable_if_t<std::is_base_of_v<Object, T>>> { static const Variant::Type VARIANT_TYPE = Variant::OBJECT; static const GodotTypeInfo::Metadata METADATA = GodotTypeInfo::METADATA_NONE; static inline PropertyInfo get_class_info() { diff --git a/doc/classes/Animation.xml b/doc/classes/Animation.xml index b00889f483..4e9b642674 100644 --- a/doc/classes/Animation.xml +++ b/doc/classes/Animation.xml @@ -602,7 +602,7 @@ <member name="loop_mode" type="int" setter="set_loop_mode" getter="get_loop_mode" enum="Animation.LoopMode" default="0"> Determines the behavior of both ends of the animation timeline during animation playback. This is used for correct interpolation of animation cycles, and for hinting the player that it must restart the animation. </member> - <member name="step" type="float" setter="set_step" getter="get_step" default="0.1"> + <member name="step" type="float" setter="set_step" getter="get_step" default="0.0333333"> The animation step value. </member> </members> diff --git a/doc/classes/CanvasItem.xml b/doc/classes/CanvasItem.xml index 1cfd44467f..18413c4be5 100644 --- a/doc/classes/CanvasItem.xml +++ b/doc/classes/CanvasItem.xml @@ -8,6 +8,7 @@ Any [CanvasItem] can draw. For this, [method queue_redraw] is called by the engine, then [constant NOTIFICATION_DRAW] will be received on idle time to request a redraw. Because of this, canvas items don't need to be redrawn on every frame, improving the performance significantly. Several functions for drawing on the [CanvasItem] are provided (see [code]draw_*[/code] functions). However, they can only be used inside [method _draw], its corresponding [method Object._notification] or methods connected to the [signal draw] signal. Canvas items are drawn in tree order on their canvas layer. By default, children are on top of their parents, so a root [CanvasItem] will be drawn behind everything. This behavior can be changed on a per-item basis. A [CanvasItem] can be hidden, which will also hide its children. By adjusting various other properties of a [CanvasItem], you can also modulate its color (via [member modulate] or [member self_modulate]), change its Z-index, blend mode, and more. + Note that properties like transform, modulation, and visibility are only propagated to [i]direct[/i] [CanvasItem] child nodes. If there is a non-[CanvasItem] node in between, like [Node] or [AnimationPlayer], the [CanvasItem] nodes below will have an independent position and [member modulate] chain. See also [member top_level]. </description> <tutorials> <link title="Viewport and canvas transforms">$DOCS_URL/tutorials/2d/2d_transforms.html</link> diff --git a/doc/classes/FileAccess.xml b/doc/classes/FileAccess.xml index fafc02734a..99574da808 100644 --- a/doc/classes/FileAccess.xml +++ b/doc/classes/FileAccess.xml @@ -8,23 +8,23 @@ Here's a sample on how to write and read from a file: [codeblocks] [gdscript] - func save(content): + func save_to_file(content): var file = FileAccess.open("user://save_game.dat", FileAccess.WRITE) file.store_string(content) - func load(): + func load_from_file(): var file = FileAccess.open("user://save_game.dat", FileAccess.READ) var content = file.get_as_text() return content [/gdscript] [csharp] - public void Save(string content) + public void SaveToFile(string content) { using var file = FileAccess.Open("user://save_game.dat", FileAccess.ModeFlags.Write); file.StoreString(content); } - public string Load() + public string LoadFromFile() { using var file = FileAccess.Open("user://save_game.dat", FileAccess.ModeFlags.Read); string content = file.GetAsText(); diff --git a/doc/classes/ParticleProcessMaterial.xml b/doc/classes/ParticleProcessMaterial.xml index 8d0ae317b9..1502690b45 100644 --- a/doc/classes/ParticleProcessMaterial.xml +++ b/doc/classes/ParticleProcessMaterial.xml @@ -185,6 +185,7 @@ </member> <member name="emission_box_extents" type="Vector3" setter="set_emission_box_extents" getter="get_emission_box_extents"> The box's extents if [member emission_shape] is set to [constant EMISSION_SHAPE_BOX]. + [b]Note:[/b] [member emission_box_extents] starts from the center point and applies the X, Y, and Z values in both directions. The size is twice the area of the extents. </member> <member name="emission_color_texture" type="Texture2D" setter="set_emission_color_texture" getter="get_emission_color_texture"> Particle color will be modulated by color determined by sampling this texture at the same point as the [member emission_point_texture]. diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml index d7a659c255..5d90cd6b92 100644 --- a/doc/classes/RenderingServer.xml +++ b/doc/classes/RenderingServer.xml @@ -92,13 +92,13 @@ Sets the exposure values that will be used by the renderers. The normalization amount is used to bake a given Exposure Value (EV) into rendering calculations to reduce the dynamic range of the scene. The normalization factor can be calculated from exposure value (EV100) as follows: [codeblock] - func get_exposure_normalization(float ev100): - return 1.0 / (pow(2.0, ev100) * 1.2) + func get_exposure_normalization(ev100: float): + return 1.0 / (pow(2.0, ev100) * 1.2) [/codeblock] The exposure value can be calculated from aperture (in f-stops), shutter speed (in seconds), and sensitivity (in ISO) as follows: [codeblock] - func get_exposure(float aperture, float shutter_speed, float sensitivity): - return log2((aperture * aperture) / shutterSpeed * (100.0 / sensitivity)) + func get_exposure(aperture: float, shutter_speed: float, sensitivity: float): + return log((aperture * aperture) / shutter_speed * (100.0 / sensitivity)) / log(2) [/codeblock] </description> </method> diff --git a/doc/classes/TabContainer.xml b/doc/classes/TabContainer.xml index f4d69c3076..090afa0220 100644 --- a/doc/classes/TabContainer.xml +++ b/doc/classes/TabContainer.xml @@ -64,6 +64,13 @@ Returns the [Texture2D] for the tab at index [param tab_idx] or [code]null[/code] if the tab has no [Texture2D]. </description> </method> + <method name="get_tab_icon_max_width" qualifiers="const"> + <return type="int" /> + <param index="0" name="tab_idx" type="int" /> + <description> + Returns the maximum allowed width of the icon for the tab at index [param tab_idx]. + </description> + </method> <method name="get_tab_idx_at_point" qualifiers="const"> <return type="int" /> <param index="0" name="point" type="Vector2" /> @@ -164,6 +171,14 @@ Sets an icon for the tab at index [param tab_idx]. </description> </method> + <method name="set_tab_icon_max_width"> + <return type="void" /> + <param index="0" name="tab_idx" type="int" /> + <param index="1" name="width" type="int" /> + <description> + Sets the maximum allowed width of the icon for the tab at index [param tab_idx]. This limit is applied on top of the default size of the icon and on top of [theme_item icon_max_width]. The height is adjusted according to the icon's ratio. + </description> + </method> <method name="set_tab_metadata"> <return type="void" /> <param index="0" name="tab_idx" type="int" /> diff --git a/doc/classes/Tree.xml b/doc/classes/Tree.xml index d95492479c..43cd1a8aaa 100644 --- a/doc/classes/Tree.xml +++ b/doc/classes/Tree.xml @@ -546,16 +546,16 @@ The maximum allowed width of the icon in item's cells. This limit is applied on top of the default size of the icon, but before the value set with [method TreeItem.set_icon_max_width]. The height is adjusted according to the icon's ratio. </theme_item> <theme_item name="inner_item_margin_bottom" data_type="constant" type="int" default="0"> - The inner bottom margin of an item. + The inner bottom margin of a cell. </theme_item> <theme_item name="inner_item_margin_left" data_type="constant" type="int" default="0"> - The inner left margin of an item. + The inner left margin of a cell. </theme_item> <theme_item name="inner_item_margin_right" data_type="constant" type="int" default="0"> - The inner right margin of an item. + The inner right margin of a cell. </theme_item> <theme_item name="inner_item_margin_top" data_type="constant" type="int" default="0"> - The inner top margin of an item. + The inner top margin of a cell. </theme_item> <theme_item name="item_margin" data_type="constant" type="int" default="16"> The horizontal margin at the start of an item. This is used when folding is enabled for the item. diff --git a/doc/classes/XRHandTracker.xml b/doc/classes/XRHandTracker.xml index 69390df696..636af6625b 100644 --- a/doc/classes/XRHandTracker.xml +++ b/doc/classes/XRHandTracker.xml @@ -11,12 +11,6 @@ <link title="XR documentation index">$DOCS_URL/tutorials/xr/index.html</link> </tutorials> <methods> - <method name="get_hand" qualifiers="const"> - <return type="int" enum="XRHandTracker.Hand" /> - <description> - Returns the type of hand. - </description> - </method> <method name="get_hand_joint_angular_velocity" qualifiers="const"> <return type="Vector3" /> <param index="0" name="joint" type="int" enum="XRHandTracker.HandJoint" /> @@ -52,13 +46,6 @@ Returns the transform for the given hand joint. </description> </method> - <method name="set_hand"> - <return type="void" /> - <param index="0" name="hand" type="int" enum="XRHandTracker.Hand" /> - <description> - Sets the type of hand. - </description> - </method> <method name="set_hand_joint_angular_velocity"> <return type="void" /> <param index="0" name="joint" type="int" enum="XRHandTracker.HandJoint" /> @@ -101,6 +88,7 @@ </method> </methods> <members> + <member name="hand" type="int" setter="set_tracker_hand" getter="get_tracker_hand" overrides="XRPositionalTracker" enum="XRPositionalTracker.TrackerHand" default="1" /> <member name="hand_tracking_source" type="int" setter="set_hand_tracking_source" getter="get_hand_tracking_source" enum="XRHandTracker.HandTrackingSource" default="0"> The source of the hand tracking data. </member> @@ -110,15 +98,6 @@ <member name="type" type="int" setter="set_tracker_type" getter="get_tracker_type" overrides="XRTracker" enum="XRServer.TrackerType" default="16" /> </members> <constants> - <constant name="HAND_LEFT" value="0" enum="Hand"> - A left hand. - </constant> - <constant name="HAND_RIGHT" value="1" enum="Hand"> - A right hand. - </constant> - <constant name="HAND_MAX" value="2" enum="Hand"> - Represents the size of the [enum Hand] enum. - </constant> <constant name="HAND_TRACKING_SOURCE_UNKNOWN" value="0" enum="HandTrackingSource"> The source of hand tracking data is unknown. </constant> diff --git a/drivers/gles3/effects/post_effects.cpp b/drivers/gles3/effects/post_effects.cpp index 8ad872f319..105c8f6b71 100644 --- a/drivers/gles3/effects/post_effects.cpp +++ b/drivers/gles3/effects/post_effects.cpp @@ -87,7 +87,7 @@ void PostEffects::_draw_screen_triangle() { glBindVertexArray(0); } -void PostEffects::post_copy(GLuint p_dest_framebuffer, Size2i p_dest_size, GLuint p_source_color, Size2i p_source_size, float p_luminance_multiplier, const Glow::GLOWLEVEL *p_glow_buffers, float p_glow_intensity, uint32_t p_view, bool p_use_multiview) { +void PostEffects::post_copy(GLuint p_dest_framebuffer, Size2i p_dest_size, GLuint p_source_color, Size2i p_source_size, float p_luminance_multiplier, const Glow::GLOWLEVEL *p_glow_buffers, float p_glow_intensity, uint32_t p_view, bool p_use_multiview, uint64_t p_spec_constants) { glDisable(GL_DEPTH_TEST); glDepthMask(GL_FALSE); glDisable(GL_BLEND); @@ -96,7 +96,7 @@ void PostEffects::post_copy(GLuint p_dest_framebuffer, Size2i p_dest_size, GLuin glViewport(0, 0, p_dest_size.x, p_dest_size.y); PostShaderGLES3::ShaderVariant mode = PostShaderGLES3::MODE_DEFAULT; - uint64_t flags = 0; + uint64_t flags = p_spec_constants; if (p_use_multiview) { flags |= PostShaderGLES3::USE_MULTIVIEW; } diff --git a/drivers/gles3/effects/post_effects.h b/drivers/gles3/effects/post_effects.h index b90c77d6c7..916d29a052 100644 --- a/drivers/gles3/effects/post_effects.h +++ b/drivers/gles3/effects/post_effects.h @@ -59,7 +59,7 @@ public: PostEffects(); ~PostEffects(); - void post_copy(GLuint p_dest_framebuffer, Size2i p_dest_size, GLuint p_source_color, Size2i p_source_size, float p_luminance_multiplier, const Glow::GLOWLEVEL *p_glow_buffers, float p_glow_intensity, uint32_t p_view = 0, bool p_use_multiview = false); + void post_copy(GLuint p_dest_framebuffer, Size2i p_dest_size, GLuint p_source_color, Size2i p_source_size, float p_luminance_multiplier, const Glow::GLOWLEVEL *p_glow_buffers, float p_glow_intensity, uint32_t p_view = 0, bool p_use_multiview = false, uint64_t p_spec_constants = 0); }; } //namespace GLES3 diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp index bc1af86938..606bcee775 100644 --- a/drivers/gles3/rasterizer_scene_gles3.cpp +++ b/drivers/gles3/rasterizer_scene_gles3.cpp @@ -788,7 +788,6 @@ void RasterizerSceneGLES3::_draw_sky(RID p_env, const Projection &p_projection, } if (!p_apply_color_adjustments_in_post) { spec_constants |= SkyShaderGLES3::APPLY_TONEMAPPING; - // TODO add BCS and color corrections once supported. } RS::EnvironmentBG background = environment_get_background(p_env); @@ -2336,9 +2335,18 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_ SceneState::TonemapUBO tonemap_ubo; if (render_data.environment.is_valid()) { + bool use_bcs = environment_get_adjustments_enabled(render_data.environment); + if (use_bcs) { + apply_color_adjustments_in_post = true; + } + tonemap_ubo.exposure = environment_get_exposure(render_data.environment); tonemap_ubo.white = environment_get_white(render_data.environment); tonemap_ubo.tonemapper = int32_t(environment_get_tone_mapper(render_data.environment)); + + tonemap_ubo.brightness = environment_get_adjustments_brightness(render_data.environment); + tonemap_ubo.contrast = environment_get_adjustments_contrast(render_data.environment); + tonemap_ubo.saturation = environment_get_adjustments_saturation(render_data.environment); } if (scene_state.tonemap_buffer == 0) { @@ -2558,8 +2566,6 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_ if (!apply_color_adjustments_in_post) { spec_constant_base_flags |= SceneShaderGLES3::APPLY_TONEMAPPING; - - // TODO add BCS and Color corrections here once supported. } } // Render Opaque Objects. @@ -2700,6 +2706,29 @@ void RasterizerSceneGLES3::_render_post_processing(const RenderDataGLES3 *p_rend rb->check_glow_buffers(); } + bool use_bcs = environment_get_adjustments_enabled(p_render_data->environment); + uint64_t bcs_spec_constants = 0; + RID color_correction_texture = environment_get_color_correction(p_render_data->environment); + if (use_bcs && color_correction_texture.is_valid()) { + bcs_spec_constants |= PostShaderGLES3::USE_BCS; + bcs_spec_constants |= PostShaderGLES3::USE_COLOR_CORRECTION; + + bool use_1d_lut = environment_get_use_1d_color_correction(p_render_data->environment); + GLenum texture_target = GL_TEXTURE_3D; + if (use_1d_lut) { + bcs_spec_constants |= PostShaderGLES3::USE_1D_LUT; + texture_target = GL_TEXTURE_2D; + } + + glActiveTexture(GL_TEXTURE2); + glBindTexture(texture_target, texture_storage->texture_get_texid(color_correction_texture)); + glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(texture_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + } + if (view_count == 1) { // Resolve if needed. if (fbo_msaa_3d != 0 && msaa3d_needs_resolve) { @@ -2735,7 +2764,7 @@ void RasterizerSceneGLES3::_render_post_processing(const RenderDataGLES3 *p_rend } // Copy color buffer - post_effects->post_copy(fbo_rt, target_size, color, internal_size, p_render_data->luminance_multiplier, glow_buffers, glow_intensity); + post_effects->post_copy(fbo_rt, target_size, color, internal_size, p_render_data->luminance_multiplier, glow_buffers, glow_intensity, 0, false, bcs_spec_constants); // Copy depth buffer glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo_int); @@ -2803,7 +2832,7 @@ void RasterizerSceneGLES3::_render_post_processing(const RenderDataGLES3 *p_rend glBindFramebuffer(GL_FRAMEBUFFER, fbos[2]); glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, write_color, 0, v); - post_effects->post_copy(fbos[2], target_size, source_color, internal_size, p_render_data->luminance_multiplier, glow_buffers, glow_intensity, v, true); + post_effects->post_copy(fbos[2], target_size, source_color, internal_size, p_render_data->luminance_multiplier, glow_buffers, glow_intensity, v, true, bcs_spec_constants); } // Copy depth @@ -2824,6 +2853,9 @@ void RasterizerSceneGLES3::_render_post_processing(const RenderDataGLES3 *p_rend glBindFramebuffer(GL_FRAMEBUFFER, fbo_rt); glDeleteFramebuffers(3, fbos); } + + glActiveTexture(GL_TEXTURE2); + glBindTexture(GL_TEXTURE_2D, 0); } template <PassMode p_pass_mode> diff --git a/drivers/gles3/rasterizer_scene_gles3.h b/drivers/gles3/rasterizer_scene_gles3.h index cc479bd4e9..c656ee3cc7 100644 --- a/drivers/gles3/rasterizer_scene_gles3.h +++ b/drivers/gles3/rasterizer_scene_gles3.h @@ -434,6 +434,11 @@ private: float white = 1.0; int32_t tonemapper = 0; int32_t pad = 0; + + int32_t pad2 = 0; + float brightness = 1.0; + float contrast = 1.0; + float saturation = 1.0; }; static_assert(sizeof(TonemapUBO) % 16 == 0, "Tonemap UBO size must be a multiple of 16 bytes"); diff --git a/drivers/gles3/shaders/canvas.glsl b/drivers/gles3/shaders/canvas.glsl index efddbe9ad2..65332c06be 100644 --- a/drivers/gles3/shaders/canvas.glsl +++ b/drivers/gles3/shaders/canvas.glsl @@ -776,6 +776,12 @@ void main() { vec2 tex_uv = (vec4(vertex, 0.0, 1.0) * mat4(light_array[light_base].texture_matrix[0], light_array[light_base].texture_matrix[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy; //multiply inverse given its transposed. Optimizer removes useless operations. vec2 tex_uv_atlas = tex_uv * light_array[light_base].atlas_rect.zw + light_array[light_base].atlas_rect.xy; + + if (any(lessThan(tex_uv, vec2(0.0, 0.0))) || any(greaterThanEqual(tex_uv, vec2(1.0, 1.0)))) { + //if outside the light texture, light color is zero + continue; + } + vec4 light_color = textureLod(atlas_texture, tex_uv_atlas, 0.0); vec4 light_base_color = light_array[light_base].color; @@ -800,10 +806,6 @@ void main() { light_color.rgb *= base_color.rgb; } #endif - if (any(lessThan(tex_uv, vec2(0.0, 0.0))) || any(greaterThanEqual(tex_uv, vec2(1.0, 1.0)))) { - //if outside the light texture, light color is zero - light_color.a = 0.0; - } if (bool(light_array[light_base].flags & LIGHT_FLAGS_HAS_SHADOW)) { vec2 shadow_pos = (vec4(shadow_vertex, 0.0, 1.0) * mat4(light_array[light_base].shadow_matrix[0], light_array[light_base].shadow_matrix[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy; //multiply inverse given its transposed. Optimizer removes useless operations. diff --git a/drivers/gles3/shaders/effects/post.glsl b/drivers/gles3/shaders/effects/post.glsl index e61171c92a..1d17510c52 100644 --- a/drivers/gles3/shaders/effects/post.glsl +++ b/drivers/gles3/shaders/effects/post.glsl @@ -1,13 +1,15 @@ /* clang-format off */ #[modes] -mode_default = #define MODE_DEFAULT -// mode_glow = #define MODE_GLOW +mode_default = #[specializations] USE_MULTIVIEW = false USE_GLOW = false USE_LUMINANCE_MULTIPLIER = false +USE_BCS = false +USE_COLOR_CORRECTION = false +USE_1D_LUT = false #[vertex] layout(location = 0) in vec2 vertex_attrib; @@ -25,6 +27,9 @@ void main() { #[fragment] /* clang-format on */ +// If we reach this code, we always tonemap. +#define APPLY_TONEMAPPING + #include "../tonemap_inc.glsl" #ifdef USE_MULTIVIEW @@ -57,6 +62,35 @@ vec4 get_glow_color(vec2 uv) { } #endif // USE_GLOW +#ifdef USE_COLOR_CORRECTION +#ifdef USE_1D_LUT +uniform sampler2D source_color_correction; //texunit:2 + +vec3 apply_color_correction(vec3 color) { + color.r = texture(source_color_correction, vec2(color.r, 0.0f)).r; + color.g = texture(source_color_correction, vec2(color.g, 0.0f)).g; + color.b = texture(source_color_correction, vec2(color.b, 0.0f)).b; + return color; +} +#else +uniform sampler3D source_color_correction; //texunit:2 + +vec3 apply_color_correction(vec3 color) { + return textureLod(source_color_correction, color, 0.0).rgb; +} +#endif // USE_1D_LUT +#endif // USE_COLOR_CORRECTION + +#ifdef USE_BCS +vec3 apply_bcs(vec3 color) { + color = mix(vec3(0.0), color, brightness); + color = mix(vec3(0.5), color, contrast); + color = mix(vec3(dot(vec3(1.0), color) * 0.33333), color, saturation); + + return color; +} +#endif + in vec2 uv_interp; layout(location = 0) out vec4 frag_color; @@ -85,11 +119,11 @@ void main() { color.rgb = linear_to_srgb(color.rgb); #ifdef USE_BCS - color.rgb = apply_bcs(color.rgb, bcs); + color.rgb = apply_bcs(color.rgb); #endif #ifdef USE_COLOR_CORRECTION - color.rgb = apply_color_correction(color.rgb, color_correction); + color.rgb = apply_color_correction(color.rgb); #endif frag_color = color; diff --git a/drivers/gles3/shaders/scene.glsl b/drivers/gles3/shaders/scene.glsl index 36bbca8728..e1ddbe1d35 100644 --- a/drivers/gles3/shaders/scene.glsl +++ b/drivers/gles3/shaders/scene.glsl @@ -1511,16 +1511,31 @@ void main() { float alpha_antialiasing_edge = 0.0; vec2 alpha_texture_coordinate = vec2(0.0, 0.0); #endif // ALPHA_ANTIALIASING_EDGE_USED + +#ifdef LIGHT_VERTEX_USED + vec3 light_vertex = vertex; +#endif //LIGHT_VERTEX_USED + { #CODE : FRAGMENT } +#ifdef LIGHT_VERTEX_USED + vertex = light_vertex; +#ifdef USE_MULTIVIEW + view = -normalize(vertex - eye_offset); +#else + view = -normalize(vertex); +#endif //USE_MULTIVIEW +#endif //LIGHT_VERTEX_USED + #ifndef USE_SHADOW_TO_OPACITY #if defined(ALPHA_SCISSOR_USED) if (alpha < alpha_scissor_threshold) { discard; } + alpha = 1.0; #else #ifdef MODE_RENDER_DEPTH #ifdef USE_OPAQUE_PREPASS @@ -1920,13 +1935,6 @@ void main() { #endif frag_color.rgb = linear_to_srgb(frag_color.rgb); -#ifdef USE_BCS - frag_color.rgb = apply_bcs(frag_color.rgb, bcs); -#endif - -#ifdef USE_COLOR_CORRECTION - frag_color.rgb = apply_color_correction(frag_color.rgb, color_correction); -#endif #else // !BASE_PASS frag_color = vec4(0.0, 0.0, 0.0, alpha); #endif // !BASE_PASS @@ -2138,14 +2146,6 @@ void main() { #endif additive_light_color = linear_to_srgb(additive_light_color); -#ifdef USE_BCS - additive_light_color = apply_bcs(additive_light_color, bcs); -#endif - -#ifdef USE_COLOR_CORRECTION - additive_light_color = apply_color_correction(additive_light_color, color_correction); -#endif - frag_color.rgb += additive_light_color; #endif // USE_ADDITIVE_LIGHTING diff --git a/drivers/gles3/shaders/sky.glsl b/drivers/gles3/shaders/sky.glsl index 6c33bf7123..26549901a6 100644 --- a/drivers/gles3/shaders/sky.glsl +++ b/drivers/gles3/shaders/sky.glsl @@ -209,14 +209,6 @@ void main() { #endif color = linear_to_srgb(color); -#ifdef USE_BCS - color = apply_bcs(color, bcs); -#endif - -#ifdef USE_COLOR_CORRECTION - color = apply_color_correction(color, color_correction); -#endif - frag_color.rgb = color * luminance_multiplier; frag_color.a = alpha; diff --git a/drivers/gles3/shaders/tonemap.glsl b/drivers/gles3/shaders/tonemap.glsl deleted file mode 100644 index 0b769e77f2..0000000000 --- a/drivers/gles3/shaders/tonemap.glsl +++ /dev/null @@ -1,333 +0,0 @@ -/* clang-format off */ -[vertex] - -#ifdef USE_GLES_OVER_GL -#define lowp -#define mediump -#define highp -#else -precision highp float; -precision highp int; -#endif - -layout(location = 0) in vec2 vertex_attrib; -/* clang-format on */ -layout(location = 4) in vec2 uv_in; - -out vec2 uv_interp; - -void main() { - gl_Position = vec4(vertex_attrib, 0.0, 1.0); - - uv_interp = uv_in; -} - -/* clang-format off */ -[fragment] - -#ifdef USE_GLES_OVER_GL -#define lowp -#define mediump -#define highp -#else -#if defined(USE_HIGHP_PRECISION) -precision highp float; -precision highp int; -#else -precision mediump float; -precision mediump int; -#endif -#endif - -in vec2 uv_interp; -/* clang-format on */ - -layout(location = 0) out vec4 frag_color; - -#ifdef USE_MULTIVIEW -uniform highp sampler2DArray source; //texunit:0 -#else -uniform highp sampler2D source; //texunit:0 -#endif - -#if defined(USE_GLOW_LEVEL1) || defined(USE_GLOW_LEVEL2) || defined(USE_GLOW_LEVEL3) || defined(USE_GLOW_LEVEL4) || defined(USE_GLOW_LEVEL5) || defined(USE_GLOW_LEVEL6) || defined(USE_GLOW_LEVEL7) -#define USING_GLOW // only use glow when at least one glow level is selected - -#ifdef USE_MULTI_TEXTURE_GLOW -uniform highp sampler2D source_glow1; //texunit:2 -uniform highp sampler2D source_glow2; //texunit:3 -uniform highp sampler2D source_glow3; //texunit:4 -uniform highp sampler2D source_glow4; //texunit:5 -uniform highp sampler2D source_glow5; //texunit:6 -uniform highp sampler2D source_glow6; //texunit:7 -#ifdef USE_GLOW_LEVEL7 -uniform highp sampler2D source_glow7; //texunit:8 -#endif -#else -uniform highp sampler2D source_glow; //texunit:2 -#endif -uniform highp float glow_intensity; -#endif - -#ifdef USE_BCS -uniform vec3 bcs; -#endif - -#ifdef USE_FXAA -uniform vec2 pixel_size; -#endif - -#ifdef USE_COLOR_CORRECTION -uniform sampler2D color_correction; //texunit:1 -#endif - -#ifdef USE_GLOW_FILTER_BICUBIC -// w0, w1, w2, and w3 are the four cubic B-spline basis functions -float w0(float a) { - return (1.0 / 6.0) * (a * (a * (-a + 3.0) - 3.0) + 1.0); -} - -float w1(float a) { - return (1.0 / 6.0) * (a * a * (3.0 * a - 6.0) + 4.0); -} - -float w2(float a) { - return (1.0 / 6.0) * (a * (a * (-3.0 * a + 3.0) + 3.0) + 1.0); -} - -float w3(float a) { - return (1.0 / 6.0) * (a * a * a); -} - -// g0 and g1 are the two amplitude functions -float g0(float a) { - return w0(a) + w1(a); -} - -float g1(float a) { - return w2(a) + w3(a); -} - -// h0 and h1 are the two offset functions -float h0(float a) { - return -1.0 + w1(a) / (w0(a) + w1(a)); -} - -float h1(float a) { - return 1.0 + w3(a) / (w2(a) + w3(a)); -} - -uniform ivec2 glow_texture_size; - -vec4 texture_bicubic(sampler2D tex, vec2 uv, int p_lod) { - float lod = float(p_lod); - vec2 tex_size = vec2(glow_texture_size >> p_lod); - vec2 texel_size = vec2(1.0) / tex_size; - - uv = uv * tex_size + vec2(0.5); - - vec2 iuv = floor(uv); - vec2 fuv = fract(uv); - - float g0x = g0(fuv.x); - float g1x = g1(fuv.x); - float h0x = h0(fuv.x); - float h1x = h1(fuv.x); - float h0y = h0(fuv.y); - float h1y = h1(fuv.y); - - vec2 p0 = (vec2(iuv.x + h0x, iuv.y + h0y) - vec2(0.5)) * texel_size; - vec2 p1 = (vec2(iuv.x + h1x, iuv.y + h0y) - vec2(0.5)) * texel_size; - vec2 p2 = (vec2(iuv.x + h0x, iuv.y + h1y) - vec2(0.5)) * texel_size; - vec2 p3 = (vec2(iuv.x + h1x, iuv.y + h1y) - vec2(0.5)) * texel_size; - - return (g0(fuv.y) * (g0x * textureLod(tex, p0, lod) + g1x * textureLod(tex, p1, lod))) + - (g1(fuv.y) * (g0x * textureLod(tex, p2, lod) + g1x * textureLod(tex, p3, lod))); -} - -#define GLOW_TEXTURE_SAMPLE(m_tex, m_uv, m_lod) texture_bicubic(m_tex, m_uv, m_lod) -#else //!USE_GLOW_FILTER_BICUBIC -#define GLOW_TEXTURE_SAMPLE(m_tex, m_uv, m_lod) textureLod(m_tex, m_uv, float(m_lod)) -#endif //USE_GLOW_FILTER_BICUBIC - -vec3 apply_glow(vec3 color, vec3 glow) { // apply glow using the selected blending mode -#ifdef USE_GLOW_REPLACE - color = glow; -#endif - -#ifdef USE_GLOW_SCREEN - color = max((color + glow) - (color * glow), vec3(0.0)); -#endif - -#ifdef USE_GLOW_SOFTLIGHT - glow = glow * vec3(0.5) + vec3(0.5); - - color.r = (glow.r <= 0.5) ? (color.r - (1.0 - 2.0 * glow.r) * color.r * (1.0 - color.r)) : (((glow.r > 0.5) && (color.r <= 0.25)) ? (color.r + (2.0 * glow.r - 1.0) * (4.0 * color.r * (4.0 * color.r + 1.0) * (color.r - 1.0) + 7.0 * color.r)) : (color.r + (2.0 * glow.r - 1.0) * (sqrt(color.r) - color.r))); - color.g = (glow.g <= 0.5) ? (color.g - (1.0 - 2.0 * glow.g) * color.g * (1.0 - color.g)) : (((glow.g > 0.5) && (color.g <= 0.25)) ? (color.g + (2.0 * glow.g - 1.0) * (4.0 * color.g * (4.0 * color.g + 1.0) * (color.g - 1.0) + 7.0 * color.g)) : (color.g + (2.0 * glow.g - 1.0) * (sqrt(color.g) - color.g))); - color.b = (glow.b <= 0.5) ? (color.b - (1.0 - 2.0 * glow.b) * color.b * (1.0 - color.b)) : (((glow.b > 0.5) && (color.b <= 0.25)) ? (color.b + (2.0 * glow.b - 1.0) * (4.0 * color.b * (4.0 * color.b + 1.0) * (color.b - 1.0) + 7.0 * color.b)) : (color.b + (2.0 * glow.b - 1.0) * (sqrt(color.b) - color.b))); -#endif - -#if !defined(USE_GLOW_SCREEN) && !defined(USE_GLOW_SOFTLIGHT) && !defined(USE_GLOW_REPLACE) // no other selected -> additive - color += glow; -#endif - - return color; -} - -vec3 apply_bcs(vec3 color, vec3 bcs) { - color = mix(vec3(0.0), color, bcs.x); - color = mix(vec3(0.5), color, bcs.y); - color = mix(vec3(dot(vec3(1.0), color) * 0.33333), color, bcs.z); - - return color; -} - -vec3 apply_color_correction(vec3 color, sampler2D correction_tex) { - color.r = texture(correction_tex, vec2(color.r, 0.0)).r; - color.g = texture(correction_tex, vec2(color.g, 0.0)).g; - color.b = texture(correction_tex, vec2(color.b, 0.0)).b; - - return color; -} - -vec3 apply_fxaa(vec3 color, vec2 uv_interp, vec2 pixel_size) { - const float FXAA_REDUCE_MIN = (1.0 / 128.0); - const float FXAA_REDUCE_MUL = (1.0 / 8.0); - const float FXAA_SPAN_MAX = 8.0; - -#ifdef USE_MULTIVIEW - vec3 rgbNW = textureLod(source, vec3(uv_interp + vec2(-1.0, -1.0) * pixel_size, ViewIndex), 0.0).xyz; - vec3 rgbNE = textureLod(source, vec3(uv_interp + vec2(1.0, -1.0) * pixel_size, ViewIndex), 0.0).xyz; - vec3 rgbSW = textureLod(source, vec3(uv_interp + vec2(-1.0, 1.0) * pixel_size, ViewIndex), 0.0).xyz; - vec3 rgbSE = textureLod(source, vec3(uv_interp + vec2(1.0, 1.0) * pixel_size, ViewIndex), 0.0).xyz; -#else - vec3 rgbNW = textureLod(source, uv_interp + vec2(-1.0, -1.0) * pixel_size, 0.0).xyz; - vec3 rgbNE = textureLod(source, uv_interp + vec2(1.0, -1.0) * pixel_size, 0.0).xyz; - vec3 rgbSW = textureLod(source, uv_interp + vec2(-1.0, 1.0) * pixel_size, 0.0).xyz; - vec3 rgbSE = textureLod(source, uv_interp + vec2(1.0, 1.0) * pixel_size, 0.0).xyz; -#endif - vec3 rgbM = color; - vec3 luma = vec3(0.299, 0.587, 0.114); - float lumaNW = dot(rgbNW, luma); - float lumaNE = dot(rgbNE, luma); - float lumaSW = dot(rgbSW, luma); - float lumaSE = dot(rgbSE, luma); - float lumaM = dot(rgbM, luma); - float lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE))); - float lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE))); - - vec2 dir; - dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE)); - dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE)); - - float dirReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) * - (0.25 * FXAA_REDUCE_MUL), - FXAA_REDUCE_MIN); - - float rcpDirMin = 1.0 / (min(abs(dir.x), abs(dir.y)) + dirReduce); - dir = min(vec2(FXAA_SPAN_MAX, FXAA_SPAN_MAX), - max(vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX), - dir * rcpDirMin)) * - pixel_size; - -#ifdef USE_MULTIVIEW - vec3 rgbA = 0.5 * (textureLod(source, vec3(uv_interp + dir * (1.0 / 3.0 - 0.5), ViewIndex), 0.0).xyz + textureLod(source, vec3(uv_interp + dir * (2.0 / 3.0 - 0.5), ViewIndex), 0.0).xyz); - vec3 rgbB = rgbA * 0.5 + 0.25 * (textureLod(source, vec3(uv_interp + dir * -0.5, ViewIndex), 0.0).xyz + textureLod(source, vec3(uv_interp + dir * 0.5, ViewIndex), 0.0).xyz); -#else - vec3 rgbA = 0.5 * (textureLod(source, uv_interp + dir * (1.0 / 3.0 - 0.5), 0.0).xyz + textureLod(source, uv_interp + dir * (2.0 / 3.0 - 0.5), 0.0).xyz); - vec3 rgbB = rgbA * 0.5 + 0.25 * (textureLod(source, uv_interp + dir * -0.5, 0.0).xyz + textureLod(source, uv_interp + dir * 0.5, 0.0).xyz); -#endif - - float lumaB = dot(rgbB, luma); - if ((lumaB < lumaMin) || (lumaB > lumaMax)) { - return rgbA; - } else { - return rgbB; - } -} - -void main() { -#ifdef USE_MULTIVIEW - vec4 color = textureLod(source, vec3(uv_interp, ViewIndex), 0.0); -#else - vec4 color = textureLod(source, uv_interp, 0.0); -#endif - -#ifdef USE_FXAA - color.rgb = apply_fxaa(color.rgb, uv_interp, pixel_size); -#endif - - // Glow - -#ifdef USING_GLOW - vec3 glow = vec3(0.0); -#ifdef USE_MULTI_TEXTURE_GLOW -#ifdef USE_GLOW_LEVEL1 - glow += GLOW_TEXTURE_SAMPLE(source_glow1, uv_interp, 0).rgb; -#ifdef USE_GLOW_LEVEL2 - glow += GLOW_TEXTURE_SAMPLE(source_glow2, uv_interp, 0).rgb; -#ifdef USE_GLOW_LEVEL3 - glow += GLOW_TEXTURE_SAMPLE(source_glow3, uv_interp, 0).rgb; -#ifdef USE_GLOW_LEVEL4 - glow += GLOW_TEXTURE_SAMPLE(source_glow4, uv_interp, 0).rgb; -#ifdef USE_GLOW_LEVEL5 - glow += GLOW_TEXTURE_SAMPLE(source_glow5, uv_interp, 0).rgb; -#ifdef USE_GLOW_LEVEL6 - glow += GLOW_TEXTURE_SAMPLE(source_glow6, uv_interp, 0).rgb; -#ifdef USE_GLOW_LEVEL7 - glow += GLOW_TEXTURE_SAMPLE(source_glow7, uv_interp, 0).rgb; -#endif -#endif -#endif -#endif -#endif -#endif -#endif - -#else - -#ifdef USE_GLOW_LEVEL1 - glow += GLOW_TEXTURE_SAMPLE(source_glow, uv_interp, 1).rgb; -#endif - -#ifdef USE_GLOW_LEVEL2 - glow += GLOW_TEXTURE_SAMPLE(source_glow, uv_interp, 2).rgb; -#endif - -#ifdef USE_GLOW_LEVEL3 - glow += GLOW_TEXTURE_SAMPLE(source_glow, uv_interp, 3).rgb; -#endif - -#ifdef USE_GLOW_LEVEL4 - glow += GLOW_TEXTURE_SAMPLE(source_glow, uv_interp, 4).rgb; -#endif - -#ifdef USE_GLOW_LEVEL5 - glow += GLOW_TEXTURE_SAMPLE(source_glow, uv_interp, 5).rgb; -#endif - -#ifdef USE_GLOW_LEVEL6 - glow += GLOW_TEXTURE_SAMPLE(source_glow, uv_interp, 6).rgb; -#endif - -#ifdef USE_GLOW_LEVEL7 - glow += GLOW_TEXTURE_SAMPLE(source_glow, uv_interp, 7).rgb; -#endif -#endif //USE_MULTI_TEXTURE_GLOW - - glow *= glow_intensity; - color.rgb = apply_glow(color.rgb, glow); -#endif - - // Additional effects - -#ifdef USE_BCS - color.rgb = apply_bcs(color.rgb, bcs); -#endif - -#ifdef USE_COLOR_CORRECTION - color.rgb = apply_color_correction(color.rgb, color_correction); -#endif - - frag_color = color; -} diff --git a/drivers/gles3/shaders/tonemap_inc.glsl b/drivers/gles3/shaders/tonemap_inc.glsl index f8f12760ec..fb915aeb38 100644 --- a/drivers/gles3/shaders/tonemap_inc.glsl +++ b/drivers/gles3/shaders/tonemap_inc.glsl @@ -1,43 +1,31 @@ -#ifdef USE_BCS -uniform vec3 bcs; -#endif - -#ifdef USE_COLOR_CORRECTION -#ifdef USE_1D_LUT -uniform sampler2D source_color_correction; //texunit:-1 -#else -uniform sampler3D source_color_correction; //texunit:-1 -#endif -#endif - layout(std140) uniform TonemapData { //ubo:0 float exposure; float white; int tonemapper; int pad; -}; -vec3 apply_bcs(vec3 color, vec3 bcs) { - color = mix(vec3(0.0), color, bcs.x); - color = mix(vec3(0.5), color, bcs.y); - color = mix(vec3(dot(vec3(1.0), color) * 0.33333), color, bcs.z); + int pad2; + float brightness; + float contrast; + float saturation; +}; - return color; -} -#ifdef USE_COLOR_CORRECTION -#ifdef USE_1D_LUT -vec3 apply_color_correction(vec3 color) { - color.r = texture(source_color_correction, vec2(color.r, 0.0f)).r; - color.g = texture(source_color_correction, vec2(color.g, 0.0f)).g; - color.b = texture(source_color_correction, vec2(color.b, 0.0f)).b; - return color; +// This expects 0-1 range input. +vec3 linear_to_srgb(vec3 color) { + //color = clamp(color, vec3(0.0), vec3(1.0)); + //const vec3 a = vec3(0.055f); + //return mix((vec3(1.0f) + a) * pow(color.rgb, vec3(1.0f / 2.4f)) - a, 12.92f * color.rgb, lessThan(color.rgb, vec3(0.0031308f))); + // Approximation from http://chilliant.blogspot.com/2012/08/srgb-approximations-for-hlsl.html + return max(vec3(1.055) * pow(color, vec3(0.416666667)) - vec3(0.055), vec3(0.0)); } -#else -vec3 apply_color_correction(vec3 color) { - return textureLod(source_color_correction, color, 0.0).rgb; + +// This expects 0-1 range input, outside that range it behaves poorly. +vec3 srgb_to_linear(vec3 color) { + // Approximation from http://chilliant.blogspot.com/2012/08/srgb-approximations-for-hlsl.html + return color * (color * (color * 0.305306011 + 0.682171111) + 0.012522878); } -#endif -#endif + +#ifdef APPLY_TONEMAPPING vec3 tonemap_filmic(vec3 color, float p_white) { // exposure bias: input scale (color *= bias, white *= bias) to make the brightness consistent with other tonemappers @@ -92,21 +80,6 @@ vec3 tonemap_reinhard(vec3 color, float p_white) { return (p_white * color + color) / (color * p_white + p_white); } -// This expects 0-1 range input. -vec3 linear_to_srgb(vec3 color) { - //color = clamp(color, vec3(0.0), vec3(1.0)); - //const vec3 a = vec3(0.055f); - //return mix((vec3(1.0f) + a) * pow(color.rgb, vec3(1.0f / 2.4f)) - a, 12.92f * color.rgb, lessThan(color.rgb, vec3(0.0031308f))); - // Approximation from http://chilliant.blogspot.com/2012/08/srgb-approximations-for-hlsl.html - return max(vec3(1.055) * pow(color, vec3(0.416666667)) - vec3(0.055), vec3(0.0)); -} - -// This expects 0-1 range input, outside that range it behaves poorly. -vec3 srgb_to_linear(vec3 color) { - // Approximation from http://chilliant.blogspot.com/2012/08/srgb-approximations-for-hlsl.html - return color * (color * (color * 0.305306011 + 0.682171111) + 0.012522878); -} - #define TONEMAPPER_LINEAR 0 #define TONEMAPPER_REINHARD 1 #define TONEMAPPER_FILMIC 2 @@ -125,3 +98,5 @@ vec3 apply_tonemapping(vec3 color, float p_white) { // inputs are LINEAR, always return tonemap_aces(max(vec3(0.0f), color), p_white); } } + +#endif // APPLY_TONEMAPPING diff --git a/drivers/gles3/storage/material_storage.cpp b/drivers/gles3/storage/material_storage.cpp index 23376b4381..62d22dac4d 100644 --- a/drivers/gles3/storage/material_storage.cpp +++ b/drivers/gles3/storage/material_storage.cpp @@ -1274,6 +1274,7 @@ MaterialStorage::MaterialStorage() { actions.renames["CUSTOM2"] = "custom2_attrib"; actions.renames["CUSTOM3"] = "custom3_attrib"; actions.renames["OUTPUT_IS_SRGB"] = "SHADER_IS_SRGB"; + actions.renames["LIGHT_VERTEX"] = "light_vertex"; actions.renames["NODE_POSITION_WORLD"] = "model_matrix[3].xyz"; actions.renames["CAMERA_POSITION_WORLD"] = "scene_data.inv_view_matrix[3].xyz"; @@ -1320,6 +1321,7 @@ MaterialStorage::MaterialStorage() { actions.usage_defines["COLOR"] = "#define COLOR_USED\n"; actions.usage_defines["INSTANCE_CUSTOM"] = "#define ENABLE_INSTANCE_CUSTOM\n"; actions.usage_defines["POSITION"] = "#define OVERRIDE_POSITION\n"; + actions.usage_defines["LIGHT_VERTEX"] = "#define LIGHT_VERTEX_USED\n"; actions.usage_defines["ALPHA_SCISSOR_THRESHOLD"] = "#define ALPHA_SCISSOR_USED\n"; actions.usage_defines["ALPHA_HASH_SCALE"] = "#define ALPHA_HASH_USED\n"; diff --git a/drivers/vulkan/rendering_device_driver_vulkan.cpp b/drivers/vulkan/rendering_device_driver_vulkan.cpp index 1906d168fe..803555cb07 100644 --- a/drivers/vulkan/rendering_device_driver_vulkan.cpp +++ b/drivers/vulkan/rendering_device_driver_vulkan.cpp @@ -1008,7 +1008,7 @@ VkResult RenderingDeviceDriverVulkan::_create_render_pass(VkDevice p_device, con const uint32_t depth_attachment_index = vector_base_index + 3; _convert_subpass_attachments(p_create_info->pSubpasses[i].pInputAttachments, p_create_info->pSubpasses[i].inputAttachmentCount, subpasses_attachments[input_attachments_index]); _convert_subpass_attachments(p_create_info->pSubpasses[i].pColorAttachments, p_create_info->pSubpasses[i].colorAttachmentCount, subpasses_attachments[color_attachments_index]); - _convert_subpass_attachments(p_create_info->pSubpasses[i].pResolveAttachments, p_create_info->pSubpasses[i].colorAttachmentCount, subpasses_attachments[resolve_attachments_index]); + _convert_subpass_attachments(p_create_info->pSubpasses[i].pResolveAttachments, (p_create_info->pSubpasses[i].pResolveAttachments != nullptr) ? p_create_info->pSubpasses[i].colorAttachmentCount : 0, subpasses_attachments[resolve_attachments_index]); _convert_subpass_attachments(p_create_info->pSubpasses[i].pDepthStencilAttachment, (p_create_info->pSubpasses[i].pDepthStencilAttachment != nullptr) ? 1 : 0, subpasses_attachments[depth_attachment_index]); // Ignores sType and pNext from the subpass. diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 5b24fb1559..134733fa11 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -456,6 +456,9 @@ void EditorNode::_update_from_settings() { void EditorNode::_gdextensions_reloaded() { // In case the developer is inspecting an object that will be changed by the reload. InspectorDock::get_inspector_singleton()->update_tree(); + + // Regenerate documentation. + EditorHelp::generate_doc(); } void EditorNode::_select_default_main_screen_plugin() { diff --git a/editor/export/editor_export_platform.cpp b/editor/export/editor_export_platform.cpp index 33fdd7418e..aa44189782 100644 --- a/editor/export/editor_export_platform.cpp +++ b/editor/export/editor_export_platform.cpp @@ -510,6 +510,12 @@ HashSet<String> EditorExportPlatform::get_features(const Ref<EditorExportPreset> result.insert("template_release"); } +#ifdef REAL_T_IS_DOUBLE + result.insert("double"); +#else + result.insert("single"); +#endif // REAL_T_IS_DOUBLE + if (!p_preset->get_custom_features().is_empty()) { Vector<String> tmp_custom_list = p_preset->get_custom_features().split(","); diff --git a/editor/export/project_export.cpp b/editor/export/project_export.cpp index 038e357ce2..c995e590f1 100644 --- a/editor/export/project_export.cpp +++ b/editor/export/project_export.cpp @@ -419,6 +419,12 @@ void ProjectExportDialog::_update_feature_list() { feature_set.insert(E); } +#ifdef REAL_T_IS_DOUBLE + feature_set.insert("double"); +#else + feature_set.insert("single"); +#endif // REAL_T_IS_DOUBLE + custom_feature_display->clear(); String text; bool first = true; diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp index b7deb6afa6..12c2408d0f 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -2394,6 +2394,12 @@ void FileSystemDock::_file_option(int p_option, const Vector<String> &p_selected } } break; + case FILE_SHOW_IN_FILESYSTEM: { + if (!p_selected.is_empty()) { + navigate_to_path(p_selected[0]); + } + } break; + case FILE_DEPENDENCIES: { // Checkout the file dependencies. if (!p_selected.is_empty()) { @@ -3286,8 +3292,33 @@ void FileSystemDock::_file_and_folders_fill_popup(PopupMenu *p_popup, const Vect if (p_paths.size() == 1) { const String &fpath = p_paths[0]; + bool added_separator = false; + + if (favorites_list.has(fpath)) { + TreeItem *favorites_item = tree->get_root()->get_first_child(); + TreeItem *cursor_item = tree->get_selected(); + bool is_item_in_favorites = false; + while (cursor_item != nullptr) { + if (cursor_item == favorites_item) { + is_item_in_favorites = true; + break; + } + + cursor_item = cursor_item->get_parent(); + } + + if (is_item_in_favorites) { + p_popup->add_separator(); + added_separator = true; + p_popup->add_icon_item(get_editor_theme_icon(SNAME("ShowInFileSystem")), TTR("Show in FileSystem"), FILE_SHOW_IN_FILESYSTEM); + } + } + #if !defined(ANDROID_ENABLED) && !defined(WEB_ENABLED) - p_popup->add_separator(); + if (!added_separator) { + p_popup->add_separator(); + added_separator = true; + } // Opening the system file manager is not supported on the Android and web editors. const bool is_directory = fpath.ends_with("/"); diff --git a/editor/filesystem_dock.h b/editor/filesystem_dock.h index 058886c91a..7449657c06 100644 --- a/editor/filesystem_dock.h +++ b/editor/filesystem_dock.h @@ -116,6 +116,7 @@ private: FILE_INSTANTIATE, FILE_ADD_FAVORITE, FILE_REMOVE_FAVORITE, + FILE_SHOW_IN_FILESYSTEM, FILE_DEPENDENCIES, FILE_OWNERS, FILE_MOVE, diff --git a/editor/gui/editor_dir_dialog.cpp b/editor/gui/editor_dir_dialog.cpp index 9d5464210b..08612f419c 100644 --- a/editor/gui/editor_dir_dialog.cpp +++ b/editor/gui/editor_dir_dialog.cpp @@ -117,23 +117,13 @@ void EditorDirDialog::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { FileSystemDock::get_singleton()->connect("folder_color_changed", callable_mp(this, &EditorDirDialog::reload).bind("")); - EditorFileSystem::get_singleton()->connect("filesystem_changed", callable_mp(this, &EditorDirDialog::reload).bind("")); reload(); - - if (!tree->is_connected("item_collapsed", callable_mp(this, &EditorDirDialog::_item_collapsed))) { - tree->connect("item_collapsed", callable_mp(this, &EditorDirDialog::_item_collapsed), CONNECT_DEFERRED); - } - - if (!EditorFileSystem::get_singleton()->is_connected("filesystem_changed", callable_mp(this, &EditorDirDialog::reload))) { - EditorFileSystem::get_singleton()->connect("filesystem_changed", callable_mp(this, &EditorDirDialog::reload).bind("")); - } } break; case NOTIFICATION_EXIT_TREE: { - if (EditorFileSystem::get_singleton()->is_connected("filesystem_changed", callable_mp(this, &EditorDirDialog::reload))) { - EditorFileSystem::get_singleton()->disconnect("filesystem_changed", callable_mp(this, &EditorDirDialog::reload)); - } + EditorFileSystem::get_singleton()->disconnect("filesystem_changed", callable_mp(this, &EditorDirDialog::reload)); + FileSystemDock::get_singleton()->disconnect("folder_color_changed", callable_mp(this, &EditorDirDialog::reload)); } break; case NOTIFICATION_VISIBILITY_CHANGED: { @@ -229,6 +219,7 @@ EditorDirDialog::EditorDirDialog() { vb->add_child(tree); tree->set_v_size_flags(Control::SIZE_EXPAND_FILL); tree->connect("item_activated", callable_mp(this, &EditorDirDialog::_item_activated)); + tree->connect("item_collapsed", callable_mp(this, &EditorDirDialog::_item_collapsed), CONNECT_DEFERRED); set_ok_button_text(TTR("Move")); diff --git a/editor/import/3d/editor_import_collada.cpp b/editor/import/3d/editor_import_collada.cpp index 4b91b1431a..04a3f23154 100644 --- a/editor/import/3d/editor_import_collada.cpp +++ b/editor/import/3d/editor_import_collada.cpp @@ -1556,6 +1556,7 @@ void ColladaImport::create_animation(int p_clip, bool p_import_value_tracks) { } animation->set_length(anim_length); + animation->set_step(snapshot_interval); bool tracks_found = false; diff --git a/editor/import/3d/resource_importer_scene.cpp b/editor/import/3d/resource_importer_scene.cpp index dce68a97ac..2560d43241 100644 --- a/editor/import/3d/resource_importer_scene.cpp +++ b/editor/import/3d/resource_importer_scene.cpp @@ -1908,6 +1908,7 @@ void ResourceImporterScene::_create_slices(AnimationPlayer *ap, Ref<Animation> a new_anim->set_loop_mode(loop_mode); new_anim->set_length(to - from); + new_anim->set_step(anim->get_step()); al->add_animation(name, new_anim); diff --git a/editor/import_dock.cpp b/editor/import_dock.cpp index 6f4a376496..fa503cc4df 100644 --- a/editor/import_dock.cpp +++ b/editor/import_dock.cpp @@ -200,7 +200,8 @@ void ImportDock::_update_options(const String &p_path, const Ref<ConfigFile> &p_ params->update(); _update_preset_menu(); - if (params->importer.is_valid() && params->paths.size() == 1 && params->importer->has_advanced_options()) { + bool was_imported = p_config.is_valid() && p_config->get_value("remap", "importer") != "skip" && p_config->get_value("remap", "importer") != "keep"; + if (was_imported && params->importer.is_valid() && params->paths.size() == 1 && params->importer->has_advanced_options()) { advanced->show(); advanced_spacer->show(); } else { @@ -508,6 +509,18 @@ static bool _find_owners(EditorFileSystemDirectory *efsd, const String &p_path) return false; } +void ImportDock::_reimport_pressed() { + _reimport_attempt(); + + if (params->importer.is_valid() && params->paths.size() == 1 && params->importer->has_advanced_options()) { + advanced->show(); + advanced_spacer->show(); + } else { + advanced->hide(); + advanced_spacer->hide(); + } +} + void ImportDock::_reimport_attempt() { bool used_in_resources = false; @@ -528,7 +541,7 @@ void ImportDock::_reimport_attempt() { ERR_CONTINUE(err != OK); String imported_with = config->get_value("remap", "importer"); - if (imported_with != importer_name) { + if (imported_with != importer_name && imported_with != "keep" && imported_with != "skip") { Ref<Resource> resource = ResourceLoader::load(params->paths[i]); if (resource.is_valid()) { need_cleanup.push_back(params->paths[i]); @@ -575,7 +588,10 @@ void ImportDock::_reimport_and_cleanup() { for (const String &path : need_cleanup) { Ref<Resource> old_res = old_resources[path]; - Ref<Resource> new_res = ResourceLoader::load(path); + Ref<Resource> new_res; + if (params->importer.is_valid()) { + new_res = ResourceLoader::load(path); + } for (int i = 0; i < EditorNode::get_editor_data().get_edited_scene_count(); i++) { Node *edited_scene_root = EditorNode::get_editor_data().get_edited_scene_root(i); @@ -782,7 +798,7 @@ ImportDock::ImportDock() { import = memnew(Button); import->set_text(TTR("Reimport")); import->set_disabled(true); - import->connect("pressed", callable_mp(this, &ImportDock::_reimport_attempt)); + import->connect("pressed", callable_mp(this, &ImportDock::_reimport_pressed)); if (!DisplayServer::get_singleton()->get_swap_cancel_ok()) { advanced_spacer = hb->add_spacer(); advanced = memnew(Button); diff --git a/editor/import_dock.h b/editor/import_dock.h index 78cd6a556c..c0a1dee7ca 100644 --- a/editor/import_dock.h +++ b/editor/import_dock.h @@ -76,6 +76,7 @@ class ImportDock : public VBoxContainer { void _property_edited(const StringName &p_prop); void _property_toggled(const StringName &p_prop, bool p_checked); void _set_dirty(bool p_dirty); + void _reimport_pressed(); void _reimport_attempt(); void _reimport_and_cleanup(); void _reimport(); diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp index 43928b9c17..3399f265fc 100644 --- a/editor/plugins/animation_player_editor_plugin.cpp +++ b/editor/plugins/animation_player_editor_plugin.cpp @@ -548,8 +548,13 @@ void AnimationPlayerEditor::_animation_name_edited() { } break; case TOOL_NEW_ANIM: { + String current = animation->get_item_text(animation->get_selected()); + Ref<Animation> current_anim = player->get_animation(current); Ref<Animation> new_anim = Ref<Animation>(memnew(Animation)); new_anim->set_name(new_name); + if (current_anim.is_valid()) { + new_anim->set_step(current_anim->get_step()); + } String library_name; Ref<AnimationLibrary> al; library_name = library->get_item_metadata(library->get_selected()); diff --git a/editor/plugins/mesh_instance_3d_editor_plugin.cpp b/editor/plugins/mesh_instance_3d_editor_plugin.cpp index d62eddeeea..0c922ea070 100644 --- a/editor/plugins/mesh_instance_3d_editor_plugin.cpp +++ b/editor/plugins/mesh_instance_3d_editor_plugin.cpp @@ -59,200 +59,159 @@ void MeshInstance3DEditor::edit(MeshInstance3D *p_mesh) { node = p_mesh; } -void MeshInstance3DEditor::_menu_option(int p_option) { - Ref<Mesh> mesh = node->get_mesh(); - if (mesh.is_null()) { - err_dialog->set_text(TTR("Mesh is empty!")); - err_dialog->popup_centered(); - return; - } - +Vector<Ref<Shape3D>> MeshInstance3DEditor::create_shape_from_mesh(Ref<Mesh> p_mesh, int p_option, bool p_verbose) { + Vector<Ref<Shape3D>> shapes; switch (p_option) { - case MENU_OPTION_CREATE_STATIC_TRIMESH_BODY: { - EditorSelection *editor_selection = EditorNode::get_singleton()->get_editor_selection(); - EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton(); - - List<Node *> selection = editor_selection->get_selected_node_list(); - - if (selection.is_empty()) { - Ref<ConcavePolygonShape3D> shape = mesh->create_trimesh_shape(); - if (shape.is_null()) { - err_dialog->set_text(TTR("Couldn't create a Trimesh collision shape.")); - err_dialog->popup_centered(); - return; - } - - CollisionShape3D *cshape = memnew(CollisionShape3D); - cshape->set_shape(shape); - StaticBody3D *body = memnew(StaticBody3D); - body->add_child(cshape, true); - - Node *owner = get_tree()->get_edited_scene_root(); - - ur->create_action(TTR("Create Static Trimesh Body")); - ur->add_do_method(node, "add_child", body, true); - ur->add_do_method(body, "set_owner", owner); - ur->add_do_method(cshape, "set_owner", owner); - ur->add_do_method(Node3DEditor::get_singleton(), SceneStringNames::get_singleton()->_request_gizmo, body); - ur->add_do_method(Node3DEditor::get_singleton(), SceneStringNames::get_singleton()->_request_gizmo, cshape); - ur->add_do_reference(body); - ur->add_undo_method(node, "remove_child", body); - ur->commit_action(); - return; - } + case SHAPE_TYPE_TRIMESH: { + shapes.push_back(p_mesh->create_trimesh_shape()); - ur->create_action(TTR("Create Static Trimesh Body")); - - for (Node *E : selection) { - MeshInstance3D *instance = Object::cast_to<MeshInstance3D>(E); - if (!instance) { - continue; - } - - Ref<Mesh> m = instance->get_mesh(); - if (m.is_null()) { - continue; - } - - Ref<ConcavePolygonShape3D> shape = m->create_trimesh_shape(); - if (shape.is_null()) { - continue; - } - - CollisionShape3D *cshape = memnew(CollisionShape3D); - cshape->set_shape(shape); - StaticBody3D *body = memnew(StaticBody3D); - body->add_child(cshape, true); - - Node *owner = get_tree()->get_edited_scene_root(); - - ur->add_do_method(instance, "add_child", body, true); - ur->add_do_method(body, "set_owner", owner); - ur->add_do_method(cshape, "set_owner", owner); - ur->add_do_method(Node3DEditor::get_singleton(), SceneStringNames::get_singleton()->_request_gizmo, body); - ur->add_do_method(Node3DEditor::get_singleton(), SceneStringNames::get_singleton()->_request_gizmo, cshape); - ur->add_do_reference(body); - ur->add_undo_method(instance, "remove_child", body); + if (p_verbose && shapes.is_empty()) { + err_dialog->set_text(TTR("Couldn't create a Trimesh collision shape.")); + err_dialog->popup_centered(); } - - ur->commit_action(); - } break; - case MENU_OPTION_CREATE_TRIMESH_COLLISION_SHAPE: { - if (node == get_tree()->get_edited_scene_root()) { - err_dialog->set_text(TTR("This doesn't work on scene root!")); - err_dialog->popup_centered(); - return; - } + case SHAPE_TYPE_SINGLE_CONVEX: { + shapes.push_back(p_mesh->create_convex_shape(true, false)); - Ref<ConcavePolygonShape3D> shape = mesh->create_trimesh_shape(); - if (shape.is_null()) { - return; + if (p_verbose && shapes.is_empty()) { + err_dialog->set_text(TTR("Couldn't create a single collision shape.")); + err_dialog->popup_centered(); } - - CollisionShape3D *cshape = memnew(CollisionShape3D); - cshape->set_shape(shape); - cshape->set_transform(node->get_transform()); - - Node *owner = get_tree()->get_edited_scene_root(); - - EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton(); - - ur->create_action(TTR("Create Trimesh Static Shape")); - - ur->add_do_method(node->get_parent(), "add_child", cshape, true); - ur->add_do_method(node->get_parent(), "move_child", cshape, node->get_index() + 1); - ur->add_do_method(cshape, "set_owner", owner); - ur->add_do_method(Node3DEditor::get_singleton(), SceneStringNames::get_singleton()->_request_gizmo, cshape); - ur->add_do_reference(cshape); - ur->add_undo_method(node->get_parent(), "remove_child", cshape); - ur->commit_action(); } break; - case MENU_OPTION_CREATE_SINGLE_CONVEX_COLLISION_SHAPE: - case MENU_OPTION_CREATE_SIMPLIFIED_CONVEX_COLLISION_SHAPE: { - if (node == get_tree()->get_edited_scene_root()) { - err_dialog->set_text(TTR("Can't create a single convex collision shape for the scene root.")); + case SHAPE_TYPE_SIMPLIFIED_CONVEX: { + shapes.push_back(p_mesh->create_convex_shape(true, true)); + + if (p_verbose && shapes.is_empty()) { + err_dialog->set_text(TTR("Couldn't create a simplified collision shape.")); err_dialog->popup_centered(); - return; } + } break; - bool simplify = (p_option == MENU_OPTION_CREATE_SIMPLIFIED_CONVEX_COLLISION_SHAPE); + case SHAPE_TYPE_MULTIPLE_CONVEX: { + Ref<MeshConvexDecompositionSettings> settings; + settings.instantiate(); + settings->set_max_convex_hulls(32); + settings->set_max_concavity(0.001); - Ref<ConvexPolygonShape3D> shape = mesh->create_convex_shape(true, simplify); + shapes = p_mesh->convex_decompose(settings); - if (shape.is_null()) { - err_dialog->set_text(TTR("Couldn't create a single convex collision shape.")); + if (p_verbose && shapes.is_empty()) { + err_dialog->set_text(TTR("Couldn't create any collision shapes.")); err_dialog->popup_centered(); - return; } - EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton(); + } break; - if (simplify) { - ur->create_action(TTR("Create Simplified Convex Shape")); - } else { - ur->create_action(TTR("Create Single Convex Shape")); - } + default: + break; + } + return shapes; +} - CollisionShape3D *cshape = memnew(CollisionShape3D); - cshape->set_shape(shape); - cshape->set_transform(node->get_transform()); +void MeshInstance3DEditor::_create_collision_shape() { + int placement_option = shape_placement->get_selected(); + int shape_type_option = shape_type->get_selected(); - Node *owner = get_tree()->get_edited_scene_root(); + EditorSelection *editor_selection = EditorNode::get_singleton()->get_editor_selection(); + EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton(); - ur->add_do_method(node->get_parent(), "add_child", cshape, true); - ur->add_do_method(node->get_parent(), "move_child", cshape, node->get_index() + 1); - ur->add_do_method(cshape, "set_owner", owner); - ur->add_do_method(Node3DEditor::get_singleton(), SceneStringNames::get_singleton()->_request_gizmo, cshape); - ur->add_do_reference(cshape); - ur->add_undo_method(node->get_parent(), "remove_child", cshape); + switch (shape_type_option) { + case SHAPE_TYPE_TRIMESH: { + ur->create_action(TTR(placement_option == SHAPE_PLACEMENT_SIBLING ? "Create Trimesh Collision Shape Sibling" : "Create Trimesh Static Body")); + } break; + case SHAPE_TYPE_SINGLE_CONVEX: { + ur->create_action(TTR(placement_option == SHAPE_PLACEMENT_SIBLING ? "Create Single Convex Collision Shape Sibling" : "Create Single Convex Static Body")); + } break; + case SHAPE_TYPE_SIMPLIFIED_CONVEX: { + ur->create_action(TTR(placement_option == SHAPE_PLACEMENT_SIBLING ? "Create Simplified Convex Collision Shape Sibling" : "Create Simplified Convex Static Body")); + } break; + case SHAPE_TYPE_MULTIPLE_CONVEX: { + ur->create_action(TTR(placement_option == SHAPE_PLACEMENT_SIBLING ? "Create Multiple Convex Collision Shape Siblings" : "Create Multiple Convex Static Body")); + } break; + default: + break; + } - ur->commit_action(); + List<Node *> selection = editor_selection->get_selected_node_list(); - } break; + bool verbose = false; + if (selection.is_empty()) { + selection.push_back(node); + verbose = true; + } - case MENU_OPTION_CREATE_MULTIPLE_CONVEX_COLLISION_SHAPES: { - if (node == get_tree()->get_edited_scene_root()) { - err_dialog->set_text(TTR("Can't create multiple convex collision shapes for the scene root.")); + for (Node *E : selection) { + if (placement_option == SHAPE_PLACEMENT_SIBLING && E == get_tree()->get_edited_scene_root()) { + if (verbose) { + err_dialog->set_text(TTR("Can't create a collision shape as sibling for the scene root.")); err_dialog->popup_centered(); - return; } + continue; + } - Ref<MeshConvexDecompositionSettings> settings = Ref<MeshConvexDecompositionSettings>(); - settings.instantiate(); - settings->set_max_convex_hulls(32); - settings->set_max_concavity(0.001); + MeshInstance3D *instance = Object::cast_to<MeshInstance3D>(E); + if (!instance) { + continue; + } - Vector<Ref<Shape3D>> shapes = mesh->convex_decompose(settings); + Ref<Mesh> m = instance->get_mesh(); + if (m.is_null()) { + continue; + } - if (!shapes.size()) { - err_dialog->set_text(TTR("Couldn't create any collision shapes.")); - err_dialog->popup_centered(); - return; - } - EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton(); + Vector<Ref<Shape3D>> shapes = create_shape_from_mesh(m, shape_type_option, verbose); + if (shapes.is_empty()) { + return; + } + + Node *owner = get_tree()->get_edited_scene_root(); + if (placement_option == SHAPE_PLACEMENT_STATIC_BODY_CHILD) { + StaticBody3D *body = memnew(StaticBody3D); - ur->create_action(TTR("Create Multiple Convex Shapes")); + ur->add_do_method(instance, "add_child", body, true); + ur->add_do_method(body, "set_owner", owner); + ur->add_do_method(Node3DEditor::get_singleton(), SceneStringNames::get_singleton()->_request_gizmo, body); - for (int i = 0; i < shapes.size(); i++) { + for (Ref<Shape3D> shape : shapes) { CollisionShape3D *cshape = memnew(CollisionShape3D); + cshape->set_shape(shape); + body->add_child(cshape, true); + ur->add_do_method(cshape, "set_owner", owner); + ur->add_do_method(Node3DEditor::get_singleton(), SceneStringNames::get_singleton()->_request_gizmo, cshape); + } + ur->add_do_reference(body); + ur->add_undo_method(instance, "remove_child", body); + } else { + for (Ref<Shape3D> shape : shapes) { + CollisionShape3D *cshape = memnew(CollisionShape3D); + cshape->set_shape(shape); cshape->set_name("CollisionShape3D"); - - cshape->set_shape(shapes[i]); cshape->set_transform(node->get_transform()); - - Node *owner = get_tree()->get_edited_scene_root(); - - ur->add_do_method(node->get_parent(), "add_child", cshape); - ur->add_do_method(node->get_parent(), "move_child", cshape, node->get_index() + 1); + ur->add_do_method(E->get_parent(), "add_child", cshape); + ur->add_do_method(E->get_parent(), "move_child", cshape, E->get_index() + 1); ur->add_do_method(cshape, "set_owner", owner); ur->add_do_method(Node3DEditor::get_singleton(), SceneStringNames::get_singleton()->_request_gizmo, cshape); ur->add_do_reference(cshape); ur->add_undo_method(node->get_parent(), "remove_child", cshape); } - ur->commit_action(); + } + } + + ur->commit_action(); +} + +void MeshInstance3DEditor::_menu_option(int p_option) { + Ref<Mesh> mesh = node->get_mesh(); + if (mesh.is_null()) { + err_dialog->set_text(TTR("Mesh is empty!")); + err_dialog->popup_centered(); + return; + } + switch (p_option) { + case MENU_OPTION_CREATE_COLLISION_SHAPE: { + shape_dialog->popup_centered(); } break; case MENU_OPTION_CREATE_NAVMESH: { @@ -571,18 +530,7 @@ MeshInstance3DEditor::MeshInstance3DEditor() { options->set_switch_on_hover(true); Node3DEditor::get_singleton()->add_control_to_menu_panel(options); - options->get_popup()->add_item(TTR("Create Trimesh Static Body"), MENU_OPTION_CREATE_STATIC_TRIMESH_BODY); - options->get_popup()->set_item_tooltip(-1, TTR("Creates a StaticBody3D and assigns a polygon-based collision shape to it automatically.\nThis is the most accurate (but slowest) option for collision detection.")); - options->get_popup()->add_separator(); - options->get_popup()->add_item(TTR("Create Trimesh Collision Sibling"), MENU_OPTION_CREATE_TRIMESH_COLLISION_SHAPE); - options->get_popup()->set_item_tooltip(-1, TTR("Creates a polygon-based collision shape.\nThis is the most accurate (but slowest) option for collision detection.")); - options->get_popup()->add_item(TTR("Create Single Convex Collision Sibling"), MENU_OPTION_CREATE_SINGLE_CONVEX_COLLISION_SHAPE); - options->get_popup()->set_item_tooltip(-1, TTR("Creates a single convex collision shape.\nThis is the fastest (but least accurate) option for collision detection.")); - options->get_popup()->add_item(TTR("Create Simplified Convex Collision Sibling"), MENU_OPTION_CREATE_SIMPLIFIED_CONVEX_COLLISION_SHAPE); - options->get_popup()->set_item_tooltip(-1, TTR("Creates a simplified convex collision shape.\nThis is similar to single collision shape, but can result in a simpler geometry in some cases, at the cost of accuracy.")); - options->get_popup()->add_item(TTR("Create Multiple Convex Collision Siblings"), MENU_OPTION_CREATE_MULTIPLE_CONVEX_COLLISION_SHAPES); - options->get_popup()->set_item_tooltip(-1, TTR("Creates a polygon-based collision shape.\nThis is a performance middle-ground between a single convex collision and a polygon-based collision.")); - options->get_popup()->add_separator(); + options->get_popup()->add_item(TTR("Create Collision Shape..."), MENU_OPTION_CREATE_COLLISION_SHAPE); options->get_popup()->add_item(TTR("Create Navigation Mesh"), MENU_OPTION_CREATE_NAVMESH); options->get_popup()->add_separator(); options->get_popup()->add_item(TTR("Create Outline Mesh..."), MENU_OPTION_CREATE_OUTLINE_MESH); @@ -613,6 +561,44 @@ MeshInstance3DEditor::MeshInstance3DEditor() { add_child(outline_dialog); outline_dialog->connect("confirmed", callable_mp(this, &MeshInstance3DEditor::_create_outline_mesh)); + shape_dialog = memnew(ConfirmationDialog); + shape_dialog->set_title(TTR("Create Collision Shape")); + shape_dialog->set_ok_button_text(TTR("Create")); + + VBoxContainer *shape_dialog_vbc = memnew(VBoxContainer); + shape_dialog->add_child(shape_dialog_vbc); + + Label *l = memnew(Label); + l->set_text(TTR("Collision Shape placement")); + shape_dialog_vbc->add_child(l); + + shape_placement = memnew(OptionButton); + shape_placement->set_h_size_flags(SIZE_EXPAND_FILL); + shape_placement->add_item(TTR("Sibling"), SHAPE_PLACEMENT_SIBLING); + shape_placement->set_item_tooltip(-1, TTR("Creates collision shapes as Sibling.")); + shape_placement->add_item(TTR("Static Body Child"), SHAPE_PLACEMENT_STATIC_BODY_CHILD); + shape_placement->set_item_tooltip(-1, TTR("Creates a StaticBody3D as child and assigns collision shapes to it.")); + shape_dialog_vbc->add_child(shape_placement); + + l = memnew(Label); + l->set_text(TTR("Collision Shape Type")); + shape_dialog_vbc->add_child(l); + + shape_type = memnew(OptionButton); + shape_type->set_h_size_flags(SIZE_EXPAND_FILL); + shape_type->add_item(TTR("Trimesh"), SHAPE_TYPE_TRIMESH); + shape_type->set_item_tooltip(-1, TTR("Creates a polygon-based collision shape.\nThis is the most accurate (but slowest) option for collision detection.")); + shape_type->add_item(TTR("Single Convex"), SHAPE_TYPE_SINGLE_CONVEX); + shape_type->set_item_tooltip(-1, TTR("Creates a single convex collision shape.\nThis is the fastest (but least accurate) option for collision detection.")); + shape_type->add_item(TTR("Simplified Convex"), SHAPE_TYPE_SIMPLIFIED_CONVEX); + shape_type->set_item_tooltip(-1, TTR("Creates a simplified convex collision shape.\nThis is similar to single collision shape, but can result in a simpler geometry in some cases, at the cost of accuracy.")); + shape_type->add_item(TTR("Multiple Convex"), SHAPE_TYPE_MULTIPLE_CONVEX); + shape_type->set_item_tooltip(-1, TTR("Creates a polygon-based collision shape.\nThis is a performance middle-ground between a single convex collision and a polygon-based collision.")); + shape_dialog_vbc->add_child(shape_type); + + add_child(shape_dialog); + shape_dialog->connect("confirmed", callable_mp(this, &MeshInstance3DEditor::_create_collision_shape)); + err_dialog = memnew(AcceptDialog); add_child(err_dialog); diff --git a/editor/plugins/mesh_instance_3d_editor_plugin.h b/editor/plugins/mesh_instance_3d_editor_plugin.h index eb984e240e..ce7d23239c 100644 --- a/editor/plugins/mesh_instance_3d_editor_plugin.h +++ b/editor/plugins/mesh_instance_3d_editor_plugin.h @@ -33,6 +33,7 @@ #include "editor/editor_plugin.h" #include "scene/3d/mesh_instance_3d.h" +#include "scene/gui/option_button.h" class AcceptDialog; class ConfirmationDialog; @@ -43,11 +44,7 @@ class MeshInstance3DEditor : public Control { GDCLASS(MeshInstance3DEditor, Control); enum Menu { - MENU_OPTION_CREATE_STATIC_TRIMESH_BODY, - MENU_OPTION_CREATE_TRIMESH_COLLISION_SHAPE, - MENU_OPTION_CREATE_SINGLE_CONVEX_COLLISION_SHAPE, - MENU_OPTION_CREATE_SIMPLIFIED_CONVEX_COLLISION_SHAPE, - MENU_OPTION_CREATE_MULTIPLE_CONVEX_COLLISION_SHAPES, + MENU_OPTION_CREATE_COLLISION_SHAPE, MENU_OPTION_CREATE_NAVMESH, MENU_OPTION_CREATE_OUTLINE_MESH, MENU_OPTION_CREATE_DEBUG_TANGENTS, @@ -56,6 +53,18 @@ class MeshInstance3DEditor : public Control { MENU_OPTION_DEBUG_UV2, }; + enum ShapePlacement { + SHAPE_PLACEMENT_SIBLING, + SHAPE_PLACEMENT_STATIC_BODY_CHILD, + }; + + enum ShapeType { + SHAPE_TYPE_TRIMESH, + SHAPE_TYPE_SINGLE_CONVEX, + SHAPE_TYPE_SIMPLIFIED_CONVEX, + SHAPE_TYPE_MULTIPLE_CONVEX, + }; + MeshInstance3D *node = nullptr; MenuButton *options = nullptr; @@ -63,12 +72,18 @@ class MeshInstance3DEditor : public Control { ConfirmationDialog *outline_dialog = nullptr; SpinBox *outline_size = nullptr; + ConfirmationDialog *shape_dialog = nullptr; + OptionButton *shape_type = nullptr; + OptionButton *shape_placement = nullptr; + AcceptDialog *err_dialog = nullptr; AcceptDialog *debug_uv_dialog = nullptr; Control *debug_uv = nullptr; Vector<Vector2> uv_lines; + void _create_collision_shape(); + Vector<Ref<Shape3D>> create_shape_from_mesh(Ref<Mesh> p_mesh, int p_option, bool p_verbose); void _menu_option(int p_option); void _create_outline_mesh(); diff --git a/editor/plugins/path_3d_editor_plugin.cpp b/editor/plugins/path_3d_editor_plugin.cpp index 1cffdb6454..4e317114a3 100644 --- a/editor/plugins/path_3d_editor_plugin.cpp +++ b/editor/plugins/path_3d_editor_plugin.cpp @@ -169,21 +169,17 @@ void Path3DGizmo::set_handle(int p_id, bool p_secondary, Camera3D *p_camera, con const Basis posture = c->get_point_baked_posture(idx); const Vector3 tangent = -posture.get_column(2); const Vector3 up = posture.get_column(1); - const Plane p_tilt = Plane(tangent, position); + const Plane tilt_plane_global = gt.xform(Plane(tangent, position)); Vector3 intersection; - if (p_tilt.intersects_ray(ray_from, ray_dir, &intersection)) { - Vector3 direction = intersection - position; - direction.normalize(); // FIXME: redundant? + if (tilt_plane_global.intersects_ray(ray_from, ray_dir, &intersection)) { + Vector3 direction = gi.xform(intersection) - position; real_t tilt_angle = up.signed_angle_to(direction, tangent); if (Node3DEditor::get_singleton()->is_snap_enabled()) { - real_t snap = Node3DEditor::get_singleton()->get_rotate_snap(); - - tilt_angle = Math::rad_to_deg(tilt_angle) + snap * 0.5; // Else it won't reach +180. - tilt_angle -= Math::fmod(tilt_angle, snap); - tilt_angle = Math::deg_to_rad(tilt_angle); + real_t snap_degrees = Node3DEditor::get_singleton()->get_rotate_snap(); + tilt_angle = Math::deg_to_rad(Math::snapped(Math::rad_to_deg(tilt_angle), snap_degrees)); } c->set_point_tilt(idx, tilt_angle); diff --git a/editor/plugins/polygon_2d_editor_plugin.cpp b/editor/plugins/polygon_2d_editor_plugin.cpp index 707cf13cad..24ec607f63 100644 --- a/editor/plugins/polygon_2d_editor_plugin.cpp +++ b/editor/plugins/polygon_2d_editor_plugin.cpp @@ -293,8 +293,7 @@ void Polygon2DEditor::_uv_edit_mode_select(int p_mode) { } void Polygon2DEditor::_uv_edit_popup_hide() { - EditorSettings::get_singleton()->set("interface/dialogs/uv_editor_bounds", Rect2(uv_edit->get_position(), uv_edit->get_size())); - + EditorSettings::get_singleton()->set_project_metadata("dialog_bounds", "uv_editor", Rect2(uv_edit->get_position(), uv_edit->get_size())); _cancel_editing(); } @@ -321,8 +320,9 @@ void Polygon2DEditor::_menu_option(int p_option) { undo_redo->commit_action(); } - if (EditorSettings::get_singleton()->has_setting("interface/dialogs/uv_editor_bounds")) { - uv_edit->popup(EDITOR_GET("interface/dialogs/uv_editor_bounds")); + const Rect2 bounds = EditorSettings::get_singleton()->get_project_metadata("dialog_bounds", "uv_editor", Rect2()); + if (bounds.has_area()) { + uv_edit->popup(bounds); } else { uv_edit->popup_centered_ratio(0.85); } @@ -1308,6 +1308,7 @@ Polygon2DEditor::Polygon2DEditor() { uv_edit = memnew(AcceptDialog); add_child(uv_edit); uv_edit->set_title(TTR("Polygon 2D UV Editor")); + uv_edit->connect("confirmed", callable_mp(this, &Polygon2DEditor::_uv_edit_popup_hide)); uv_edit->connect("canceled", callable_mp(this, &Polygon2DEditor::_uv_edit_popup_hide)); VBoxContainer *uv_main_vb = memnew(VBoxContainer); diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index c832570fee..cc9e887448 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -863,19 +863,20 @@ void ScriptEditor::_close_tab(int p_idx, bool p_save, bool p_history_back) { _save_editor_state(current); } memdelete(tselected); - if (idx >= tab_container->get_tab_count()) { - idx = tab_container->get_tab_count() - 1; - } - if (idx >= 0) { - if (history_pos >= 0) { - idx = tab_container->get_tab_idx_from_control(history[history_pos].control); - } - _go_to_tab(idx); - } else { - _update_selected_editor_menu(); - } if (script_close_queue.is_empty()) { + if (idx >= tab_container->get_tab_count()) { + idx = tab_container->get_tab_count() - 1; + } + if (idx >= 0) { + if (history_pos >= 0) { + idx = tab_container->get_tab_idx_from_control(history[history_pos].control); + } + _go_to_tab(idx); + } else { + _update_selected_editor_menu(); + } + _update_history_arrows(); _update_script_names(); _save_layout(); @@ -883,8 +884,8 @@ void ScriptEditor::_close_tab(int p_idx, bool p_save, bool p_history_back) { } } -void ScriptEditor::_close_current_tab(bool p_save) { - _close_tab(tab_container->get_current_tab(), p_save); +void ScriptEditor::_close_current_tab(bool p_save, bool p_history_back) { + _close_tab(tab_container->get_current_tab(), p_save, p_history_back); } void ScriptEditor::_close_discard_current_tab(const String &p_str) { @@ -948,7 +949,7 @@ void ScriptEditor::_queue_close_tabs() { } } - _close_current_tab(false); + _close_current_tab(false, false); } _update_find_replace_bar(); } @@ -4153,7 +4154,7 @@ ScriptEditor::ScriptEditor(WindowWrapper *p_wrapper) { erase_tab_confirm = memnew(ConfirmationDialog); erase_tab_confirm->set_ok_button_text(TTR("Save")); erase_tab_confirm->add_button(TTR("Discard"), DisplayServer::get_singleton()->get_swap_cancel_ok(), "discard"); - erase_tab_confirm->connect("confirmed", callable_mp(this, &ScriptEditor::_close_current_tab).bind(true)); + erase_tab_confirm->connect("confirmed", callable_mp(this, &ScriptEditor::_close_current_tab).bind(true, true)); erase_tab_confirm->connect("custom_action", callable_mp(this, &ScriptEditor::_close_discard_current_tab)); add_child(erase_tab_confirm); diff --git a/editor/plugins/script_editor_plugin.h b/editor/plugins/script_editor_plugin.h index 0252e10b43..f87cb0958c 100644 --- a/editor/plugins/script_editor_plugin.h +++ b/editor/plugins/script_editor_plugin.h @@ -374,7 +374,7 @@ class ScriptEditor : public PanelContainer { void _close_tab(int p_idx, bool p_save = true, bool p_history_back = true); void _update_find_replace_bar(); - void _close_current_tab(bool p_save = true); + void _close_current_tab(bool p_save = true, bool p_history_back = true); void _close_discard_current_tab(const String &p_str); void _close_docs_tab(); void _close_other_tabs(); diff --git a/editor/plugins/tiles/tile_map_layer_editor.cpp b/editor/plugins/tiles/tile_map_layer_editor.cpp index e1a58a0998..f177245acc 100644 --- a/editor/plugins/tiles/tile_map_layer_editor.cpp +++ b/editor/plugins/tiles/tile_map_layer_editor.cpp @@ -1079,9 +1079,6 @@ HashMap<Vector2i, TileMapCell> TileMapLayerEditorTilesPlugin::_draw_line(Vector2 } // Get or create the pattern. - Ref<TileMapPattern> erase_pattern; - erase_pattern.instantiate(); - erase_pattern->set_cell(Vector2i(0, 0), TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE); Ref<TileMapPattern> pattern = p_erase ? erase_pattern : selection_pattern; HashMap<Vector2i, TileMapCell> output; @@ -1132,9 +1129,6 @@ HashMap<Vector2i, TileMapCell> TileMapLayerEditorTilesPlugin::_draw_rect(Vector2 rect.size += Vector2i(1, 1); // Get or create the pattern. - Ref<TileMapPattern> erase_pattern; - erase_pattern.instantiate(); - erase_pattern->set_cell(Vector2i(0, 0), TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE); Ref<TileMapPattern> pattern = p_erase ? erase_pattern : selection_pattern; HashMap<Vector2i, TileMapCell> err_output; @@ -1189,9 +1183,6 @@ HashMap<Vector2i, TileMapCell> TileMapLayerEditorTilesPlugin::_draw_bucket_fill( HashMap<Vector2i, TileMapCell> output; // Get or create the pattern. - Ref<TileMapPattern> erase_pattern; - erase_pattern.instantiate(); - erase_pattern->set_cell(Vector2i(0, 0), TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE); Ref<TileMapPattern> pattern = p_erase ? erase_pattern : selection_pattern; if (!pattern->is_empty()) { @@ -2200,6 +2191,9 @@ TileMapLayerEditorTilesPlugin::TileMapLayerEditorTilesPlugin() { tile_map_clipboard.instantiate(); selection_pattern.instantiate(); + erase_pattern.instantiate(); + erase_pattern->set_cell(Vector2i(0, 0), TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE); + // --- Toolbar --- toolbar = memnew(HBoxContainer); diff --git a/editor/plugins/tiles/tile_map_layer_editor.h b/editor/plugins/tiles/tile_map_layer_editor.h index 2603261449..7d749be1ba 100644 --- a/editor/plugins/tiles/tile_map_layer_editor.h +++ b/editor/plugins/tiles/tile_map_layer_editor.h @@ -154,6 +154,7 @@ private: RBSet<Vector2i> tile_map_selection; Ref<TileMapPattern> tile_map_clipboard; Ref<TileMapPattern> selection_pattern; + Ref<TileMapPattern> erase_pattern; void _set_tile_map_selection(const TypedArray<Vector2i> &p_selection); TypedArray<Vector2i> _get_tile_map_selection() const; diff --git a/editor/script_create_dialog.cpp b/editor/script_create_dialog.cpp index cd043e165e..c12f95753f 100644 --- a/editor/script_create_dialog.cpp +++ b/editor/script_create_dialog.cpp @@ -883,6 +883,7 @@ ScriptCreateDialog::ScriptCreateDialog() { parent_name->connect("text_changed", callable_mp(this, &ScriptCreateDialog::_parent_name_changed)); parent_name->set_h_size_flags(Control::SIZE_EXPAND_FILL); hb->add_child(parent_name); + register_text_enter(parent_name); parent_search_button = memnew(Button); parent_search_button->connect("pressed", callable_mp(this, &ScriptCreateDialog::_browse_class_in_tree)); hb->add_child(parent_search_button); diff --git a/editor/shader_globals_editor.cpp b/editor/shader_globals_editor.cpp index 86a78d813e..216ccd71ab 100644 --- a/editor/shader_globals_editor.cpp +++ b/editor/shader_globals_editor.cpp @@ -219,7 +219,7 @@ protected: case RS::GLOBAL_VAR_TYPE_SAMPLER2DARRAY: { pinfo.type = Variant::OBJECT; pinfo.hint = PROPERTY_HINT_RESOURCE_TYPE; - pinfo.hint_string = "Texture2DArray"; + pinfo.hint_string = "Texture2DArray,CompressedTexture2DArray"; } break; case RS::GLOBAL_VAR_TYPE_SAMPLER3D: { pinfo.type = Variant::OBJECT; @@ -229,7 +229,7 @@ protected: case RS::GLOBAL_VAR_TYPE_SAMPLERCUBE: { pinfo.type = Variant::OBJECT; pinfo.hint = PROPERTY_HINT_RESOURCE_TYPE; - pinfo.hint_string = "Cubemap"; + pinfo.hint_string = "Cubemap,CompressedCubemap"; } break; default: { } break; diff --git a/main/main.cpp b/main/main.cpp index 78a539fff7..905740fd90 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -2341,10 +2341,13 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph Engine::get_singleton()->set_max_physics_steps_per_frame(GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "physics/common/max_physics_steps_per_frame", PROPERTY_HINT_RANGE, "1,100,1"), 8)); Engine::get_singleton()->set_physics_jitter_fix(GLOBAL_DEF("physics/common/physics_jitter_fix", 0.5)); Engine::get_singleton()->set_max_fps(GLOBAL_DEF(PropertyInfo(Variant::INT, "application/run/max_fps", PROPERTY_HINT_RANGE, "0,1000,1"), 0)); - Engine::get_singleton()->set_audio_output_latency(GLOBAL_DEF_RST(PropertyInfo(Variant::INT, "audio/driver/output_latency", PROPERTY_HINT_RANGE, "1,100,1"), 15)); + + GLOBAL_DEF_RST(PropertyInfo(Variant::INT, "audio/driver/output_latency", PROPERTY_HINT_RANGE, "1,100,1"), 15); // Use a safer default output_latency for web to avoid audio cracking on low-end devices, especially mobile. GLOBAL_DEF_RST("audio/driver/output_latency.web", 50); + Engine::get_singleton()->set_audio_output_latency(GLOBAL_GET("audio/driver/output_latency")); + GLOBAL_DEF("debug/settings/stdout/print_fps", false); GLOBAL_DEF("debug/settings/stdout/print_gpu_profile", false); GLOBAL_DEF("debug/settings/stdout/verbose_stdout", false); diff --git a/methods.py b/methods.py index f0e51c7d6f..01b127ea30 100644 --- a/methods.py +++ b/methods.py @@ -763,19 +763,15 @@ def detect_visual_c_compiler_version(tools_env): def find_visual_c_batch_file(env): from SCons.Tool.MSCommon.vc import get_default_version, get_host_target, find_batch_file, find_vc_pdir - # Syntax changed in SCons 4.4.0. - from SCons import __version__ as scons_raw_version - - scons_ver = env._get_major_minor_revision(scons_raw_version) - msvc_version = get_default_version(env) - if scons_ver >= (4, 4, 0): + # Syntax changed in SCons 4.4.0. + if env.scons_version >= (4, 4, 0): (host_platform, target_platform, _) = get_host_target(env, msvc_version) else: (host_platform, target_platform, _) = get_host_target(env) - if scons_ver < (4, 6, 0): + if env.scons_version < (4, 6, 0): return find_batch_file(env, msvc_version, host_platform, target_platform)[0] # Scons 4.6.0+ removed passing env, so we need to get the product_dir ourselves first, diff --git a/modules/fbx/fbx_document.cpp b/modules/fbx/fbx_document.cpp index 95b4a91809..5f94a80566 100644 --- a/modules/fbx/fbx_document.cpp +++ b/modules/fbx/fbx_document.cpp @@ -86,6 +86,17 @@ static Quaternion _as_quaternion(const ufbx_quat &p_quat) { return Quaternion(real_t(p_quat.x), real_t(p_quat.y), real_t(p_quat.z), real_t(p_quat.w)); } +static Transform3D _as_transform(const ufbx_transform &p_xform) { + Transform3D result; + result.origin = FBXDocument::_as_vec3(p_xform.translation); + result.basis.set_quaternion_scale(_as_quaternion(p_xform.rotation), FBXDocument::_as_vec3(p_xform.scale)); + return result; +} + +static real_t _relative_error(const Vector3 &p_a, const Vector3 &p_b) { + return p_a.distance_to(p_b) / MAX(p_a.length(), p_b.length()); +} + static Color _material_color(const ufbx_material_map &p_map) { if (p_map.value_components == 1) { float r = float(p_map.value_real); @@ -196,6 +207,16 @@ static uint32_t _decode_vertex_index(const Vector3 &p_vertex) { return uint32_t(p_vertex.x) | uint32_t(p_vertex.y) << 16; } +static ufbx_skin_deformer *_find_skin_deformer(ufbx_skin_cluster *p_cluster) { + for (const ufbx_connection &conn : p_cluster->element.connections_src) { + ufbx_skin_deformer *deformer = ufbx_as_skin_deformer(conn.dst); + if (deformer) { + return deformer; + } + } + return nullptr; +} + struct ThreadPoolFBX { struct Group { ufbx_thread_pool_context ctx = {}; @@ -333,23 +354,67 @@ Error FBXDocument::_parse_nodes(Ref<FBXState> p_state) { } { - node->transform.origin = _as_vec3(fbx_node->local_transform.translation); - node->transform.basis.set_quaternion_scale(_as_quaternion(fbx_node->local_transform.rotation), _as_vec3(fbx_node->local_transform.scale)); - - if (fbx_node->bind_pose) { - ufbx_bone_pose *pose = ufbx_get_bone_pose(fbx_node->bind_pose, fbx_node); - ufbx_transform rest_transform = ufbx_matrix_to_transform(&pose->bone_to_parent); - - Vector3 rest_position = _as_vec3(rest_transform.translation); - Quaternion rest_rotation = _as_quaternion(rest_transform.rotation); - Vector3 rest_scale = _as_vec3(rest_transform.scale); - Transform3D godot_rest_xform; - godot_rest_xform.basis.set_quaternion_scale(rest_rotation, rest_scale); - godot_rest_xform.origin = rest_position; - node->set_additional_data("GODOT_rest_transform", godot_rest_xform); - } else { - node->set_additional_data("GODOT_rest_transform", node->transform); + node->transform = _as_transform(fbx_node->local_transform); + + bool found_rest_xform = false; + bool bad_rest_xform = false; + Transform3D candidate_rest_xform; + + if (fbx_node->parent) { + // Attempt to resolve a rest pose for bones: This uses internal FBX connections to find + // all skin clusters connected to the bone. + for (const ufbx_connection &child_conn : fbx_node->element.connections_src) { + ufbx_skin_cluster *child_cluster = ufbx_as_skin_cluster(child_conn.dst); + if (!child_cluster) + continue; + ufbx_skin_deformer *child_deformer = _find_skin_deformer(child_cluster); + if (!child_deformer) + continue; + + // Found a skin cluster: Now iterate through all the skin clusters of the parent and + // try to find one that used by the same deformer. + for (const ufbx_connection &parent_conn : fbx_node->parent->element.connections_src) { + ufbx_skin_cluster *parent_cluster = ufbx_as_skin_cluster(parent_conn.dst); + if (!parent_cluster) + continue; + ufbx_skin_deformer *parent_deformer = _find_skin_deformer(parent_cluster); + if (parent_deformer != child_deformer) + continue; + + // Success: Found two skin clusters from the same deformer, now we can resolve the + // local bind pose from the difference between the two world-space bind poses. + ufbx_matrix child_to_world = child_cluster->bind_to_world; + ufbx_matrix world_to_parent = ufbx_matrix_invert(&parent_cluster->bind_to_world); + ufbx_matrix child_to_parent = ufbx_matrix_mul(&world_to_parent, &child_to_world); + Transform3D xform = _as_transform(ufbx_matrix_to_transform(&child_to_parent)); + + if (!found_rest_xform) { + // Found the first bind pose for the node, assume that this one is good + found_rest_xform = true; + candidate_rest_xform = xform; + } else if (!bad_rest_xform) { + // Found another: Let's hope it's similar to the previous one, if not warn and + // use the initial pose, which is used by default if rest pose is not found. + real_t error = 0.0f; + error += _relative_error(candidate_rest_xform.origin, xform.origin); + for (int i = 0; i < 3; i++) { + error += _relative_error(candidate_rest_xform.basis.rows[i], xform.basis.rows[i]); + } + const real_t max_error = 0.01f; + if (error >= max_error) { + WARN_PRINT(vformat("FBX: Node '%s' has multiple bind poses, using initial pose as rest pose.", node->get_name())); + bad_rest_xform = true; + } + } + } + } + } + + Transform3D godot_rest_xform = node->transform; + if (found_rest_xform && !bad_rest_xform) { + godot_rest_xform = candidate_rest_xform; } + node->set_additional_data("GODOT_rest_transform", godot_rest_xform); } for (const ufbx_node *child : fbx_node->children) { @@ -1699,6 +1764,7 @@ void FBXDocument::_import_animation(Ref<FBXState> p_state, AnimationPlayer *p_an Ref<Animation> animation; animation.instantiate(); animation->set_name(anim_name); + animation->set_step(1.0 / p_bake_fps); if (anim->get_loop()) { animation->set_loop_mode(Animation::LOOP_LINEAR); diff --git a/modules/gdscript/gdscript_byte_codegen.cpp b/modules/gdscript/gdscript_byte_codegen.cpp index 36b157521d..bfe090edb0 100644 --- a/modules/gdscript/gdscript_byte_codegen.cpp +++ b/modules/gdscript/gdscript_byte_codegen.cpp @@ -45,8 +45,8 @@ uint32_t GDScriptByteCodeGenerator::add_parameter(const StringName &p_name, bool } uint32_t GDScriptByteCodeGenerator::add_local(const StringName &p_name, const GDScriptDataType &p_type) { - int stack_pos = locals.size() + RESERVED_STACK; - locals.push_back(StackSlot(p_type.builtin_type)); + int stack_pos = locals.size() + GDScriptFunction::FIXED_ADDRESSES_MAX; + locals.push_back(StackSlot(p_type.builtin_type, p_type.can_contain_object())); add_stack_identifier(p_name, stack_pos); return stack_pos; } @@ -122,7 +122,7 @@ uint32_t GDScriptByteCodeGenerator::add_temporary(const GDScriptDataType &p_type List<int> &pool = temporaries_pool[temp_type]; if (pool.is_empty()) { - StackSlot new_temp(temp_type); + StackSlot new_temp(temp_type, p_type.can_contain_object()); int idx = temporaries.size(); pool.push_back(idx); temporaries.push_back(new_temp); @@ -136,15 +136,14 @@ uint32_t GDScriptByteCodeGenerator::add_temporary(const GDScriptDataType &p_type void GDScriptByteCodeGenerator::pop_temporary() { ERR_FAIL_COND(used_temporaries.is_empty()); int slot_idx = used_temporaries.back()->get(); - const StackSlot &slot = temporaries[slot_idx]; - if (slot.type == Variant::NIL) { + if (temporaries[slot_idx].can_contain_object) { // Avoid keeping in the stack long-lived references to objects, - // which may prevent RefCounted objects from being freed. + // which may prevent `RefCounted` objects from being freed. // However, the cleanup will be performed an the end of the // statement, to allow object references to survive chaining. - temporaries_pending_clear.push_back(slot_idx); + temporaries_pending_clear.insert(slot_idx); } - temporaries_pool[slot.type].push_back(slot_idx); + temporaries_pool[temporaries[slot_idx].type].push_back(slot_idx); used_temporaries.pop_back(); } @@ -187,7 +186,7 @@ GDScriptFunction *GDScriptByteCodeGenerator::write_end() { append_opcode(GDScriptFunction::OPCODE_END); for (int i = 0; i < temporaries.size(); i++) { - int stack_index = i + max_locals + RESERVED_STACK; + int stack_index = i + max_locals + GDScriptFunction::FIXED_ADDRESSES_MAX; for (int j = 0; j < temporaries[i].bytecode_indices.size(); j++) { opcodes.write[temporaries[i].bytecode_indices[j]] = stack_index | (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS); } @@ -398,7 +397,7 @@ GDScriptFunction *GDScriptByteCodeGenerator::write_end() { if (debug_stack) { function->stack_debug = stack_debug; } - function->_stack_size = RESERVED_STACK + max_locals + temporaries.size(); + function->_stack_size = GDScriptFunction::FIXED_ADDRESSES_MAX + max_locals + temporaries.size(); function->_instruction_args_size = instr_args_max; #ifdef DEBUG_ENABLED @@ -945,6 +944,11 @@ void GDScriptByteCodeGenerator::write_assign(const Address &p_target, const Addr } } +void GDScriptByteCodeGenerator::write_assign_null(const Address &p_target) { + append_opcode(GDScriptFunction::OPCODE_ASSIGN_NULL); + append(p_target); +} + void GDScriptByteCodeGenerator::write_assign_true(const Address &p_target) { append_opcode(GDScriptFunction::OPCODE_ASSIGN_TRUE); append(p_target); @@ -1579,9 +1583,8 @@ void GDScriptByteCodeGenerator::write_for(const Address &p_variable, bool p_use_ if (p_use_conversion) { write_assign_with_conversion(p_variable, temp); - const GDScriptDataType &type = p_variable.type; - if (type.kind != GDScriptDataType::BUILTIN || type.builtin_type == Variant::ARRAY || type.builtin_type == Variant::DICTIONARY) { - write_assign_false(temp); // Can contain RefCounted, so clear it. + if (p_variable.type.can_contain_object()) { + clear_address(temp); // Can contain `RefCounted`, so clear it. } } } @@ -1746,23 +1749,56 @@ void GDScriptByteCodeGenerator::end_block() { pop_stack_identifiers(); } -void GDScriptByteCodeGenerator::clean_temporaries() { - List<int>::Element *E = temporaries_pending_clear.front(); - while (E) { - // The temporary may have been re-used as something else than an object - // since it was added to the list. In that case, there's no need to clear it. - int slot_idx = E->get(); - const StackSlot &slot = temporaries[slot_idx]; - if (slot.type == Variant::NIL) { - write_assign_false(Address(Address::TEMPORARY, slot_idx)); +void GDScriptByteCodeGenerator::clear_temporaries() { + for (int slot_idx : temporaries_pending_clear) { + // The temporary may have been re-used as something else since it was added to the list. + // In that case, there's **no** need to clear it. + if (temporaries[slot_idx].can_contain_object) { + clear_address(Address(Address::TEMPORARY, slot_idx)); // Can contain `RefCounted`, so clear it. + } + } + temporaries_pending_clear.clear(); +} + +void GDScriptByteCodeGenerator::clear_address(const Address &p_address) { + // Do not check `is_local_dirty()` here! Always clear the address since the codegen doesn't track the compiler. + // Also, this method is used to initialize local variables of built-in types, since they cannot be `null`. + + if (p_address.type.has_type && p_address.type.kind == GDScriptDataType::BUILTIN) { + switch (p_address.type.builtin_type) { + case Variant::BOOL: + write_assign_false(p_address); + break; + case Variant::ARRAY: + if (p_address.type.has_container_element_type(0)) { + write_construct_typed_array(p_address, p_address.type.get_container_element_type(0), Vector<GDScriptCodeGenerator::Address>()); + } else { + write_construct(p_address, p_address.type.builtin_type, Vector<GDScriptCodeGenerator::Address>()); + } + break; + case Variant::NIL: + case Variant::OBJECT: + write_assign_null(p_address); + break; + default: + write_construct(p_address, p_address.type.builtin_type, Vector<GDScriptCodeGenerator::Address>()); + break; } + } else { + write_assign_null(p_address); + } - List<int>::Element *next = E->next(); - E->erase(); - E = next; + if (p_address.mode == Address::LOCAL_VARIABLE) { + dirty_locals.erase(p_address.address); } } +// Returns `true` if the local has been re-used and not cleaned up with `clear_address()`. +bool GDScriptByteCodeGenerator::is_local_dirty(const Address &p_address) const { + ERR_FAIL_COND_V(p_address.mode != Address::LOCAL_VARIABLE, false); + return dirty_locals.has(p_address.address); +} + GDScriptByteCodeGenerator::~GDScriptByteCodeGenerator() { if (!ended && function != nullptr) { memdelete(function); diff --git a/modules/gdscript/gdscript_byte_codegen.h b/modules/gdscript/gdscript_byte_codegen.h index f902cb10cc..5a736b2554 100644 --- a/modules/gdscript/gdscript_byte_codegen.h +++ b/modules/gdscript/gdscript_byte_codegen.h @@ -38,15 +38,14 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator { struct StackSlot { Variant::Type type = Variant::NIL; + bool can_contain_object = true; Vector<int> bytecode_indices; StackSlot() = default; - StackSlot(Variant::Type p_type) : - type(p_type) {} + StackSlot(Variant::Type p_type, bool p_can_contain_object) : + type(p_type), can_contain_object(p_can_contain_object) {} }; - const static int RESERVED_STACK = 3; // For self, class, and nil. - struct CallTarget { Address target; bool is_new_temporary = false; @@ -85,9 +84,11 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator { RBMap<StringName, int> local_constants; Vector<StackSlot> locals; + HashSet<int> dirty_locals; + Vector<StackSlot> temporaries; List<int> used_temporaries; - List<int> temporaries_pending_clear; + HashSet<int> temporaries_pending_clear; RBMap<Variant::Type, List<int>> temporaries_pool; List<GDScriptFunction::StackDebug> stack_debug; @@ -193,6 +194,9 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator { ERR_PRINT("Leaving block with non-zero temporary variables: " + itos(used_temporaries.size())); } #endif + for (int i = current_locals; i < locals.size(); i++) { + dirty_locals.insert(i + GDScriptFunction::FIXED_ADDRESSES_MAX); + } locals.resize(current_locals); if (debug_stack) { for (const KeyValue<StringName, int> &E : block_identifiers) { @@ -455,7 +459,9 @@ public: virtual uint32_t add_or_get_name(const StringName &p_name) override; virtual uint32_t add_temporary(const GDScriptDataType &p_type) override; virtual void pop_temporary() override; - virtual void clean_temporaries() override; + virtual void clear_temporaries() override; + virtual void clear_address(const Address &p_address) override; + virtual bool is_local_dirty(const Address &p_address) const override; virtual void start_parameters() override; virtual void end_parameters() override; @@ -496,6 +502,7 @@ public: virtual void write_get_static_variable(const Address &p_target, const Address &p_class, int p_index) override; virtual void write_assign(const Address &p_target, const Address &p_source) override; virtual void write_assign_with_conversion(const Address &p_target, const Address &p_source) override; + virtual void write_assign_null(const Address &p_target) override; virtual void write_assign_true(const Address &p_target) override; virtual void write_assign_false(const Address &p_target) override; virtual void write_assign_default_parameter(const Address &p_dst, const Address &p_src, bool p_use_conversion) override; diff --git a/modules/gdscript/gdscript_codegen.h b/modules/gdscript/gdscript_codegen.h index 7ad8f841aa..4c33ed499a 100644 --- a/modules/gdscript/gdscript_codegen.h +++ b/modules/gdscript/gdscript_codegen.h @@ -73,7 +73,9 @@ public: virtual uint32_t add_or_get_name(const StringName &p_name) = 0; virtual uint32_t add_temporary(const GDScriptDataType &p_type) = 0; virtual void pop_temporary() = 0; - virtual void clean_temporaries() = 0; + virtual void clear_temporaries() = 0; + virtual void clear_address(const Address &p_address) = 0; + virtual bool is_local_dirty(const Address &p_address) const = 0; virtual void start_parameters() = 0; virtual void end_parameters() = 0; @@ -114,6 +116,7 @@ public: virtual void write_get_static_variable(const Address &p_target, const Address &p_class, int p_index) = 0; virtual void write_assign(const Address &p_target, const Address &p_source) = 0; virtual void write_assign_with_conversion(const Address &p_target, const Address &p_source) = 0; + virtual void write_assign_null(const Address &p_target) = 0; virtual void write_assign_true(const Address &p_target) = 0; virtual void write_assign_false(const Address &p_target) = 0; virtual void write_assign_default_parameter(const Address &dst, const Address &src, bool p_use_conversion) = 0; diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index c526d9c0a4..734e37bc09 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -1826,7 +1826,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c ERR_FAIL_V_MSG(p_previous_test, "Reaching the end of pattern compilation without matching a pattern."); } -List<GDScriptCodeGenerator::Address> GDScriptCompiler::_add_locals_in_block(CodeGen &codegen, const GDScriptParser::SuiteNode *p_block) { +List<GDScriptCodeGenerator::Address> GDScriptCompiler::_add_block_locals(CodeGen &codegen, const GDScriptParser::SuiteNode *p_block) { List<GDScriptCodeGenerator::Address> addresses; for (int i = 0; i < p_block->locals.size(); i++) { if (p_block->locals[i].type == GDScriptParser::SuiteNode::Local::PARAMETER || p_block->locals[i].type == GDScriptParser::SuiteNode::Local::FOR_VARIABLE) { @@ -1838,27 +1838,25 @@ List<GDScriptCodeGenerator::Address> GDScriptCompiler::_add_locals_in_block(Code return addresses; } -// Avoid keeping in the stack long-lived references to objects, which may prevent RefCounted objects from being freed. -void GDScriptCompiler::_clear_addresses(CodeGen &codegen, const List<GDScriptCodeGenerator::Address> &p_addresses) { - for (const List<GDScriptCodeGenerator::Address>::Element *E = p_addresses.front(); E; E = E->next()) { - GDScriptDataType type = E->get().type; - // If not an object and cannot contain an object, no need to clear. - if (type.kind != GDScriptDataType::BUILTIN || type.builtin_type == Variant::ARRAY || type.builtin_type == Variant::DICTIONARY) { - codegen.generator->write_assign_false(E->get()); +// Avoid keeping in the stack long-lived references to objects, which may prevent `RefCounted` objects from being freed. +void GDScriptCompiler::_clear_block_locals(CodeGen &codegen, const List<GDScriptCodeGenerator::Address> &p_locals) { + for (const GDScriptCodeGenerator::Address &local : p_locals) { + if (local.type.can_contain_object()) { + codegen.generator->clear_address(local); } } } -Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::SuiteNode *p_block, bool p_add_locals, bool p_reset_locals) { +Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::SuiteNode *p_block, bool p_add_locals, bool p_clear_locals) { Error err = OK; GDScriptCodeGenerator *gen = codegen.generator; List<GDScriptCodeGenerator::Address> block_locals; - gen->clean_temporaries(); + gen->clear_temporaries(); codegen.start_block(); if (p_add_locals) { - block_locals = _add_locals_in_block(codegen, p_block); + block_locals = _add_block_locals(codegen, p_block); } for (int i = 0; i < p_block->statements.size(); i++) { @@ -1873,7 +1871,7 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui case GDScriptParser::Node::MATCH: { const GDScriptParser::MatchNode *match = static_cast<const GDScriptParser::MatchNode *>(s); - codegen.start_block(); + codegen.start_block(); // Add an extra block, since the binding pattern and @special variables belong to the branch scope. // Evaluate the match expression. GDScriptCodeGenerator::Address value = codegen.add_local("@match_value", _gdtype_from_datatype(match->test->get_datatype(), codegen.script)); @@ -1914,7 +1912,7 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui codegen.start_block(); // Create an extra block around for binds. // Add locals in block before patterns, so temporaries don't use the stack address for binds. - List<GDScriptCodeGenerator::Address> branch_locals = _add_locals_in_block(codegen, branch->block); + List<GDScriptCodeGenerator::Address> branch_locals = _add_block_locals(codegen, branch->block); #ifdef DEBUG_ENABLED // Add a newline before each branch, since the debugger needs those. @@ -1961,7 +1959,7 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui return err; } - _clear_addresses(codegen, branch_locals); + _clear_block_locals(codegen, branch_locals); codegen.end_block(); // Get out of extra block. } @@ -2003,7 +2001,8 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui case GDScriptParser::Node::FOR: { const GDScriptParser::ForNode *for_n = static_cast<const GDScriptParser::ForNode *>(s); - codegen.start_block(); + codegen.start_block(); // Add an extra block, since the iterator and @special variables belong to the loop scope. + GDScriptCodeGenerator::Address iterator = codegen.add_local(for_n->variable->name, _gdtype_from_datatype(for_n->variable->get_datatype(), codegen.script)); gen->start_for(iterator.type, _gdtype_from_datatype(for_n->list->get_datatype(), codegen.script)); @@ -2021,14 +2020,21 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui gen->write_for(iterator, for_n->use_conversion_assign); - err = _parse_block(codegen, for_n->loop); + // Loop variables must be cleared even when `break`/`continue` is used. + List<GDScriptCodeGenerator::Address> loop_locals = _add_block_locals(codegen, for_n->loop); + + //_clear_block_locals(codegen, loop_locals); // Inside loop, before block - for `continue`. // TODO + + err = _parse_block(codegen, for_n->loop, false); // Don't add locals again. if (err) { return err; } gen->write_endfor(); - codegen.end_block(); + _clear_block_locals(codegen, loop_locals); // Outside loop, after block - for `break` and normal exit. + + codegen.end_block(); // Get out of extra block. } break; case GDScriptParser::Node::WHILE: { const GDScriptParser::WhileNode *while_n = static_cast<const GDScriptParser::WhileNode *>(s); @@ -2046,12 +2052,19 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui codegen.generator->pop_temporary(); } - err = _parse_block(codegen, while_n->loop); + // Loop variables must be cleared even when `break`/`continue` is used. + List<GDScriptCodeGenerator::Address> loop_locals = _add_block_locals(codegen, while_n->loop); + + //_clear_block_locals(codegen, loop_locals); // Inside loop, before block - for `continue`. // TODO + + err = _parse_block(codegen, while_n->loop, false); // Don't add locals again. if (err) { return err; } gen->write_endwhile(); + + _clear_block_locals(codegen, loop_locals); // Outside loop, after block - for `break` and normal exit. } break; case GDScriptParser::Node::BREAK: { gen->write_break(); @@ -2134,21 +2147,16 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui codegen.generator->pop_temporary(); } initialized = true; - } else if (local_type.has_type) { - // Initialize with default for type. - if (local_type.has_container_element_type(0)) { - codegen.generator->write_construct_typed_array(local, local_type.get_container_element_type(0), Vector<GDScriptCodeGenerator::Address>()); - initialized = true; - } else if (local_type.kind == GDScriptDataType::BUILTIN) { - codegen.generator->write_construct(local, local_type.builtin_type, Vector<GDScriptCodeGenerator::Address>()); - initialized = true; - } - // The `else` branch is for objects, in such case we leave it as `null`. + } else if ((local_type.has_type && local_type.kind == GDScriptDataType::BUILTIN) || codegen.generator->is_local_dirty(local)) { + // Initialize with default for the type. Built-in types must always be cleared (they cannot be `null`). + // Objects and untyped variables are assigned to `null` only if the stack address has been re-used and not cleared. + codegen.generator->clear_address(local); + initialized = true; } - // Assigns a null for the unassigned variables in loops. + // Don't check `is_local_dirty()` since the variable must be assigned to `null` **on each iteration**. if (!initialized && p_block->is_in_loop) { - codegen.generator->write_construct(local, Variant::NIL, Vector<GDScriptCodeGenerator::Address>()); + codegen.generator->clear_address(local); } } break; case GDScriptParser::Node::CONSTANT: { @@ -2180,11 +2188,11 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui } break; } - gen->clean_temporaries(); + gen->clear_temporaries(); } - if (p_add_locals && p_reset_locals) { - _clear_addresses(codegen, block_locals); + if (p_add_locals && p_clear_locals) { + _clear_block_locals(codegen, block_locals); } codegen.end_block(); diff --git a/modules/gdscript/gdscript_compiler.h b/modules/gdscript/gdscript_compiler.h index 637d61ca3b..45f0f9e19b 100644 --- a/modules/gdscript/gdscript_compiler.h +++ b/modules/gdscript/gdscript_compiler.h @@ -153,9 +153,9 @@ class GDScriptCompiler { GDScriptCodeGenerator::Address _parse_expression(CodeGen &codegen, Error &r_error, const GDScriptParser::ExpressionNode *p_expression, bool p_root = false, bool p_initializer = false); GDScriptCodeGenerator::Address _parse_match_pattern(CodeGen &codegen, Error &r_error, const GDScriptParser::PatternNode *p_pattern, const GDScriptCodeGenerator::Address &p_value_addr, const GDScriptCodeGenerator::Address &p_type_addr, const GDScriptCodeGenerator::Address &p_previous_test, bool p_is_first, bool p_is_nested); - List<GDScriptCodeGenerator::Address> _add_locals_in_block(CodeGen &codegen, const GDScriptParser::SuiteNode *p_block); - void _clear_addresses(CodeGen &codegen, const List<GDScriptCodeGenerator::Address> &p_addresses); - Error _parse_block(CodeGen &codegen, const GDScriptParser::SuiteNode *p_block, bool p_add_locals = true, bool p_reset_locals = true); + List<GDScriptCodeGenerator::Address> _add_block_locals(CodeGen &codegen, const GDScriptParser::SuiteNode *p_block); + void _clear_block_locals(CodeGen &codegen, const List<GDScriptCodeGenerator::Address> &p_locals); + Error _parse_block(CodeGen &codegen, const GDScriptParser::SuiteNode *p_block, bool p_add_locals = true, bool p_clear_locals = true); GDScriptFunction *_parse_function(Error &r_error, GDScript *p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::FunctionNode *p_func, bool p_for_ready = false, bool p_for_lambda = false); GDScriptFunction *_make_static_initializer(Error &r_error, GDScript *p_script, const GDScriptParser::ClassNode *p_class); Error _parse_setter_getter(GDScript *p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::VariableNode *p_variable, bool p_is_setter); diff --git a/modules/gdscript/gdscript_disassembler.cpp b/modules/gdscript/gdscript_disassembler.cpp index 26f7cb7537..c7873dcd52 100644 --- a/modules/gdscript/gdscript_disassembler.cpp +++ b/modules/gdscript/gdscript_disassembler.cpp @@ -360,6 +360,13 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const { incr += 3; } break; + case OPCODE_ASSIGN_NULL: { + text += "assign "; + text += DADDR(1); + text += " = null"; + + incr += 2; + } break; case OPCODE_ASSIGN_TRUE: { text += "assign "; text += DADDR(1); diff --git a/modules/gdscript/gdscript_function.h b/modules/gdscript/gdscript_function.h index 002fc159fa..184d256bcd 100644 --- a/modules/gdscript/gdscript_function.h +++ b/modules/gdscript/gdscript_function.h @@ -78,17 +78,17 @@ public: if (valid && builtin_type == Variant::ARRAY && has_container_element_type(0)) { Array array = p_variant; if (array.is_typed()) { - GDScriptDataType array_container_type = get_container_element_type(0); + const GDScriptDataType &elem_type = container_element_types[0]; Variant::Type array_builtin_type = (Variant::Type)array.get_typed_builtin(); StringName array_native_type = array.get_typed_class_name(); Ref<Script> array_script_type_ref = array.get_typed_script(); if (array_script_type_ref.is_valid()) { - valid = (array_container_type.kind == SCRIPT || array_container_type.kind == GDSCRIPT) && array_container_type.script_type == array_script_type_ref.ptr(); + valid = (elem_type.kind == SCRIPT || elem_type.kind == GDSCRIPT) && elem_type.script_type == array_script_type_ref.ptr(); } else if (array_native_type != StringName()) { - valid = array_container_type.kind == NATIVE && array_container_type.native_type == array_native_type; + valid = elem_type.kind == NATIVE && elem_type.native_type == array_native_type; } else { - valid = array_container_type.kind == BUILTIN && array_container_type.builtin_type == array_builtin_type; + valid = elem_type.kind == BUILTIN && elem_type.builtin_type == array_builtin_type; } } else { valid = false; @@ -147,6 +147,25 @@ public: return false; } + bool can_contain_object() const { + if (has_type && kind == BUILTIN) { + switch (builtin_type) { + case Variant::ARRAY: + if (has_container_element_type(0)) { + return container_element_types[0].can_contain_object(); + } + return true; + case Variant::DICTIONARY: + case Variant::NIL: + case Variant::OBJECT: + return true; + default: + return false; + } + } + return true; + } + void set_container_element_type(int p_index, const GDScriptDataType &p_element_type) { ERR_FAIL_COND(p_index < 0); while (p_index >= container_element_types.size()) { @@ -218,6 +237,7 @@ public: OPCODE_SET_STATIC_VARIABLE, // Only for GDScript. OPCODE_GET_STATIC_VARIABLE, // Only for GDScript. OPCODE_ASSIGN, + OPCODE_ASSIGN_NULL, OPCODE_ASSIGN_TRUE, OPCODE_ASSIGN_FALSE, OPCODE_ASSIGN_TYPED_BUILTIN, diff --git a/modules/gdscript/gdscript_vm.cpp b/modules/gdscript/gdscript_vm.cpp index 1a8c22cc11..842975698b 100644 --- a/modules/gdscript/gdscript_vm.cpp +++ b/modules/gdscript/gdscript_vm.cpp @@ -234,6 +234,7 @@ void (*type_init_function_table[])(Variant *) = { &&OPCODE_SET_STATIC_VARIABLE, \ &&OPCODE_GET_STATIC_VARIABLE, \ &&OPCODE_ASSIGN, \ + &&OPCODE_ASSIGN_NULL, \ &&OPCODE_ASSIGN_TRUE, \ &&OPCODE_ASSIGN_FALSE, \ &&OPCODE_ASSIGN_TYPED_BUILTIN, \ @@ -1256,6 +1257,16 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a } DISPATCH_OPCODE; + OPCODE(OPCODE_ASSIGN_NULL) { + CHECK_SPACE(2); + GET_VARIANT_PTR(dst, 0); + + *dst = Variant(); + + ip += 2; + } + DISPATCH_OPCODE; + OPCODE(OPCODE_ASSIGN_TRUE) { CHECK_SPACE(2); GET_VARIANT_PTR(dst, 0); @@ -2649,6 +2660,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a GET_VARIANT_PTR(counter, 0); GET_VARIANT_PTR(container, 1); + *counter = Variant(); + bool valid; if (!container->iter_init(*counter, valid)) { #ifdef DEBUG_ENABLED @@ -2976,20 +2989,22 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a #else Object *obj = *VariantInternal::get_object(container); #endif + + *counter = Variant(); + Array ref; ref.push_back(*counter); Variant vref; VariantInternal::initialize(&vref, Variant::ARRAY); *VariantInternal::get_array(&vref) = ref; - Variant **args = instruction_args; // Overriding an instruction argument, but we don't need access to that anymore. - args[0] = &vref; + const Variant *args[] = { &vref }; Callable::CallError ce; - Variant has_next = obj->callp(CoreStringNames::get_singleton()->_iter_init, (const Variant **)args, 1, ce); + Variant has_next = obj->callp(CoreStringNames::get_singleton()->_iter_init, args, 1, ce); #ifdef DEBUG_ENABLED - if (ce.error != Callable::CallError::CALL_OK) { + if (ref.size() != 1 || ce.error != Callable::CallError::CALL_OK) { err_text = vformat(R"(There was an error calling "_iter_next" on iterator object of type %s.)", *container); OPCODE_BREAK; } @@ -2999,8 +3014,10 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size); ip = jumpto; } else { + *counter = ref[0]; + GET_VARIANT_PTR(iterator, 2); - *iterator = obj->callp(CoreStringNames::get_singleton()->_iter_get, (const Variant **)args, 1, ce); + *iterator = obj->callp(CoreStringNames::get_singleton()->_iter_get, (const Variant **)&counter, 1, ce); #ifdef DEBUG_ENABLED if (ce.error != Callable::CallError::CALL_OK) { err_text = vformat(R"(There was an error calling "_iter_get" on iterator object of type %s.)", *container); @@ -3307,20 +3324,20 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a #else Object *obj = *VariantInternal::get_object(container); #endif + Array ref; ref.push_back(*counter); Variant vref; VariantInternal::initialize(&vref, Variant::ARRAY); *VariantInternal::get_array(&vref) = ref; - Variant **args = instruction_args; // Overriding an instruction argument, but we don't need access to that anymore. - args[0] = &vref; + const Variant *args[] = { &vref }; Callable::CallError ce; - Variant has_next = obj->callp(CoreStringNames::get_singleton()->_iter_next, (const Variant **)args, 1, ce); + Variant has_next = obj->callp(CoreStringNames::get_singleton()->_iter_next, args, 1, ce); #ifdef DEBUG_ENABLED - if (ce.error != Callable::CallError::CALL_OK) { + if (ref.size() != 1 || ce.error != Callable::CallError::CALL_OK) { err_text = vformat(R"(There was an error calling "_iter_next" on iterator object of type %s.)", *container); OPCODE_BREAK; } @@ -3330,8 +3347,10 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size); ip = jumpto; } else { + *counter = ref[0]; + GET_VARIANT_PTR(iterator, 2); - *iterator = obj->callp(CoreStringNames::get_singleton()->_iter_get, (const Variant **)args, 1, ce); + *iterator = obj->callp(CoreStringNames::get_singleton()->_iter_get, (const Variant **)&counter, 1, ce); #ifdef DEBUG_ENABLED if (ce.error != Callable::CallError::CALL_OK) { err_text = vformat(R"(There was an error calling "_iter_get" on iterator object of type %s.)", *container); diff --git a/modules/gdscript/tests/scripts/runtime/features/object_iterators.gd b/modules/gdscript/tests/scripts/runtime/features/object_iterators.gd new file mode 100644 index 0000000000..6fe28c6f78 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/object_iterators.gd @@ -0,0 +1,49 @@ +class MyIterator: + var count: int + + func _init(p_count: int) -> void: + count = p_count + + func _iter_init(arg: Array) -> bool: + prints("_iter_init", arg) + arg[0] = 0 + return arg[0] < count + + func _iter_next(arg: Array) -> bool: + prints("_iter_next", arg) + arg[0] += 1 + return arg[0] < count + + func _iter_get(arg: Variant) -> Variant: + prints("_iter_get", arg) + return arg + +func test(): + var container := PackedDataContainer.new() + var _err := container.pack([{ + id = 123, + node_path = ^"/some/path", + data = PackedByteArray(), + }]) + + for ref: PackedDataContainerRef in container: + for key: String in ref: + print(key) + + print("===") + + for ref: Variant in container: + for key: String in ref: + print(key) + + print("===") + + var hard_custom := MyIterator.new(3) + for x in hard_custom: + print(x) + + print("===") + + var weak_custom: Variant = MyIterator.new(3) + for x in weak_custom: + print(x) diff --git a/modules/gdscript/tests/scripts/runtime/features/object_iterators.out b/modules/gdscript/tests/scripts/runtime/features/object_iterators.out new file mode 100644 index 0000000000..942a2c9dd8 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/object_iterators.out @@ -0,0 +1,30 @@ +GDTEST_OK +id +node_path +data +=== +id +node_path +data +=== +_iter_init [<null>] +_iter_get 0 +0 +_iter_next [0] +_iter_get 1 +1 +_iter_next [1] +_iter_get 2 +2 +_iter_next [2] +=== +_iter_init [<null>] +_iter_get 0 +0 +_iter_next [0] +_iter_get 1 +1 +_iter_next [1] +_iter_get 2 +2 +_iter_next [2] diff --git a/modules/gdscript/tests/scripts/runtime/features/reset_uninit_local_vars.gd b/modules/gdscript/tests/scripts/runtime/features/reset_uninit_local_vars.gd new file mode 100644 index 0000000000..ef2d9a1d60 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/reset_uninit_local_vars.gd @@ -0,0 +1,21 @@ +# GH-89958 + +func test(): + if true: + @warning_ignore("unused_variable") + var a = 1 + @warning_ignore("unused_variable") + var b := 1 + @warning_ignore("unused_variable") + var c := 1 + + if true: + var a + @warning_ignore("unassigned_variable") + print(a) + var b + @warning_ignore("unassigned_variable") + print(b) + var c: Object + @warning_ignore("unassigned_variable") + print(c) diff --git a/modules/gdscript/tests/scripts/runtime/features/reset_uninit_local_vars.out b/modules/gdscript/tests/scripts/runtime/features/reset_uninit_local_vars.out new file mode 100644 index 0000000000..279ea2d3b1 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/reset_uninit_local_vars.out @@ -0,0 +1,4 @@ +GDTEST_OK +<null> +<null> +<null> diff --git a/modules/gltf/editor/editor_scene_importer_blend.cpp b/modules/gltf/editor/editor_scene_importer_blend.cpp index abc89e4e97..42c3ecc7cb 100644 --- a/modules/gltf/editor/editor_scene_importer_blend.cpp +++ b/modules/gltf/editor/editor_scene_importer_blend.cpp @@ -298,6 +298,7 @@ Node *EditorSceneFormatImporterBlend::import_scene(const String &p_path, uint32_ } return nullptr; } + ERR_FAIL_COND_V(!p_options.has("animation/fps"), nullptr); #ifndef DISABLE_DEPRECATED bool trimming = p_options.has("animation/trimming") ? (bool)p_options["animation/trimming"] : false; diff --git a/modules/gltf/extensions/physics/gltf_document_extension_physics.cpp b/modules/gltf/extensions/physics/gltf_document_extension_physics.cpp index 352b439332..7e52cde059 100644 --- a/modules/gltf/extensions/physics/gltf_document_extension_physics.cpp +++ b/modules/gltf/extensions/physics/gltf_document_extension_physics.cpp @@ -127,6 +127,11 @@ Error GLTFDocumentExtensionPhysics::parse_node_extensions(Ref<GLTFState> p_state trigger_body->set_body_type("trigger"); p_gltf_node->set_additional_data(StringName("GLTFPhysicsBody"), trigger_body); } + // If this node defines explicit member shape nodes, save this information. + if (node_trigger.has("nodes")) { + Array node_trigger_nodes = node_trigger["nodes"]; + p_gltf_node->set_additional_data(StringName("GLTFPhysicsCompoundTriggerNodes"), node_trigger_nodes); + } } if (physics_body_ext.has("motion") || physics_body_ext.has("type")) { p_gltf_node->set_additional_data(StringName("GLTFPhysicsBody"), GLTFPhysicsBody::from_dictionary(physics_body_ext)); @@ -241,6 +246,19 @@ Node3D *_add_physics_node_to_given_node(Node3D *p_current_node, Node3D *p_child, return p_current_node; } +Array _get_ancestor_compound_trigger_nodes(Ref<GLTFState> p_state, TypedArray<GLTFNode> p_state_nodes, CollisionObject3D *p_ancestor_col_obj) { + GLTFNodeIndex ancestor_index = p_state->get_node_index(p_ancestor_col_obj); + ERR_FAIL_INDEX_V(ancestor_index, p_state_nodes.size(), Array()); + Ref<GLTFNode> ancestor_gltf_node = p_state_nodes[ancestor_index]; + Variant compound_trigger_nodes = ancestor_gltf_node->get_additional_data(StringName("GLTFPhysicsCompoundTriggerNodes")); + if (compound_trigger_nodes.is_array()) { + return compound_trigger_nodes; + } + Array ret; + ancestor_gltf_node->set_additional_data(StringName("GLTFPhysicsCompoundTriggerNodes"), ret); + return ret; +} + Node3D *GLTFDocumentExtensionPhysics::generate_scene_node(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Node *p_scene_parent) { Ref<GLTFPhysicsBody> gltf_physics_body = p_gltf_node->get_additional_data(StringName("GLTFPhysicsBody")); #ifndef DISABLE_DEPRECATED @@ -269,12 +287,27 @@ Node3D *GLTFDocumentExtensionPhysics::generate_scene_node(Ref<GLTFState> p_state #endif // DISABLE_DEPRECATED Node3D *ret = nullptr; CollisionObject3D *ancestor_col_obj = nullptr; + Ref<GLTFPhysicsShape> gltf_physics_collider_shape = p_gltf_node->get_additional_data(StringName("GLTFPhysicsColliderShape")); + Ref<GLTFPhysicsShape> gltf_physics_trigger_shape = p_gltf_node->get_additional_data(StringName("GLTFPhysicsTriggerShape")); if (gltf_physics_body.is_valid()) { ancestor_col_obj = gltf_physics_body->to_node(); ret = ancestor_col_obj; } else { ancestor_col_obj = _get_ancestor_collision_object(p_scene_parent); - if (!Object::cast_to<PhysicsBody3D>(ancestor_col_obj)) { + if (Object::cast_to<Area3D>(ancestor_col_obj) && gltf_physics_trigger_shape.is_valid()) { + // At this point, we found an ancestor Area3D node. But do we want to use it for this trigger shape? + TypedArray<GLTFNode> state_nodes = p_state->get_nodes(); + GLTFNodeIndex self_index = state_nodes.find(p_gltf_node); + Array compound_trigger_nodes = _get_ancestor_compound_trigger_nodes(p_state, state_nodes, ancestor_col_obj); + // Check if the ancestor specifies compound trigger nodes, and if this node is in there. + // Remember that JSON does not have integers, only "number", aka double-precision floats. + if (compound_trigger_nodes.size() > 0 && !compound_trigger_nodes.has(double(self_index))) { + // If the compound trigger we found is not the intended user of + // this shape node, then we need to create a new Area3D node. + ancestor_col_obj = memnew(Area3D); + ret = ancestor_col_obj; + } + } else if (!Object::cast_to<PhysicsBody3D>(ancestor_col_obj)) { if (p_gltf_node->get_additional_data(StringName("GLTFPhysicsCompoundCollider"))) { // If the GLTF file wants this node to group solid shapes together, // and there is no parent body, we need to create a static body. @@ -288,8 +321,6 @@ Node3D *GLTFDocumentExtensionPhysics::generate_scene_node(Ref<GLTFState> p_state // set above. If there is no ancestor body, we will either generate an // Area3D or StaticBody3D implicitly, so prefer an Area3D as the base // node for best compatibility with signal connections to this node. - Ref<GLTFPhysicsShape> gltf_physics_collider_shape = p_gltf_node->get_additional_data(StringName("GLTFPhysicsColliderShape")); - Ref<GLTFPhysicsShape> gltf_physics_trigger_shape = p_gltf_node->get_additional_data(StringName("GLTFPhysicsTriggerShape")); bool is_ancestor_col_obj_solid = Object::cast_to<PhysicsBody3D>(ancestor_col_obj); if (is_ancestor_col_obj_solid && gltf_physics_collider_shape.is_valid()) { Node3D *child = _generate_shape_node_and_body_if_needed(p_state, p_gltf_node, gltf_physics_collider_shape, ancestor_col_obj, false); @@ -362,8 +393,14 @@ void GLTFDocumentExtensionPhysics::convert_scene_node(Ref<GLTFState> p_state, Re gltf_shape->set_mesh_index(_get_or_insert_mesh_in_state(p_state, importer_mesh)); } } - if (cast_to<Area3D>(_get_ancestor_collision_object(p_scene_node->get_parent()))) { + CollisionObject3D *ancestor_col_obj = _get_ancestor_collision_object(p_scene_node->get_parent()); + if (cast_to<Area3D>(ancestor_col_obj)) { p_gltf_node->set_additional_data(StringName("GLTFPhysicsTriggerShape"), gltf_shape); + // Write explicit member shape nodes to the ancestor compound trigger node. + TypedArray<GLTFNode> state_nodes = p_state->get_nodes(); + GLTFNodeIndex self_index = state_nodes.size(); // The current p_gltf_node will be inserted next. + Array compound_trigger_nodes = _get_ancestor_compound_trigger_nodes(p_state, p_state->get_nodes(), ancestor_col_obj); + compound_trigger_nodes.push_back(double(self_index)); } else { p_gltf_node->set_additional_data(StringName("GLTFPhysicsColliderShape"), gltf_shape); } @@ -422,6 +459,11 @@ Error GLTFDocumentExtensionPhysics::export_node(Ref<GLTFState> p_state, Ref<GLTF Ref<GLTFPhysicsBody> physics_body = p_gltf_node->get_additional_data(StringName("GLTFPhysicsBody")); if (physics_body.is_valid()) { physics_body_ext = physics_body->to_dictionary(); + Variant compound_trigger_nodes = p_gltf_node->get_additional_data(StringName("GLTFPhysicsCompoundTriggerNodes")); + if (compound_trigger_nodes.is_array()) { + Dictionary trigger_property = physics_body_ext.get_or_add("trigger", {}); + trigger_property["nodes"] = compound_trigger_nodes; + } } Ref<GLTFPhysicsShape> collider_shape = p_gltf_node->get_additional_data(StringName("GLTFPhysicsColliderShape")); if (collider_shape.is_valid()) { diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp index 8f0f0d219e..4c32a29ce0 100644 --- a/modules/gltf/gltf_document.cpp +++ b/modules/gltf/gltf_document.cpp @@ -5881,6 +5881,7 @@ void GLTFDocument::_import_animation(Ref<GLTFState> p_state, AnimationPlayer *p_ Ref<Animation> animation; animation.instantiate(); animation->set_name(anim_name); + animation->set_step(1.0 / p_bake_fps); if (anim->get_loop()) { animation->set_loop_mode(Animation::LOOP_LINEAR); diff --git a/modules/mono/build_scripts/build_assemblies.py b/modules/mono/build_scripts/build_assemblies.py index b3f583b1b8..f9709362eb 100755 --- a/modules/mono/build_scripts/build_assemblies.py +++ b/modules/mono/build_scripts/build_assemblies.py @@ -151,7 +151,7 @@ def find_any_msbuild_tool(mono_prefix): return None -def run_msbuild(tools: ToolsLocation, sln: str, msbuild_args: Optional[List[str]] = None): +def run_msbuild(tools: ToolsLocation, sln: str, chdir_to: str, msbuild_args: Optional[List[str]] = None): using_msbuild_mono = False # Preference order: dotnet CLI > Standalone MSBuild > Mono's MSBuild @@ -190,7 +190,8 @@ def run_msbuild(tools: ToolsLocation, sln: str, msbuild_args: Optional[List[str] } ) - return subprocess.call(args, env=msbuild_env) + # We want to control cwd when running msbuild, because that's where the search for global.json begins. + return subprocess.call(args, env=msbuild_env, cwd=chdir_to) def build_godot_api(msbuild_tool, module_dir, output_dir, push_nupkgs_local, precision): @@ -218,11 +219,7 @@ def build_godot_api(msbuild_tool, module_dir, output_dir, push_nupkgs_local, pre args += ["/p:GodotFloat64=true"] sln = os.path.join(module_dir, "glue/GodotSharp/GodotSharp.sln") - exit_code = run_msbuild( - msbuild_tool, - sln=sln, - msbuild_args=args, - ) + exit_code = run_msbuild(msbuild_tool, sln=sln, chdir_to=module_dir, msbuild_args=args) if exit_code != 0: return exit_code @@ -361,7 +358,7 @@ def build_all(msbuild_tool, module_dir, output_dir, godot_platform, dev_debug, p args += ["/p:ClearNuGetLocalCache=true", "/p:PushNuGetToLocalSource=" + push_nupkgs_local] if precision == "double": args += ["/p:GodotFloat64=true"] - exit_code = run_msbuild(msbuild_tool, sln=sln, msbuild_args=args) + exit_code = run_msbuild(msbuild_tool, sln=sln, chdir_to=module_dir, msbuild_args=args) if exit_code != 0: return exit_code @@ -372,7 +369,7 @@ def build_all(msbuild_tool, module_dir, output_dir, godot_platform, dev_debug, p if precision == "double": args += ["/p:GodotFloat64=true"] sln = os.path.join(module_dir, "editor/Godot.NET.Sdk/Godot.NET.Sdk.sln") - exit_code = run_msbuild(msbuild_tool, sln=sln, msbuild_args=args) + exit_code = run_msbuild(msbuild_tool, sln=sln, chdir_to=module_dir, msbuild_args=args) if exit_code != 0: return exit_code diff --git a/modules/mono/global.json b/modules/mono/global.json new file mode 100644 index 0000000000..8eb62bed1e --- /dev/null +++ b/modules/mono/global.json @@ -0,0 +1,6 @@ +{ + "sdk": { + "version": "8.0.0", + "rollForward": "latestMajor" + } +} diff --git a/modules/navigation/2d/nav_mesh_generator_2d.cpp b/modules/navigation/2d/nav_mesh_generator_2d.cpp index 13399b858e..15a645816c 100644 --- a/modules/navigation/2d/nav_mesh_generator_2d.cpp +++ b/modules/navigation/2d/nav_mesh_generator_2d.cpp @@ -284,7 +284,7 @@ void NavMeshGenerator2D::generator_parse_meshinstance2d_node(const Ref<Navigatio using namespace Clipper2Lib; - Paths64 subject_paths, dummy_clip_paths; + PathsD subject_paths, dummy_clip_paths; for (int i = 0; i < mesh->get_surface_count(); i++) { if (mesh->surface_get_primitive_type(i) != Mesh::PRIMITIVE_TRIANGLES) { @@ -295,7 +295,7 @@ void NavMeshGenerator2D::generator_parse_meshinstance2d_node(const Ref<Navigatio continue; } - Path64 subject_path; + PathD subject_path; int index_count = 0; if (mesh->surface_get_format(i) & Mesh::ARRAY_FORMAT_INDEX) { @@ -314,19 +314,19 @@ void NavMeshGenerator2D::generator_parse_meshinstance2d_node(const Ref<Navigatio Vector<int> mesh_indices = a[Mesh::ARRAY_INDEX]; for (int vertex_index : mesh_indices) { const Vector2 &vertex = mesh_vertices[vertex_index]; - const Point64 &point = Point64(vertex.x, vertex.y); + const PointD &point = PointD(vertex.x, vertex.y); subject_path.push_back(point); } } else { for (const Vector2 &vertex : mesh_vertices) { - const Point64 &point = Point64(vertex.x, vertex.y); + const PointD &point = PointD(vertex.x, vertex.y); subject_path.push_back(point); } } subject_paths.push_back(subject_path); } - Paths64 path_solution; + PathsD path_solution; path_solution = Union(subject_paths, dummy_clip_paths, FillRule::NonZero); @@ -334,9 +334,9 @@ void NavMeshGenerator2D::generator_parse_meshinstance2d_node(const Ref<Navigatio Vector<Vector<Vector2>> polypaths; - for (const Path64 &scaled_path : path_solution) { + for (const PathD &scaled_path : path_solution) { Vector<Vector2> shape_outline; - for (const Point64 &scaled_point : scaled_path) { + for (const PointD &scaled_point : scaled_path) { shape_outline.push_back(Point2(static_cast<real_t>(scaled_point.x), static_cast<real_t>(scaled_point.y))); } @@ -372,7 +372,7 @@ void NavMeshGenerator2D::generator_parse_multimeshinstance2d_node(const Ref<Navi using namespace Clipper2Lib; - Paths64 mesh_subject_paths, dummy_clip_paths; + PathsD mesh_subject_paths, dummy_clip_paths; for (int i = 0; i < mesh->get_surface_count(); i++) { if (mesh->surface_get_primitive_type(i) != Mesh::PRIMITIVE_TRIANGLES) { @@ -383,7 +383,7 @@ void NavMeshGenerator2D::generator_parse_multimeshinstance2d_node(const Ref<Navi continue; } - Path64 subject_path; + PathD subject_path; int index_count = 0; if (mesh->surface_get_format(i) & Mesh::ARRAY_FORMAT_INDEX) { @@ -402,19 +402,19 @@ void NavMeshGenerator2D::generator_parse_multimeshinstance2d_node(const Ref<Navi Vector<int> mesh_indices = a[Mesh::ARRAY_INDEX]; for (int vertex_index : mesh_indices) { const Vector2 &vertex = mesh_vertices[vertex_index]; - const Point64 &point = Point64(vertex.x, vertex.y); + const PointD &point = PointD(vertex.x, vertex.y); subject_path.push_back(point); } } else { for (const Vector2 &vertex : mesh_vertices) { - const Point64 &point = Point64(vertex.x, vertex.y); + const PointD &point = PointD(vertex.x, vertex.y); subject_path.push_back(point); } } mesh_subject_paths.push_back(subject_path); } - Paths64 mesh_path_solution = Union(mesh_subject_paths, dummy_clip_paths, FillRule::NonZero); + PathsD mesh_path_solution = Union(mesh_subject_paths, dummy_clip_paths, FillRule::NonZero); //path_solution = RamerDouglasPeucker(path_solution, 0.025); @@ -428,10 +428,10 @@ void NavMeshGenerator2D::generator_parse_multimeshinstance2d_node(const Ref<Navi for (int i = 0; i < multimesh_instance_count; i++) { const Transform2D multimesh_instance_mesh_instance_xform = multimesh_instance_xform * multimesh->get_instance_transform_2d(i); - for (const Path64 &mesh_path : mesh_path_solution) { + for (const PathD &mesh_path : mesh_path_solution) { Vector<Vector2> shape_outline; - for (const Point64 &mesh_path_point : mesh_path) { + for (const PointD &mesh_path_point : mesh_path) { shape_outline.push_back(Point2(static_cast<real_t>(mesh_path_point.x), static_cast<real_t>(mesh_path_point.y))); } @@ -793,12 +793,12 @@ void NavMeshGenerator2D::generator_parse_source_geometry_data(Ref<NavigationPoly } }; -static void generator_recursive_process_polytree_items(List<TPPLPoly> &p_tppl_in_polygon, const Clipper2Lib::PolyPath64 *p_polypath_item) { +static void generator_recursive_process_polytree_items(List<TPPLPoly> &p_tppl_in_polygon, const Clipper2Lib::PolyPathD *p_polypath_item) { using namespace Clipper2Lib; Vector<Vector2> polygon_vertices; - for (const Point64 &polypath_point : p_polypath_item->Polygon()) { + for (const PointD &polypath_point : p_polypath_item->Polygon()) { polygon_vertices.push_back(Vector2(static_cast<real_t>(polypath_point.x), static_cast<real_t>(polypath_point.y))); } @@ -817,7 +817,7 @@ static void generator_recursive_process_polytree_items(List<TPPLPoly> &p_tppl_in p_tppl_in_polygon.push_back(tp); for (size_t i = 0; i < p_polypath_item->Count(); i++) { - const PolyPath64 *polypath_item = p_polypath_item->Child(i); + const PolyPathD *polypath_item = p_polypath_item->Child(i); generator_recursive_process_polytree_items(p_tppl_in_polygon, polypath_item); } } @@ -892,38 +892,38 @@ void NavMeshGenerator2D::generator_bake_from_source_geometry_data(Ref<Navigation using namespace Clipper2Lib; - Paths64 traversable_polygon_paths; - Paths64 obstruction_polygon_paths; + PathsD traversable_polygon_paths; + PathsD obstruction_polygon_paths; traversable_polygon_paths.reserve(outline_count + traversable_outlines.size()); obstruction_polygon_paths.reserve(obstruction_outlines.size()); for (int i = 0; i < outline_count; i++) { const Vector<Vector2> &traversable_outline = p_navigation_mesh->get_outline(i); - Path64 subject_path; + PathD subject_path; subject_path.reserve(traversable_outline.size()); for (const Vector2 &traversable_point : traversable_outline) { - const Point64 &point = Point64(traversable_point.x, traversable_point.y); + const PointD &point = PointD(traversable_point.x, traversable_point.y); subject_path.push_back(point); } traversable_polygon_paths.push_back(subject_path); } for (const Vector<Vector2> &traversable_outline : traversable_outlines) { - Path64 subject_path; + PathD subject_path; subject_path.reserve(traversable_outline.size()); for (const Vector2 &traversable_point : traversable_outline) { - const Point64 &point = Point64(traversable_point.x, traversable_point.y); + const PointD &point = PointD(traversable_point.x, traversable_point.y); subject_path.push_back(point); } traversable_polygon_paths.push_back(subject_path); } for (const Vector<Vector2> &obstruction_outline : obstruction_outlines) { - Path64 clip_path; + PathD clip_path; clip_path.reserve(obstruction_outline.size()); for (const Vector2 &obstruction_point : obstruction_outline) { - const Point64 &point = Point64(obstruction_point.x, obstruction_point.y); + const PointD &point = PointD(obstruction_point.x, obstruction_point.y); clip_path.push_back(point); } obstruction_polygon_paths.push_back(clip_path); @@ -940,10 +940,10 @@ void NavMeshGenerator2D::generator_bake_from_source_geometry_data(Ref<Navigation continue; } - Path64 clip_path; + PathD clip_path; clip_path.reserve(projected_obstruction.vertices.size() / 2); for (int i = 0; i < projected_obstruction.vertices.size() / 2; i++) { - const Point64 &point = Point64(projected_obstruction.vertices[i * 2], projected_obstruction.vertices[i * 2 + 1]); + const PointD &point = PointD(projected_obstruction.vertices[i * 2], projected_obstruction.vertices[i * 2 + 1]); clip_path.push_back(point); } if (!IsPositive(clip_path)) { @@ -962,17 +962,16 @@ void NavMeshGenerator2D::generator_bake_from_source_geometry_data(Ref<Navigation const int rect_end_x = baking_rect.position[0] + baking_rect.size[0] + baking_rect_offset.x; const int rect_end_y = baking_rect.position[1] + baking_rect.size[1] + baking_rect_offset.y; - Rect64 clipper_rect = Rect64(rect_begin_x, rect_begin_y, rect_end_x, rect_end_y); - RectClip64 rect_clip = RectClip64(clipper_rect); + RectD clipper_rect = RectD(rect_begin_x, rect_begin_y, rect_end_x, rect_end_y); - traversable_polygon_paths = rect_clip.Execute(traversable_polygon_paths); - obstruction_polygon_paths = rect_clip.Execute(obstruction_polygon_paths); + traversable_polygon_paths = RectClip(clipper_rect, traversable_polygon_paths); + obstruction_polygon_paths = RectClip(clipper_rect, obstruction_polygon_paths); } - Paths64 path_solution; + PathsD path_solution; // first merge all traversable polygons according to user specified fill rule - Paths64 dummy_clip_path; + PathsD dummy_clip_path; traversable_polygon_paths = Union(traversable_polygon_paths, dummy_clip_path, FillRule::NonZero); // merge all obstruction polygons, don't allow holes for what is considered "solid" 2D geometry obstruction_polygon_paths = Union(obstruction_polygon_paths, dummy_clip_path, FillRule::NonZero); @@ -994,10 +993,10 @@ void NavMeshGenerator2D::generator_bake_from_source_geometry_data(Ref<Navigation continue; } - Path64 clip_path; + PathD clip_path; clip_path.reserve(projected_obstruction.vertices.size() / 2); for (int i = 0; i < projected_obstruction.vertices.size() / 2; i++) { - const Point64 &point = Point64(projected_obstruction.vertices[i * 2], projected_obstruction.vertices[i * 2 + 1]); + const PointD &point = PointD(projected_obstruction.vertices[i * 2], projected_obstruction.vertices[i * 2 + 1]); clip_path.push_back(point); } if (!IsPositive(clip_path)) { @@ -1021,17 +1020,16 @@ void NavMeshGenerator2D::generator_bake_from_source_geometry_data(Ref<Navigation const int rect_end_x = baking_rect.position[0] + baking_rect.size[0] + baking_rect_offset.x - border_size; const int rect_end_y = baking_rect.position[1] + baking_rect.size[1] + baking_rect_offset.y - border_size; - Rect64 clipper_rect = Rect64(rect_begin_x, rect_begin_y, rect_end_x, rect_end_y); - RectClip64 rect_clip = RectClip64(clipper_rect); + RectD clipper_rect = RectD(rect_begin_x, rect_begin_y, rect_end_x, rect_end_y); - path_solution = rect_clip.Execute(path_solution); + path_solution = RectClip(clipper_rect, path_solution); } Vector<Vector<Vector2>> new_baked_outlines; - for (const Path64 &scaled_path : path_solution) { + for (const PathD &scaled_path : path_solution) { Vector<Vector2> polypath; - for (const Point64 &scaled_point : scaled_path) { + for (const PointD &scaled_point : scaled_path) { polypath.push_back(Vector2(static_cast<real_t>(scaled_point.x), static_cast<real_t>(scaled_point.y))); } new_baked_outlines.push_back(polypath); @@ -1043,13 +1041,13 @@ void NavMeshGenerator2D::generator_bake_from_source_geometry_data(Ref<Navigation return; } - Paths64 polygon_paths; + PathsD polygon_paths; polygon_paths.reserve(new_baked_outlines.size()); for (const Vector<Vector2> &baked_outline : new_baked_outlines) { - Path64 polygon_path; + PathD polygon_path; for (const Vector2 &baked_outline_point : baked_outline) { - const Point64 &point = Point64(baked_outline_point.x, baked_outline_point.y); + const PointD &point = PointD(baked_outline_point.x, baked_outline_point.y); polygon_path.push_back(point); } polygon_paths.push_back(polygon_path); @@ -1059,14 +1057,14 @@ void NavMeshGenerator2D::generator_bake_from_source_geometry_data(Ref<Navigation List<TPPLPoly> tppl_in_polygon, tppl_out_polygon; - PolyTree64 polytree; - Clipper64 clipper_64; + PolyTreeD polytree; + ClipperD clipper_D; - clipper_64.AddSubject(polygon_paths); - clipper_64.Execute(clipper_cliptype, FillRule::NonZero, polytree); + clipper_D.AddSubject(polygon_paths); + clipper_D.Execute(clipper_cliptype, FillRule::NonZero, polytree); for (size_t i = 0; i < polytree.Count(); i++) { - const PolyPath64 *polypath_item = polytree[i]; + const PolyPathD *polypath_item = polytree[i]; generator_recursive_process_polytree_items(tppl_in_polygon, polypath_item); } diff --git a/modules/openxr/extensions/openxr_hand_tracking_extension.cpp b/modules/openxr/extensions/openxr_hand_tracking_extension.cpp index f8cc3d1d8c..12fa3bed7e 100644 --- a/modules/openxr/extensions/openxr_hand_tracking_extension.cpp +++ b/modules/openxr/extensions/openxr_hand_tracking_extension.cpp @@ -195,7 +195,7 @@ void OpenXRHandTrackingExtension::on_process() { Ref<XRHandTracker> godot_tracker; godot_tracker.instantiate(); - godot_tracker->set_hand(i == 0 ? XRHandTracker::HAND_LEFT : XRHandTracker::HAND_RIGHT); + godot_tracker->set_tracker_hand(i == 0 ? XRPositionalTracker::TRACKER_HAND_LEFT : XRPositionalTracker::TRACKER_HAND_RIGHT); godot_tracker->set_tracker_name(i == 0 ? "/user/hand_tracker/left" : "/user/hand_tracker/right"); XRServer::get_singleton()->add_tracker(godot_tracker); hand_trackers[i].godot_tracker = godot_tracker; diff --git a/modules/webxr/webxr_interface_js.cpp b/modules/webxr/webxr_interface_js.cpp index 535d464d6f..45c1d8ec06 100644 --- a/modules/webxr/webxr_interface_js.cpp +++ b/modules/webxr/webxr_interface_js.cpp @@ -713,7 +713,7 @@ void WebXRInterfaceJS::_update_input_source(int p_input_source_id) { if (unlikely(hand_tracker.is_null())) { hand_tracker.instantiate(); - hand_tracker->set_hand(p_input_source_id == 0 ? XRHandTracker::HAND_LEFT : XRHandTracker::HAND_RIGHT); + hand_tracker->set_tracker_hand(p_input_source_id == 0 ? XRPositionalTracker::TRACKER_HAND_LEFT : XRPositionalTracker::TRACKER_HAND_RIGHT); hand_tracker->set_tracker_name(p_input_source_id == 0 ? "/user/hand_tracker/left" : "/user/hand_tracker/right"); // These flags always apply, since WebXR doesn't give us enough insight to be more fine grained. diff --git a/platform/linuxbsd/freedesktop_portal_desktop.cpp b/platform/linuxbsd/freedesktop_portal_desktop.cpp index 214725832f..e65404a531 100644 --- a/platform/linuxbsd/freedesktop_portal_desktop.cpp +++ b/platform/linuxbsd/freedesktop_portal_desktop.cpp @@ -496,24 +496,30 @@ Error FreeDesktopPortalDesktop::file_dialog_show(DisplayServer::WindowID p_windo return OK; } -void FreeDesktopPortalDesktop::_file_dialog_callback(const Callable &p_callable, const Variant &p_status, const Variant &p_list, const Variant &p_index, const Variant &p_options, bool p_opt_in_cb) { - if (p_opt_in_cb) { - Variant ret; - Callable::CallError ce; - const Variant *args[4] = { &p_status, &p_list, &p_index, &p_options }; - - p_callable.callp(args, 4, ret, ce); - if (ce.error != Callable::CallError::CALL_OK) { - ERR_PRINT(vformat("Failed to execute file dialogs callback: %s.", Variant::get_callable_error_text(p_callable, args, 4, ce))); - } - } else { - Variant ret; - Callable::CallError ce; - const Variant *args[3] = { &p_status, &p_list, &p_index }; +void FreeDesktopPortalDesktop::process_file_dialog_callbacks() { + MutexLock lock(file_dialog_mutex); + while (!pending_cbs.is_empty()) { + FileDialogCallback cb = pending_cbs.front()->get(); + pending_cbs.pop_front(); + + if (cb.opt_in_cb) { + Variant ret; + Callable::CallError ce; + const Variant *args[4] = { &cb.status, &cb.files, &cb.index, &cb.options }; + + cb.callback.callp(args, 4, ret, ce); + if (ce.error != Callable::CallError::CALL_OK) { + ERR_PRINT(vformat("Failed to execute file dialog callback: %s.", Variant::get_callable_error_text(cb.callback, args, 4, ce))); + } + } else { + Variant ret; + Callable::CallError ce; + const Variant *args[3] = { &cb.status, &cb.files, &cb.index }; - p_callable.callp(args, 3, ret, ce); - if (ce.error != Callable::CallError::CALL_OK) { - ERR_PRINT(vformat("Failed to execute file dialogs callback: %s.", Variant::get_callable_error_text(p_callable, args, 3, ce))); + cb.callback.callp(args, 3, ret, ce); + if (ce.error != Callable::CallError::CALL_OK) { + ERR_PRINT(vformat("Failed to execute file dialog callback: %s.", Variant::get_callable_error_text(cb.callback, args, 3, ce))); + } } } } @@ -556,7 +562,14 @@ void FreeDesktopPortalDesktop::_thread_monitor(void *p_ud) { file_chooser_parse_response(&iter, fd.filter_names, cancel, uris, index, options); if (fd.callback.is_valid()) { - callable_mp(portal, &FreeDesktopPortalDesktop::_file_dialog_callback).call_deferred(fd.callback, !cancel, uris, index, options, fd.opt_in_cb); + FileDialogCallback cb; + cb.callback = fd.callback; + cb.status = !cancel; + cb.files = uris; + cb.index = index; + cb.options = options; + cb.opt_in_cb = fd.opt_in_cb; + portal->pending_cbs.push_back(cb); } if (fd.prev_focus != DisplayServer::INVALID_WINDOW_ID) { callable_mp(DisplayServer::get_singleton(), &DisplayServer::window_move_to_foreground).call_deferred(fd.prev_focus); diff --git a/platform/linuxbsd/freedesktop_portal_desktop.h b/platform/linuxbsd/freedesktop_portal_desktop.h index ba9c352e62..96c38de2c2 100644 --- a/platform/linuxbsd/freedesktop_portal_desktop.h +++ b/platform/linuxbsd/freedesktop_portal_desktop.h @@ -56,8 +56,6 @@ private: static void append_dbus_dict_bool(DBusMessageIter *p_iter, const String &p_key, bool p_value); static bool file_chooser_parse_response(DBusMessageIter *p_iter, const Vector<String> &p_names, bool &r_cancel, Vector<String> &r_urls, int &r_index, Dictionary &r_options); - void _file_dialog_callback(const Callable &p_callable, const Variant &p_status, const Variant &p_list, const Variant &p_index, const Variant &p_options, bool p_opt_in_cb); - struct FileDialogData { Vector<String> filter_names; DisplayServer::WindowID prev_focus = DisplayServer::INVALID_WINDOW_ID; @@ -67,6 +65,16 @@ private: bool opt_in_cb = false; }; + struct FileDialogCallback { + Callable callback; + Variant status; + Variant files; + Variant index; + Variant options; + bool opt_in_cb = false; + }; + List<FileDialogCallback> pending_cbs; + Mutex file_dialog_mutex; Vector<FileDialogData> file_dialogs; Thread monitor_thread; @@ -86,6 +94,7 @@ public: bool is_supported() { return !unsupported; } Error file_dialog_show(DisplayServer::WindowID p_window_id, const String &p_xid, const String &p_title, const String &p_current_directory, const String &p_root, const String &p_filename, DisplayServer::FileDialogMode p_mode, const Vector<String> &p_filters, const TypedArray<Dictionary> &p_options, const Callable &p_callback, bool p_options_in_cb); + void process_file_dialog_callbacks(); // Retrieve the system's preferred color scheme. // 0: No preference or unknown. diff --git a/platform/linuxbsd/wayland/display_server_wayland.cpp b/platform/linuxbsd/wayland/display_server_wayland.cpp index a815db1c05..da70dae4ff 100644 --- a/platform/linuxbsd/wayland/display_server_wayland.cpp +++ b/platform/linuxbsd/wayland/display_server_wayland.cpp @@ -1161,6 +1161,12 @@ void DisplayServerWayland::process_events() { } } +#ifdef DBUS_ENABLED + if (portal_desktop) { + portal_desktop->process_file_dialog_callbacks(); + } +#endif + wayland_thread.mutex.unlock(); Input::get_singleton()->flush_buffered_events(); diff --git a/platform/linuxbsd/x11/display_server_x11.cpp b/platform/linuxbsd/x11/display_server_x11.cpp index 4c7dfbb107..0041b4c7f3 100644 --- a/platform/linuxbsd/x11/display_server_x11.cpp +++ b/platform/linuxbsd/x11/display_server_x11.cpp @@ -5097,6 +5097,12 @@ void DisplayServerX11::process_events() { */ } +#ifdef DBUS_ENABLED + if (portal_desktop) { + portal_desktop->process_file_dialog_callbacks(); + } +#endif + _THREAD_SAFE_UNLOCK_ Input::get_singleton()->flush_buffered_events(); diff --git a/scene/2d/camera_2d.cpp b/scene/2d/camera_2d.cpp index 4b866fc6de..822f1b58fd 100644 --- a/scene/2d/camera_2d.cpp +++ b/scene/2d/camera_2d.cpp @@ -877,7 +877,7 @@ void Camera2D::_bind_methods() { ClassDB::bind_method(D_METHOD("is_margin_drawing_enabled"), &Camera2D::is_margin_drawing_enabled); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "offset", PROPERTY_HINT_NONE, "suffix:px"), "set_offset", "get_offset"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "anchor_mode", PROPERTY_HINT_ENUM, "Fixed TopLeft,Drag Center"), "set_anchor_mode", "get_anchor_mode"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "anchor_mode", PROPERTY_HINT_ENUM, "Fixed Top Left,Drag Center"), "set_anchor_mode", "get_anchor_mode"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ignore_rotation"), "set_ignore_rotation", "is_ignoring_rotation"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "is_enabled"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "zoom", PROPERTY_HINT_LINK), "set_zoom", "get_zoom"); diff --git a/scene/2d/skeleton_2d.cpp b/scene/2d/skeleton_2d.cpp index 69e0414855..fe21c7f21b 100644 --- a/scene/2d/skeleton_2d.cpp +++ b/scene/2d/skeleton_2d.cpp @@ -635,36 +635,47 @@ Bone2D *Skeleton2D::get_bone(int p_idx) { } void Skeleton2D::_notification(int p_what) { - if (p_what == NOTIFICATION_READY) { - if (bone_setup_dirty) { - _update_bone_setup(); - } - if (transform_dirty) { - _update_transform(); - } - request_ready(); - } + switch (p_what) { + case NOTIFICATION_READY: { + if (bone_setup_dirty) { + _update_bone_setup(); + } + if (transform_dirty) { + _update_transform(); + } + request_ready(); + } break; - if (p_what == NOTIFICATION_TRANSFORM_CHANGED) { - RS::get_singleton()->skeleton_set_base_transform_2d(skeleton, get_global_transform()); - } else if (p_what == NOTIFICATION_INTERNAL_PROCESS) { - if (modification_stack.is_valid()) { - execute_modifications(get_process_delta_time(), SkeletonModificationStack2D::EXECUTION_MODE::execution_mode_process); - } - } else if (p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) { - if (modification_stack.is_valid()) { - execute_modifications(get_physics_process_delta_time(), SkeletonModificationStack2D::EXECUTION_MODE::execution_mode_physics_process); - } - } -#ifdef TOOLS_ENABLED - else if (p_what == NOTIFICATION_DRAW) { - if (Engine::get_singleton()->is_editor_hint()) { + case NOTIFICATION_TRANSFORM_CHANGED: { + RS::get_singleton()->skeleton_set_base_transform_2d(skeleton, get_global_transform()); + } break; + + case NOTIFICATION_INTERNAL_PROCESS: { if (modification_stack.is_valid()) { - modification_stack->draw_editor_gizmos(); + execute_modifications(get_process_delta_time(), SkeletonModificationStack2D::EXECUTION_MODE::execution_mode_process); } - } - } + } break; + + case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { + if (modification_stack.is_valid()) { + execute_modifications(get_physics_process_delta_time(), SkeletonModificationStack2D::EXECUTION_MODE::execution_mode_physics_process); + } + } break; + + case NOTIFICATION_POST_ENTER_TREE: { + set_modification_stack(modification_stack); + } break; + +#ifdef TOOLS_ENABLED + case NOTIFICATION_DRAW: { + if (Engine::get_singleton()->is_editor_hint()) { + if (modification_stack.is_valid()) { + modification_stack->draw_editor_gizmos(); + } + } + } break; #endif // TOOLS_ENABLED + } } RID Skeleton2D::get_skeleton() const { @@ -692,7 +703,7 @@ void Skeleton2D::set_modification_stack(Ref<SkeletonModificationStack2D> p_stack set_physics_process_internal(false); } modification_stack = p_stack; - if (modification_stack.is_valid()) { + if (modification_stack.is_valid() && is_inside_tree()) { modification_stack->set_skeleton(this); modification_stack->setup(); diff --git a/scene/3d/physics/character_body_3d.cpp b/scene/3d/physics/character_body_3d.cpp index b13c279234..dda3ea9cca 100644 --- a/scene/3d/physics/character_body_3d.cpp +++ b/scene/3d/physics/character_body_3d.cpp @@ -232,7 +232,7 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo } else { // Travel is too high to be safely canceled, we take it into account. result.travel = result.travel.slide(up_direction); - motion = motion.normalized() * result.travel.length(); + motion = result.remainder; } set_global_transform(gt); // Determines if you are on the ground, and limits the possibility of climbing on the walls because of the approximations. diff --git a/scene/3d/xr_body_modifier_3d.cpp b/scene/3d/xr_body_modifier_3d.cpp index 8aec3e856e..cf73882a7b 100644 --- a/scene/3d/xr_body_modifier_3d.cpp +++ b/scene/3d/xr_body_modifier_3d.cpp @@ -312,7 +312,7 @@ void XRBodyModifier3D::_process_modification() { } } -void XRBodyModifier3D::_tracker_changed(const StringName &p_tracker_name, const Ref<XRBodyTracker> &p_tracker) { +void XRBodyModifier3D::_tracker_changed(const StringName &p_tracker_name, XRServer::TrackerType p_tracker_type) { if (tracker_name == p_tracker_name) { _get_joint_data(); } @@ -327,18 +327,18 @@ void XRBodyModifier3D::_notification(int p_what) { case NOTIFICATION_ENTER_TREE: { XRServer *xr_server = XRServer::get_singleton(); if (xr_server) { - xr_server->connect("body_tracker_added", callable_mp(this, &XRBodyModifier3D::_tracker_changed)); - 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>())); + xr_server->connect("tracker_added", callable_mp(this, &XRBodyModifier3D::_tracker_changed)); + xr_server->connect("tracker_updated", callable_mp(this, &XRBodyModifier3D::_tracker_changed)); + xr_server->connect("tracker_removed", callable_mp(this, &XRBodyModifier3D::_tracker_changed)); } _get_joint_data(); } break; case NOTIFICATION_EXIT_TREE: { XRServer *xr_server = XRServer::get_singleton(); if (xr_server) { - xr_server->disconnect("body_tracker_added", callable_mp(this, &XRBodyModifier3D::_tracker_changed)); - 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>())); + xr_server->disconnect("tracker_added", callable_mp(this, &XRBodyModifier3D::_tracker_changed)); + xr_server->disconnect("tracker_updated", callable_mp(this, &XRBodyModifier3D::_tracker_changed)); + xr_server->disconnect("tracker_removed", callable_mp(this, &XRBodyModifier3D::_tracker_changed)); } for (int i = 0; i < XRBodyTracker::JOINT_MAX; i++) { joints[i].bone = -1; diff --git a/scene/3d/xr_body_modifier_3d.h b/scene/3d/xr_body_modifier_3d.h index 9ff0cd7207..78d70146ee 100644 --- a/scene/3d/xr_body_modifier_3d.h +++ b/scene/3d/xr_body_modifier_3d.h @@ -86,7 +86,7 @@ private: JointData joints[XRBodyTracker::JOINT_MAX]; void _get_joint_data(); - void _tracker_changed(const StringName &p_tracker_name, const Ref<XRBodyTracker> &p_tracker); + void _tracker_changed(const StringName &p_tracker_name, XRServer::TrackerType p_tracker_type); }; VARIANT_BITFIELD_CAST(XRBodyModifier3D::BodyUpdate) diff --git a/scene/gui/graph_node.cpp b/scene/gui/graph_node.cpp index 1b960a9b62..d035515b51 100644 --- a/scene/gui/graph_node.cpp +++ b/scene/gui/graph_node.cpp @@ -336,7 +336,8 @@ void GraphNode::_notification(int p_what) { int width = get_size().width - sb_panel->get_minimum_size().x; - if (get_child_count() > 0) { + // Take the HboxContainer child into account. + if (get_child_count(false) > 0) { int slot_index = 0; for (const KeyValue<int, Slot> &E : slot_table) { if (E.key < 0 || E.key >= slot_y_cache.size()) { @@ -647,6 +648,7 @@ void GraphNode::_port_pos_update() { left_port_cache.clear(); right_port_cache.clear(); int vertical_ofs = titlebar_hbox->get_size().height + sb_titlebar->get_minimum_size().height + sb_panel->get_margin(SIDE_TOP); + int slot_index = 0; for (int i = 0; i < get_child_count(false); i++) { Control *child = Object::cast_to<Control>(get_child(i, false)); @@ -656,27 +658,28 @@ void GraphNode::_port_pos_update() { Size2i size = child->get_rect().size; - if (slot_table.has(i)) { - if (slot_table[i].enable_left) { + if (slot_table.has(slot_index)) { + if (slot_table[slot_index].enable_left) { PortCache port_cache; port_cache.pos = Point2i(edgeofs, vertical_ofs + size.height / 2); - port_cache.type = slot_table[i].type_left; - port_cache.color = slot_table[i].color_left; - port_cache.slot_index = child->get_index(false); + port_cache.type = slot_table[slot_index].type_left; + port_cache.color = slot_table[slot_index].color_left; + port_cache.slot_index = slot_index; left_port_cache.push_back(port_cache); } - if (slot_table[i].enable_right) { + if (slot_table[slot_index].enable_right) { PortCache port_cache; port_cache.pos = Point2i(get_size().width - edgeofs, vertical_ofs + size.height / 2); - port_cache.type = slot_table[i].type_right; - port_cache.color = slot_table[i].color_right; - port_cache.slot_index = child->get_index(false); + port_cache.type = slot_table[slot_index].type_right; + port_cache.color = slot_table[slot_index].color_right; + port_cache.slot_index = slot_index; right_port_cache.push_back(port_cache); } } vertical_ofs += separation; vertical_ofs += size.height; + slot_index++; } port_pos_dirty = false; diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp index 260956a775..9b991972be 100644 --- a/scene/gui/popup_menu.cpp +++ b/scene/gui/popup_menu.cpp @@ -276,6 +276,7 @@ Size2 PopupMenu::_get_contents_minimum_size() const { } } + minsize.height = Math::ceil(minsize.height); // Ensures enough height at fractional content scales to prevent the v_scroll_bar from showing. return minsize; } @@ -2832,9 +2833,9 @@ void PopupMenu::popup(const Rect2i &p_bounds) { if (!is_embedded()) { float win_scale = get_parent_visible_window()->get_content_scale_factor(); set_content_scale_factor(win_scale); - Size2 minsize = get_contents_minimum_size(); - minsize.height += 0.5 * win_scale; // Ensures enough height at fractional content scales to prevent the v_scroll_bar from showing. - set_min_size(minsize * win_scale); + Size2 minsize = get_contents_minimum_size() * win_scale; + minsize.height = Math::ceil(minsize.height); // Ensures enough height at fractional content scales to prevent the v_scroll_bar from showing. + set_min_size(minsize); // `height` is truncated here by the cast to Size2i for Window.min_size. set_size(Vector2(0, 0)); // Shrinkwraps to min size. } Popup::popup(p_bounds); diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp index 05f44891f6..e2feb59a8c 100644 --- a/scene/gui/tab_container.cpp +++ b/scene/gui/tab_container.cpp @@ -794,6 +794,22 @@ Ref<Texture2D> TabContainer::get_tab_icon(int p_tab) const { return tab_bar->get_tab_icon(p_tab); } +void TabContainer::set_tab_icon_max_width(int p_tab, int p_width) { + if (tab_bar->get_tab_icon_max_width(p_tab) == p_width) { + return; + } + + tab_bar->set_tab_icon_max_width(p_tab, p_width); + + _update_margins(); + _repaint(); + queue_redraw(); +} + +int TabContainer::get_tab_icon_max_width(int p_tab) const { + return tab_bar->get_tab_icon_max_width(p_tab); +} + void TabContainer::set_tab_disabled(int p_tab, bool p_disabled) { if (tab_bar->is_tab_disabled(p_tab) == p_disabled) { return; @@ -990,6 +1006,8 @@ void TabContainer::_bind_methods() { ClassDB::bind_method(D_METHOD("get_tab_tooltip", "tab_idx"), &TabContainer::get_tab_tooltip); ClassDB::bind_method(D_METHOD("set_tab_icon", "tab_idx", "icon"), &TabContainer::set_tab_icon); ClassDB::bind_method(D_METHOD("get_tab_icon", "tab_idx"), &TabContainer::get_tab_icon); + ClassDB::bind_method(D_METHOD("set_tab_icon_max_width", "tab_idx", "width"), &TabContainer::set_tab_icon_max_width); + ClassDB::bind_method(D_METHOD("get_tab_icon_max_width", "tab_idx"), &TabContainer::get_tab_icon_max_width); ClassDB::bind_method(D_METHOD("set_tab_disabled", "tab_idx", "disabled"), &TabContainer::set_tab_disabled); ClassDB::bind_method(D_METHOD("is_tab_disabled", "tab_idx"), &TabContainer::is_tab_disabled); ClassDB::bind_method(D_METHOD("set_tab_hidden", "tab_idx", "hidden"), &TabContainer::set_tab_hidden); diff --git a/scene/gui/tab_container.h b/scene/gui/tab_container.h index c11d9824e7..e00bc780d4 100644 --- a/scene/gui/tab_container.h +++ b/scene/gui/tab_container.h @@ -161,6 +161,9 @@ public: void set_tab_icon(int p_tab, const Ref<Texture2D> &p_icon); Ref<Texture2D> get_tab_icon(int p_tab) const; + void set_tab_icon_max_width(int p_tab, int p_width); + int get_tab_icon_max_width(int p_tab) const; + void set_tab_disabled(int p_tab, bool p_disabled); bool is_tab_disabled(int p_tab) const; diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 0bb77a92f2..39fba72e09 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -752,16 +752,15 @@ void TextEdit::_notification(int p_what) { // Draw the minimap. // Add visual feedback when dragging or hovering the visible area rectangle. - float viewport_alpha; + Color viewport_color = theme_cache.caret_color; if (dragging_minimap) { - viewport_alpha = 0.25; + viewport_color.a = 0.25; } else if (hovering_minimap) { - viewport_alpha = 0.175; + viewport_color.a = 0.175; } else { - viewport_alpha = 0.1; + viewport_color.a = 0.1; } - const Color viewport_color = (theme_cache.background_color.get_v() < 0.5) ? Color(1, 1, 1, viewport_alpha) : Color(0, 0, 0, viewport_alpha); if (rtl) { RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(size.width - (xmargin_end + 2) - minimap_width, viewport_offset_y, minimap_width, viewport_height), viewport_color); } else { diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index 982473deee..b17d345f1f 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -379,8 +379,8 @@ void TreeItem::set_text_overrun_behavior(int p_column, TextServer::OverrunBehavi cells.write[p_column].text_buf->set_text_overrun_behavior(p_behavior); cells.write[p_column].dirty = true; - _changed_notify(p_column); cells.write[p_column].cached_minimum_size_dirty = true; + _changed_notify(p_column); } TextServer::OverrunBehavior TreeItem::get_text_overrun_behavior(int p_column) const { @@ -1512,7 +1512,9 @@ Size2 TreeItem::get_minimum_size(int p_column) { const TreeItem::Cell &cell = cells[p_column]; if (cell.cached_minimum_size_dirty) { - Size2 size; + Size2 size = Size2( + parent_tree->theme_cache.inner_item_margin_left + parent_tree->theme_cache.inner_item_margin_right, + parent_tree->theme_cache.inner_item_margin_top + parent_tree->theme_cache.inner_item_margin_bottom); // Text. if (!cell.text.is_empty()) { @@ -1520,7 +1522,9 @@ Size2 TreeItem::get_minimum_size(int p_column) { parent_tree->update_item_cell(this, p_column); } Size2 text_size = cell.text_buf->get_size(); - size.width += text_size.width; + if (get_text_overrun_behavior(p_column) == TextServer::OVERRUN_NO_TRIMMING) { + size.width += text_size.width; + } size.height = MAX(size.height, text_size.height); } @@ -1539,13 +1543,10 @@ Size2 TreeItem::get_minimum_size(int p_column) { Ref<Texture2D> texture = cell.buttons[i].texture; if (texture.is_valid()) { Size2 button_size = texture->get_size() + parent_tree->theme_cache.button_pressed->get_minimum_size(); - size.width += button_size.width; + size.width += button_size.width + parent_tree->theme_cache.button_margin; size.height = MAX(size.height, button_size.height); } } - if (cell.buttons.size() >= 2) { - size.width += (cell.buttons.size() - 1) * parent_tree->theme_cache.button_margin; - } cells.write[p_column].cached_minimum_size = size; cells.write[p_column].cached_minimum_size_dirty = false; @@ -4394,17 +4395,23 @@ void Tree::_update_all() { } Size2 Tree::get_minimum_size() const { - if (h_scroll_enabled && v_scroll_enabled) { - return Size2(); - } else { - Vector2 min_size = get_internal_min_size(); - Ref<StyleBox> bg = theme_cache.panel_style; - if (bg.is_valid()) { - min_size.x += bg->get_margin(SIDE_LEFT) + bg->get_margin(SIDE_RIGHT); - min_size.y += bg->get_margin(SIDE_TOP) + bg->get_margin(SIDE_BOTTOM); - } - return Vector2(h_scroll_enabled ? 0 : min_size.x, v_scroll_enabled ? 0 : min_size.y); + Vector2 min_size = Vector2(0, _get_title_button_height()); + + if (theme_cache.panel_style.is_valid()) { + min_size += theme_cache.panel_style->get_minimum_size(); + } + + Vector2 content_min_size = get_internal_min_size(); + if (h_scroll_enabled) { + content_min_size.x = 0; + min_size.y += h_scroll->get_combined_minimum_size().height; + } + if (v_scroll_enabled) { + min_size.x += v_scroll->get_combined_minimum_size().width; + content_min_size.y = 0; } + + return min_size + content_min_size; } TreeItem *Tree::create_item(TreeItem *p_parent, int p_index) { @@ -4582,6 +4589,7 @@ void Tree::set_hide_root(bool p_enabled) { hide_root = p_enabled; queue_redraw(); + update_minimum_size(); } bool Tree::is_root_hidden() const { @@ -4730,31 +4738,33 @@ int Tree::get_column_minimum_width(int p_column) const { min_width = MAX(theme_cache.font->get_string_size(columns[p_column].xl_title, HORIZONTAL_ALIGNMENT_LEFT, -1, theme_cache.font_size).width + padding, min_width); } - if (!columns[p_column].clip_content) { - int depth = 0; - TreeItem *next; - for (TreeItem *item = get_root(); item; item = next) { - next = item->get_next_visible(); - // Compute the depth in tree. - if (next && p_column == 0) { - if (next->get_parent() == item) { - depth += 1; - } else { - TreeItem *common_parent = item->get_parent(); - while (common_parent != next->get_parent() && common_parent) { - common_parent = common_parent->get_parent(); - depth -= 1; + if (root && !columns[p_column].clip_content) { + int depth = 1; + + TreeItem *last = nullptr; + TreeItem *first = hide_root ? root->get_next_visible() : root; + for (TreeItem *item = first; item; last = item, item = item->get_next_visible()) { + // Get column indentation. + int indent; + if (p_column == 0) { + if (last) { + if (item->parent == last) { + depth += 1; + } else if (item->parent != last->parent) { + depth = hide_root ? 0 : 1; + for (TreeItem *iter = item->parent; iter; iter = iter->parent) { + depth += 1; + } } } + indent = theme_cache.item_margin * depth; + } else { + indent = theme_cache.h_separation; } // Get the item minimum size. Size2 item_size = item->get_minimum_size(p_column); - if (p_column == 0) { - item_size.width += theme_cache.item_margin * depth; - } else { - item_size.width += theme_cache.h_separation; - } + item_size.width += indent; // Check if the item is wider. min_width = MAX(min_width, item_size.width); @@ -4968,6 +4978,7 @@ void Tree::set_column_titles_visible(bool p_show) { show_column_titles = p_show; queue_redraw(); + update_minimum_size(); } bool Tree::are_column_titles_visible() const { diff --git a/scene/resources/2d/skeleton/skeleton_modification_2d.cpp b/scene/resources/2d/skeleton/skeleton_modification_2d.cpp index 4e7563fdc3..5450f544c3 100644 --- a/scene/resources/2d/skeleton/skeleton_modification_2d.cpp +++ b/scene/resources/2d/skeleton/skeleton_modification_2d.cpp @@ -233,6 +233,11 @@ void SkeletonModification2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "execution_mode", PROPERTY_HINT_ENUM, "process,physics_process"), "set_execution_mode", "get_execution_mode"); } +void SkeletonModification2D::reset_state() { + stack = nullptr; + is_setup = false; +} + SkeletonModification2D::SkeletonModification2D() { stack = nullptr; is_setup = false; diff --git a/scene/resources/2d/skeleton/skeleton_modification_2d.h b/scene/resources/2d/skeleton/skeleton_modification_2d.h index 413b860a99..6a6f1bb39b 100644 --- a/scene/resources/2d/skeleton/skeleton_modification_2d.h +++ b/scene/resources/2d/skeleton/skeleton_modification_2d.h @@ -57,6 +57,8 @@ protected: bool _print_execution_error(bool p_condition, String p_message); + virtual void reset_state() override; + GDVIRTUAL1(_execute, double) GDVIRTUAL1(_setup_modification, Ref<SkeletonModificationStack2D>) GDVIRTUAL0(_draw_editor_gizmo) diff --git a/scene/resources/2d/skeleton/skeleton_modification_2d_ccdik.cpp b/scene/resources/2d/skeleton/skeleton_modification_2d_ccdik.cpp index 1ad8d0eccc..051c4eabc0 100644 --- a/scene/resources/2d/skeleton/skeleton_modification_2d_ccdik.cpp +++ b/scene/resources/2d/skeleton/skeleton_modification_2d_ccdik.cpp @@ -266,7 +266,9 @@ void SkeletonModification2DCCDIK::_draw_editor_gizmo() { void SkeletonModification2DCCDIK::update_target_cache() { if (!is_setup || !stack) { - ERR_PRINT_ONCE("Cannot update target cache: modification is not properly setup!"); + if (is_setup) { + ERR_PRINT_ONCE("Cannot update target cache: modification is not properly setup!"); + } return; } @@ -287,7 +289,9 @@ void SkeletonModification2DCCDIK::update_target_cache() { void SkeletonModification2DCCDIK::update_tip_cache() { if (!is_setup || !stack) { - ERR_PRINT_ONCE("Cannot update tip cache: modification is not properly setup!"); + if (is_setup) { + ERR_PRINT_ONCE("Cannot update tip cache: modification is not properly setup!"); + } return; } @@ -309,7 +313,9 @@ void SkeletonModification2DCCDIK::update_tip_cache() { void SkeletonModification2DCCDIK::ccdik_joint_update_bone2d_cache(int p_joint_idx) { ERR_FAIL_INDEX_MSG(p_joint_idx, ccdik_data_chain.size(), "Cannot update bone2d cache: joint index out of range!"); if (!is_setup || !stack) { - ERR_PRINT_ONCE("Cannot update CCDIK Bone2D cache: modification is not properly setup!"); + if (is_setup) { + ERR_PRINT_ONCE("Cannot update CCDIK Bone2D cache: modification is not properly setup!"); + } return; } @@ -390,7 +396,6 @@ void SkeletonModification2DCCDIK::set_ccdik_joint_bone_index(int p_joint_idx, in ccdik_data_chain.write[p_joint_idx].bone_idx = p_bone_idx; } } else { - WARN_PRINT("Cannot verify the CCDIK joint " + itos(p_joint_idx) + " bone index for this modification..."); ccdik_data_chain.write[p_joint_idx].bone_idx = p_bone_idx; } diff --git a/scene/resources/2d/skeleton/skeleton_modification_2d_fabrik.cpp b/scene/resources/2d/skeleton/skeleton_modification_2d_fabrik.cpp index dd1c4a91d5..16a6166878 100644 --- a/scene/resources/2d/skeleton/skeleton_modification_2d_fabrik.cpp +++ b/scene/resources/2d/skeleton/skeleton_modification_2d_fabrik.cpp @@ -289,13 +289,21 @@ void SkeletonModification2DFABRIK::_setup_modification(SkeletonModificationStack if (stack != nullptr) { is_setup = true; + + if (stack->skeleton) { + for (int i = 0; i < fabrik_data_chain.size(); i++) { + fabrik_joint_update_bone2d_cache(i); + } + } update_target_cache(); } } void SkeletonModification2DFABRIK::update_target_cache() { if (!is_setup || !stack) { - ERR_PRINT_ONCE("Cannot update target cache: modification is not properly setup!"); + if (is_setup) { + ERR_PRINT_ONCE("Cannot update target cache: modification is not properly setup!"); + } return; } @@ -317,7 +325,9 @@ void SkeletonModification2DFABRIK::update_target_cache() { void SkeletonModification2DFABRIK::fabrik_joint_update_bone2d_cache(int p_joint_idx) { ERR_FAIL_INDEX_MSG(p_joint_idx, fabrik_data_chain.size(), "Cannot update bone2d cache: joint index out of range!"); if (!is_setup || !stack) { - ERR_PRINT_ONCE("Cannot update FABRIK Bone2D cache: modification is not properly setup!"); + if (is_setup) { + ERR_PRINT_ONCE("Cannot update FABRIK Bone2D cache: modification is not properly setup!"); + } return; } @@ -389,7 +399,6 @@ void SkeletonModification2DFABRIK::set_fabrik_joint_bone_index(int p_joint_idx, fabrik_data_chain.write[p_joint_idx].bone_idx = p_bone_idx; } } else { - WARN_PRINT("Cannot verify the FABRIK joint " + itos(p_joint_idx) + " bone index for this modification..."); fabrik_data_chain.write[p_joint_idx].bone_idx = p_bone_idx; } diff --git a/scene/resources/2d/skeleton/skeleton_modification_2d_jiggle.cpp b/scene/resources/2d/skeleton/skeleton_modification_2d_jiggle.cpp index 2ace9577e4..b7200b49c4 100644 --- a/scene/resources/2d/skeleton/skeleton_modification_2d_jiggle.cpp +++ b/scene/resources/2d/skeleton/skeleton_modification_2d_jiggle.cpp @@ -254,6 +254,8 @@ void SkeletonModification2DJiggle::_setup_modification(SkeletonModificationStack Bone2D *bone2d_node = stack->skeleton->get_bone(bone_idx); jiggle_data_chain.write[i].dynamic_position = bone2d_node->get_global_position(); } + + jiggle_joint_update_bone2d_cache(i); } } @@ -263,7 +265,9 @@ void SkeletonModification2DJiggle::_setup_modification(SkeletonModificationStack void SkeletonModification2DJiggle::update_target_cache() { if (!is_setup || !stack) { - ERR_PRINT_ONCE("Cannot update target cache: modification is not properly setup!"); + if (is_setup) { + ERR_PRINT_ONCE("Cannot update target cache: modification is not properly setup!"); + } return; } @@ -285,7 +289,9 @@ void SkeletonModification2DJiggle::update_target_cache() { void SkeletonModification2DJiggle::jiggle_joint_update_bone2d_cache(int p_joint_idx) { ERR_FAIL_INDEX_MSG(p_joint_idx, jiggle_data_chain.size(), "Cannot update bone2d cache: joint index out of range!"); if (!is_setup || !stack) { - ERR_PRINT_ONCE("Cannot update Jiggle " + itos(p_joint_idx) + " Bone2D cache: modification is not properly setup!"); + if (is_setup) { + ERR_PRINT_ONCE("Cannot update Jiggle " + itos(p_joint_idx) + " Bone2D cache: modification is not properly setup!"); + } return; } @@ -425,7 +431,6 @@ void SkeletonModification2DJiggle::set_jiggle_joint_bone_index(int p_joint_idx, jiggle_data_chain.write[p_joint_idx].bone_idx = p_bone_idx; } } else { - WARN_PRINT("Cannot verify the Jiggle joint " + itos(p_joint_idx) + " bone index for this modification..."); jiggle_data_chain.write[p_joint_idx].bone_idx = p_bone_idx; } diff --git a/scene/resources/2d/skeleton/skeleton_modification_2d_lookat.cpp b/scene/resources/2d/skeleton/skeleton_modification_2d_lookat.cpp index 8f6f6bc4ae..cd4ca8e090 100644 --- a/scene/resources/2d/skeleton/skeleton_modification_2d_lookat.cpp +++ b/scene/resources/2d/skeleton/skeleton_modification_2d_lookat.cpp @@ -200,7 +200,9 @@ void SkeletonModification2DLookAt::_draw_editor_gizmo() { void SkeletonModification2DLookAt::update_bone2d_cache() { if (!is_setup || !stack) { - ERR_PRINT_ONCE("Cannot update Bone2D cache: modification is not properly setup!"); + if (is_setup) { + ERR_PRINT_ONCE("Cannot update Bone2D cache: modification is not properly setup!"); + } return; } @@ -256,7 +258,6 @@ void SkeletonModification2DLookAt::set_bone_index(int p_bone_idx) { bone_idx = p_bone_idx; } } else { - WARN_PRINT("Cannot verify the bone index for this modification..."); bone_idx = p_bone_idx; } @@ -265,7 +266,9 @@ void SkeletonModification2DLookAt::set_bone_index(int p_bone_idx) { void SkeletonModification2DLookAt::update_target_cache() { if (!is_setup || !stack) { - ERR_PRINT_ONCE("Cannot update target cache: modification is not properly setup!"); + if (is_setup) { + ERR_PRINT_ONCE("Cannot update target cache: modification is not properly setup!"); + } return; } diff --git a/scene/resources/2d/skeleton/skeleton_modification_2d_physicalbones.cpp b/scene/resources/2d/skeleton/skeleton_modification_2d_physicalbones.cpp index 61e5aed150..aa8d7d0b3b 100644 --- a/scene/resources/2d/skeleton/skeleton_modification_2d_physicalbones.cpp +++ b/scene/resources/2d/skeleton/skeleton_modification_2d_physicalbones.cpp @@ -153,7 +153,7 @@ void SkeletonModification2DPhysicalBones::_setup_modification(SkeletonModificati void SkeletonModification2DPhysicalBones::_physical_bone_update_cache(int p_joint_idx) { ERR_FAIL_INDEX_MSG(p_joint_idx, physical_bone_chain.size(), "Cannot update PhysicalBone2D cache: joint index out of range!"); if (!is_setup || !stack) { - if (!stack) { + if (is_setup) { ERR_PRINT_ONCE("Cannot update PhysicalBone2D cache: modification is not properly setup!"); } return; diff --git a/scene/resources/2d/skeleton/skeleton_modification_2d_twoboneik.cpp b/scene/resources/2d/skeleton/skeleton_modification_2d_twoboneik.cpp index c3366d5c36..41e4ea828e 100644 --- a/scene/resources/2d/skeleton/skeleton_modification_2d_twoboneik.cpp +++ b/scene/resources/2d/skeleton/skeleton_modification_2d_twoboneik.cpp @@ -250,7 +250,9 @@ void SkeletonModification2DTwoBoneIK::_draw_editor_gizmo() { void SkeletonModification2DTwoBoneIK::update_target_cache() { if (!is_setup || !stack) { - ERR_PRINT_ONCE("Cannot update target cache: modification is not properly setup!"); + if (is_setup) { + ERR_PRINT_ONCE("Cannot update target cache: modification is not properly setup!"); + } return; } @@ -271,7 +273,9 @@ void SkeletonModification2DTwoBoneIK::update_target_cache() { void SkeletonModification2DTwoBoneIK::update_joint_one_bone2d_cache() { if (!is_setup || !stack) { - ERR_PRINT_ONCE("Cannot update joint one Bone2D cache: modification is not properly setup!"); + if (is_setup) { + ERR_PRINT_ONCE("Cannot update joint one Bone2D cache: modification is not properly setup!"); + } return; } @@ -299,7 +303,9 @@ void SkeletonModification2DTwoBoneIK::update_joint_one_bone2d_cache() { void SkeletonModification2DTwoBoneIK::update_joint_two_bone2d_cache() { if (!is_setup || !stack) { - ERR_PRINT_ONCE("Cannot update joint two Bone2D cache: modification is not properly setup!"); + if (is_setup) { + ERR_PRINT_ONCE("Cannot update joint two Bone2D cache: modification is not properly setup!"); + } return; } @@ -400,7 +406,6 @@ void SkeletonModification2DTwoBoneIK::set_joint_one_bone_idx(int p_bone_idx) { joint_one_bone_idx = p_bone_idx; } } else { - WARN_PRINT("TwoBoneIK: Cannot verify the joint bone index for joint one..."); joint_one_bone_idx = p_bone_idx; } @@ -425,7 +430,6 @@ void SkeletonModification2DTwoBoneIK::set_joint_two_bone_idx(int p_bone_idx) { joint_two_bone_idx = p_bone_idx; } } else { - WARN_PRINT("TwoBoneIK: Cannot verify the joint bone index for joint two..."); joint_two_bone_idx = p_bone_idx; } diff --git a/scene/resources/animation.h b/scene/resources/animation.h index c72327e464..6005172c11 100644 --- a/scene/resources/animation.h +++ b/scene/resources/animation.h @@ -266,7 +266,7 @@ private: _FORCE_INLINE_ void _track_get_key_indices_in_range(const Vector<T> &p_array, double from_time, double to_time, List<int> *p_indices, bool p_is_backward) const; double length = 1.0; - real_t step = 0.1; + real_t step = 1.0 / 30; LoopMode loop_mode = LOOP_NONE; void _track_update_hash(int p_track); diff --git a/scene/resources/particle_process_material.cpp b/scene/resources/particle_process_material.cpp index 30b90841e3..685625ab72 100644 --- a/scene/resources/particle_process_material.cpp +++ b/scene/resources/particle_process_material.cpp @@ -741,26 +741,33 @@ void ParticleProcessMaterial::_update_shader() { code += "vec3 get_random_direction_from_spread(inout uint alt_seed, float spread_angle){\n"; code += " float pi = 3.14159;\n"; code += " float degree_to_rad = pi / 180.0;\n"; - code += " vec3 velocity = vec3(0.);\n"; code += " float spread_rad = spread_angle * degree_to_rad;\n"; - code += " float angle1_rad = rand_from_seed_m1_p1(alt_seed) * spread_rad;\n"; - code += " float angle2_rad = rand_from_seed_m1_p1(alt_seed) * spread_rad * (1.0 - flatness);\n"; - code += " vec3 direction_xz = vec3(sin(angle1_rad), 0.0, cos(angle1_rad));\n"; - code += " vec3 direction_yz = vec3(0.0, sin(angle2_rad), cos(angle2_rad));\n"; - code += " direction_yz.z = direction_yz.z / max(0.0001,sqrt(abs(direction_yz.z))); // better uniform distribution\n"; - code += " vec3 spread_direction = vec3(direction_xz.x * direction_yz.z, direction_yz.y, direction_xz.z * direction_yz.z);\n"; - code += " vec3 direction_nrm = length(direction) > 0.0 ? normalize(direction) : vec3(0.0, 0.0, 1.0);\n"; - code += " // rotate spread to direction\n"; - code += " vec3 binormal = cross(vec3(0.0, 1.0, 0.0), direction_nrm);\n"; - code += " if (length(binormal) < 0.0001) {\n"; - code += " // direction is parallel to Y. Choose Z as the binormal.\n"; - code += " binormal = vec3(0.0, 0.0, 1.0);\n"; - code += " }\n"; - code += " binormal = normalize(binormal);\n"; - code += " vec3 normal = cross(binormal, direction_nrm);\n"; - code += " spread_direction = binormal * spread_direction.x + normal * spread_direction.y + direction_nrm * spread_direction.z;\n"; - code += " return spread_direction;\n"; - + if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) { + // Spread calculation for 2D. + code += " float angle1_rad = rand_from_seed_m1_p1(alt_seed) * spread_rad;\n"; + code += " angle1_rad += direction.x != 0.0 ? atan(direction.y, direction.x) : sign(direction.y) * (pi / 2.0);\n"; + code += " vec3 spread_direction = vec3(cos(angle1_rad), sin(angle1_rad), 0.0);\n"; + code += " return spread_direction;\n"; + } else { + // Spread calculation for 3D. + code += " float angle1_rad = rand_from_seed_m1_p1(alt_seed) * spread_rad;\n"; + code += " float angle2_rad = rand_from_seed_m1_p1(alt_seed) * spread_rad * (1.0 - flatness);\n"; + code += " vec3 direction_xz = vec3(sin(angle1_rad), 0.0, cos(angle1_rad));\n"; + code += " vec3 direction_yz = vec3(0.0, sin(angle2_rad), cos(angle2_rad));\n"; + code += " direction_yz.z = direction_yz.z / max(0.0001,sqrt(abs(direction_yz.z))); // better uniform distribution\n"; + code += " vec3 spread_direction = vec3(direction_xz.x * direction_yz.z, direction_yz.y, direction_xz.z * direction_yz.z);\n"; + code += " vec3 direction_nrm = length(direction) > 0.0 ? normalize(direction) : vec3(0.0, 0.0, 1.0);\n"; + code += " // rotate spread to direction\n"; + code += " vec3 binormal = cross(vec3(0.0, 1.0, 0.0), direction_nrm);\n"; + code += " if (length(binormal) < 0.0001) {\n"; + code += " // direction is parallel to Y. Choose Z as the binormal.\n"; + code += " binormal = vec3(0.0, 0.0, 1.0);\n"; + code += " }\n"; + code += " binormal = normalize(binormal);\n"; + code += " vec3 normal = cross(binormal, direction_nrm);\n"; + code += " spread_direction = binormal * spread_direction.x + normal * spread_direction.y + direction_nrm * spread_direction.z;\n"; + code += " return normalize(spread_direction);\n"; + } code += "}\n"; code += "vec3 process_radial_displacement(DynamicsParameters param, float lifetime, inout uint alt_seed, mat4 transform, mat4 emission_transform, float delta){\n"; diff --git a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp index 209fabeddf..8d865ba440 100644 --- a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp +++ b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp @@ -624,6 +624,7 @@ void SceneShaderForwardClustered::init(const String p_defines) { actions.renames["CUSTOM2"] = "custom2_attrib"; actions.renames["CUSTOM3"] = "custom3_attrib"; actions.renames["OUTPUT_IS_SRGB"] = "SHADER_IS_SRGB"; + actions.renames["LIGHT_VERTEX"] = "light_vertex"; actions.renames["NODE_POSITION_WORLD"] = "read_model_matrix[3].xyz"; actions.renames["CAMERA_POSITION_WORLD"] = "scene_data.inv_view_matrix[3].xyz"; @@ -670,6 +671,7 @@ void SceneShaderForwardClustered::init(const String p_defines) { actions.usage_defines["COLOR"] = "#define COLOR_USED\n"; actions.usage_defines["INSTANCE_CUSTOM"] = "#define ENABLE_INSTANCE_CUSTOM\n"; actions.usage_defines["POSITION"] = "#define OVERRIDE_POSITION\n"; + actions.usage_defines["LIGHT_VERTEX"] = "#define LIGHT_VERTEX_USED\n"; actions.usage_defines["ALPHA_SCISSOR_THRESHOLD"] = "#define ALPHA_SCISSOR_USED\n"; actions.usage_defines["ALPHA_HASH_SCALE"] = "#define ALPHA_HASH_USED\n"; diff --git a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp index a2f112669c..0810f567cb 100644 --- a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp +++ b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp @@ -528,6 +528,7 @@ void SceneShaderForwardMobile::init(const String p_defines) { actions.renames["CUSTOM2"] = "custom2_attrib"; actions.renames["CUSTOM3"] = "custom3_attrib"; actions.renames["OUTPUT_IS_SRGB"] = "SHADER_IS_SRGB"; + actions.renames["LIGHT_VERTEX"] = "light_vertex"; actions.renames["NODE_POSITION_WORLD"] = "read_model_matrix[3].xyz"; actions.renames["CAMERA_POSITION_WORLD"] = "scene_data.inv_view_matrix[3].xyz"; @@ -574,6 +575,7 @@ void SceneShaderForwardMobile::init(const String p_defines) { actions.usage_defines["COLOR"] = "#define COLOR_USED\n"; actions.usage_defines["INSTANCE_CUSTOM"] = "#define ENABLE_INSTANCE_CUSTOM\n"; actions.usage_defines["POSITION"] = "#define OVERRIDE_POSITION\n"; + actions.usage_defines["LIGHT_VERTEX"] = "#define LIGHT_VERTEX\n"; actions.usage_defines["ALPHA_SCISSOR_THRESHOLD"] = "#define ALPHA_SCISSOR_USED\n"; actions.usage_defines["ALPHA_HASH_SCALE"] = "#define ALPHA_HASH_USED\n"; diff --git a/servers/rendering/renderer_rd/shaders/canvas.glsl b/servers/rendering/renderer_rd/shaders/canvas.glsl index 235c772e2d..dbff09c301 100644 --- a/servers/rendering/renderer_rd/shaders/canvas.glsl +++ b/servers/rendering/renderer_rd/shaders/canvas.glsl @@ -665,6 +665,12 @@ void main() { vec2 tex_uv = (vec4(vertex, 0.0, 1.0) * mat4(light_array.data[light_base].texture_matrix[0], light_array.data[light_base].texture_matrix[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy; //multiply inverse given its transposed. Optimizer removes useless operations. vec2 tex_uv_atlas = tex_uv * light_array.data[light_base].atlas_rect.zw + light_array.data[light_base].atlas_rect.xy; + + if (any(lessThan(tex_uv, vec2(0.0, 0.0))) || any(greaterThanEqual(tex_uv, vec2(1.0, 1.0)))) { + //if outside the light texture, light color is zero + continue; + } + vec4 light_color = textureLod(sampler2D(atlas_texture, texture_sampler), tex_uv_atlas, 0.0); vec4 light_base_color = light_array.data[light_base].color; @@ -689,10 +695,6 @@ void main() { light_color.rgb *= base_color.rgb; } #endif - if (any(lessThan(tex_uv, vec2(0.0, 0.0))) || any(greaterThanEqual(tex_uv, vec2(1.0, 1.0)))) { - //if outside the light texture, light color is zero - light_color.a = 0.0; - } if (bool(light_array.data[light_base].flags & LIGHT_FLAGS_HAS_SHADOW)) { vec2 shadow_pos = (vec4(shadow_vertex, 0.0, 1.0) * mat4(light_array.data[light_base].shadow_matrix[0], light_array.data[light_base].shadow_matrix[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy; //multiply inverse given its transposed. Optimizer removes useless operations. diff --git a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl index 359d7799e5..47d273d789 100644 --- a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl +++ b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl @@ -1022,6 +1022,11 @@ void fragment_shader(in SceneData scene_data) { inv_view_matrix[1][3] = 0.0; inv_view_matrix[2][3] = 0.0; #endif + +#ifdef LIGHT_VERTEX_USED + vec3 light_vertex = vertex; +#endif //LIGHT_VERTEX_USED + mat4 read_view_matrix = scene_data.view_matrix; vec2 read_viewport_size = scene_data.viewport_size; { @@ -1032,6 +1037,15 @@ void fragment_shader(in SceneData scene_data) { transmittance_color.a *= sss_strength; #endif +#ifdef LIGHT_VERTEX_USED + vertex = light_vertex; +#ifdef USE_MULTIVIEW + view = -normalize(vertex - eye_offset); +#else + view = -normalize(vertex); +#endif //USE_MULTIVIEW +#endif //LIGHT_VERTEX_USED + #ifndef USE_SHADOW_TO_OPACITY #ifdef ALPHA_SCISSOR_USED @@ -2043,7 +2057,7 @@ void fragment_shader(in SceneData scene_data) { shadow = 1.0; #endif - float size_A = sc_use_light_soft_shadows ? directional_lights.data[i].size : 0.0; + float size_A = sc_use_directional_soft_shadows ? directional_lights.data[i].size : 0.0; light_compute(normal, directional_lights.data[i].direction, normalize(view), size_A, #ifndef DEBUG_DRAW_PSSM_SPLITS diff --git a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl index c26313092b..f0a5141856 100644 --- a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl +++ b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl @@ -830,6 +830,10 @@ void main() { inv_view_matrix[2][3] = 0.0; #endif +#ifdef LIGHT_VERTEX_USED + vec3 light_vertex = vertex; +#endif //LIGHT_VERTEX_USED + mat4 read_view_matrix = scene_data.view_matrix; vec2 read_viewport_size = scene_data.viewport_size; @@ -837,6 +841,15 @@ void main() { #CODE : FRAGMENT } +#ifdef LIGHT_VERTEX_USED + vertex = light_vertex; +#ifdef USE_MULTIVIEW + view = -normalize(vertex - eye_offset); +#else + view = -normalize(vertex); +#endif //USE_MULTIVIEW +#endif //LIGHT_VERTEX_USED + #ifdef LIGHT_TRANSMITTANCE_USED #ifdef SSS_MODE_SKIN transmittance_color.a = sss_strength; diff --git a/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl b/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl index 47e6fe5873..40ca74ae07 100644 --- a/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl +++ b/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl @@ -375,7 +375,7 @@ float sample_directional_soft_shadow(texture2D shadow, vec3 pssm_coord, vec2 tex for (uint i = 0; i < sc_directional_penumbra_shadow_samples; i++) { vec2 suv = pssm_coord.xy + (disk_rotation * scene_data_block.data.directional_penumbra_shadow_kernel[i].xy) * tex_scale; float d = textureLod(sampler2D(shadow, SAMPLER_LINEAR_CLAMP), suv, 0.0).r; - if (d < pssm_coord.z) { + if (d > pssm_coord.z) { blocker_average += d; blocker_count += 1.0; } @@ -384,7 +384,7 @@ float sample_directional_soft_shadow(texture2D shadow, vec3 pssm_coord, vec2 tex if (blocker_count > 0.0) { //blockers found, do soft shadow blocker_average /= blocker_count; - float penumbra = (pssm_coord.z - blocker_average) / blocker_average; + float penumbra = (-pssm_coord.z + blocker_average) / (1.0 - blocker_average); tex_scale *= penumbra; float s = 0.0; @@ -488,7 +488,7 @@ float light_process_omni_shadow(uint idx, vec3 vertex, vec3 normal) { if (blocker_count > 0.0) { //blockers found, do soft shadow blocker_average /= blocker_count; - float penumbra = (z_norm + blocker_average) / blocker_average; + float penumbra = (-z_norm + blocker_average) / (1.0 - blocker_average); tangent *= penumbra; bitangent *= penumbra; @@ -736,7 +736,7 @@ float light_process_spot_shadow(uint idx, vec3 vertex, vec3 normal) { vec2 suv = shadow_uv + (disk_rotation * scene_data_block.data.penumbra_shadow_kernel[i].xy) * uv_size; suv = clamp(suv, spot_lights.data[idx].atlas_rect.xy, clamp_max); float d = textureLod(sampler2D(shadow_atlas, SAMPLER_LINEAR_CLAMP), suv, 0.0).r; - if (d < splane.z) { + if (d > splane.z) { blocker_average += d; blocker_count += 1.0; } @@ -745,7 +745,7 @@ float light_process_spot_shadow(uint idx, vec3 vertex, vec3 normal) { if (blocker_count > 0.0) { //blockers found, do soft shadow blocker_average /= blocker_count; - float penumbra = (z_norm - blocker_average) / blocker_average; + float penumbra = (-z_norm + blocker_average) / (1.0 - blocker_average); uv_size *= penumbra; shadow = 0.0; diff --git a/servers/rendering/shader_types.cpp b/servers/rendering/shader_types.cpp index aff874f452..af51083dc3 100644 --- a/servers/rendering/shader_types.cpp +++ b/servers/rendering/shader_types.cpp @@ -111,6 +111,7 @@ ShaderTypes::ShaderTypes() { shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["EYE_OFFSET"] = constt(ShaderLanguage::TYPE_VEC3); shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["VERTEX"] = constt(ShaderLanguage::TYPE_VEC3); + shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["LIGHT_VERTEX"] = ShaderLanguage::TYPE_VEC3; shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["FRAGCOORD"] = constt(ShaderLanguage::TYPE_VEC4); shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["FRONT_FACING"] = constt(ShaderLanguage::TYPE_BOOL); shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["NORMAL"] = ShaderLanguage::TYPE_VEC3; diff --git a/servers/rendering/storage/environment_storage.cpp b/servers/rendering/storage/environment_storage.cpp index 7b75a12a19..1bbb5da6bb 100644 --- a/servers/rendering/storage/environment_storage.cpp +++ b/servers/rendering/storage/environment_storage.cpp @@ -773,11 +773,7 @@ RS::EnvironmentSDFGIYScale RendererEnvironmentStorage::environment_get_sdfgi_y_s void RendererEnvironmentStorage::environment_set_adjustment(RID p_env, bool p_enable, float p_brightness, float p_contrast, float p_saturation, bool p_use_1d_color_correction, RID p_color_correction) { Environment *env = environment_owner.get_or_null(p_env); ERR_FAIL_NULL(env); -#ifdef DEBUG_ENABLED - if (OS::get_singleton()->get_current_rendering_method() == "gl_compatibility" && p_enable) { - WARN_PRINT_ONCE_ED("Adjustments are not supported when using the GL Compatibility backend yet. Support will be added in a future release."); - } -#endif + env->adjustments_enabled = p_enable; env->adjustments_brightness = p_brightness; env->adjustments_contrast = p_contrast; diff --git a/servers/xr/xr_hand_tracker.cpp b/servers/xr/xr_hand_tracker.cpp index cb0fbfb35f..abfe2e9867 100644 --- a/servers/xr/xr_hand_tracker.cpp +++ b/servers/xr/xr_hand_tracker.cpp @@ -33,9 +33,6 @@ #include "xr_body_tracker.h" void XRHandTracker::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_hand", "hand"), &XRHandTracker::set_hand); - ClassDB::bind_method(D_METHOD("get_hand"), &XRHandTracker::get_hand); - ClassDB::bind_method(D_METHOD("set_has_tracking_data", "has_data"), &XRHandTracker::set_has_tracking_data); ClassDB::bind_method(D_METHOD("get_has_tracking_data"), &XRHandTracker::get_has_tracking_data); @@ -60,10 +57,6 @@ void XRHandTracker::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "has_tracking_data", PROPERTY_HINT_NONE), "set_has_tracking_data", "get_has_tracking_data"); ADD_PROPERTY(PropertyInfo(Variant::INT, "hand_tracking_source", PROPERTY_HINT_ENUM, "Unknown,Unobstructed,Controller"), "set_hand_tracking_source", "get_hand_tracking_source"); - BIND_ENUM_CONSTANT(HAND_LEFT); - BIND_ENUM_CONSTANT(HAND_RIGHT); - BIND_ENUM_CONSTANT(HAND_MAX); - BIND_ENUM_CONSTANT(HAND_TRACKING_SOURCE_UNKNOWN); BIND_ENUM_CONSTANT(HAND_TRACKING_SOURCE_UNOBSTRUCTED); BIND_ENUM_CONSTANT(HAND_TRACKING_SOURCE_CONTROLLER); @@ -110,48 +103,8 @@ void XRHandTracker::set_tracker_type(XRServer::TrackerType p_type) { } void XRHandTracker::set_tracker_hand(const XRPositionalTracker::TrackerHand p_hand) { - ERR_FAIL_INDEX(p_hand, TRACKER_HAND_MAX); - - switch (p_hand) { - case TRACKER_HAND_LEFT: - tracker_hand = TRACKER_HAND_LEFT; - hand = HAND_LEFT; - break; - - case TRACKER_HAND_RIGHT: - tracker_hand = TRACKER_HAND_RIGHT; - hand = HAND_RIGHT; - break; - - case TRACKER_HAND_UNKNOWN: - default: - ERR_FAIL_MSG("XRHandTracker must specify hand"); - break; - } -} - -void XRHandTracker::set_hand(XRHandTracker::Hand p_hand) { - ERR_FAIL_INDEX(p_hand, HAND_MAX); - - switch (p_hand) { - case HAND_LEFT: - tracker_hand = TRACKER_HAND_LEFT; - hand = HAND_LEFT; - break; - - case HAND_RIGHT: - tracker_hand = TRACKER_HAND_RIGHT; - hand = HAND_RIGHT; - break; - - default: - ERR_FAIL_MSG("XRHandTracker must specify hand"); - break; - } -} - -XRHandTracker::Hand XRHandTracker::get_hand() const { - return hand; + ERR_FAIL_COND_MSG(p_hand != TRACKER_HAND_LEFT && p_hand != TRACKER_HAND_RIGHT, "XRHandTracker must specify hand."); + tracker_hand = p_hand; } void XRHandTracker::set_has_tracking_data(bool p_has_tracking_data) { @@ -222,4 +175,5 @@ Vector3 XRHandTracker::get_hand_joint_angular_velocity(XRHandTracker::HandJoint XRHandTracker::XRHandTracker() { type = XRServer::TRACKER_HAND; + tracker_hand = TRACKER_HAND_LEFT; } diff --git a/servers/xr/xr_hand_tracker.h b/servers/xr/xr_hand_tracker.h index 8ef3c229c3..c7c18a31f8 100644 --- a/servers/xr/xr_hand_tracker.h +++ b/servers/xr/xr_hand_tracker.h @@ -38,12 +38,6 @@ class XRHandTracker : public XRPositionalTracker { _THREAD_SAFE_CLASS_ public: - enum Hand { - HAND_LEFT, - HAND_RIGHT, - HAND_MAX, - }; - enum HandTrackingSource { HAND_TRACKING_SOURCE_UNKNOWN, HAND_TRACKING_SOURCE_UNOBSTRUCTED, @@ -93,9 +87,6 @@ public: void set_tracker_type(XRServer::TrackerType p_type) override; void set_tracker_hand(const XRPositionalTracker::TrackerHand p_hand) override; - void set_hand(Hand p_hand); - Hand get_hand() const; - void set_has_tracking_data(bool p_has_tracking_data); bool get_has_tracking_data() const; @@ -123,7 +114,6 @@ protected: static void _bind_methods(); private: - Hand hand = HAND_LEFT; bool has_tracking_data = false; HandTrackingSource hand_tracking_source = HAND_TRACKING_SOURCE_UNKNOWN; @@ -134,7 +124,6 @@ private: Vector3 hand_joint_angular_velocities[HAND_JOINT_MAX]; }; -VARIANT_ENUM_CAST(XRHandTracker::Hand) VARIANT_ENUM_CAST(XRHandTracker::HandTrackingSource) VARIANT_ENUM_CAST(XRHandTracker::HandJoint) VARIANT_BITFIELD_CAST(XRHandTracker::HandJointFlags) diff --git a/tests/scene/test_animation.h b/tests/scene/test_animation.h index 89bf296815..6c89592e0d 100644 --- a/tests/scene/test_animation.h +++ b/tests/scene/test_animation.h @@ -41,7 +41,7 @@ TEST_CASE("[Animation] Empty animation getters") { const Ref<Animation> animation = memnew(Animation); CHECK(animation->get_length() == doctest::Approx(real_t(1.0))); - CHECK(animation->get_step() == doctest::Approx(real_t(0.1))); + CHECK(animation->get_step() == doctest::Approx(real_t(1.0 / 30))); } TEST_CASE("[Animation] Create value track") { diff --git a/tests/scene/test_graph_node.h b/tests/scene/test_graph_node.h new file mode 100644 index 0000000000..72b8b682c9 --- /dev/null +++ b/tests/scene/test_graph_node.h @@ -0,0 +1,59 @@ +/**************************************************************************/ +/* test_graph_node.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef TEST_GRAPH_NODE_H +#define TEST_GRAPH_NODE_H + +#include "scene/gui/graph_node.h" +#include "scene/main/window.h" + +#include "tests/test_macros.h" + +namespace TestGraphNode { + +TEST_CASE("[GraphNode][SceneTree]") { + SUBCASE("[GraphNode] Graph Node only child on delete should not cause error.") { + // Setup. + GraphNode *test_node = memnew(GraphNode); + test_child->set_name("Graph Node"); + Control *test_child = memnew(Control); + test_child->set_name("child"); + test_node->add_child(test_child); + + // Test. + CHECK_NOTHROW_MESSAGE(test_node->remove_child(test_child)); + + memdelete(test_node); + } +} + +} // namespace TestGraphNode + +#endif // TEST_GRAPH_NODE_H diff --git a/tests/scene/test_navigation_region_3d.h b/tests/scene/test_navigation_region_3d.h index 0b20b3a1b2..f3d7f27361 100644 --- a/tests/scene/test_navigation_region_3d.h +++ b/tests/scene/test_navigation_region_3d.h @@ -65,7 +65,9 @@ TEST_SUITE("[Navigation]") { CHECK_EQ(navigation_mesh->get_vertices().size(), 0); SUBCASE("Synchronous bake should have immediate effects") { + ERR_PRINT_OFF; // Suppress warning about baking from visual meshes as source geometry. navigation_region->bake_navigation_mesh(false); + ERR_PRINT_ON; CHECK_FALSE(navigation_region->is_baking()); CHECK_NE(navigation_mesh->get_polygon_count(), 0); CHECK_NE(navigation_mesh->get_vertices().size(), 0); |
