diff options
135 files changed, 1298 insertions, 818 deletions
diff --git a/.github/workflows/macos_builds.yml b/.github/workflows/macos_builds.yml index 4967fe379f..70031ec4c3 100644 --- a/.github/workflows/macos_builds.yml +++ b/.github/workflows/macos_builds.yml @@ -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 260e6bb48a..18511ff5ee 100644 --- a/SConstruct +++ b/SConstruct @@ -13,6 +13,7 @@ import time from types import ModuleType from collections import OrderedDict from importlib.util import spec_from_file_location, module_from_spec +from SCons import __version__ as scons_raw_version # 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 @@ -130,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 @@ -168,7 +170,11 @@ if profile: opts = Variables(customs, ARGUMENTS) # Target build options -opts.Add(["platform", "p"], "Target platform (%s)" % "|".join(platform_list), "") +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), "") + opts.Add("p", "Alias for 'platform'", "") opts.Add(EnumVariable("target", "Compilation target", "editor", ("editor", "template_release", "template_debug"))) opts.Add(EnumVariable("arch", "CPU architecture", "auto", ["auto"] + architectures, architecture_aliases)) opts.Add(BoolVariable("dev_build", "Developer build with dev-only debugging code (DEV_ENABLED)", False)) @@ -286,6 +292,9 @@ if env["import_env_vars"]: selected_platform = env["platform"] +if env.scons_version < (4, 3) and not selected_platform: + selected_platform = env["p"] + if selected_platform == "": # Missing `platform` argument, try to detect platform automatically if ( @@ -972,21 +981,16 @@ if env["vsproj"]: env.vs_incs = [] env.vs_srcs = [] -# CompileDB and Ninja are only available with certain SCons versions which -# not everybody might have yet, so we have to check. -from SCons import __version__ as scons_raw_version - -scons_ver = env._get_major_minor_revision(scons_raw_version) -if env["compiledb"] and scons_ver < (4, 0, 0): +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/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/core/variant/variant.cpp b/core/variant/variant.cpp index 155a5b2781..8e702ce8bb 100644 --- a/core/variant/variant.cpp +++ b/core/variant/variant.cpp @@ -2373,184 +2373,183 @@ Variant::operator IPAddress() const { return IPAddress(operator String()); } -Variant::Variant(bool p_bool) { - type = BOOL; +Variant::Variant(bool p_bool) : + type(BOOL) { _data._bool = p_bool; } -Variant::Variant(int64_t p_int64) { - type = INT; +Variant::Variant(int64_t p_int64) : + type(INT) { _data._int = p_int64; } -Variant::Variant(int32_t p_int32) { - type = INT; +Variant::Variant(int32_t p_int32) : + type(INT) { _data._int = p_int32; } -Variant::Variant(int16_t p_int16) { - type = INT; +Variant::Variant(int16_t p_int16) : + type(INT) { _data._int = p_int16; } -Variant::Variant(int8_t p_int8) { - type = INT; +Variant::Variant(int8_t p_int8) : + type(INT) { _data._int = p_int8; } -Variant::Variant(uint64_t p_uint64) { - type = INT; +Variant::Variant(uint64_t p_uint64) : + type(INT) { _data._int = p_uint64; } -Variant::Variant(uint32_t p_uint32) { - type = INT; +Variant::Variant(uint32_t p_uint32) : + type(INT) { _data._int = p_uint32; } -Variant::Variant(uint16_t p_uint16) { - type = INT; +Variant::Variant(uint16_t p_uint16) : + type(INT) { _data._int = p_uint16; } -Variant::Variant(uint8_t p_uint8) { - type = INT; +Variant::Variant(uint8_t p_uint8) : + type(INT) { _data._int = p_uint8; } -Variant::Variant(float p_float) { - type = FLOAT; +Variant::Variant(float p_float) : + type(FLOAT) { _data._float = p_float; } -Variant::Variant(double p_double) { - type = FLOAT; +Variant::Variant(double p_double) : + type(FLOAT) { _data._float = p_double; } -Variant::Variant(const ObjectID &p_id) { - type = INT; +Variant::Variant(const ObjectID &p_id) : + type(INT) { _data._int = p_id; } -Variant::Variant(const StringName &p_string) { - type = STRING_NAME; +Variant::Variant(const StringName &p_string) : + type(STRING_NAME) { memnew_placement(_data._mem, StringName(p_string)); } -Variant::Variant(const String &p_string) { - type = STRING; +Variant::Variant(const String &p_string) : + type(STRING) { memnew_placement(_data._mem, String(p_string)); } -Variant::Variant(const char *const p_cstring) { - type = STRING; +Variant::Variant(const char *const p_cstring) : + type(STRING) { memnew_placement(_data._mem, String((const char *)p_cstring)); } -Variant::Variant(const char32_t *p_wstring) { - type = STRING; +Variant::Variant(const char32_t *p_wstring) : + type(STRING) { memnew_placement(_data._mem, String(p_wstring)); } -Variant::Variant(const Vector3 &p_vector3) { - type = VECTOR3; +Variant::Variant(const Vector3 &p_vector3) : + type(VECTOR3) { memnew_placement(_data._mem, Vector3(p_vector3)); } -Variant::Variant(const Vector3i &p_vector3i) { - type = VECTOR3I; +Variant::Variant(const Vector3i &p_vector3i) : + type(VECTOR3I) { memnew_placement(_data._mem, Vector3i(p_vector3i)); } -Variant::Variant(const Vector4 &p_vector4) { - type = VECTOR4; +Variant::Variant(const Vector4 &p_vector4) : + type(VECTOR4) { memnew_placement(_data._mem, Vector4(p_vector4)); } -Variant::Variant(const Vector4i &p_vector4i) { - type = VECTOR4I; +Variant::Variant(const Vector4i &p_vector4i) : + type(VECTOR4I) { memnew_placement(_data._mem, Vector4i(p_vector4i)); } -Variant::Variant(const Vector2 &p_vector2) { - type = VECTOR2; +Variant::Variant(const Vector2 &p_vector2) : + type(VECTOR2) { memnew_placement(_data._mem, Vector2(p_vector2)); } -Variant::Variant(const Vector2i &p_vector2i) { - type = VECTOR2I; +Variant::Variant(const Vector2i &p_vector2i) : + type(VECTOR2I) { memnew_placement(_data._mem, Vector2i(p_vector2i)); } -Variant::Variant(const Rect2 &p_rect2) { - type = RECT2; +Variant::Variant(const Rect2 &p_rect2) : + type(RECT2) { memnew_placement(_data._mem, Rect2(p_rect2)); } -Variant::Variant(const Rect2i &p_rect2i) { - type = RECT2I; +Variant::Variant(const Rect2i &p_rect2i) : + type(RECT2I) { memnew_placement(_data._mem, Rect2i(p_rect2i)); } -Variant::Variant(const Plane &p_plane) { - type = PLANE; +Variant::Variant(const Plane &p_plane) : + type(PLANE) { memnew_placement(_data._mem, Plane(p_plane)); } -Variant::Variant(const ::AABB &p_aabb) { - type = AABB; +Variant::Variant(const ::AABB &p_aabb) : + type(AABB) { _data._aabb = (::AABB *)Pools::_bucket_small.alloc(); memnew_placement(_data._aabb, ::AABB(p_aabb)); } -Variant::Variant(const Basis &p_matrix) { - type = BASIS; +Variant::Variant(const Basis &p_matrix) : + type(BASIS) { _data._basis = (Basis *)Pools::_bucket_medium.alloc(); memnew_placement(_data._basis, Basis(p_matrix)); } -Variant::Variant(const Quaternion &p_quaternion) { - type = QUATERNION; +Variant::Variant(const Quaternion &p_quaternion) : + type(QUATERNION) { memnew_placement(_data._mem, Quaternion(p_quaternion)); } -Variant::Variant(const Transform3D &p_transform) { - type = TRANSFORM3D; +Variant::Variant(const Transform3D &p_transform) : + type(TRANSFORM3D) { _data._transform3d = (Transform3D *)Pools::_bucket_medium.alloc(); memnew_placement(_data._transform3d, Transform3D(p_transform)); } -Variant::Variant(const Projection &pp_projection) { - type = PROJECTION; +Variant::Variant(const Projection &pp_projection) : + type(PROJECTION) { _data._projection = (Projection *)Pools::_bucket_large.alloc(); memnew_placement(_data._projection, Projection(pp_projection)); } -Variant::Variant(const Transform2D &p_transform) { - type = TRANSFORM2D; +Variant::Variant(const Transform2D &p_transform) : + type(TRANSFORM2D) { _data._transform2d = (Transform2D *)Pools::_bucket_small.alloc(); memnew_placement(_data._transform2d, Transform2D(p_transform)); } -Variant::Variant(const Color &p_color) { - type = COLOR; +Variant::Variant(const Color &p_color) : + type(COLOR) { memnew_placement(_data._mem, Color(p_color)); } -Variant::Variant(const NodePath &p_node_path) { - type = NODE_PATH; +Variant::Variant(const NodePath &p_node_path) : + type(NODE_PATH) { memnew_placement(_data._mem, NodePath(p_node_path)); } -Variant::Variant(const ::RID &p_rid) { - type = RID; +Variant::Variant(const ::RID &p_rid) : + type(RID) { memnew_placement(_data._mem, ::RID(p_rid)); } -Variant::Variant(const Object *p_object) { - type = OBJECT; - +Variant::Variant(const Object *p_object) : + type(OBJECT) { memnew_placement(_data._mem, ObjData); if (p_object) { @@ -2571,76 +2570,74 @@ Variant::Variant(const Object *p_object) { } } -Variant::Variant(const Callable &p_callable) { - type = CALLABLE; +Variant::Variant(const Callable &p_callable) : + type(CALLABLE) { memnew_placement(_data._mem, Callable(p_callable)); } -Variant::Variant(const Signal &p_callable) { - type = SIGNAL; +Variant::Variant(const Signal &p_callable) : + type(SIGNAL) { memnew_placement(_data._mem, Signal(p_callable)); } -Variant::Variant(const Dictionary &p_dictionary) { - type = DICTIONARY; +Variant::Variant(const Dictionary &p_dictionary) : + type(DICTIONARY) { memnew_placement(_data._mem, Dictionary(p_dictionary)); } -Variant::Variant(const Array &p_array) { - type = ARRAY; +Variant::Variant(const Array &p_array) : + type(ARRAY) { memnew_placement(_data._mem, Array(p_array)); } -Variant::Variant(const PackedByteArray &p_byte_array) { - type = PACKED_BYTE_ARRAY; - +Variant::Variant(const PackedByteArray &p_byte_array) : + type(PACKED_BYTE_ARRAY) { _data.packed_array = PackedArrayRef<uint8_t>::create(p_byte_array); } -Variant::Variant(const PackedInt32Array &p_int32_array) { - type = PACKED_INT32_ARRAY; +Variant::Variant(const PackedInt32Array &p_int32_array) : + type(PACKED_INT32_ARRAY) { _data.packed_array = PackedArrayRef<int32_t>::create(p_int32_array); } -Variant::Variant(const PackedInt64Array &p_int64_array) { - type = PACKED_INT64_ARRAY; +Variant::Variant(const PackedInt64Array &p_int64_array) : + type(PACKED_INT64_ARRAY) { _data.packed_array = PackedArrayRef<int64_t>::create(p_int64_array); } -Variant::Variant(const PackedFloat32Array &p_float32_array) { - type = PACKED_FLOAT32_ARRAY; +Variant::Variant(const PackedFloat32Array &p_float32_array) : + type(PACKED_FLOAT32_ARRAY) { _data.packed_array = PackedArrayRef<float>::create(p_float32_array); } -Variant::Variant(const PackedFloat64Array &p_float64_array) { - type = PACKED_FLOAT64_ARRAY; +Variant::Variant(const PackedFloat64Array &p_float64_array) : + type(PACKED_FLOAT64_ARRAY) { _data.packed_array = PackedArrayRef<double>::create(p_float64_array); } -Variant::Variant(const PackedStringArray &p_string_array) { - type = PACKED_STRING_ARRAY; +Variant::Variant(const PackedStringArray &p_string_array) : + type(PACKED_STRING_ARRAY) { _data.packed_array = PackedArrayRef<String>::create(p_string_array); } -Variant::Variant(const PackedVector2Array &p_vector2_array) { - type = PACKED_VECTOR2_ARRAY; +Variant::Variant(const PackedVector2Array &p_vector2_array) : + type(PACKED_VECTOR2_ARRAY) { _data.packed_array = PackedArrayRef<Vector2>::create(p_vector2_array); } -Variant::Variant(const PackedVector3Array &p_vector3_array) { - type = PACKED_VECTOR3_ARRAY; +Variant::Variant(const PackedVector3Array &p_vector3_array) : + type(PACKED_VECTOR3_ARRAY) { _data.packed_array = PackedArrayRef<Vector3>::create(p_vector3_array); } -Variant::Variant(const PackedColorArray &p_color_array) { - type = PACKED_COLOR_ARRAY; +Variant::Variant(const PackedColorArray &p_color_array) : + type(PACKED_COLOR_ARRAY) { _data.packed_array = PackedArrayRef<Color>::create(p_color_array); } /* helpers */ -Variant::Variant(const Vector<::RID> &p_array) { - type = ARRAY; - +Variant::Variant(const Vector<::RID> &p_array) : + type(ARRAY) { Array *rid_array = memnew_placement(_data._mem, Array); rid_array->resize(p_array.size()); @@ -2650,9 +2647,8 @@ Variant::Variant(const Vector<::RID> &p_array) { } } -Variant::Variant(const Vector<Plane> &p_array) { - type = ARRAY; - +Variant::Variant(const Vector<Plane> &p_array) : + type(ARRAY) { Array *plane_array = memnew_placement(_data._mem, Array); plane_array->resize(p_array.size()); @@ -2662,7 +2658,8 @@ Variant::Variant(const Vector<Plane> &p_array) { } } -Variant::Variant(const Vector<Face3> &p_face_array) { +Variant::Variant(const Vector<Face3> &p_face_array) : + type(NIL) { PackedVector3Array vertices; int face_count = p_face_array.size(); vertices.resize(face_count * 3); @@ -2678,13 +2675,11 @@ Variant::Variant(const Vector<Face3> &p_face_array) { } } - type = NIL; - *this = vertices; } -Variant::Variant(const Vector<Variant> &p_array) { - type = NIL; +Variant::Variant(const Vector<Variant> &p_array) : + type(NIL) { Array arr; arr.resize(p_array.size()); for (int i = 0; i < p_array.size(); i++) { @@ -2693,8 +2688,8 @@ Variant::Variant(const Vector<Variant> &p_array) { *this = arr; } -Variant::Variant(const Vector<StringName> &p_array) { - type = NIL; +Variant::Variant(const Vector<StringName> &p_array) : + type(NIL) { PackedStringArray v; int len = p_array.size(); v.resize(len); @@ -2863,12 +2858,13 @@ void Variant::operator=(const Variant &p_variant) { } } -Variant::Variant(const IPAddress &p_address) { - type = STRING; +Variant::Variant(const IPAddress &p_address) : + type(STRING) { memnew_placement(_data._mem, String(p_address)); } -Variant::Variant(const Variant &p_variant) { +Variant::Variant(const Variant &p_variant) : + type(NIL) { reference(p_variant); } diff --git a/core/variant/variant.h b/core/variant/variant.h index 10f8dc3c7f..e40df3171f 100644 --- a/core/variant/variant.h +++ b/core/variant/variant.h @@ -165,7 +165,7 @@ private: // Variant takes 20 bytes when real_t is float, and 36 if double // it only allocates extra memory for aabb/matrix. - Type type = NIL; + Type type; struct ObjData { ObjectID id; @@ -483,8 +483,8 @@ public: Variant(const IPAddress &p_address); #define VARIANT_ENUM_CLASS_CONSTRUCTOR(m_enum) \ - Variant(m_enum p_value) { \ - type = INT; \ + Variant(m_enum p_value) : \ + type(INT) { \ _data._int = (int64_t)p_value; \ } @@ -788,7 +788,8 @@ public: static void unregister_types(); Variant(const Variant &p_variant); - _FORCE_INLINE_ Variant() {} + _FORCE_INLINE_ Variant() : + type(NIL) {} _FORCE_INLINE_ ~Variant() { clear(); } diff --git a/core/variant/variant_parser.cpp b/core/variant/variant_parser.cpp index 06daaab6a2..50f8007efa 100644 --- a/core/variant/variant_parser.cpp +++ b/core/variant/variant_parser.cpp @@ -1789,7 +1789,7 @@ static String rtos_fix(double p_value) { } } -Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_string_func, void *p_store_string_ud, EncodeResourceFunc p_encode_res_func, void *p_encode_res_ud, int p_recursion_count) { +Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_string_func, void *p_store_string_ud, EncodeResourceFunc p_encode_res_func, void *p_encode_res_ud, int p_recursion_count, bool p_compat) { switch (p_variant.get_type()) { case Variant::NIL: { p_store_string_func(p_store_string_ud, "null"); @@ -2009,7 +2009,7 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str } p_store_string_func(p_store_string_ud, "\"" + E.name + "\":"); - write(obj->get(E.name), p_store_string_func, p_store_string_ud, p_encode_res_func, p_encode_res_ud, p_recursion_count); + write(obj->get(E.name), p_store_string_func, p_store_string_ud, p_encode_res_func, p_encode_res_ud, p_recursion_count, p_compat); } } @@ -2035,9 +2035,9 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str p_store_string_func(p_store_string_ud, "{\n"); for (List<Variant>::Element *E = keys.front(); E; E = E->next()) { - write(E->get(), p_store_string_func, p_store_string_ud, p_encode_res_func, p_encode_res_ud, p_recursion_count); + write(E->get(), p_store_string_func, p_store_string_ud, p_encode_res_func, p_encode_res_ud, p_recursion_count, p_compat); p_store_string_func(p_store_string_ud, ": "); - write(dict[E->get()], p_store_string_func, p_store_string_ud, p_encode_res_func, p_encode_res_ud, p_recursion_count); + write(dict[E->get()], p_store_string_func, p_store_string_ud, p_encode_res_func, p_encode_res_ud, p_recursion_count, p_compat); if (E->next()) { p_store_string_func(p_store_string_ud, ",\n"); } else { @@ -2096,7 +2096,7 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str } else { p_store_string_func(p_store_string_ud, ", "); } - write(var, p_store_string_func, p_store_string_ud, p_encode_res_func, p_encode_res_ud, p_recursion_count); + write(var, p_store_string_func, p_store_string_ud, p_encode_res_func, p_encode_res_ud, p_recursion_count, p_compat); } p_store_string_func(p_store_string_ud, "]"); @@ -2110,7 +2110,16 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str case Variant::PACKED_BYTE_ARRAY: { p_store_string_func(p_store_string_ud, "PackedByteArray("); Vector<uint8_t> data = p_variant; - if (data.size() > 0) { + if (p_compat) { + int len = data.size(); + const uint8_t *ptr = data.ptr(); + for (int i = 0; i < len; i++) { + if (i > 0) { + p_store_string_func(p_store_string_ud, ", "); + } + p_store_string_func(p_store_string_ud, itos(ptr[i])); + } + } else if (data.size() > 0) { p_store_string_func(p_store_string_ud, "\""); p_store_string_func(p_store_string_ud, CryptoCore::b64_encode_str(data.ptr(), data.size())); p_store_string_func(p_store_string_ud, "\""); @@ -2255,8 +2264,8 @@ static Error _write_to_str(void *ud, const String &p_string) { return OK; } -Error VariantWriter::write_to_string(const Variant &p_variant, String &r_string, EncodeResourceFunc p_encode_res_func, void *p_encode_res_ud) { +Error VariantWriter::write_to_string(const Variant &p_variant, String &r_string, EncodeResourceFunc p_encode_res_func, void *p_encode_res_ud, bool p_compat) { r_string = String(); - return write(p_variant, _write_to_str, &r_string, p_encode_res_func, p_encode_res_ud); + return write(p_variant, _write_to_str, &r_string, p_encode_res_func, p_encode_res_ud, 0, p_compat); } diff --git a/core/variant/variant_parser.h b/core/variant/variant_parser.h index 2f8974849f..b0ac07170d 100644 --- a/core/variant/variant_parser.h +++ b/core/variant/variant_parser.h @@ -161,8 +161,8 @@ public: typedef Error (*StoreStringFunc)(void *ud, const String &p_string); typedef String (*EncodeResourceFunc)(void *ud, const Ref<Resource> &p_resource); - static Error write(const Variant &p_variant, StoreStringFunc p_store_string_func, void *p_store_string_ud, EncodeResourceFunc p_encode_res_func, void *p_encode_res_ud, int p_recursion_count = 0); - static Error write_to_string(const Variant &p_variant, String &r_string, EncodeResourceFunc p_encode_res_func = nullptr, void *p_encode_res_ud = nullptr); + static Error write(const Variant &p_variant, StoreStringFunc p_store_string_func, void *p_store_string_ud, EncodeResourceFunc p_encode_res_func, void *p_encode_res_ud, int p_recursion_count = 0, bool p_compat = true); + static Error write_to_string(const Variant &p_variant, String &r_string, EncodeResourceFunc p_encode_res_func = nullptr, void *p_encode_res_ud = nullptr, bool p_compat = true); }; #endif // VARIANT_PARSER_H 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/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/TabBar.xml b/doc/classes/TabBar.xml index a2beef81eb..d080294c1a 100644 --- a/doc/classes/TabBar.xml +++ b/doc/classes/TabBar.xml @@ -111,6 +111,13 @@ Returns the title of the tab at index [param tab_idx]. </description> </method> + <method name="get_tab_tooltip" qualifiers="const"> + <return type="String" /> + <param index="0" name="tab_idx" type="int" /> + <description> + Returns the tooltip text of the tab at index [param tab_idx]. + </description> + </method> <method name="is_tab_disabled" qualifiers="const"> <return type="bool" /> <param index="0" name="tab_idx" type="int" /> @@ -224,6 +231,15 @@ Sets a [param title] for the tab at index [param tab_idx]. </description> </method> + <method name="set_tab_tooltip"> + <return type="void" /> + <param index="0" name="tab_idx" type="int" /> + <param index="1" name="tooltip" type="String" /> + <description> + Sets a [param tooltip] for tab at index [param tab_idx]. + [b]Note:[/b] By default, if the [param tooltip] is empty and the tab text is truncated (not all characters fit into the tab), the title will be displayed as a tooltip. To hide the tooltip, assign [code]" "[/code] as the [param tooltip] text. + </description> + </method> </methods> <members> <member name="clip_tabs" type="bool" setter="set_clip_tabs" getter="get_clip_tabs" default="true"> diff --git a/doc/classes/TabContainer.xml b/doc/classes/TabContainer.xml index b0e3f67791..f4d69c3076 100644 --- a/doc/classes/TabContainer.xml +++ b/doc/classes/TabContainer.xml @@ -92,6 +92,13 @@ Returns the title of the tab at index [param tab_idx]. Tab titles default to the name of the indexed child node, but this can be overridden with [method set_tab_title]. </description> </method> + <method name="get_tab_tooltip" qualifiers="const"> + <return type="String" /> + <param index="0" name="tab_idx" type="int" /> + <description> + Returns the tooltip text of the tab at index [param tab_idx]. + </description> + </method> <method name="is_tab_disabled" qualifiers="const"> <return type="bool" /> <param index="0" name="tab_idx" type="int" /> @@ -173,6 +180,15 @@ Sets a custom title for the tab at index [param tab_idx] (tab titles default to the name of the indexed child node). Set it back to the child's name to make the tab default to it again. </description> </method> + <method name="set_tab_tooltip"> + <return type="void" /> + <param index="0" name="tab_idx" type="int" /> + <param index="1" name="tooltip" type="String" /> + <description> + Sets a custom tooltip text for tab at index [param tab_idx]. + [b]Note:[/b] By default, if the [param tooltip] is empty and the tab text is truncated (not all characters fit into the tab), the title will be displayed as a tooltip. To hide the tooltip, assign [code]" "[/code] as the [param tooltip] text. + </description> + </method> </methods> <members> <member name="all_tabs_in_front" type="bool" setter="set_all_tabs_in_front" getter="is_all_tabs_in_front" default="false"> 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/WorkerThreadPool.xml b/doc/classes/WorkerThreadPool.xml index fa1f08b149..2b1bc136cb 100644 --- a/doc/classes/WorkerThreadPool.xml +++ b/doc/classes/WorkerThreadPool.xml @@ -58,6 +58,7 @@ Adds [param action] as a group task to be executed by the worker threads. The [Callable] will be called a number of times based on [param elements], with the first thread calling it with the value [code]0[/code] as a parameter, and each consecutive execution incrementing this value by 1 until it reaches [code]element - 1[/code]. The number of threads the task is distributed to is defined by [param tasks_needed], where the default value [code]-1[/code] means it is distributed to all worker threads. [param high_priority] determines if the task has a high priority or a low priority (default). You can optionally provide a [param description] to help with debugging. Returns a group task ID that can be used by other methods. + [b]Warning:[/b] Every task must be waited for completion using [method wait_for_task_completion] or [method wait_for_group_task_completion] at some point so that any allocated resources inside the task can be cleaned up. </description> </method> <method name="add_task"> @@ -68,6 +69,7 @@ <description> Adds [param action] as a task to be executed by a worker thread. [param high_priority] determines if the task has a high priority or a low priority (default). You can optionally provide a [param description] to help with debugging. Returns a task ID that can be used by other methods. + [b]Warning:[/b] Every task must be waited for completion using [method wait_for_task_completion] or [method wait_for_group_task_completion] at some point so that any allocated resources inside the task can be cleaned up. </description> </method> <method name="get_group_processed_element_count" qualifiers="const"> @@ -83,6 +85,7 @@ <param index="0" name="group_id" type="int" /> <description> Returns [code]true[/code] if the group task with the given ID is completed. + [b]Note:[/b] You should only call this method between adding the group task and awaiting its completion. </description> </method> <method name="is_task_completed" qualifiers="const"> @@ -90,6 +93,7 @@ <param index="0" name="task_id" type="int" /> <description> Returns [code]true[/code] if the task with the given ID is completed. + [b]Note:[/b] You should only call this method between adding the task and awaiting its completion. </description> </method> <method name="wait_for_group_task_completion"> diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp index 9fa95a93f8..5fabeb94f5 100644 --- a/drivers/gles3/rasterizer_canvas_gles3.cpp +++ b/drivers/gles3/rasterizer_canvas_gles3.cpp @@ -1922,7 +1922,7 @@ void RasterizerCanvasGLES3::render_sdf(RID p_render_target, LightOccluderInstanc while (instance) { OccluderPolygon *oc = occluder_polygon_owner.get_or_null(instance->occluder); - if (!oc || oc->sdf_vertex_array == 0) { + if (!oc || oc->sdf_vertex_array == 0 || !instance->sdf_collision) { instance = instance->next; continue; } diff --git a/editor/editor_dock_manager.cpp b/editor/editor_dock_manager.cpp index 06dd33d8ab..b6250671ee 100644 --- a/editor/editor_dock_manager.cpp +++ b/editor/editor_dock_manager.cpp @@ -147,6 +147,7 @@ void EditorDockManager::_update_layout() { if (!dock_context_popup->is_inside_tree() || EditorNode::get_singleton()->is_exiting()) { return; } + EditorNode::get_singleton()->edit_current(); dock_context_popup->docks_updated(); _update_docks_menu(); EditorNode::get_singleton()->save_editor_layout_delayed(); diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index 98feba38fd..50cc89c618 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -3477,9 +3477,7 @@ void EditorInspector::edit(Object *p_object) { next_object = p_object; // Some plugins need to know the next edited object when clearing the inspector. if (object) { _clear(); - if (object->is_connected("property_list_changed", callable_mp(this, &EditorInspector::_changed_callback))) { - object->disconnect("property_list_changed", callable_mp(this, &EditorInspector::_changed_callback)); - } + object->disconnect("property_list_changed", callable_mp(this, &EditorInspector::_changed_callback)); } per_array_page.clear(); @@ -4021,13 +4019,14 @@ void EditorInspector::_notification(int p_what) { } break; case NOTIFICATION_PREDELETE: { - edit(nullptr); + edit(nullptr); //just in case } break; case NOTIFICATION_EXIT_TREE: { if (!sub_inspector) { get_tree()->disconnect("node_removed", callable_mp(this, &EditorInspector::_node_removed)); } + edit(nullptr); } break; case NOTIFICATION_VISIBILITY_CHANGED: { diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index ca6be130f9..752b060513 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -990,6 +990,29 @@ EditorSettings *EditorSettings::get_singleton() { return singleton.ptr(); } +String EditorSettings::get_existing_settings_path() { + const String config_dir = EditorPaths::get_singleton()->get_config_dir(); + int minor = VERSION_MINOR; + String filename; + + do { + if (VERSION_MAJOR == 4 && minor < 3) { + // Minor version is used since 4.3, so special case to load older settings. + filename = vformat("editor_settings-%d.tres", VERSION_MAJOR); + minor = -1; + } else { + filename = vformat("editor_settings-%d.%d.tres", VERSION_MAJOR, minor); + minor--; + } + } while (minor >= 0 && !FileAccess::exists(config_dir.path_join(filename))); + return config_dir.path_join(filename); +} + +String EditorSettings::get_newest_settings_path() { + const String config_file_name = vformat("editor_settings-%d.%d.tres", VERSION_MAJOR, VERSION_MINOR); + return EditorPaths::get_singleton()->get_config_dir().path_join(config_file_name); +} + void EditorSettings::create() { // IMPORTANT: create() *must* create a valid EditorSettings singleton, // as the rest of the engine code will assume it. As such, it should never @@ -1017,16 +1040,16 @@ void EditorSettings::create() { if (EditorPaths::get_singleton()->are_paths_valid()) { // Validate editor config file. - Ref<DirAccess> dir = DirAccess::open(EditorPaths::get_singleton()->get_config_dir()); - ERR_FAIL_COND(dir.is_null()); + ERR_FAIL_COND(!DirAccess::dir_exists_absolute(EditorPaths::get_singleton()->get_config_dir())); - String config_file_name = "editor_settings-" + itos(VERSION_MAJOR) + ".tres"; - config_file_path = EditorPaths::get_singleton()->get_config_dir().path_join(config_file_name); - if (!dir->file_exists(config_file_name)) { + config_file_path = get_existing_settings_path(); + if (!FileAccess::exists(config_file_path)) { + config_file_path = get_newest_settings_path(); goto fail; } singleton = ResourceLoader::load(config_file_path, "EditorSettings"); + singleton->set_path(get_newest_settings_path()); // Settings can be loaded from older version file, so make sure it's newest. if (singleton.is_null()) { ERR_PRINT("Could not load editor settings from path: " + config_file_path); diff --git a/editor/editor_settings.h b/editor/editor_settings.h index a058f91be8..6a329f6979 100644 --- a/editor/editor_settings.h +++ b/editor/editor_settings.h @@ -124,6 +124,8 @@ public: }; static EditorSettings *get_singleton(); + static String get_existing_settings_path(); + static String get_newest_settings_path(); static void create(); void setup_language(); 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/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/main/main.cpp b/main/main.cpp index 801e8934b0..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); @@ -2518,12 +2521,10 @@ Error Main::setup2() { // Editor setting class is not available, load config directly. if (!init_use_custom_screen && (editor || project_manager) && EditorPaths::get_singleton()->are_paths_valid()) { - Ref<DirAccess> dir = DirAccess::open(EditorPaths::get_singleton()->get_config_dir()); - ERR_FAIL_COND_V(dir.is_null(), FAILED); + ERR_FAIL_COND_V(!DirAccess::dir_exists_absolute(EditorPaths::get_singleton()->get_config_dir()), FAILED); - String config_file_name = "editor_settings-" + itos(VERSION_MAJOR) + ".tres"; - String config_file_path = EditorPaths::get_singleton()->get_config_dir().path_join(config_file_name); - if (dir->file_exists(config_file_name)) { + String config_file_path = EditorSettings::get_existing_settings_path(); + if (FileAccess::exists(config_file_path)) { Error err; Ref<FileAccess> f = FileAccess::open(config_file_path, FileAccess::READ, &err); if (f.is_valid()) { 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 44a929d285..e92609f42f 100644 --- a/modules/fbx/fbx_document.cpp +++ b/modules/fbx/fbx_document.cpp @@ -320,7 +320,7 @@ Error FBXDocument::_parse_nodes(Ref<FBXState> p_state) { node->set_name(_as_string(fbx_node->name)); node->set_original_name(node->get_name()); } else if (fbx_node->is_root) { - node->set_name("Root"); + node->set_name("RootNode"); } if (fbx_node->camera) { node->camera = fbx_node->camera->typed_id; @@ -1699,6 +1699,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/editor/gdscript_docgen.cpp b/modules/gdscript/editor/gdscript_docgen.cpp index 601db5414b..35b69fab8c 100644 --- a/modules/gdscript/editor/gdscript_docgen.cpp +++ b/modules/gdscript/editor/gdscript_docgen.cpp @@ -229,6 +229,36 @@ String GDScriptDocGen::_docvalue_from_variant(const Variant &p_variant, int p_re } } +String GDScriptDocGen::_docvalue_from_expression(const GDP::ExpressionNode *p_expression) { + ERR_FAIL_NULL_V(p_expression, String()); + + if (p_expression->is_constant) { + return _docvalue_from_variant(p_expression->reduced_value); + } + + switch (p_expression->type) { + case GDP::Node::ARRAY: { + const GDP::ArrayNode *array = static_cast<const GDP::ArrayNode *>(p_expression); + return array->elements.is_empty() ? "[]" : "[...]"; + } break; + case GDP::Node::CALL: { + const GDP::CallNode *call = static_cast<const GDP::CallNode *>(p_expression); + return call->function_name.operator String() + (call->arguments.is_empty() ? "()" : "(...)"); + } break; + case GDP::Node::DICTIONARY: { + const GDP::DictionaryNode *dict = static_cast<const GDP::DictionaryNode *>(p_expression); + return dict->elements.is_empty() ? "{}" : "{...}"; + } break; + case GDP::Node::IDENTIFIER: { + const GDP::IdentifierNode *id = static_cast<const GDP::IdentifierNode *>(p_expression); + return id->name; + } break; + default: { + return "<unknown>"; + } break; + } +} + void GDScriptDocGen::_generate_docs(GDScript *p_script, const GDP::ClassNode *p_class) { p_script->_clear_doc(); @@ -328,16 +358,12 @@ void GDScriptDocGen::_generate_docs(GDScript *p_script, const GDP::ClassNode *p_ method_doc.return_type = "Variant"; } - for (const GDScriptParser::ParameterNode *p : m_func->parameters) { + for (const GDP::ParameterNode *p : m_func->parameters) { DocData::ArgumentDoc arg_doc; arg_doc.name = p->identifier->name; _doctype_from_gdtype(p->get_datatype(), arg_doc.type, arg_doc.enumeration); if (p->initializer != nullptr) { - if (p->initializer->is_constant) { - arg_doc.default_value = _docvalue_from_variant(p->initializer->reduced_value); - } else { - arg_doc.default_value = "<unknown>"; - } + arg_doc.default_value = _docvalue_from_expression(p->initializer); } method_doc.arguments.push_back(arg_doc); } @@ -359,7 +385,7 @@ void GDScriptDocGen::_generate_docs(GDScript *p_script, const GDP::ClassNode *p_ signal_doc.is_experimental = m_signal->doc_data.is_experimental; signal_doc.experimental_message = m_signal->doc_data.experimental_message; - for (const GDScriptParser::ParameterNode *p : m_signal->parameters) { + for (const GDP::ParameterNode *p : m_signal->parameters) { DocData::ArgumentDoc arg_doc; arg_doc.name = p->identifier->name; _doctype_from_gdtype(p->get_datatype(), arg_doc.type, arg_doc.enumeration); @@ -405,12 +431,8 @@ void GDScriptDocGen::_generate_docs(GDScript *p_script, const GDP::ClassNode *p_ break; } - if (m_var->initializer) { - if (m_var->initializer->is_constant) { - prop_doc.default_value = _docvalue_from_variant(m_var->initializer->reduced_value); - } else { - prop_doc.default_value = "<unknown>"; - } + if (m_var->initializer != nullptr) { + prop_doc.default_value = _docvalue_from_expression(m_var->initializer); } prop_doc.overridden = false; diff --git a/modules/gdscript/editor/gdscript_docgen.h b/modules/gdscript/editor/gdscript_docgen.h index 651a4fb198..0ae37c4133 100644 --- a/modules/gdscript/editor/gdscript_docgen.h +++ b/modules/gdscript/editor/gdscript_docgen.h @@ -45,6 +45,7 @@ class GDScriptDocGen { static String _get_class_name(const GDP::ClassNode &p_class); static void _doctype_from_gdtype(const GDType &p_gdtype, String &r_type, String &r_enum, bool p_is_return = false); static String _docvalue_from_variant(const Variant &p_variant, int p_recursion_level = 1); + static String _docvalue_from_expression(const GDP::ExpressionNode *p_expression); static void _generate_docs(GDScript *p_script, const GDP::ClassNode *p_class); public: 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..3cb011b251 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); diff --git a/modules/gdscript/tests/README.md b/modules/gdscript/tests/README.md index 72b5316532..714e38397f 100644 --- a/modules/gdscript/tests/README.md +++ b/modules/gdscript/tests/README.md @@ -13,6 +13,12 @@ The `script/completion` folder contains test for the GDScript autocompletion. Each test case consists of at least one `.gd` file, which contains the code, and one `.cfg` file, which contains expected results and configuration. Inside of the GDScript file the character `➡` represents the cursor position, at which autocompletion is invoked. +The script files won't be parsable GDScript since it contains an invalid char and and often the code is not complete during autocompletion. To allow for a valid base when used with a scene, the +runner will remove the line which contains `➡`. Therefore the scripts need to be valid if this line is removed, otherwise the test might behave in unexpected ways. This may for example require +adding an additional `pass` statement. + +This also means, that the runner will add the script to its owner node, so the script should not be loaded through the scene file. + The config file contains two section: `[input]` contains keys that configure the test environment. The following keys are possible: @@ -20,6 +26,7 @@ The config file contains two section: - `cs: boolean = false`: If `true`, the test will be skipped when running a non C# build. - `use_single_quotes: boolean = false`: Configures the corresponding editor setting for the test. - `scene: String`: Allows to specify a scene which is opened while autocompletion is performed. If this is not set the test runner will search for a `.tscn` file with the same basename as the GDScript file. If that isn't found either, autocompletion will behave as if no scene was opened. +- `node_path: String`: The node path of the node which holds the current script inside of the scene. Defaults to the scene root node. `[output]` specifies the expected results for the test. The following key are supported: diff --git a/modules/gdscript/tests/scripts/completion/argument_options/argument_options.tscn b/modules/gdscript/tests/scripts/completion/argument_options/argument_options.tscn new file mode 100644 index 0000000000..d3dea6b12b --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/argument_options/argument_options.tscn @@ -0,0 +1,3 @@ +[gd_scene load_steps=1 format=3 uid="uid://dl28pdkxcjvym"] + +[node name="GetNode" type="Node"] diff --git a/modules/gdscript/tests/scripts/completion/argument_options/connect.cfg b/modules/gdscript/tests/scripts/completion/argument_options/connect.cfg new file mode 100644 index 0000000000..8c3fbf90da --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/argument_options/connect.cfg @@ -0,0 +1,8 @@ +[input] +scene="res://completion/argument_options/argument_options.tscn" +[output] +include=[ + ; Node + {"display": "\"signal_a\""}, + {"display": "\"child_entered_tree\""}, +] diff --git a/modules/gdscript/tests/scripts/completion/argument_options/connect.gd b/modules/gdscript/tests/scripts/completion/argument_options/connect.gd new file mode 100644 index 0000000000..1137070c4e --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/argument_options/connect.gd @@ -0,0 +1,7 @@ +extends Node + +signal signal_a() + +func _ready(): + connect(➡) + pass diff --git a/modules/gdscript/tests/scripts/completion/get_node/literal/dollar.gd b/modules/gdscript/tests/scripts/completion/get_node/literal/dollar.gd index df458a9435..dc7cc99554 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/literal/dollar.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/literal/dollar.gd @@ -2,3 +2,4 @@ extends Node func a(): %AnimationPlayer.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/get_node/literal/percent.gd b/modules/gdscript/tests/scripts/completion/get_node/literal/percent.gd index 7050761b86..5586317938 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/literal/percent.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/literal/percent.gd @@ -2,3 +2,4 @@ extends Node func a(): $UniqueAnimationPlayer.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/get_node/literal_scene/dollar_class_scene.gd b/modules/gdscript/tests/scripts/completion/get_node/literal_scene/dollar_class_scene.gd index a84283a1de..69fcac4471 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/literal_scene/dollar_class_scene.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/literal_scene/dollar_class_scene.gd @@ -2,3 +2,4 @@ extends Node func a(): $A.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/get_node/literal_scene/dollar_native_scene.gd b/modules/gdscript/tests/scripts/completion/get_node/literal_scene/dollar_native_scene.gd index 6e3fee1696..0b13a207ff 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/literal_scene/dollar_native_scene.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/literal_scene/dollar_native_scene.gd @@ -2,3 +2,4 @@ extends Node func a(): $AnimationPlayer.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/get_node/literal_scene/percent_class_scene.gd b/modules/gdscript/tests/scripts/completion/get_node/literal_scene/percent_class_scene.gd index 27f059c944..48945db38e 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/literal_scene/percent_class_scene.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/literal_scene/percent_class_scene.gd @@ -2,3 +2,4 @@ extends Node func a(): %UniqueA.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/get_node/literal_scene/percent_native_scene.gd b/modules/gdscript/tests/scripts/completion/get_node/literal_scene/percent_native_scene.gd index 07068fc5a4..3684edc73b 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/literal_scene/percent_native_scene.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/literal_scene/percent_native_scene.gd @@ -2,3 +2,4 @@ extends Node func a(): %UniqueAnimationPlayer.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/get_node/local/local.gd b/modules/gdscript/tests/scripts/completion/get_node/local/local.gd index 596ad80ef2..dcd232d82d 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/local/local.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/local/local.gd @@ -3,3 +3,4 @@ extends Node func a(): var test = $AnimationPlayer test.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/get_node/local_interfered/local_interfered.gd b/modules/gdscript/tests/scripts/completion/get_node/local_interfered/local_interfered.gd index 6f87af3c85..7710c2d13b 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/local_interfered/local_interfered.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/local_interfered/local_interfered.gd @@ -3,3 +3,4 @@ extends Node func a(): var test := $AnimationPlayer test.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/get_node/local_interfered_scene/class_local_interfered_scene.gd b/modules/gdscript/tests/scripts/completion/get_node/local_interfered_scene/class_local_interfered_scene.gd index a710c8bbd7..6b29bf5526 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/local_interfered_scene/class_local_interfered_scene.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/local_interfered_scene/class_local_interfered_scene.gd @@ -3,3 +3,4 @@ extends Node func a(): var test := $A test.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/get_node/local_interfered_scene/native_local_interfered_scene.gd b/modules/gdscript/tests/scripts/completion/get_node/local_interfered_scene/native_local_interfered_scene.gd index 6f87af3c85..7710c2d13b 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/local_interfered_scene/native_local_interfered_scene.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/local_interfered_scene/native_local_interfered_scene.gd @@ -3,3 +3,4 @@ extends Node func a(): var test := $AnimationPlayer test.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/get_node/local_scene/class_local_scene.gd b/modules/gdscript/tests/scripts/completion/get_node/local_scene/class_local_scene.gd index 2fc88f93dd..02c0d2dceb 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/local_scene/class_local_scene.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/local_scene/class_local_scene.gd @@ -3,3 +3,4 @@ extends Node func a(): var test = $A test.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/get_node/local_scene/native_local_scene.gd b/modules/gdscript/tests/scripts/completion/get_node/local_scene/native_local_scene.gd index 596ad80ef2..dcd232d82d 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/local_scene/native_local_scene.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/local_scene/native_local_scene.gd @@ -3,3 +3,4 @@ extends Node func a(): var test = $AnimationPlayer test.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/get_node/local_typehint/class_local_typehint.gd b/modules/gdscript/tests/scripts/completion/get_node/local_typehint/class_local_typehint.gd index b6d2074939..3277beccab 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/local_typehint/class_local_typehint.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/local_typehint/class_local_typehint.gd @@ -5,3 +5,4 @@ const A := preload("res://completion/class_a.notest.gd") func a(): var test: A = $A test.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/get_node/local_typehint/native_local_typehint.gd b/modules/gdscript/tests/scripts/completion/get_node/local_typehint/native_local_typehint.gd index 13b541a35d..e6d22af296 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/local_typehint/native_local_typehint.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/local_typehint/native_local_typehint.gd @@ -3,3 +3,4 @@ extends Node func a(): var test: AnimationPlayer = $AnimationPlayer test.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene/class_local_typehint_scene.gd b/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene/class_local_typehint_scene.gd index b6d2074939..3277beccab 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene/class_local_typehint_scene.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene/class_local_typehint_scene.gd @@ -5,3 +5,4 @@ const A := preload("res://completion/class_a.notest.gd") func a(): var test: A = $A test.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene/native_local_typehint_scene.gd b/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene/native_local_typehint_scene.gd index 13b541a35d..e6d22af296 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene/native_local_typehint_scene.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene/native_local_typehint_scene.gd @@ -3,3 +3,4 @@ extends Node func a(): var test: AnimationPlayer = $AnimationPlayer test.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene_broad/class_local_typehint_scene_broad.notest.gd b/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene_broad/class_local_typehint_scene_broad.notest.gd index 5c785b3ddc..3266679a6a 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene_broad/class_local_typehint_scene_broad.notest.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene_broad/class_local_typehint_scene_broad.notest.gd @@ -4,3 +4,4 @@ extends Node func a(): var test: Node = $A test.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene_broad/native_local_typehint_scene_broad.notest.gd b/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene_broad/native_local_typehint_scene_broad.notest.gd index 57f4e16e3c..f637fc3d0f 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene_broad/native_local_typehint_scene_broad.notest.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene_broad/native_local_typehint_scene_broad.notest.gd @@ -4,3 +4,4 @@ extends Node func a(): var test: Node = $AnimationPlayer test.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene_incompatible/class_local_typehint_scene_incompatible.gd b/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene_incompatible/class_local_typehint_scene_incompatible.gd index c6adfe0dd3..8ce75d9996 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene_incompatible/class_local_typehint_scene_incompatible.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene_incompatible/class_local_typehint_scene_incompatible.gd @@ -3,3 +3,4 @@ extends Node func a(): var test: Area2D = $A test.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene_incompatible/native_local_typehint_scene_incompatible.gd b/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene_incompatible/native_local_typehint_scene_incompatible.gd index f53fce9bfe..2b39e3ada7 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene_incompatible/native_local_typehint_scene_incompatible.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene_incompatible/native_local_typehint_scene_incompatible.gd @@ -3,3 +3,4 @@ extends Node func a(): var test: Area2D = $AnimationPlayer test.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/get_node/member/member.gd b/modules/gdscript/tests/scripts/completion/get_node/member/member.gd index 6bcc0a0298..80b4943953 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/member/member.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/member/member.gd @@ -4,3 +4,4 @@ var test = $AnimationPlayer func a(): test.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_interfered/member_interfered.gd b/modules/gdscript/tests/scripts/completion/get_node/member_interfered/member_interfered.gd index 542197e643..97b288334e 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/member_interfered/member_interfered.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/member_interfered/member_interfered.gd @@ -4,3 +4,4 @@ var test := $AnimationPlayer func a(): test.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_interfered_scene/class_member_interfered_scene.gd b/modules/gdscript/tests/scripts/completion/get_node/member_interfered_scene/class_member_interfered_scene.gd index da0b1b11d4..402fd1d275 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/member_interfered_scene/class_member_interfered_scene.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/member_interfered_scene/class_member_interfered_scene.gd @@ -4,3 +4,4 @@ var test := $A func a(): test.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_interfered_scene/native_member_interfered_scene.gd b/modules/gdscript/tests/scripts/completion/get_node/member_interfered_scene/native_member_interfered_scene.gd index 542197e643..97b288334e 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/member_interfered_scene/native_member_interfered_scene.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/member_interfered_scene/native_member_interfered_scene.gd @@ -4,3 +4,4 @@ var test := $AnimationPlayer func a(): test.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_scene/class_member_scene.gd b/modules/gdscript/tests/scripts/completion/get_node/member_scene/class_member_scene.gd index 4a35661e94..6188a6e843 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/member_scene/class_member_scene.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/member_scene/class_member_scene.gd @@ -4,3 +4,4 @@ var test = $A func a(): test.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_scene/native_member_scene.gd b/modules/gdscript/tests/scripts/completion/get_node/member_scene/native_member_scene.gd index 6bcc0a0298..80b4943953 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/member_scene/native_member_scene.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/member_scene/native_member_scene.gd @@ -4,3 +4,4 @@ var test = $AnimationPlayer func a(): test.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_typehint/class_member_typehint.gd b/modules/gdscript/tests/scripts/completion/get_node/member_typehint/class_member_typehint.gd index e4edc3a4e4..01b7c76dbf 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/member_typehint/class_member_typehint.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/member_typehint/class_member_typehint.gd @@ -6,3 +6,4 @@ var test: A = $A func a(): test.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_typehint/native_member_typehint.gd b/modules/gdscript/tests/scripts/completion/get_node/member_typehint/native_member_typehint.gd index eda94ae34d..7f2cb4e8cc 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/member_typehint/native_member_typehint.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/member_typehint/native_member_typehint.gd @@ -4,3 +4,4 @@ var test: AnimationPlayer = $AnimationPlayer func a(): test.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene/class_member_typehint_scene.gd b/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene/class_member_typehint_scene.gd index 8f68f54072..43d8b666ab 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene/class_member_typehint_scene.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene/class_member_typehint_scene.gd @@ -6,3 +6,4 @@ const A := preload("res://completion/class_a.notest.gd") func a(): test.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene/native_member_typehint_scene.gd b/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene/native_member_typehint_scene.gd index eda94ae34d..7f2cb4e8cc 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene/native_member_typehint_scene.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene/native_member_typehint_scene.gd @@ -4,3 +4,4 @@ var test: AnimationPlayer = $AnimationPlayer func a(): test.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_broad/class_member_typehint_scene_broad.gd b/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_broad/class_member_typehint_scene_broad.gd index 7b0ed4ecd8..aac450be9f 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_broad/class_member_typehint_scene_broad.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_broad/class_member_typehint_scene_broad.gd @@ -4,3 +4,4 @@ var test: Node = $A func a(): test.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_broad/native_member_typehint_scene_broad.gd b/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_broad/native_member_typehint_scene_broad.gd index 87342f9a21..9eb10e4933 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_broad/native_member_typehint_scene_broad.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_broad/native_member_typehint_scene_broad.gd @@ -4,3 +4,4 @@ var test: Node = $AnimationPlayer func a(): test.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_incompatible/class_member_typehint_scene_incompatible.gd b/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_incompatible/class_member_typehint_scene_incompatible.gd index 5f78bcdf04..ff8214c463 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_incompatible/class_member_typehint_scene_incompatible.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_incompatible/class_member_typehint_scene_incompatible.gd @@ -4,3 +4,4 @@ var test: Area2D = $A func a(): test.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_incompatible/native_member_typehint_scene_incompatible.gd b/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_incompatible/native_member_typehint_scene_incompatible.gd index c14df5cd1b..30cd7d6a21 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_incompatible/native_member_typehint_scene_incompatible.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_incompatible/native_member_typehint_scene_incompatible.gd @@ -4,3 +4,4 @@ var test: Area2D = $AnimationPlayer func a(): test.➡ + pass 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/gdscript/tests/test_completion.h b/modules/gdscript/tests/test_completion.h index ac9ffcd915..327446acee 100644 --- a/modules/gdscript/tests/test_completion.h +++ b/modules/gdscript/tests/test_completion.h @@ -33,6 +33,7 @@ #ifdef TOOLS_ENABLED +#include "core/config/project_settings.h" #include "core/io/config_file.h" #include "core/io/dir_access.h" #include "core/io/file_access.h" @@ -111,7 +112,10 @@ static void test_directory(const String &p_dir) { // For ease of reading ➡ (0x27A1) acts as sentinel char instead of 0xFFFF in the files. code = code.replace_first(String::chr(0x27A1), String::chr(0xFFFF)); // Require pointer sentinel char in scripts. - CHECK(code.find_char(0xFFFF) != -1); + int location = code.find_char(0xFFFF); + CHECK(location != -1); + + String res_path = ProjectSettings::get_singleton()->localize_path(path.path_join(next)); ConfigFile conf; if (conf.load(path.path_join(next.get_basename() + ".cfg")) != OK) { @@ -137,20 +141,46 @@ static void test_directory(const String &p_dir) { String call_hint; bool forced; - Node *owner = nullptr; + Node *scene = nullptr; if (conf.has_section_key("input", "scene")) { - Ref<PackedScene> scene = ResourceLoader::load(conf.get_value("input", "scene"), "PackedScene", ResourceFormatLoader::CACHE_MODE_IGNORE_DEEP); - if (scene.is_valid()) { - owner = scene->instantiate(); + Ref<PackedScene> packed_scene = ResourceLoader::load(conf.get_value("input", "scene"), "PackedScene", ResourceFormatLoader::CACHE_MODE_IGNORE_DEEP); + if (packed_scene.is_valid()) { + scene = packed_scene->instantiate(); } } else if (dir->file_exists(next.get_basename() + ".tscn")) { - Ref<PackedScene> scene = ResourceLoader::load(path.path_join(next.get_basename() + ".tscn"), "PackedScene"); - if (scene.is_valid()) { - owner = scene->instantiate(); + Ref<PackedScene> packed_scene = ResourceLoader::load(path.path_join(next.get_basename() + ".tscn"), "PackedScene"); + if (packed_scene.is_valid()) { + scene = packed_scene->instantiate(); + } + } + Node *owner = nullptr; + if (scene != nullptr) { + owner = scene->get_node(conf.get_value("input", "node_path", ".")); + } + + if (owner != nullptr) { + // Remove the line which contains the sentinel char, to get a valid script. + Ref<GDScript> scr; + scr.instantiate(); + int start = location; + int end = location; + for (; start >= 0; --start) { + if (code.get(start) == '\n') { + break; + } + } + for (; end < code.size(); ++end) { + if (code.get(end) == '\n') { + break; + } } + scr->set_source_code(code.erase(start, end - start)); + scr->reload(); + scr->set_path(res_path); + owner->set_script(scr); } - GDScriptLanguage::get_singleton()->complete_code(code, path.path_join(next), owner, &options, forced, call_hint); + GDScriptLanguage::get_singleton()->complete_code(code, res_path, owner, &options, forced, call_hint); String contains_excluded; for (ScriptLanguage::CodeCompletionOption &option : options) { for (const Dictionary &E : exclude) { @@ -179,8 +209,8 @@ static void test_directory(const String &p_dir) { CHECK(expected_call_hint == call_hint); CHECK(expected_forced == forced); - if (owner) { - memdelete(owner); + if (scene) { + memdelete(scene); } } next = dir->get_next(); 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/multiplayer/scene_cache_interface.cpp b/modules/multiplayer/scene_cache_interface.cpp index 33b05d4cc2..c08ccbe4cc 100644 --- a/modules/multiplayer/scene_cache_interface.cpp +++ b/modules/multiplayer/scene_cache_interface.cpp @@ -52,6 +52,9 @@ void SceneCacheInterface::_remove_node_cache(ObjectID p_oid) { if (!nc) { return; } + if (nc->cache_id) { + assigned_ids.erase(nc->cache_id); + } for (KeyValue<int, int> &E : nc->recv_ids) { PeerInfo *pinfo = peers_info.getptr(E.key); ERR_CONTINUE(!pinfo); @@ -117,16 +120,12 @@ void SceneCacheInterface::process_simplify_path(int p_from, const uint8_t *p_pac NodeCache &cache = _track(node); cache.recv_ids.insert(p_from, id); - // Encode path to send ack. - CharString pname = String(path).utf8(); - int len = encode_cstring(pname.get_data(), nullptr); - + // Send ack. Vector<uint8_t> packet; - - packet.resize(1 + 1 + len); + packet.resize(1 + 1 + 4); packet.write[0] = SceneMultiplayer::NETWORK_COMMAND_CONFIRM_PATH; packet.write[1] = valid_rpc_checksum; - encode_cstring(pname.get_data(), &packet.write[2]); + encode_uint32(id, &packet.write[2]); Ref<MultiplayerPeer> multiplayer_peer = multiplayer->get_multiplayer_peer(); ERR_FAIL_COND(multiplayer_peer.is_null()); @@ -137,26 +136,26 @@ void SceneCacheInterface::process_simplify_path(int p_from, const uint8_t *p_pac } void SceneCacheInterface::process_confirm_path(int p_from, const uint8_t *p_packet, int p_packet_len) { - ERR_FAIL_COND_MSG(p_packet_len < 3, "Invalid packet received. Size too small."); + ERR_FAIL_COND_MSG(p_packet_len != 6, "Invalid packet received. Size too small."); Node *root_node = SceneTree::get_singleton()->get_root()->get_node(multiplayer->get_root_path()); ERR_FAIL_NULL(root_node); const bool valid_rpc_checksum = p_packet[1]; + int id = decode_uint32(&p_packet[2]); - String paths; - paths.parse_utf8((const char *)&p_packet[2], p_packet_len - 2); - - const NodePath path = paths; + const ObjectID *oid = assigned_ids.getptr(id); + if (oid == nullptr) { + return; // May be trying to confirm a node that was removed. + } if (valid_rpc_checksum == false) { - ERR_PRINT("The rpc node checksum failed. Make sure to have the same methods on both nodes. Node path: " + path); + const Node *node = Object::cast_to<Node>(ObjectDB::get_instance(*oid)); + ERR_FAIL_NULL(node); // Bug. + ERR_PRINT("The rpc node checksum failed. Make sure to have the same methods on both nodes. Node path: " + node->get_path()); } - Node *node = root_node->get_node(path); - ERR_FAIL_NULL(node); - - NodeCache *cache = nodes_cache.getptr(node->get_instance_id()); - ERR_FAIL_NULL_MSG(cache, "Invalid packet received. Tries to confirm a node which was not requested."); + NodeCache *cache = nodes_cache.getptr(*oid); + ERR_FAIL_NULL(cache); // Bug. bool *confirmed = cache->confirmed_peers.getptr(p_from); ERR_FAIL_NULL_MSG(confirmed, "Invalid packet received. Tries to confirm a node which was not requested."); @@ -216,6 +215,7 @@ int SceneCacheInterface::make_object_cache(Object *p_obj) { NodeCache &cache = _track(node); if (cache.cache_id == 0) { cache.cache_id = last_send_cache_id++; + assigned_ids[cache.cache_id] = p_obj->get_instance_id(); } return cache.cache_id; } @@ -227,6 +227,7 @@ bool SceneCacheInterface::send_object_cache(Object *p_obj, int p_peer_id, int &r NodeCache &cache = _track(node); if (cache.cache_id == 0) { cache.cache_id = last_send_cache_id++; + assigned_ids[cache.cache_id] = p_obj->get_instance_id(); } r_id = cache.cache_id; @@ -289,5 +290,6 @@ void SceneCacheInterface::clear() { } peers_info.clear(); nodes_cache.clear(); + assigned_ids.clear(); last_send_cache_id = 1; } diff --git a/modules/multiplayer/scene_cache_interface.h b/modules/multiplayer/scene_cache_interface.h index ab4a20c078..73d6bde6ef 100644 --- a/modules/multiplayer/scene_cache_interface.h +++ b/modules/multiplayer/scene_cache_interface.h @@ -44,7 +44,7 @@ private: //path sent caches struct NodeCache { - int cache_id; + int cache_id = 0; HashMap<int, int> recv_ids; // peer id, remote cache id HashMap<int, bool> confirmed_peers; // peer id, confirmed }; @@ -55,6 +55,7 @@ private: }; HashMap<ObjectID, NodeCache> nodes_cache; + HashMap<int, ObjectID> assigned_ids; HashMap<int, PeerInfo> peers_info; int last_send_cache_id = 1; 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/platform/ios/display_server_ios.h b/platform/ios/display_server_ios.h index 3f9211c572..c6015a058c 100644 --- a/platform/ios/display_server_ios.h +++ b/platform/ios/display_server_ios.h @@ -129,10 +129,10 @@ public: // MARK: Motion - void update_gravity(float p_x, float p_y, float p_z); - void update_accelerometer(float p_x, float p_y, float p_z); - void update_magnetometer(float p_x, float p_y, float p_z); - void update_gyroscope(float p_x, float p_y, float p_z); + void update_gravity(const Vector3 &p_gravity); + void update_accelerometer(const Vector3 &p_accelerometer); + void update_magnetometer(const Vector3 &p_magnetometer); + void update_gyroscope(const Vector3 &p_gyroscope); // MARK: - diff --git a/platform/ios/display_server_ios.mm b/platform/ios/display_server_ios.mm index f84fb01ad0..cd6f855d77 100644 --- a/platform/ios/display_server_ios.mm +++ b/platform/ios/display_server_ios.mm @@ -289,26 +289,20 @@ void DisplayServerIOS::key(Key p_key, char32_t p_char, Key p_unshifted, Key p_ph // MARK: Motion -void DisplayServerIOS::update_gravity(float p_x, float p_y, float p_z) { - Input::get_singleton()->set_gravity(Vector3(p_x, p_y, p_z)); +void DisplayServerIOS::update_gravity(const Vector3 &p_gravity) { + Input::get_singleton()->set_gravity(p_gravity); } -void DisplayServerIOS::update_accelerometer(float p_x, float p_y, float p_z) { - // Found out the Z should not be negated! Pass as is! - Vector3 v_accelerometer = Vector3( - p_x / kDisplayServerIOSAcceleration, - p_y / kDisplayServerIOSAcceleration, - p_z / kDisplayServerIOSAcceleration); - - Input::get_singleton()->set_accelerometer(v_accelerometer); +void DisplayServerIOS::update_accelerometer(const Vector3 &p_accelerometer) { + Input::get_singleton()->set_accelerometer(p_accelerometer / kDisplayServerIOSAcceleration); } -void DisplayServerIOS::update_magnetometer(float p_x, float p_y, float p_z) { - Input::get_singleton()->set_magnetometer(Vector3(p_x, p_y, p_z)); +void DisplayServerIOS::update_magnetometer(const Vector3 &p_magnetometer) { + Input::get_singleton()->set_magnetometer(p_magnetometer); } -void DisplayServerIOS::update_gyroscope(float p_x, float p_y, float p_z) { - Input::get_singleton()->set_gyroscope(Vector3(p_x, p_y, p_z)); +void DisplayServerIOS::update_gyroscope(const Vector3 &p_gyroscope) { + Input::get_singleton()->set_gyroscope(p_gyroscope); } // MARK: - diff --git a/platform/ios/godot_view.mm b/platform/ios/godot_view.mm index 4b87863fc5..1dddc9306e 100644 --- a/platform/ios/godot_view.mm +++ b/platform/ios/godot_view.mm @@ -451,28 +451,28 @@ static const float earth_gravity = 9.80665; switch (interfaceOrientation) { case UIInterfaceOrientationLandscapeLeft: { - DisplayServerIOS::get_singleton()->update_gravity(-gravity.y, gravity.x, gravity.z); - DisplayServerIOS::get_singleton()->update_accelerometer(-(acceleration.y + gravity.y), (acceleration.x + gravity.x), acceleration.z + gravity.z); - DisplayServerIOS::get_singleton()->update_magnetometer(-magnetic.y, magnetic.x, magnetic.z); - DisplayServerIOS::get_singleton()->update_gyroscope(-rotation.y, rotation.x, rotation.z); + DisplayServerIOS::get_singleton()->update_gravity(Vector3(gravity.x, gravity.y, gravity.z).rotated(Vector3(0, 0, 1), -Math_PI * 0.5)); + DisplayServerIOS::get_singleton()->update_accelerometer(Vector3(acceleration.x + gravity.x, acceleration.y + gravity.y, acceleration.z + gravity.z).rotated(Vector3(0, 0, 1), -Math_PI * 0.5)); + DisplayServerIOS::get_singleton()->update_magnetometer(Vector3(magnetic.x, magnetic.y, magnetic.z).rotated(Vector3(0, 0, 1), -Math_PI * 0.5)); + DisplayServerIOS::get_singleton()->update_gyroscope(Vector3(rotation.x, rotation.y, rotation.z).rotated(Vector3(0, 0, 1), -Math_PI * 0.5)); } break; case UIInterfaceOrientationLandscapeRight: { - DisplayServerIOS::get_singleton()->update_gravity(gravity.y, -gravity.x, gravity.z); - DisplayServerIOS::get_singleton()->update_accelerometer((acceleration.y + gravity.y), -(acceleration.x + gravity.x), acceleration.z + gravity.z); - DisplayServerIOS::get_singleton()->update_magnetometer(magnetic.y, -magnetic.x, magnetic.z); - DisplayServerIOS::get_singleton()->update_gyroscope(rotation.y, -rotation.x, rotation.z); + DisplayServerIOS::get_singleton()->update_gravity(Vector3(gravity.x, gravity.y, gravity.z).rotated(Vector3(0, 0, 1), Math_PI * 0.5)); + DisplayServerIOS::get_singleton()->update_accelerometer(Vector3(acceleration.x + gravity.x, acceleration.y + gravity.y, acceleration.z + gravity.z).rotated(Vector3(0, 0, 1), Math_PI * 0.5)); + DisplayServerIOS::get_singleton()->update_magnetometer(Vector3(magnetic.x, magnetic.y, magnetic.z).rotated(Vector3(0, 0, 1), Math_PI * 0.5)); + DisplayServerIOS::get_singleton()->update_gyroscope(Vector3(rotation.x, rotation.y, rotation.z).rotated(Vector3(0, 0, 1), Math_PI * 0.5)); } break; case UIInterfaceOrientationPortraitUpsideDown: { - DisplayServerIOS::get_singleton()->update_gravity(-gravity.x, gravity.y, gravity.z); - DisplayServerIOS::get_singleton()->update_accelerometer(-(acceleration.x + gravity.x), (acceleration.y + gravity.y), acceleration.z + gravity.z); - DisplayServerIOS::get_singleton()->update_magnetometer(-magnetic.x, magnetic.y, magnetic.z); - DisplayServerIOS::get_singleton()->update_gyroscope(-rotation.x, rotation.y, rotation.z); + DisplayServerIOS::get_singleton()->update_gravity(Vector3(gravity.x, gravity.y, gravity.z).rotated(Vector3(0, 0, 1), Math_PI)); + DisplayServerIOS::get_singleton()->update_accelerometer(Vector3(acceleration.x + gravity.x, acceleration.y + gravity.y, acceleration.z + gravity.z).rotated(Vector3(0, 0, 1), Math_PI)); + DisplayServerIOS::get_singleton()->update_magnetometer(Vector3(magnetic.x, magnetic.y, magnetic.z).rotated(Vector3(0, 0, 1), Math_PI)); + DisplayServerIOS::get_singleton()->update_gyroscope(Vector3(rotation.x, rotation.y, rotation.z).rotated(Vector3(0, 0, 1), Math_PI)); } break; default: { // assume portrait - DisplayServerIOS::get_singleton()->update_gravity(gravity.x, gravity.y, gravity.z); - DisplayServerIOS::get_singleton()->update_accelerometer(acceleration.x + gravity.x, acceleration.y + gravity.y, acceleration.z + gravity.z); - DisplayServerIOS::get_singleton()->update_magnetometer(magnetic.x, magnetic.y, magnetic.z); - DisplayServerIOS::get_singleton()->update_gyroscope(rotation.x, rotation.y, rotation.z); + DisplayServerIOS::get_singleton()->update_gravity(Vector3(gravity.x, gravity.y, gravity.z)); + DisplayServerIOS::get_singleton()->update_accelerometer(Vector3(acceleration.x + gravity.x, acceleration.y + gravity.y, acceleration.z + gravity.z)); + DisplayServerIOS::get_singleton()->update_magnetometer(Vector3(magnetic.x, magnetic.y, magnetic.z)); + DisplayServerIOS::get_singleton()->update_gyroscope(Vector3(rotation.x, rotation.y, rotation.z)); } break; } } 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/platform/windows/rendering_context_driver_vulkan_windows.h b/platform/windows/rendering_context_driver_vulkan_windows.h index 34546c9ea6..1bb70cb0ff 100644 --- a/platform/windows/rendering_context_driver_vulkan_windows.h +++ b/platform/windows/rendering_context_driver_vulkan_windows.h @@ -52,7 +52,7 @@ public: }; RenderingContextDriverVulkanWindows(); - ~RenderingContextDriverVulkanWindows() override final; + ~RenderingContextDriverVulkanWindows() override; }; #endif // VULKAN_ENABLED 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/line_builder.cpp b/scene/2d/line_builder.cpp index e898dc7fab..f8a8aa487c 100644 --- a/scene/2d/line_builder.cpp +++ b/scene/2d/line_builder.cpp @@ -353,7 +353,20 @@ void LineBuilder::build() { } else if (current_joint_mode == Line2D::LINE_JOINT_ROUND && !(wrap_around && i == segments_count)) { Vector2 vbegin = cbegin - pos1; Vector2 vend = cend - pos1; - strip_add_arc(pos1, vbegin.angle_to(vend), orientation); + // We want to use vbegin.angle_to(vend) below, which evaluates to + // Math::atan2(vbegin.cross(vend), vbegin.dot(vend)) but we need to + // calculate this ourselves as we need to check if the cross product + // in that calculation ends up being -0.f and flip it if so, effectively + // flipping the resulting angle_delta to not return -PI but +PI instead + float cross_product = vbegin.cross(vend); + float dot_product = vbegin.dot(vend); + // Note that we're comparing against -0.f for clarity but 0.f would + // match as well, therefore we need the explicit signbit check too. + if (cross_product == -0.f && signbit(cross_product)) { + cross_product = 0.f; + } + float angle_delta = Math::atan2(cross_product, dot_product); + strip_add_arc(pos1, angle_delta, orientation); } if (!is_intersecting) { 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/gui/graph_node.cpp b/scene/gui/graph_node.cpp index 1b960a9b62..fea9c377a7 100644 --- a/scene/gui/graph_node.cpp +++ b/scene/gui/graph_node.cpp @@ -647,6 +647,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 +657,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/tab_bar.cpp b/scene/gui/tab_bar.cpp index 2d687eb201..0e130d60af 100644 --- a/scene/gui/tab_bar.cpp +++ b/scene/gui/tab_bar.cpp @@ -323,6 +323,19 @@ void TabBar::gui_input(const Ref<InputEvent> &p_event) { } } +String TabBar::get_tooltip(const Point2 &p_pos) const { + int tab_idx = get_tab_idx_at_point(p_pos); + if (tab_idx < 0) { + return Control::get_tooltip(p_pos); + } + + if (tabs[tab_idx].tooltip.is_empty() && tabs[tab_idx].truncated) { + return tabs[tab_idx].text; + } + + return tabs[tab_idx].tooltip; +} + void TabBar::_shape(int p_tab) { tabs.write[p_tab].text_buf->clear(); tabs.write[p_tab].text_buf->set_width(-1); @@ -757,6 +770,16 @@ String TabBar::get_tab_title(int p_tab) const { return tabs[p_tab].text; } +void TabBar::set_tab_tooltip(int p_tab, const String &p_tooltip) { + ERR_FAIL_INDEX(p_tab, tabs.size()); + tabs.write[p_tab].tooltip = p_tooltip; +} + +String TabBar::get_tab_tooltip(int p_tab) const { + ERR_FAIL_INDEX_V(p_tab, tabs.size(), ""); + return tabs[p_tab].tooltip; +} + void TabBar::set_tab_text_direction(int p_tab, Control::TextDirection p_text_direction) { ERR_FAIL_INDEX(p_tab, tabs.size()); ERR_FAIL_COND((int)p_text_direction < -1 || (int)p_text_direction > 3); @@ -998,7 +1021,8 @@ void TabBar::_update_cache(bool p_update_hover) { tabs.write[i].size_text = Math::ceil(tabs[i].text_buf->get_size().x); tabs.write[i].size_cache = get_tab_width(i); - if (max_width > 0 && tabs[i].size_cache > max_width) { + tabs.write[i].truncated = max_width > 0 && tabs[i].size_cache > max_width; + if (tabs[i].truncated) { int size_textless = tabs[i].size_cache - tabs[i].size_text; int mw = MAX(size_textless, max_width); @@ -1730,6 +1754,8 @@ void TabBar::_bind_methods() { ClassDB::bind_method(D_METHOD("select_next_available"), &TabBar::select_next_available); ClassDB::bind_method(D_METHOD("set_tab_title", "tab_idx", "title"), &TabBar::set_tab_title); ClassDB::bind_method(D_METHOD("get_tab_title", "tab_idx"), &TabBar::get_tab_title); + ClassDB::bind_method(D_METHOD("set_tab_tooltip", "tab_idx", "tooltip"), &TabBar::set_tab_tooltip); + ClassDB::bind_method(D_METHOD("get_tab_tooltip", "tab_idx"), &TabBar::get_tab_tooltip); ClassDB::bind_method(D_METHOD("set_tab_text_direction", "tab_idx", "direction"), &TabBar::set_tab_text_direction); ClassDB::bind_method(D_METHOD("get_tab_text_direction", "tab_idx"), &TabBar::get_tab_text_direction); ClassDB::bind_method(D_METHOD("set_tab_language", "tab_idx", "language"), &TabBar::set_tab_language); @@ -1843,6 +1869,7 @@ void TabBar::_bind_methods() { base_property_helper.set_prefix("tab_"); base_property_helper.register_property(PropertyInfo(Variant::STRING, "title"), defaults.text, &TabBar::set_tab_title, &TabBar::get_tab_title); + base_property_helper.register_property(PropertyInfo(Variant::STRING, "tooltip"), defaults.tooltip, &TabBar::set_tab_tooltip, &TabBar::get_tab_tooltip); base_property_helper.register_property(PropertyInfo(Variant::OBJECT, "icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), defaults.icon, &TabBar::set_tab_icon, &TabBar::get_tab_icon); base_property_helper.register_property(PropertyInfo(Variant::BOOL, "disabled"), defaults.disabled, &TabBar::set_tab_disabled, &TabBar::is_tab_disabled); } diff --git a/scene/gui/tab_bar.h b/scene/gui/tab_bar.h index 6c09e960f1..52f1da5ec8 100644 --- a/scene/gui/tab_bar.h +++ b/scene/gui/tab_bar.h @@ -56,6 +56,7 @@ public: private: struct Tab { String text; + String tooltip; String language; Control::TextDirection text_direction = Control::TEXT_DIRECTION_INHERITED; @@ -66,6 +67,8 @@ private: bool disabled = false; bool hidden = false; + bool truncated = false; + Variant metadata; int ofs_cache = 0; int size_cache = 0; @@ -168,6 +171,7 @@ private: protected: virtual void gui_input(const Ref<InputEvent> &p_event) override; + virtual String get_tooltip(const Point2 &p_pos) const override; bool _set(const StringName &p_name, const Variant &p_value) { return property_helper.property_set_value(p_name, p_value); } bool _get(const StringName &p_name, Variant &r_ret) const { return property_helper.property_get_value(p_name, r_ret); } @@ -192,6 +196,9 @@ public: void set_tab_title(int p_tab, const String &p_title); String get_tab_title(int p_tab) const; + void set_tab_tooltip(int p_tab, const String &p_tooltip); + String get_tab_tooltip(int p_tab) const; + void set_tab_text_direction(int p_tab, TextDirection p_text_direction); TextDirection get_tab_text_direction(int p_tab) const; diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp index 16358ac21f..05f44891f6 100644 --- a/scene/gui/tab_container.cpp +++ b/scene/gui/tab_container.cpp @@ -400,6 +400,7 @@ void TabContainer::move_tab_from_tab_container(TabContainer *p_from, int p_from_ // Get the tab properties before they get erased by the child removal. String tab_title = p_from->get_tab_title(p_from_index); + String tab_tooltip = p_from->get_tab_tooltip(p_from_index); Ref<Texture2D> tab_icon = p_from->get_tab_icon(p_from_index); Ref<Texture2D> tab_button_icon = p_from->get_tab_button_icon(p_from_index); bool tab_disabled = p_from->is_tab_disabled(p_from_index); @@ -417,6 +418,7 @@ void TabContainer::move_tab_from_tab_container(TabContainer *p_from, int p_from_ move_child(moving_tabc, get_tab_control(p_to_index)->get_index(false)); set_tab_title(p_to_index, tab_title); + set_tab_tooltip(p_to_index, tab_tooltip); set_tab_icon(p_to_index, tab_icon); set_tab_button_icon(p_to_index, tab_button_icon); set_tab_disabled(p_to_index, tab_disabled); @@ -760,16 +762,22 @@ void TabContainer::set_tab_title(int p_tab, const String &p_title) { child->set_meta("_tab_name", p_title); } - _update_margins(); - if (!get_clip_tabs()) { - update_minimum_size(); - } + _repaint(); + queue_redraw(); } String TabContainer::get_tab_title(int p_tab) const { return tab_bar->get_tab_title(p_tab); } +void TabContainer::set_tab_tooltip(int p_tab, const String &p_tooltip) { + tab_bar->set_tab_tooltip(p_tab, p_tooltip); +} + +String TabContainer::get_tab_tooltip(int p_tab) const { + return tab_bar->get_tab_tooltip(p_tab); +} + void TabContainer::set_tab_icon(int p_tab, const Ref<Texture2D> &p_icon) { if (tab_bar->get_tab_icon(p_tab) == p_icon) { return; @@ -978,6 +986,8 @@ void TabContainer::_bind_methods() { ClassDB::bind_method(D_METHOD("is_all_tabs_in_front"), &TabContainer::is_all_tabs_in_front); ClassDB::bind_method(D_METHOD("set_tab_title", "tab_idx", "title"), &TabContainer::set_tab_title); ClassDB::bind_method(D_METHOD("get_tab_title", "tab_idx"), &TabContainer::get_tab_title); + ClassDB::bind_method(D_METHOD("set_tab_tooltip", "tab_idx", "tooltip"), &TabContainer::set_tab_tooltip); + 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_disabled", "tab_idx", "disabled"), &TabContainer::set_tab_disabled); diff --git a/scene/gui/tab_container.h b/scene/gui/tab_container.h index 8645a6d14e..c11d9824e7 100644 --- a/scene/gui/tab_container.h +++ b/scene/gui/tab_container.h @@ -155,6 +155,9 @@ public: void set_tab_title(int p_tab, const String &p_title); String get_tab_title(int p_tab) const; + void set_tab_tooltip(int p_tab, const String &p_tooltip); + String get_tab_tooltip(int p_tab) const; + void set_tab_icon(int p_tab, const Ref<Texture2D> &p_icon); Ref<Texture2D> get_tab_icon(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 0c48fac29f..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; @@ -4344,7 +4345,7 @@ void Tree::_notification(int p_what) { } default: { - text_pos.x += sb->get_offset().x + (tbrect.size.width - columns[i].text_buf->get_size().x) / 2; + text_pos.x += (tbrect.size.width - columns[i].text_buf->get_size().x) / 2; break; } } @@ -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 { @@ -4726,34 +4734,37 @@ int Tree::get_column_minimum_width(int p_column) const { // Check if the visible title of the column is wider. if (show_column_titles) { - min_width = MAX(theme_cache.font->get_string_size(columns[p_column].xl_title, HORIZONTAL_ALIGNMENT_LEFT, -1, theme_cache.font_size).width + theme_cache.panel_style->get_margin(SIDE_LEFT) + theme_cache.panel_style->get_margin(SIDE_RIGHT), 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; + const float padding = theme_cache.title_button->get_margin(SIDE_LEFT) + theme_cache.title_button->get_margin(SIDE_RIGHT); + 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 (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); @@ -4967,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/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp index 98a12f1400..f70a00a9f6 100644 --- a/scene/resources/resource_format_text.cpp +++ b/scene/resources/resource_format_text.cpp @@ -40,6 +40,7 @@ // Version 2: changed names for Basis, AABB, Vectors, etc. // Version 3: new string ID for ext/subresources, breaks forward compat. // Version 4: PackedByteArray is now stored as base64 encoded. +#define FORMAT_VERSION_COMPAT 3 #define FORMAT_VERSION 4 #define BINARY_FORMAT_VERSION 4 @@ -845,7 +846,7 @@ void ResourceLoaderText::set_translation_remapped(bool p_remapped) { } ResourceLoaderText::ResourceLoaderText() : - stream(false) {} + stream(false), format_version(FORMAT_VERSION) {} void ResourceLoaderText::get_dependencies(Ref<FileAccess> p_f, List<String> *p_dependencies, bool p_add_types) { open(p_f); @@ -954,13 +955,13 @@ Error ResourceLoaderText::rename_dependencies(Ref<FileAccess> p_f, const String } if (is_scene) { - fw->store_line("[gd_scene load_steps=" + itos(resources_total) + " format=" + itos(FORMAT_VERSION) + uid_text + "]\n"); + fw->store_line("[gd_scene load_steps=" + itos(resources_total) + " format=" + itos(format_version) + uid_text + "]\n"); } else { String script_res_text; if (!script_class.is_empty()) { script_res_text = "script_class=\"" + script_class + "\" "; } - fw->store_line("[gd_resource type=\"" + res_type + "\" " + script_res_text + "load_steps=" + itos(resources_total) + " format=" + itos(FORMAT_VERSION) + uid_text + "]\n"); + fw->store_line("[gd_resource type=\"" + res_type + "\" " + script_res_text + "load_steps=" + itos(resources_total) + " format=" + itos(format_version) + uid_text + "]\n"); } } @@ -1063,13 +1064,15 @@ void ResourceLoaderText::open(Ref<FileAccess> p_f, bool p_skip_first_tag) { } if (tag.fields.has("format")) { - int fmt = tag.fields["format"]; - if (fmt > FORMAT_VERSION) { + format_version = tag.fields["format"]; + if (format_version > FORMAT_VERSION) { error_text = "Saved with newer format version"; _printerr(); error = ERR_FILE_UNRECOGNIZED; return; } + } else { + format_version = FORMAT_VERSION; } if (tag.name == "gd_scene") { @@ -1970,6 +1973,12 @@ void ResourceFormatSaverTextInstance::_find_resources(const Variant &p_variant, _find_resources(v); } } break; + case Variant::PACKED_BYTE_ARRAY: { + // Balance between compatibility and performance. + if (use_compat && p_variant.operator PackedByteArray().size() > 64) { + use_compat = false; + } + } break; default: { } } @@ -2005,6 +2014,7 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const Ref<Reso } // Save resources. + use_compat = true; // _find_resources() changes this. _find_resources(p_resource, true); if (packed_scene.is_valid()) { @@ -2037,7 +2047,7 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const Ref<Reso if (load_steps > 1) { title += "load_steps=" + itos(load_steps) + " "; } - title += "format=" + itos(FORMAT_VERSION) + ""; + title += "format=" + itos(use_compat ? FORMAT_VERSION_COMPAT : FORMAT_VERSION) + ""; ResourceUID::ID uid = ResourceSaver::get_resource_id_for_path(local_path, true); @@ -2223,7 +2233,7 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const Ref<Reso } String vars; - VariantWriter::write_to_string(value, vars, _write_resources, this); + VariantWriter::write_to_string(value, vars, _write_resources, this, use_compat); f->store_string(name.property_name_encode() + " = " + vars + "\n"); } } @@ -2287,14 +2297,14 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const Ref<Reso if (!instance_placeholder.is_empty()) { String vars; f->store_string(" instance_placeholder="); - VariantWriter::write_to_string(instance_placeholder, vars, _write_resources, this); + VariantWriter::write_to_string(instance_placeholder, vars, _write_resources, this, use_compat); f->store_string(vars); } if (instance.is_valid()) { String vars; f->store_string(" instance="); - VariantWriter::write_to_string(instance, vars, _write_resources, this); + VariantWriter::write_to_string(instance, vars, _write_resources, this, use_compat); f->store_string(vars); } @@ -2302,7 +2312,7 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const Ref<Reso for (int j = 0; j < state->get_node_property_count(i); j++) { String vars; - VariantWriter::write_to_string(state->get_node_property_value(i, j), vars, _write_resources, this); + VariantWriter::write_to_string(state->get_node_property_value(i, j), vars, _write_resources, this, use_compat); f->store_string(String(state->get_node_property_name(i, j)).property_name_encode() + " = " + vars + "\n"); } @@ -2336,7 +2346,7 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const Ref<Reso f->store_string(connstr); if (binds.size()) { String vars; - VariantWriter::write_to_string(binds, vars, _write_resources, this); + VariantWriter::write_to_string(binds, vars, _write_resources, this, use_compat); f->store_string(" binds= " + vars); } @@ -2368,14 +2378,14 @@ Error ResourceLoaderText::set_uid(Ref<FileAccess> p_f, ResourceUID::ID p_uid) { fw = FileAccess::open(local_path + ".uidren", FileAccess::WRITE); if (is_scene) { - fw->store_string("[gd_scene load_steps=" + itos(resources_total) + " format=" + itos(FORMAT_VERSION) + " uid=\"" + ResourceUID::get_singleton()->id_to_text(p_uid) + "\"]"); + fw->store_string("[gd_scene load_steps=" + itos(resources_total) + " format=" + itos(format_version) + " uid=\"" + ResourceUID::get_singleton()->id_to_text(p_uid) + "\"]"); } else { String script_res_text; if (!script_class.is_empty()) { script_res_text = "script_class=\"" + script_class + "\" "; } - fw->store_string("[gd_resource type=\"" + res_type + "\" " + script_res_text + "load_steps=" + itos(resources_total) + " format=" + itos(FORMAT_VERSION) + " uid=\"" + ResourceUID::get_singleton()->id_to_text(p_uid) + "\"]"); + fw->store_string("[gd_resource type=\"" + res_type + "\" " + script_res_text + "load_steps=" + itos(resources_total) + " format=" + itos(format_version) + " uid=\"" + ResourceUID::get_singleton()->id_to_text(p_uid) + "\"]"); } uint8_t c = f->get_8(); diff --git a/scene/resources/resource_format_text.h b/scene/resources/resource_format_text.h index c05b7a24e1..41363fd975 100644 --- a/scene/resources/resource_format_text.h +++ b/scene/resources/resource_format_text.h @@ -54,6 +54,7 @@ class ResourceLoaderText { }; bool is_scene = false; + int format_version; String res_type; bool ignore_resource_parsing = false; @@ -178,6 +179,7 @@ class ResourceFormatSaverTextInstance { List<Ref<Resource>> saved_resources; HashMap<Ref<Resource>, String> external_resources; HashMap<Ref<Resource>, String> internal_resources; + bool use_compat = true; struct ResourceSort { Ref<Resource> resource; diff --git a/servers/rendering/renderer_canvas_cull.cpp b/servers/rendering/renderer_canvas_cull.cpp index 46c84fd230..34f9069649 100644 --- a/servers/rendering/renderer_canvas_cull.cpp +++ b/servers/rendering/renderer_canvas_cull.cpp @@ -1970,6 +1970,8 @@ void RendererCanvasCull::canvas_light_occluder_set_polygon(RID p_occluder, RID p void RendererCanvasCull::canvas_light_occluder_set_as_sdf_collision(RID p_occluder, bool p_enable) { RendererCanvasRender::LightOccluderInstance *occluder = canvas_light_occluder_owner.get_or_null(p_occluder); ERR_FAIL_NULL(occluder); + + occluder->sdf_collision = p_enable; } void RendererCanvasCull::canvas_light_occluder_set_transform(RID p_occluder, const Transform2D &p_xform) { diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp index 6f56711151..fa8cf9c028 100644 --- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp @@ -1931,7 +1931,7 @@ void RendererCanvasRenderRD::render_sdf(RID p_render_target, LightOccluderInstan while (instance) { OccluderPolygon *co = occluder_polygon_owner.get_or_null(instance->occluder); - if (!co || co->sdf_index_array.is_null()) { + if (!co || co->sdf_index_array.is_null() || !instance->sdf_collision) { instance = instance->next; continue; } diff --git a/servers/rendering/renderer_rd/shaders/effects/copy_to_fb.glsl b/servers/rendering/renderer_rd/shaders/effects/copy_to_fb.glsl index 3f9d1cce79..26ee06aa03 100644 --- a/servers/rendering/renderer_rd/shaders/effects/copy_to_fb.glsl +++ b/servers/rendering/renderer_rd/shaders/effects/copy_to_fb.glsl @@ -57,7 +57,7 @@ void main() { } if (bool(params.flags & FLAG_USE_SRC_SECTION)) { - uv_interp = params.section.xy + uv_interp * params.section.zw; + uv_interp.xy = params.section.xy + uv_interp.xy * params.section.zw; } } diff --git a/servers/rendering/rendering_device.cpp b/servers/rendering/rendering_device.cpp index 2b6644e893..962531c866 100644 --- a/servers/rendering/rendering_device.cpp +++ b/servers/rendering/rendering_device.cpp @@ -1350,6 +1350,9 @@ Vector<uint8_t> RenderingDevice::texture_get_data(RID p_texture, uint32_t p_laye thread_local LocalVector<RDD::BufferTextureCopyRegion> command_buffer_texture_copy_regions_vector; command_buffer_texture_copy_regions_vector.clear(); + uint32_t block_w = 0, block_h = 0; + get_compressed_image_format_block_dimensions(tex->format, block_w, block_h); + uint32_t w = tex->width; uint32_t h = tex->height; uint32_t d = tex->depth; @@ -1365,8 +1368,8 @@ Vector<uint8_t> RenderingDevice::texture_get_data(RID p_texture, uint32_t p_laye copy_region.texture_region_size.z = d; command_buffer_texture_copy_regions_vector.push_back(copy_region); - w = MAX(1u, w >> 1); - h = MAX(1u, h >> 1); + w = MAX(block_w, w >> 1); + h = MAX(block_h, h >> 1); d = MAX(1u, d >> 1); } @@ -1395,8 +1398,6 @@ Vector<uint8_t> RenderingDevice::texture_get_data(RID p_texture, uint32_t p_laye for (uint32_t i = 0; i < tex->mipmaps; i++) { uint32_t width = 0, height = 0, depth = 0; uint32_t tight_mip_size = get_image_format_required_size(tex->format, w, h, d, 1, &width, &height, &depth); - uint32_t block_w = 0, block_h = 0; - get_compressed_image_format_block_dimensions(tex->format, block_w, block_h); uint32_t tight_row_pitch = tight_mip_size / ((height / block_h) * depth); // Copy row-by-row to erase padding due to alignments. @@ -1408,8 +1409,8 @@ Vector<uint8_t> RenderingDevice::texture_get_data(RID p_texture, uint32_t p_laye wp += tight_row_pitch; } - w = MAX(1u, w >> 1); - h = MAX(1u, h >> 1); + w = MAX(block_w, w >> 1); + h = MAX(block_h, h >> 1); d = MAX(1u, d >> 1); read_ptr += mip_layouts[i].size; write_ptr += tight_mip_size; @@ -6400,11 +6401,11 @@ Vector<int64_t> RenderingDevice::_draw_list_switch_to_next_pass_split(uint32_t p #endif void RenderingDevice::_draw_list_set_push_constant(DrawListID p_list, const Vector<uint8_t> &p_data, uint32_t p_data_size) { - ERR_FAIL_COND((uint32_t)p_data.size() > p_data_size); + ERR_FAIL_COND(p_data_size > (uint32_t)p_data.size()); draw_list_set_push_constant(p_list, p_data.ptr(), p_data_size); } void RenderingDevice::_compute_list_set_push_constant(ComputeListID p_list, const Vector<uint8_t> &p_data, uint32_t p_data_size) { - ERR_FAIL_COND((uint32_t)p_data.size() > p_data_size); + ERR_FAIL_COND(p_data_size > (uint32_t)p_data.size()); compute_list_set_push_constant(p_list, p_data.ptr(), p_data_size); } diff --git a/tests/core/io/test_resource.h b/tests/core/io/test_resource.h index 9ddb51220b..a83e7f88ba 100644 --- a/tests/core/io/test_resource.h +++ b/tests/core/io/test_resource.h @@ -38,6 +38,8 @@ #include "thirdparty/doctest/doctest.h" +#include "tests/test_macros.h" + namespace TestResource { TEST_CASE("[Resource] Duplication") { @@ -124,9 +126,12 @@ TEST_CASE("[Resource] Breaking circular references on save") { const String save_path_binary = OS::get_singleton()->get_cache_path().path_join("resource.res"); const String save_path_text = OS::get_singleton()->get_cache_path().path_join("resource.tres"); ResourceSaver::save(resource_a, save_path_binary); + // Suppress expected errors caused by the resources above being uncached. + ERR_PRINT_OFF; ResourceSaver::save(resource_a, save_path_text); const Ref<Resource> &loaded_resource_a_binary = ResourceLoader::load(save_path_binary); + ERR_PRINT_ON; CHECK_MESSAGE( loaded_resource_a_binary->get_name() == "A", "The loaded resource name should be equal to the expected value."); 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_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); |